diff --git a/.ci/autopep8.sh b/.ci/autopep8.sh new file mode 100755 index 000000000000..6a2b8cf328aa --- /dev/null +++ b/.ci/autopep8.sh @@ -0,0 +1,2 @@ +exclude=$(sed -e 's|^|./|' -e 's|/$||' .ci/flake8_blacklist.txt | paste -s -d ',' - ) +autopep8 -i -r --exclude $exclude --select E11,E101,E127,E201,E202,E22,E301,E302,E303,E304,E306,E711,W291,W292,W293,W391 ./lib/ ./test/ diff --git a/.ci/flake8_lint_include_list.txt b/.ci/flake8_lint_include_list.txt index 05a81fcdd661..4789b884112c 100644 --- a/.ci/flake8_lint_include_list.txt +++ b/.ci/flake8_lint_include_list.txt @@ -79,9 +79,7 @@ lib/galaxy/visualization/__init__.py lib/galaxy/visualization/plugins/__init__.py lib/galaxy/visualization/plugins/utils.py lib/galaxy/visualization/tracks/ -lib/galaxy/web/base/controllers/__init__.py -lib/galaxy/web/base/__init__.py -lib/galaxy/web/base/interactive_environments.py +lib/galaxy/web/base/ lib/galaxy/web/buildapp.py lib/galaxy/web/formatting.py lib/galaxy/web/framework/base.py @@ -96,6 +94,7 @@ lib/galaxy/web/framework/middleware/static.py lib/galaxy/web/framework/middleware/statsd.py lib/galaxy/web/framework/middleware/translogger.py lib/galaxy/web/framework/middleware/xforwardedhost.py +lib/galaxy/web/framework/webapp.py lib/galaxy/web/__init__.py lib/galaxy/web/params.py lib/galaxy/webapps/galaxy/api/genomes.py @@ -108,6 +107,7 @@ lib/galaxy/webapps/galaxy/api/roles.py lib/galaxy/webapps/galaxy/api/samples.py lib/galaxy/webapps/galaxy/api/tools.py lib/galaxy/webapps/galaxy/api/tours.py +lib/galaxy/webapps/galaxy/api/users.py lib/galaxy/webapps/galaxy/api/workflows.py lib/galaxy/webapps/galaxy/config_watchers.py lib/galaxy/webapps/galaxy/controllers/async.py @@ -190,6 +190,7 @@ scripts/build_universe_config.py scripts/check_galaxy.py scripts/check_python.py scripts/cleanup_datasets/ +scripts/communication/ scripts/create_db.py scripts/data_libraries/ scripts/db_shell.py @@ -216,6 +217,7 @@ scripts/migrate_tools/ scripts/nosetests.py scripts/others/ scripts/runtime_stats.py +scripts/secret_decoder_ring.py scripts/set_dataset_sizes.py scripts/set_user_disk_usage.py scripts/sync_reports_config.py diff --git a/.ci/py3_sources.txt b/.ci/py3_sources.txt index 676a2225f6ca..1dd598e6bbac 100644 --- a/.ci/py3_sources.txt +++ b/.ci/py3_sources.txt @@ -27,6 +27,7 @@ lib/galaxy/tools/ lib/galaxy/tours/ lib/galaxy/util/ lib/galaxy/visualization/ +lib/galaxy/web/base/ lib/galaxy/web/buildapp.py lib/galaxy/web/framework/base.py lib/galaxy/web/framework/decorators.py @@ -34,9 +35,11 @@ lib/galaxy/web/framework/__init__.py lib/galaxy/web/framework/middleware/error.py lib/galaxy/web/framework/middleware/static.py lib/galaxy/web/framework/middleware/statsd.py +lib/galaxy/web/framework/webapp.py lib/galaxy/web/__init__.py lib/galaxy/webapps/galaxy/api/histories.py lib/galaxy/webapps/galaxy/api/tours.py +lib/galaxy/webapps/galaxy/api/users.py lib/galaxy/webapps/galaxy/api/workflows.py lib/galaxy/webapps/galaxy/controllers/external_services.py lib/galaxy/webapps/galaxy/controllers/search.py @@ -63,9 +66,11 @@ scripts/check_eggs.py scripts/check_galaxy.py scripts/check_python.py scripts/cleanup_datasets/ +scripts/communication/ scripts/data_libraries/build_whoosh_index.py scripts/db_shell.py scripts/drmaa_external_runner.py +scripts/secret_decoder_ring.py test/ tool_list.py tools/ diff --git a/Makefile b/Makefile index 87d09af22f6e..771c933e6b5b 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,7 @@ MY_UPSTREAM:=origin VENV?=.venv # Source virtualenv to execute command (flake8, sphinx, twine, etc...) IN_VENV=if [ -f $(VENV)/bin/activate ]; then . $(VENV)/bin/activate; fi; +CONFIG_MANAGE=$(IN_VENV) python lib/galaxy/webapps/config_manage.py PROJECT_URL?=https://github.com/galaxyproject/galaxy GRUNT_DOCKER_NAME:=galaxy/client-builder:16.01 GRUNT_EXEC?=node_modules/grunt-cli/bin/grunt @@ -52,6 +53,42 @@ open-project: ## open project on github lint: ## check style using tox and flake8 for Python 2 and Python 3 $(IN_VENV) tox -e py27-lint && tox -e py34-lint +uwsgi-rebuild-validation: ## rebuild uwsgi_config.yml kwalify schema against latest uwsgi master. + $(CONFIG_MANAGE) build_uwsgi_yaml + +tool-shed-config-validate: ## validate tool shed YAML configuration file + $(CONFIG_MANAGE) validate tool_shed + +tool-shed-config-lint: ## lint tool shed YAML configuration file + $(CONFIG_MANAGE) lint tool_shed + +tool-shed-config-convert-dry-run: ## convert old style tool shed ini to yaml (dry run) + $(CONFIG_MANAGE) convert tool_shed --dry-run + +tool-shed-config-convert: ## convert old style tool shed ini to yaml + $(CONFIG_MANAGE) convert tool_shed + +tool-shed-config-rebuild-sample: ## Rebuild sample tool shed yaml file from schema + $(CONFIG_MANAGE) build_sample_yaml tool_shed --add-comments + +reports-config-validate: ## validate reports YAML configuration file + $(CONFIG_MANAGE) validate reports + +reports-config-convert-dry-run: ## convert old style reports ini to yaml (dry run) + $(CONFIG_MANAGE) convert reports --dry-run + +reports-config-convert: ## convert old style reports ini to yaml + $(CONFIG_MANAGE) convert reports + +reports-config-rebuild-sample: ## Rebuild sample reports yaml file from schema + $(CONFIG_MANAGE) build_sample_yaml reports --add-comments + +reports-config-lint: ## lint reports YAML configuration file + $(CONFIG_MANAGE) lint reports + +reports-config-rebuild-rst: ## Rebuild sample reports RST docs + $(CONFIG_MANAGE) build_rst reports > doc/source/admin/reports_options.rst + release-ensure-upstream: ## Ensure upstream branch for release commands setup ifeq (shell git remote -v | grep $(RELEASE_UPSTREAM), ) git remote add $(RELEASE_UPSTREAM) git@github.com:galaxyproject/galaxy.git diff --git a/client/galaxy/scripts/apps/admin.js b/client/galaxy/scripts/apps/admin.js index 876b69331b1d..f178aaf4b222 100644 --- a/client/galaxy/scripts/apps/admin.js +++ b/client/galaxy/scripts/apps/admin.js @@ -2,16 +2,121 @@ var jQuery = require( 'jquery' ), $ = jQuery, GalaxyApp = require( 'galaxy' ).GalaxyApp, AdminPanel = require( './panels/admin-panel' ), + FormWrapper = require( 'mvc/form/form-wrapper' ), + GridView = require( 'mvc/grid/grid-view' ), + Ui = require( 'mvc/ui/ui-misc' ), + QueryStringParsing = require( 'utils/query-string-parsing' ), + Router = require( 'layout/router' ), Page = require( 'layout/page' ); window.app = function app( options, bootstrapped ){ window.Galaxy = new GalaxyApp( options, bootstrapped ); Galaxy.debug( 'admin app' ); - Galaxy.params = Galaxy.config.params; - $(function(){ + + /** Routes */ + var AdminRouter = Router.extend({ + routes: { + '(/)admin(/)users' : 'show_users', + '(/)admin(/)roles' : 'show_roles', + '(/)admin(/)groups' : 'show_groups', + '(/)admin(/)tool_versions' : 'show_tool_versions', + '(/)admin(/)quotas' : 'show_quotas', + '(/)admin(/)forms(/)(:form_id)' : 'show_forms' + }, + + authenticate: function( args, name ) { + return Galaxy.user && Galaxy.user.id && Galaxy.user.get( 'is_admin' ); + }, + + show_users: function() { + this.page.display( new GridView( { url_base: Galaxy.root + 'admin/users_list', url_data: Galaxy.params, dict_format: true } ) ); + }, + + show_roles: function() { + this.page.display( new GridView( { url_base: Galaxy.root + 'admin/roles_list', url_data: Galaxy.params, dict_format: true } ) ); + }, + + show_groups: function() { + this.page.display( new GridView( { url_base: Galaxy.root + 'admin/groups_list', url_data: Galaxy.params, dict_format: true } ) ); + }, + + show_tool_versions: function() { + this.page.display( new GridView( { url_base: Galaxy.root + 'admin/tool_versions_list', url_data: Galaxy.params, dict_format: true } ) ); + }, + + show_quotas: function() { + this.page.display( new GridView( { url_base: Galaxy.root + 'admin/quotas_list', url_data: Galaxy.params, dict_format: true } ) ); + }, + + show_forms : function( form_id ) { + var id = '?id=' + QueryStringParsing.get( 'id' ); + var form_defs = { + reset_user_password: { + title : 'Reset passwords', + url : 'admin/reset_user_password' + id, + icon : 'fa-user', + submit_title : 'Save new password', + redirect : 'admin/users' + }, + manage_roles_and_groups_for_user: { + url : 'admin/manage_roles_and_groups_for_user' + id, + icon : 'fa-users', + redirect : 'admin/users' + }, + manage_users_and_groups_for_role: { + url : 'admin/manage_users_and_groups_for_role' + id, + redirect : 'admin/roles' + }, + manage_users_and_roles_for_group: { + url : 'admin/manage_users_and_roles_for_group' + id, + redirect : 'admin/groups' + }, + manage_users_and_groups_for_quota: { + url : 'admin/manage_users_and_groups_for_quota' + id, + redirect : 'admin/quotas' + }, + create_role: { + url : 'admin/create_role', + redirect : 'admin/roles' + }, + create_group: { + url : 'admin/create_group', + redirect : 'admin/groups' + }, + create_quota: { + url : 'admin/create_quota', + redirect : 'admin/quotas' + }, + rename_role: { + url : 'admin/rename_role' + id, + redirect : 'admin/roles' + }, + rename_group: { + url : 'admin/rename_group' + id, + redirect : 'admin/groups' + }, + rename_quota: { + url : 'admin/rename_quota' + id, + redirect : 'admin/quotas' + }, + edit_quota: { + url : 'admin/edit_quota' + id, + redirect : 'admin/quotas' + }, + set_quota_default: { + url : 'admin/set_quota_default' + id, + redirect : 'admin/quotas' + }, + }; + this.page.display( new FormWrapper.View ( form_defs[ form_id ] ) ); + } + }); + + $(function() { _.extend( options.config, { active_view : 'admin' } ); Galaxy.page = new Page.View( _.extend( options, { - Left : AdminPanel + Left : AdminPanel, + Router : AdminRouter } ) ); }); }; \ No newline at end of file diff --git a/client/galaxy/scripts/apps/analysis.js b/client/galaxy/scripts/apps/analysis.js index 2cf98b1dba24..833e85a765c1 100644 --- a/client/galaxy/scripts/apps/analysis.js +++ b/client/galaxy/scripts/apps/analysis.js @@ -1,11 +1,12 @@ var jQuery = require( 'jquery' ), $ = jQuery, GalaxyApp = require( 'galaxy' ).GalaxyApp, - QUERY_STRING = require( 'utils/query-string-parsing' ), + Router = require( 'layout/router' ), ToolPanel = require( './panels/tool-panel' ), HistoryPanel = require( './panels/history-panel' ), Page = require( 'layout/page' ), ToolForm = require( 'mvc/tool/tool-form' ), + FormWrapper = require( 'mvc/form/form-wrapper' ), UserPreferences = require( 'mvc/user/user-preferences' ), CustomBuilds = require( 'mvc/user/user-custom-builds' ), Tours = require( 'mvc/tours' ), @@ -13,10 +14,11 @@ var jQuery = require( 'jquery' ), GridShared = require( 'mvc/grid/grid-shared' ), Workflows = require( 'mvc/workflow/workflow' ), HistoryList = require( 'mvc/history/history-list' ), - WorkflowsConfigureMenu = require( 'mvc/workflow/workflow-configure-menu' ), ToolFormComposite = require( 'mvc/tool/tool-form-composite' ), Utils = require( 'utils/utils' ), - Ui = require( 'mvc/ui/ui-misc' ); + Ui = require( 'mvc/ui/ui-misc' ), + DatasetError = require( 'mvc/dataset/dataset-error' ), + DatasetEditAttributes = require('mvc/dataset/dataset-edit-attributes'); /** define the 'Analyze Data'/analysis/main/home page for Galaxy * * has a masthead @@ -32,53 +34,9 @@ var jQuery = require( 'jquery' ), window.app = function app( options, bootstrapped ){ window.Galaxy = new GalaxyApp( options, bootstrapped ); Galaxy.debug( 'analysis app' ); - Galaxy.params = Galaxy.config.params; - - var routingMessage = Backbone.View.extend({ - initialize: function(options) { - this.message = options.message || "Undefined Message"; - this.msg_status = options.type || 'info'; - this.render(); - }, - render: function(){ - this.$el.html(_.escape(this.message)).addClass(this.msg_status + "message"); - } - }); /** Routes */ - var Router = Backbone.Router.extend({ - // TODO: not many client routes at this point - fill and remove from server. - // since we're at root here, this may be the last to be routed entirely on the client. - initialize : function( page, options ){ - this.page = page; - this.options = options; - }, - - /** helper to push a new navigation state */ - push: function( url, data ) { - data = data || {}; - data.__identifer = Math.random().toString( 36 ).substr( 2 ); - if ( !$.isEmptyObject( data ) ) { - url += url.indexOf( '?' ) == -1 ? '?' : '&'; - url += $.param( data , true ); - } - this.navigate( url, { 'trigger': true } ); - }, - - /** override to parse query string into obj and send to each route */ - execute: function( callback, args, name ){ - Galaxy.debug( 'router execute:', callback, args, name ); - var queryObj = QUERY_STRING.parse( args.pop() ); - args.push( queryObj ); - if( callback ){ - if ( this.authenticate( args, name ) ) { - callback.apply( this, args ); - } else { - this.loginRequired(); - } - } - }, - + var AnalysisRouter = Router.extend({ routes : { '(/)' : 'home', '(/)root*' : 'home', @@ -92,22 +50,18 @@ window.app = function app( options, bootstrapped ){ '(/)workflows/list_published(/)' : 'show_workflows_published', '(/)histories(/)(:action_id)' : 'show_histories', '(/)datasets(/)list(/)' : 'show_datasets', - '(/)workflow/configure_menu(/)' : 'show_configure_menu', '(/)workflow/import_workflow' : 'show_import_workflow', - '(/)custom_builds' : 'show_custom_builds' + '(/)custom_builds' : 'show_custom_builds', + '(/)datasets/edit': 'show_dataset_edit_attributes', + '(/)datasets/error': 'show_dataset_error' }, require_login: [ 'show_user', 'show_user_form', - 'show_workflows', - 'show_configure_menu' + 'show_workflows' ], - loginRequired: function() { - this.page.display( new routingMessage({type: 'error', message: "You must be logged in to make this request."}) ); - }, - authenticate: function( args, name ) { return ( Galaxy.user && Galaxy.user.id ) || this.require_login.indexOf( name ) == -1; }, @@ -125,7 +79,8 @@ window.app = function app( options, bootstrapped ){ }, show_user_form : function( form_id ) { - this.page.display( new UserPreferences.Forms( { form_id: form_id, user_id: Galaxy.params.id } ) ); + var model = new UserPreferences.Model( { user_id: Galaxy.params.id } ); + this.page.display( new FormWrapper.View ( model.get( form_id ) ) ); }, show_visualizations : function( action_id ) { @@ -160,10 +115,6 @@ window.app = function app( options, bootstrapped ){ this.page.display( new Workflows.ImportWorkflowView() ); }, - show_configure_menu : function(){ - this.page.display( new WorkflowsConfigureMenu.View() ); - }, - show_custom_builds : function() { var self = this; var historyPanel = this.page.historyPanel.historyView; @@ -174,6 +125,14 @@ window.app = function app( options, bootstrapped ){ this.page.display( new CustomBuilds.View() ); }, + show_dataset_edit_attributes : function() { + this.page.display( new DatasetEditAttributes.View() ); + }, + + show_dataset_error : function() { + this.page.display( new DatasetError.View() ); + }, + /** */ home : function( params ){ // TODO: to router, remove Globals @@ -233,17 +192,10 @@ window.app = function app( options, bootstrapped ){ // render and start the router $(function(){ - Galaxy.page = new Page.View( _.extend( options, { Left : ToolPanel, Right : HistoryPanel, - Router : Router + Router : AnalysisRouter } ) ); - - // start the router - which will call any of the routes above - Backbone.history.start({ - root : Galaxy.root, - pushState : true, - }); }); }; diff --git a/client/galaxy/scripts/apps/panels/admin-panel.js b/client/galaxy/scripts/apps/panels/admin-panel.js index 0e4af8a140ae..b28a388a9ad1 100644 --- a/client/galaxy/scripts/apps/panels/admin-panel.js +++ b/client/galaxy/scripts/apps/panels/admin-panel.js @@ -54,7 +54,8 @@ var AdminPanel = Backbone.View.extend({ url : 'admin/package_tool' },{ title : 'Tool lineage', - url : 'admin/tool_versions' + url : 'admin/tool_versions', + target : '__use_router__' },{ title : 'Reload a tool\'s configuration', url : 'admin/reload_tool' @@ -75,13 +76,16 @@ var AdminPanel = Backbone.View.extend({ title : 'User Management', items : [ { title : 'Users', - url : 'admin/users' + url : 'admin/users', + target : '__use_router__' },{ title : 'Groups', - url : 'admin/groups' + url : 'admin/groups', + target : '__use_router__' },{ title : 'Roles', - url : 'admin/roles' + url : 'admin/roles', + target : '__use_router__' },{ title : 'API keys', url : 'userskeys/all_users' @@ -95,13 +99,15 @@ var AdminPanel = Backbone.View.extend({ items : [ { title : 'Quotas', url : 'admin/quotas', + target : '__use_router__', enabled : self.config.enable_quotas },{ title : 'Data libraries', url : 'library_admin/browse_libraries' },{ title : 'Roles', - url : 'admin/roles' + url : 'admin/roles', + target : '__use_router__' },{ title : 'Local data', url : 'data_manager' @@ -139,10 +145,13 @@ var AdminPanel = Backbone.View.extend({ var $entries = $section.find( '.ui-side-section-body' ); _.each( category.get( 'items' ), function( item ) { if ( item.enabled === undefined || item.enabled ) { - $entries.append( $( '
' ).addClass( 'ui-side-section-body-title' ) - .append( $( '' ).attr({ - href : self.root + item.url, - target : 'galaxy_main' }).text( _l( item.title ) ) ) ); + var $link = $( '' ).attr( { href : self.root + item.url } ).text( _l( item.title ) ); + if ( item.target == '__use_router__' ) { + $link.on( 'click', function( e ) { e.preventDefault(); self.page.router.push( item.url ); } ); + } else { + $link.attr( 'target', 'galaxy_main' ); + } + $entries.append( $( '
' ).addClass( 'ui-side-section-body-title' ).append( $link ) ); } }); self.$el.append( $section ); diff --git a/client/galaxy/scripts/galaxy.js b/client/galaxy/scripts/galaxy.js index 9184f707e742..27e3d636d1b5 100644 --- a/client/galaxy/scripts/galaxy.js +++ b/client/galaxy/scripts/galaxy.js @@ -46,8 +46,11 @@ GalaxyApp.prototype._init = function __init( options, bootstrapped ){ } self._processOptions( options ); - // special case for root + + // add root and url parameters self.root = options.root || '/'; + self.params = options.params || {}; + self.session_csrf_token = options.session_csrf_token || null; self._initConfig( options.config || {} ); self._patchGalaxy( window.Galaxy ); @@ -76,9 +79,10 @@ GalaxyApp.prototype._init = function __init( options, bootstrapped ){ /** default options */ GalaxyApp.prototype.defaultOptions = { /** monkey patch attributes from existing window.Galaxy object? */ - patchExisting : true, + patchExisting : true, /** root url of this app */ - root : '/' + root : '/', + session_csrf_token : null, }; /** filter to options present in defaultOptions (and default to them) */ diff --git a/client/galaxy/scripts/galaxy.pages.js b/client/galaxy/scripts/galaxy.pages.js index 2f3203f0d79b..830a30e8e756 100644 --- a/client/galaxy/scripts/galaxy.pages.js +++ b/client/galaxy/scripts/galaxy.pages.js @@ -99,7 +99,7 @@ function get_item_info( dialog_type ) // Make an item importable. function make_item_importable( item_controller, item_id, item_type ) { - ajax_url = set_accessible_url.replace( "ITEM_CONTROLLER", item_controller ); + var ajax_url = set_accessible_url.replace( "ITEM_CONTROLLER", item_controller ); $.ajax({ type: "POST", url: ajax_url, @@ -273,9 +273,9 @@ WYMeditor.editor.prototype.dialog = function( dialogType, dialogFeatures, bodyHt newCaption.innerHTML = sCaption; //we create the rows and cells - for(x=0; x' ); + this.model = new Backbone.Model( { 'dataset_id': Galaxy.params.dataset_id } ); + this.render(); + }, + + // Fetch data for the selected dataset and + // build tabs for editing its attributes + render: function() { + var url = Galaxy.root + 'dataset/edit', + self = this; + Utils.get({ + url : url, + data : { 'dataset_id' : self.model.get( 'dataset_id' ) }, + success : function( response ) { + self.render_attribute_page( self, response ); + }, + error : function( response ) { + var error_response = { + 'status': 'error', + 'message': 'Error occured while loading the dataset.', + 'persistent': true, + 'cls': 'errormessage' + }; + self.display_message( error_response, self.$( '.response-message' ) ); + } + }); + }, + + /** Render all the tabs view */ + render_attribute_page: function( self, response ) { + var message = { + 'message' : response.message, + 'status' : response.status, + 'persistent' : true, + 'cls' : response.status + 'message' + }; + self.$el.empty().append( self._templateHeader() ); + self.display_message( message, self.$( '.response-message' ) ); + // Create all tabs + self.create_tabs( response, self.$( '.edit-attr' ) ); + }, + + /** Perform AJAX post call */ + call_ajax: function( self, data, tab_name ) { + var post_url = Galaxy.root + 'dataset/edit'; + $.ajax({ + type: "PUT", + url: post_url, + data: data, + success: function( response ) { + self.render_attribute_page( self, response ); + self.reload_history(); + }, + error : function( response ) { + var error_response = { + 'status': 'error', + 'message': 'Error occured while saving. Please fill all the required fields and try again.', + 'persistent': true, + 'cls': 'errormessage' + }; + self.display_message( error_response, self.$( '.response-message' ) ); + } + }); + }, + + /** Display actions messages */ + display_message: function( response, $el ) { + $el.empty().html( new Ui.Message( response ).$el ); + }, + + /** Create tabs for different attributes of dataset*/ + create_tabs: function( response, $el_edit_attr ) { + var self = this; + self.tabs = new Tabs.View(); + self.tabs.add({ + id : 'attributes', + title : 'Attributes', + icon : 'fa fa-bars', + tooltip : 'Edit dataset attributes', + $el : self._getAttributesFormTemplate( response ) + }); + + self.tabs.add({ + id : 'convert', + title : 'Convert', + icon : 'fa-gear', + tooltip : 'Convert to new format', + $el : self._getConvertFormTemplate( response ) + }); + + self.tabs.add({ + id : 'datatype', + title : 'Datatypes', + icon : 'fa-database', + tooltip : 'Change data type', + $el : self._getChangeDataTypeFormTemplate( response ) + }); + + self.tabs.add({ + id : 'permissions', + title : 'Permissions', + icon : 'fa-user', + tooltip : 'Permissions', + $el : self._getPermissionsFormTemplate( response ) + }); + $el_edit_attr.append( self.tabs.$el ); + self.tabs.showTab( 'attributes' ); + }, + + /** Main template */ + _templateHeader: function() { + return '
' + + '
' + + '

Edit Dataset Attributes

' + + '
'; + }, + + /** Attributes tab template */ + _getAttributesFormTemplate: function( response ) { + var self = this; + var form = new Form({ + title : 'Edit attributes', + inputs : response.edit_attributes_inputs, + operations: { + 'submit_editattr' : new Ui.ButtonIcon({ + tooltip : 'Save attributes of the dataset.', + icon : 'fa-floppy-o ', + title : 'Save attributes', + onclick : function() { self._submit( self, form, response, "edit_attributes" ) } + }), + 'submit_autocorrect' : new Ui.ButtonIcon({ + tooltip : 'This will inspect the dataset and attempt to correct the values of fields if they are not accurate.', + icon : 'fa-undo ', + title : 'Auto-detect', + onclick : function() { self._submit( self, form, response, "auto-detect" ) } + }) + } + }); + return form.$el; + }, + + /** Convert tab template */ + _getConvertFormTemplate: function( response ) { + var self = this; + var form = new Form({ + title : 'Convert to new format', + inputs : response.convert_inputs, + operations: { + 'submit' : new Ui.ButtonIcon({ + tooltip : 'Convert the datatype to a new format.', + title : 'Convert datatype', + icon : 'fa-exchange ', + onclick : function() { self._submit( self, form, response, "convert" ) } + }) + } + }); + return form.$el; + }, + + /** Change datatype template */ + _getChangeDataTypeFormTemplate: function( response ) { + var self = this; + var form = new Form({ + title : 'Change datatype', + inputs : response.convert_datatype_inputs, + operations: { + 'submit' : new Ui.ButtonIcon({ + tooltip : 'Change the datatype to a new type.', + title : 'Change datatype', + icon : 'fa-exchange ', + onclick : function() { self._submit( self, form, response, "change" ) } + }) + } + }); + return form.$el; + }, + + /** Permissions template */ + _getPermissionsFormTemplate: function( response ) { + var template = "", + self = this; + if( response.can_manage_dataset ) { + var form = new Form({ + title : 'Manage dataset permissions on ' + response.display_name, + inputs : response.permission_inputs, + operations: { + 'submit': new Ui.ButtonIcon({ + tooltip : 'Save permissions.', + title : 'Save permissions', + icon : 'fa-floppy-o ', + onclick : function() { self._submit( self, form, response, "permissions" ) } + }) + } + }); + return form.$el; + } + else { + var form = new Form({ + title : 'View permissions', + inputs : response.permission_inputs + }); + return form.$el; + } + }, + + /** Submit action */ + _submit: function( self, form, response, type ) { + var form_data = form.data.create(); + form_data.dataset_id = response.dataset_id; + switch( type ) { + case "edit_attributes": + form_data.save = 'Save'; + break; + + case "auto-detect": + form_data.detect = 'Auto-detect'; + break; + + case "convert": + if ( form_data.target_type !== null && form_data.target_type ) { + form_data.dataset_id = response.dataset_id; + form_data.convert_data = 'Convert'; + } + break; + + case "change": + form_data.change = 'Save'; + break; + + case "permissions": + var post_data = {}; + post_data.permissions = JSON.stringify( form_data ); + post_data.update_roles_button = "Save"; + post_data.dataset_id = response.dataset_id; + form_data = post_data; + break; + } + self.call_ajax( self, form_data ); + }, + + /** Reload Galaxy's history after updating dataset's attributes */ + reload_history: function() { + if ( window.Galaxy ) { + window.Galaxy.currHistoryPanel.loadCurrentHistory(); + } + } + }); + + return { + View : View + }; +}); diff --git a/client/galaxy/scripts/mvc/dataset/dataset-error.js b/client/galaxy/scripts/mvc/dataset/dataset-error.js new file mode 100644 index 000000000000..e6394023c396 --- /dev/null +++ b/client/galaxy/scripts/mvc/dataset/dataset-error.js @@ -0,0 +1,176 @@ +define( [ 'utils/utils', 'mvc/ui/ui-misc', 'mvc/form/form-view' ], function( Utils, Ui, Form ) { + + /** Dataset edit attributes view */ + var View = Backbone.View.extend({ + initialize: function() { + this.setElement( '
' ); + this.model = new Backbone.Model( { 'dataset_id': Galaxy.params.dataset_id } ); + this.render(); + }, + + // Fetch data for the selected dataset and + render: function() { + var data_url = Galaxy.root + 'api/datasets/' + this.model.get( 'dataset_id' ), + self = this; + + Utils.get({ + url : data_url, + success : function( dataset ) { + var job_url = Galaxy.root + 'api/jobs/' + dataset.creating_job + '?full=True'; + Utils.get({ + url : job_url, + success : function( job ) { + var job_url = Galaxy.root + 'api/jobs/' + dataset.creating_job + '?full=True'; + self.render_error_page( self, dataset, job ); + }, + error : function( response ) { + var error_response = { + 'status': 'error', + 'message': 'Error occured while loading the job.', + 'persistent': true, + 'cls': 'errormessage' + }; + self.display_message( error_response, self.$( '.response-message' ) ); + } + }); + }, + error : function( response ) { + var error_response = { + 'status': 'error', + 'message': 'Error occured while loading the dataset.', + 'persistent': true, + 'cls': 'errormessage' + }; + self.display_message( error_response, self.$( '.response-message' ) ); + } + }); + }, + + /** Render the view */ + render_error_page: function( self, dataset, job ) { + self.$el.empty().append( self._templateHeader() ); + self.$el.append('

Dataset Error

'); + self.$el.append('

An error occured while running the tool ' + job.tool_id + '.

'); + self.$el.append('

Tool execution generated the following messages:

'); + self.$el.append('
' + job.stderr + '
'); + self.$el.append('

Report This Error'); + self.$el.append('

Usually the local Galaxy administrators regularly review errors that occur on the server. However, if you would like to provide additional information (such as what you were trying to do when the error occurred) and a contact e-mail address, we will be better able to investigate your problem and get back to you.

'); + self.$el.append(self._getBugFormTemplate(dataset, job)); + }, + + /** Display actions messages */ + display_message: function( response, $el, doNotClear, safe ) { + if(!safe){ + if(doNotClear) { + $el.append( new Ui.Message( response ).$el ); + } else { + $el.empty().html( new Ui.Message( response ).$el ); + } + } else { + if(doNotClear) { + $el.append( new Ui.UnescapedMessage( response ).$el ); + } else { + $el.empty().html( new Ui.UnescapedMessage( response ).$el ); + } + } + }, + + /** Main template */ + _templateHeader: function() { + return '
' + + '
' + + '
'; + }, + + /** Convert tab template */ + _getBugFormTemplate: function(dataset, job) { + var self = this; + var inputs = [ + { + "help": "Your email address", + "options": [], + "type": "text", + "name": "email", + "label": "Your email", + "value": Galaxy.user.get('email') + }, + { + "help": "Any additional comments you can provide regarding what you were doing at the time of the bug.", + "options": [], + "type": "text", + "area": true, + "name": "message", + "label": "Message" + } + ]; + + // TODO + if(false && response.any_public){ + inputs.push({ + "name": "public_consent", + "label": "Public Disclosure Consent", + "help": "This Galaxy is configured to report to one or more error reporting backends that public to the world. By selecting 'yes', you acknowledge that this bug report will be made public.", + "value": String( Boolean( false ) ), + "options": [], + "type": "boolean", + }) + } + + var form = new Form({ + title : 'Error Report', + inputs : inputs, + buttons : { + save : new Ui.Button({ + icon : 'fa-bug', + title : 'Report', + cls : 'ui-button btn btn-primary', + floating : 'clear', + onclick : function() { + var form_data = form.data.create(); + var url = Galaxy.root + 'api/jobs/' + job.id + '/error' + form_data.dataset_id = dataset.id; + self.submit(form_data, url) + } + }) + }, + }); + return form.$el; + }, + + /** Make ajax request */ + submit : function(form_data, url){ + var self = this; + // Some required metadata + $.ajax({ + type: "POST", + url: url, + data: form_data, + success: function( response ) { + // Clear out the div + self.$el.empty().append( self._templateHeader() ); + // And display the messages. + response.messages.forEach(function(message){ + self.display_message( { + 'status': message[1], + 'message': message[0], + 'persistent': true, + }, self.$( '.response-message' ), true, true ); + }); + }, + error : function( response ) { + var error_response = { + 'status': 'error', + 'message': 'Error occured while saving. Please fill all the required fields and try again.', + 'persistent': true, + 'cls': 'errormessage' + }; + self.display_message( error_response, self.$( '.response-message' ) ); + } + }); + } + }); + + return { + View : View + }; +}); diff --git a/client/galaxy/scripts/mvc/dataset/dataset-li-edit.js b/client/galaxy/scripts/mvc/dataset/dataset-li-edit.js index 3838252368b8..6041d6467c97 100644 --- a/client/galaxy/scripts/mvc/dataset/dataset-li-edit.js +++ b/client/galaxy/scripts/mvc/dataset/dataset-li-edit.js @@ -60,12 +60,10 @@ var DatasetListItemEdit = _super.extend( deleted = this.model.get( 'deleted' ), editBtnData = { title : _l( 'Edit attributes' ), - href : this.model.urls.edit, - target : this.linkTarget, + href : Galaxy.root + 'datasets/edit?dataset_id=' + this.model.attributes.id, faIcon : 'fa-pencil', classes : 'edit-btn' }; - // disable if purged or deleted and explain why in the tooltip if( deleted || purged ){ editBtnData.disabled = true; @@ -199,9 +197,8 @@ var DatasetListItemEdit = _super.extend( _renderErrButton : function(){ return faIconButton({ title : _l( 'View or report this error' ), - href : this.model.urls.report_error, + href : Galaxy.root + 'datasets/error?dataset_id=' + this.model.attributes.id, classes : 'report-error-btn', - target : this.linkTarget, faIcon : 'fa-bug' }); }, diff --git a/client/galaxy/scripts/mvc/form/form-parameters.js b/client/galaxy/scripts/mvc/form/form-parameters.js index 6a1beb0b07fe..e9989d77f672 100644 --- a/client/galaxy/scripts/mvc/form/form-parameters.js +++ b/client/galaxy/scripts/mvc/form/form-parameters.js @@ -6,8 +6,9 @@ define(['utils/utils', 'mvc/ui/ui-select-content', 'mvc/ui/ui-select-library', 'mvc/ui/ui-select-ftp', + 'mvc/ui/ui-select-genomespace', 'mvc/ui/ui-color-picker'], - function( Utils, Ui, SelectContent, SelectLibrary, SelectFtp, ColorPicker ) { + function( Utils, Ui, SelectContent, SelectLibrary, SelectFtp, SelectGenomeSpace, ColorPicker ) { // create form view return Backbone.Model.extend({ @@ -30,7 +31,8 @@ define(['utils/utils', 'baseurl' : '_fieldHidden', 'library_data' : '_fieldLibrary', 'ftpfile' : '_fieldFtp', - 'upload' : '_fieldUpload' + 'upload' : '_fieldUpload', + 'genomespacefile' : '_fieldGenomeSpace' }, /** Returns an input field for a given field type */ @@ -103,6 +105,7 @@ define(['utils/utils', multiple : input_def.multiple, optional : input_def.optional, onchange : input_def.onchange, + individual : input_def.individual, searchable : input_def.flavor !== 'workflow' }); }, @@ -215,6 +218,18 @@ define(['utils/utils', }); }, + /** GenomeSpace file select field + */ + _fieldGenomeSpace: function( input_def ) { + var self = this; + return new SelectGenomeSpace.View({ + id : 'field-' + input_def.id, + onchange : function() { + self.app.trigger( 'change' ); + } + }); + }, + /** Upload file field */ _fieldUpload: function( input_def ) { return new Ui.Upload({ diff --git a/client/galaxy/scripts/mvc/form/form-repeat.js b/client/galaxy/scripts/mvc/form/form-repeat.js index 57362705ba09..1955d133c856 100644 --- a/client/galaxy/scripts/mvc/form/form-repeat.js +++ b/client/galaxy/scripts/mvc/form/form-repeat.js @@ -14,8 +14,7 @@ function( Utils, Portlet, Ui ) { icon : 'fa-plus', title : 'Insert ' + this.options.title, tooltip : 'Add new ' + this.options.title + ' block', - floating: 'clear', - cls : 'ui-button-icon form-repeat-add', + cls : 'ui-button-icon ui-clear-float form-repeat-add', onclick : function() { options.onnew && options.onnew() } }); this.setElement( $( '
' ).append( this.$list = $( '
' ) ) diff --git a/client/galaxy/scripts/mvc/form/form-view.js b/client/galaxy/scripts/mvc/form/form-view.js index 68640c98cf1e..bfbd3a6618f6 100644 --- a/client/galaxy/scripts/mvc/form/form-view.js +++ b/client/galaxy/scripts/mvc/form/form-view.js @@ -10,7 +10,8 @@ function( Utils, Portlet, Ui, FormSection, FormData ) { cls : 'ui-portlet-limited', icon : null, always_refresh : true, - message_status : 'warning' + status : 'warning', + onchange : function(){} }).set( options ); this.setElement( '
' ); this.render(); @@ -111,7 +112,7 @@ function( Utils, Portlet, Ui, FormSection, FormData ) { var new_check = self.data.checksum(); if ( new_check != current_check ) { current_check = new_check; - self.model.get( 'onchange' ) && self.model.get( 'onchange' )(); + self.model.get( 'onchange' )(); } } }); @@ -141,7 +142,7 @@ function( Utils, Portlet, Ui, FormSection, FormData ) { this.portlet.append( this.section.$el ); this.$el.empty(); options.inputs && this.$el.append( this.portlet.$el ); - options.message && this.message.update( { persistent: true, status: options.message_status, message: options.message } ); + options.message && this.message.update( { persistent: true, status: options.status, message: options.message } ); Galaxy.emit.debug( 'form-view::initialize()', 'Completed' ); } }); diff --git a/client/galaxy/scripts/mvc/form/form-wrapper.js b/client/galaxy/scripts/mvc/form/form-wrapper.js new file mode 100644 index 000000000000..0e7d614494c0 --- /dev/null +++ b/client/galaxy/scripts/mvc/form/form-wrapper.js @@ -0,0 +1,81 @@ +/** Generic form view */ +define( [ 'mvc/form/form-view', 'mvc/ui/ui-misc' ], function( Form, Ui ) { + + var View = Backbone.View.extend({ + + initialize: function( options ) { + this.model = new Backbone.Model( options ); + this.url = this.model.get( 'url' ); + this.redirect = this.model.get( 'redirect' ); + this.setElement( '
' ); + this.render(); + }, + + render: function() { + var self = this; + $.ajax({ + url : Galaxy.root + this.url, + type : 'GET' + }).done( function( response ) { + var options = $.extend( {}, self.model.attributes, response ); + var form = new Form({ + title : options.title, + message: options.message, + status : options.status || 'warning', + icon : options.icon, + inputs : options.inputs, + buttons: { + 'submit': new Ui.Button({ + tooltip : options.submit_tooltip, + title : options.submit_title || 'Save', + icon : options.submit_icon || 'fa-save', + cls : 'btn btn-primary ui-clear-float', + onclick : function() { self._submit( form ) } + }) + } + }); + self.$el.empty().append( form.$el ); + }).fail( function( response ) { + self.$el.empty().append( new Ui.Message({ + message : 'Failed to load resource ' + self.url + '.', + status : 'danger', + persistent : true + }).$el ); + }); + }, + + _submit: function( form ) { + var self = this; + $.ajax( { + url : Galaxy.root + self.url, + data : JSON.stringify( form.data.create() ), + type : 'PUT', + contentType : 'application/json' + }).done( function( response ) { + var success_message = { message: response.message, status: 'success', persistent: false }; + if ( self.redirect ) { + window.location = Galaxy.root + self.redirect + '?' + $.param( success_message ); + } else { + form.data.matchModel( response, function ( input, input_id ) { + form.field_list[ input_id ].value( input.value ); + }); + self._showMessage( form, success_message ); + } + }).fail( function( response ) { + self._showMessage( form, { message: response.responseJSON.err_msg, status: 'danger', persistent: false } ); + }); + }, + + _showMessage: function( form, options ) { + var $panel = form.$el.parents().filter(function() { + return [ 'auto', 'scroll' ].indexOf( $( this ).css( 'overflow' ) ) != -1; + }).first(); + $panel.animate( { scrollTop : 0 }, 500 ); + form.message.update( options ); + } + }); + + return { + View : View + }; +}); diff --git a/client/galaxy/scripts/mvc/grid/grid-model.js b/client/galaxy/scripts/mvc/grid/grid-model.js index a19be72c9e0e..aba37d093f7c 100644 --- a/client/galaxy/scripts/mvc/grid/grid-model.js +++ b/client/galaxy/scripts/mvc/grid/grid-model.js @@ -36,22 +36,22 @@ return Backbone.Model.extend({ new_val; if (cur_val === null || cur_val === undefined) { new_val = value; - } - else if (typeof(cur_val) == 'string') { - if (cur_val == 'All') { + } else if (typeof(cur_val) == 'string') { + if (cur_val == 'All' || cur_val == value) { new_val = value; } else { // Replace string with array. var values = []; values[0] = cur_val; values[1] = value; - new_val = values; + new_val = values; } - } - else { + } else { // Current value is an array. new_val = cur_val; - new_val.push(value); + if ( new_val.indexOf( value ) === -1 ) { + new_val.push(value); + } } this.attributes.filters[key] = new_val; } diff --git a/client/galaxy/scripts/mvc/grid/grid-template.js b/client/galaxy/scripts/mvc/grid/grid-template.js index 6a973ae291da..ced04a6673b5 100644 --- a/client/galaxy/scripts/mvc/grid/grid-template.js +++ b/client/galaxy/scripts/mvc/grid/grid-template.js @@ -60,7 +60,7 @@ return { tmpl += '
  • Actions
  • ' + '
    '; } - for (i in options.global_actions) { + for (var i in options.global_actions) { var action = options.global_actions[i]; tmpl += '
  • ' + '' + action.label + '' + @@ -158,7 +158,7 @@ return { } // Data columns - for (j in options.columns) { + for (var j in options.columns) { var column = options.columns[j]; if (column.visible) { // Nowrap @@ -242,6 +242,7 @@ return { // Set max page. var max_range = page_link_range + min_offset; var max_page = cur_page_num + max_range; + var max_offset; if (max_page <= num_pages) { // Max page is fine. max_offset = 0; @@ -307,7 +308,7 @@ return { 'For selected items: '; // configure buttons for operations - for (i in options.operations) { + for (var i in options.operations) { var operation = options.operations[i]; if (operation.allow_multiple) { tmpl += ' '; @@ -355,8 +356,9 @@ return { // template message: function(options) { + var status = options.status == 'success' ? 'done' : options.status; return '

    ' + - '

    ' + options.message + '
    ' + + '
    ' + _.escape( options.message ) + '
    ' + '
    ' + '

    '; }, @@ -553,14 +555,14 @@ return { // add category filters var seperator = false; - for (cf_label in options.categorical_filters[column_key]) { + for (var cf_label in options.categorical_filters[column_key]) { // get category filter var cf = options.categorical_filters[column_key][cf_label]; // each filter will have only a single argument, so get that single argument var cf_key = ''; var cf_arg = ''; - for (key in cf) { + for (var key in cf) { cf_key = key; cf_arg = cf[key]; } diff --git a/client/galaxy/scripts/mvc/grid/grid-view.js b/client/galaxy/scripts/mvc/grid/grid-view.js index ce335d86fbb6..b5176d4d0716 100644 --- a/client/galaxy/scripts/mvc/grid/grid-view.js +++ b/client/galaxy/scripts/mvc/grid/grid-view.js @@ -18,6 +18,7 @@ return Backbone.View.extend({ // Initialize initialize: function(grid_config) { + this.grid = new GridModel(); this.dict_format = grid_config.dict_format; var self = this; window.add_tag_to_grid_filter = function( tag_name, tag_value ){ @@ -35,7 +36,7 @@ return Backbone.View.extend({ if ( this.dict_format ) { this.setElement('
    '); if ( grid_config.url_base && !grid_config.items ) { - var url_data = {}; + var url_data = grid_config.url_data || {}; _.each(grid_config.filters, function(v, k) { url_data['f-' + k] = v; }); @@ -43,7 +44,7 @@ return Backbone.View.extend({ url : grid_config.url_base + '?' + $.param( url_data ), success : function( response ) { response.embedded = grid_config.embedded; - response.filters = grid_config.filters; + response.filters = grid_config.filters || {}; self.init_grid( response ); } }); @@ -77,8 +78,7 @@ return Backbone.View.extend({ // Initialize init_grid: function(grid_config) { - // link grid model - this.grid = new GridModel(grid_config); + this.grid.set( grid_config ); // get options var options = this.grid.attributes; @@ -456,13 +456,18 @@ return Backbone.View.extend({ }); // execute operation - this.execute({ - operation: operation_name, - id: item_ids, - confirmation_text: confirmation_text - }); - - // return + var options = { + operation : operation_name, + id : item_ids, + confirmation_text : confirmation_text + }; + if ( operation.target == 'top' ) { + options = _.extend( options, { + href : operation.href, + target : operation.target + }); + } + this.execute( options ); return true; }, @@ -542,7 +547,9 @@ return Backbone.View.extend({ }); // Do operation. If operation cannot be performed asynchronously, redirect to location. - if (this.grid.can_async_op(operation) || this.dict_format) { + if ( target == 'top' ) { + window.top.location = href + '?' + $.param( this.grid.get_url_data() ); + } else if ( this.grid.can_async_op(operation) || this.dict_format ) { this.update_grid(); } else { this.go_to(target, href); @@ -576,7 +583,7 @@ return Backbone.View.extend({ this.grid.set('async', false); // get slide status - advanced_search = this.$el.find('#advanced-search').is(':visible'); + var advanced_search = this.$el.find('#advanced-search').is(':visible'); this.grid.set('advanced_search', advanced_search); // get default url @@ -625,6 +632,7 @@ return Backbone.View.extend({ // backup var embedded = self.grid.get('embedded'); var insert = self.grid.get('insert'); + var advanced_search = self.$el.find('#advanced-search').is(':visible'); // request new configuration var json = self.dict_format ? response_text : $.parseJSON(response_text); @@ -632,6 +640,7 @@ return Backbone.View.extend({ // update json.embedded = embedded; json.insert = insert; + json.advanced_search = advanced_search; // Initialize new grid config self.init_grid(json); diff --git a/client/galaxy/scripts/mvc/job/job-params.js b/client/galaxy/scripts/mvc/job/job-params.js new file mode 100644 index 000000000000..a6afed4214e1 --- /dev/null +++ b/client/galaxy/scripts/mvc/job/job-params.js @@ -0,0 +1,63 @@ +/** Workflow view */ +define( [ 'utils/utils' ], function( Utils ) { + + /** Build messages after user action */ + function build_messages( self ) { + var $el_message = self.$el.find( '.response-message' ), + status = Utils.getQueryString( 'status' ), + message = Utils.getQueryString( 'message' ); + + if( message && message !== "" ) { + $el_message.addClass( status + 'message' ); + $el_message.html( '

    ' + _.escape( message ) + '

    ' ); + } + else { + $el_message.html(""); + } + } + + /** View of the main workflow list page */ + var View = Backbone.View.extend({ + + initialize: function( options ) { + var self = this; + this.options = options; + this.setElement( '
    ' ); + this.render(); + }, + + render: function() { + console.log('HI'); + var self = this; + self.$el.empty().append( '

    Testing

    ' ); + //var self = this, + //min_query_length = 3; + //$.getJSON( Galaxy.root + 'api/workflows/', function( workflows ) { + //var $el_workflow = null; + //// Add workflow header + //// Add user actions message if any + //build_messages( self ); + //$el_workflow = self.$el.find( '.user-workflows' ); + //// Add the actions buttons + //$el_workflow.append( self._templateActionButtons() ); + //if( workflows.length > 0) { + //$el_workflow.append( self._templateWorkflowTable( self, workflows) ); + //self.adjust_actiondropdown( $el_workflow ); + //// Register delete and run workflow events + //_.each( workflows, function( wf ) { + //self.confirm_delete( self, wf ); + //}); + //// Register search workflow event + //self.search_workflow( self, self.$el.find( '.search-wf' ), self.$el.find( '.workflow-search tr' ), min_query_length ); + //} + //else { + //$el_workflow.append( self._templateNoWorkflow() ); + //} + //}); + } + }); + + return { + View : View + }; +}); diff --git a/client/galaxy/scripts/mvc/library/library-dataset-view.js b/client/galaxy/scripts/mvc/library/library-dataset-view.js index dfa7dab4e7eb..ddc4f5564931 100644 --- a/client/galaxy/scripts/mvc/library/library-dataset-view.js +++ b/client/galaxy/scripts/mvc/library/library-dataset-view.js @@ -497,7 +497,7 @@ var LibraryDatasetView = Backbone.View.extend({ * Extract the role ids from Select2 elements's 'data' */ _extractIds: function(roles_list){ - ids_list = []; + var ids_list = []; for (var i = roles_list.length - 1; i >= 0; i--) { ids_list.push(roles_list[i].id); }; @@ -531,7 +531,7 @@ var LibraryDatasetView = Backbone.View.extend({ mod_utils.get({ url : Galaxy.root + "api/datatypes?extension_only=False", success : function( datatypes ) { - for (key in datatypes) { + for (var key in datatypes) { that.list_extensions.push({ id : datatypes[key].extension, text : datatypes[key].extension, @@ -548,7 +548,7 @@ var LibraryDatasetView = Backbone.View.extend({ mod_utils.get({ url : Galaxy.root + "api/genomes", success : function( genomes ) { - for ( key in genomes ) { + for (var key in genomes ) { that.list_genomes.push({ id : genomes[key][1], text : genomes[key][0] @@ -721,6 +721,12 @@ var LibraryDatasetView = Backbone.View.extend({ '<%= _.escape(item.get("misc_info")) %>', '', '<% } %>', + '<% if (item.get("tags")) { %>', + '', + 'Tags', + '<%= _.escape(item.get("tags")) %>', + '', + '<% } %>', '', '
    ', @@ -850,6 +856,12 @@ var LibraryDatasetView = Backbone.View.extend({ '<%= _.escape(ldda.get("misc_info")) %>', '', '<% } %>', + '<% if (item.get("tags")) { %>', + '', + 'Tags', + '<%= _.escape(item.get("tags")) %>', + '', + '<% } %>', '', '
    ', '
    ',
    @@ -951,6 +963,13 @@ var LibraryDatasetView = Backbone.View.extend({
                 'Miscellaneous blurb',
                 '<%= _.escape(item.get("misc_blurb")) %>',
               '',
    +          //TODO: add functionality to modify tags here
    +          '<% if (item.get("tags")) { %>',
    +            '',
    +              'Tags',
    +              '<%= _.escape(item.get("tags")) %>',
    +            '',
    +          '<% } %>',
             '',
             '
    ', '
    ',
    diff --git a/client/galaxy/scripts/mvc/library/library-folder-view.js b/client/galaxy/scripts/mvc/library/library-folder-view.js
    index ed3c30ed2b9d..a66019ccbd78 100644
    --- a/client/galaxy/scripts/mvc/library/library-folder-view.js
    +++ b/client/galaxy/scripts/mvc/library/library-folder-view.js
    @@ -181,7 +181,7 @@ var FolderView = Backbone.View.extend({
        * Extract the role ids from Select2 elements's 'data'
        */
       _extractIds: function(roles_list){
    -    ids_list = [];
    +    var ids_list = [];
         for (var i = roles_list.length - 1; i >= 0; i--) {
           ids_list.push(roles_list[i].id);
         };
    diff --git a/client/galaxy/scripts/mvc/library/library-folderlist-view.js b/client/galaxy/scripts/mvc/library/library-folderlist-view.js
    index 644f9fdcd38d..df4467b5e843 100644
    --- a/client/galaxy/scripts/mvc/library/library-folderlist-view.js
    +++ b/client/galaxy/scripts/mvc/library/library-folderlist-view.js
    @@ -112,7 +112,7 @@ var FolderListView = Backbone.View.extend({
     
             // when dataset_id is present render its details too
             if ( this.options.dataset_id ){
    -            row = _.findWhere( that.rowViews, { id: this.options.dataset_id } );
    +            var row = _.findWhere( that.rowViews, { id: this.options.dataset_id } );
                 if ( row ) {
                   row.showDatasetDetails();
                 } else {
    @@ -277,7 +277,7 @@ var FolderListView = Backbone.View.extend({
              // Iterate each checkbox
              $(':checkbox', '#folder_list_body').each(function() {
                 this.checked = selected;
    -            $row = $(this.parentElement.parentElement);
    +            var $row = $(this.parentElement.parentElement);
                 // Change color of selected/unselected
                 if (selected) {
                   that.makeDarkRow($row);
    diff --git a/client/galaxy/scripts/mvc/library/library-folderrow-view.js b/client/galaxy/scripts/mvc/library/library-folderrow-view.js
    index ebb2d5e28222..138e487f180a 100644
    --- a/client/galaxy/scripts/mvc/library/library-folderrow-view.js
    +++ b/client/galaxy/scripts/mvc/library/library-folderrow-view.js
    @@ -66,7 +66,7 @@ var FolderRowView = Backbone.View.extend({
        * the filling of the row template of a given folder.
        */
       prepareButtons: function(folder){
    -    vis_config = this.options.visibility_config;
    +    var vis_config = this.options.visibility_config;
         if (this.options.edit_mode === false){
           vis_config.save_folder_btn = false;
           vis_config.cancel_folder_btn = false;
    diff --git a/client/galaxy/scripts/mvc/library/library-foldertoolbar-view.js b/client/galaxy/scripts/mvc/library/library-foldertoolbar-view.js
    index d6a13549869d..0c301fa1c1b4 100644
    --- a/client/galaxy/scripts/mvc/library/library-foldertoolbar-view.js
    +++ b/client/galaxy/scripts/mvc/library/library-foldertoolbar-view.js
    @@ -161,8 +161,13 @@ var FolderToolbarView = Backbone.View.extend({
           var folderDetails = this.serialize_new_folder();
           if (this.validate_new_folder(folderDetails)){
               var folder = new mod_library_model.FolderAsModel();
    -          url_items = Backbone.history.fragment.split('/');
    -          current_folder_id = url_items[url_items.length-1];
    +          var url_items = Backbone.history.fragment.split('/'),
    +              current_folder_id;
    +          if(url_items.indexOf('page') > -1){
    +            current_folder_id = url_items[url_items.length-3];
    +          }else {
    +            current_folder_id = url_items[url_items.length-1];
    +          }
               folder.url = folder.urlRoot + current_folder_id ;
     
               folder.save(folderDetails, {
    @@ -313,7 +318,7 @@ var FolderToolbarView = Backbone.View.extend({
       updateProgress: function(){
           this.progress += this.progressStep;
           $( '.progress-bar-import' ).width( Math.round( this.progress ) + '%' );
    -      txt_representation = Math.round( this.progress ) + '% Complete';
    +      var txt_representation = Math.round( this.progress ) + '% Complete';
           $( '.completion_span' ).text( txt_representation );
       },
     
    @@ -427,7 +432,7 @@ var FolderToolbarView = Backbone.View.extend({
             url      :  Galaxy.root + "api/datatypes?extension_only=False",
             success  :  function( datatypes ) {
                             that.list_extensions = [];
    -                        for (key in datatypes) {
    +                        for (var key in datatypes) {
                                 that.list_extensions.push({
                                     id              : datatypes[key].extension,
                                     text            : datatypes[key].extension,
    @@ -446,7 +451,7 @@ var FolderToolbarView = Backbone.View.extend({
             url     :    Galaxy.root + "api/genomes",
             success : function( genomes ) {
                         that.list_genomes = [];
    -                    for ( key in genomes ) {
    +                    for (var key in genomes ) {
                             that.list_genomes.push({
                                 id      : genomes[key][1],
                                 text    : genomes[key][0]
    @@ -611,7 +616,7 @@ var FolderToolbarView = Backbone.View.extend({
           this.modal.disableButton('Import');
           paths = paths.split('\n');
           for (var i = paths.length - 1; i >= 0; i--) {
    -        trimmed = paths[i].trim();
    +        var trimmed = paths[i].trim();
             if (trimmed.length!==0){
               valid_paths.push(trimmed);
             }
    @@ -670,11 +675,12 @@ var FolderToolbarView = Backbone.View.extend({
       importFromJstreePath: function ( that, options ){
         var all_nodes = $( '#jstree_browser' ).jstree().get_selected( true );
         // remove the disabled elements that could have been trigerred with the 'select all'
    -    selected_nodes = _.filter(all_nodes, function(node){ return node.state.disabled == false; })
    +    var selected_nodes = _.filter(all_nodes, function(node){ return node.state.disabled == false; })
         var preserve_dirs = this.modal.$el.find( '.preserve-checkbox' ).is( ':checked' );
         var link_data = this.modal.$el.find( '.link-checkbox' ).is( ':checked' );
         var file_type = this.select_extension.value();
         var dbkey = this.select_genome.value();
    +    var tag_using_filenames = this.modal.$el.find( '.tag-files' ).is( ':checked' );
         var selection_type = selected_nodes[0].type;
         var paths = [];
         if ( selected_nodes.length < 1 ){
    @@ -694,13 +700,15 @@ var FolderToolbarView = Backbone.View.extend({
                                               link_data: link_data,
                                               source: full_source,
                                               file_type: file_type,
    -                                          dbkey: dbkey } );
    +                                          dbkey: dbkey,
    +                                          tag_using_filenames: tag_using_filenames } );
           } else if ( selection_type === 'file' ){
             var full_source = options.source + '_file';
             this.chainCallImportingUserdirFiles( { paths : paths,
                                                    file_type: file_type,
                                                    dbkey: dbkey,
    -                                               source: full_source } );
    +                                               source: full_source,
    +                                               tag_using_filenames: tag_using_filenames } );
           }
         }
       },
    @@ -745,7 +753,7 @@ var FolderToolbarView = Backbone.View.extend({
             }
           });
           for ( var i = history_item_ids.length - 1; i >= 0; i-- ) {
    -        history_item_id = history_item_ids[i];
    +        var history_item_id = history_item_ids[i];
             var folder_item = new mod_library_model.Item();
             folder_item.url = Galaxy.root + 'api/folders/' + this.options.id + '/contents';
             if (history_item_types[i] === 'collection') {
    @@ -794,9 +802,10 @@ var FolderToolbarView = Backbone.View.extend({
       },
     
       /**
    -   * Take the array of paths and createa request for each of them
    +   * Take the array of paths and create a request for each of them
        * calling them in chain. Update the progress bar in between each.
    -   * @param  {array} paths           paths relative to user folder on Galaxy
    +   * @param  {array} paths                    paths relative to user folder on Galaxy
    +   * @param  {boolean} tag_using_filenames    add tags to datasets using names of files
        */
       chainCallImportingUserdirFiles: function( options ){
     
    @@ -815,7 +824,8 @@ var FolderToolbarView = Backbone.View.extend({
                                                            '&source=' + options.source +
                                                            '&path=' + popped_item +
                                                            '&file_type=' + options.file_type +
    -                                                       '&dbkey=' + options.dbkey ) )
    +                                                       '&dbkey=' + options.dbkey +
    +                                                       '&tag_using_filenames=' + options.tag_using_filenames ) )
         promise.done( function( response ){
                   that.updateProgress();
                   that.chainCallImportingUserdirFiles( options );
    @@ -830,11 +840,12 @@ var FolderToolbarView = Backbone.View.extend({
       /**
        * Take the array of paths and createa request for each of them
        * calling them in chain. Update the progress bar in between each.
    -   * @param  {array} paths           paths relative to Galaxy root folder
    -   * @param  {boolean} preserve_dirs indicates whether to preserve folder structure
    -   * @param  {boolean} link_data     copy files to Galaxy or link instead
    -   * @param  {str} source            string representing what type of folder
    -   *                                 is the source of import
    +   * @param  {array} paths                    paths relative to Galaxy root folder
    +   * @param  {boolean} preserve_dirs          indicates whether to preserve folder structure
    +   * @param  {boolean} link_data              copy files to Galaxy or link instead
    +   * @param  {str} source                     string representing what type of folder
    +   *                                          is the source of import
    +   * @param  {boolean} tag_using_filenames    add tags to datasets using names of files
        */
       chainCallImportingFolders: function( options ){
         // TODO need to check which paths to call
    @@ -856,7 +867,8 @@ var FolderToolbarView = Backbone.View.extend({
                                                               '&preserve_dirs=' + options.preserve_dirs +
                                                               '&link_data=' + options.link_data +
                                                               '&file_type=' + options.file_type +
    -                                                          '&dbkey=' + options.dbkey ) )
    +                                                          '&dbkey=' + options.dbkey +
    +                                                          '&tag_using_filenames=' + options.tag_using_filenames ) )
         promise.done(function(response){
                   that.updateProgress();
                   that.chainCallImportingFolders( options );
    @@ -1341,7 +1353,13 @@ var FolderToolbarView = Backbone.View.extend({
           '
    ', 'Type: ', 'Genome: ', - '
    ', + '
    ', + '
    ', + '', + '
    ', '
    ' ].join('')); }, diff --git a/client/galaxy/scripts/mvc/library/library-library-view.js b/client/galaxy/scripts/mvc/library/library-library-view.js index 42650e04cab3..9d6d6b82d191 100644 --- a/client/galaxy/scripts/mvc/library/library-library-view.js +++ b/client/galaxy/scripts/mvc/library/library-library-view.js @@ -212,7 +212,7 @@ var LibraryView = Backbone.View.extend({ }, _extractIds: function(roles_list){ - ids_list = []; + var ids_list = []; for (var i = roles_list.length - 1; i >= 0; i--) { ids_list.push(roles_list[i].id); }; diff --git a/client/galaxy/scripts/mvc/library/library-libraryrow-view.js b/client/galaxy/scripts/mvc/library/library-libraryrow-view.js index e39cc2b0da5b..757222170cbe 100644 --- a/client/galaxy/scripts/mvc/library/library-libraryrow-view.js +++ b/client/galaxy/scripts/mvc/library/library-libraryrow-view.js @@ -64,7 +64,7 @@ var LibraryRowView = Backbone.View.extend({ * the filling of the row template of given library. */ prepareButtons: function(library){ - vis_config = this.element_visibility_config; + var vis_config = this.element_visibility_config; if (this.edit_mode === false){ vis_config.save_library_btn = false; diff --git a/client/galaxy/scripts/mvc/library/library-model.js b/client/galaxy/scripts/mvc/library/library-model.js index 91f00cd92bab..be62bef4a132 100644 --- a/client/galaxy/scripts/mvc/library/library-model.js +++ b/client/galaxy/scripts/mvc/library/library-model.js @@ -41,7 +41,7 @@ define([], function() { if (search_term == "") return this; var lowercase_term = search_term.toLowerCase(); return this.filter(function(data) { - lowercase_name = data.get("name").toLowerCase(); + var lowercase_name = data.get("name").toLowerCase(); return lowercase_name.indexOf(lowercase_term) !== -1; }); }, diff --git a/client/galaxy/scripts/mvc/tool/tool-form-base.js b/client/galaxy/scripts/mvc/tool/tool-form-base.js index c26c35e96f7e..0dd48016623b 100644 --- a/client/galaxy/scripts/mvc/tool/tool-form-base.js +++ b/client/galaxy/scripts/mvc/tool/tool-form-base.js @@ -8,13 +8,10 @@ define( [ 'utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view var self = this; this.deferred = new Deferred(); FormBase.prototype.initialize.call( this, options ); - if ( this.model.get( 'inputs' ) ) { - this._buildForm( this.model.attributes ); - } else { - this.deferred.execute( function( process ) { - self._buildModel( process, self.model.attributes, true ); - }); - } + + // optional model update + this._update( this.model.get( 'initialmodel' ) ); + // listen to history panel if ( this.model.get( 'listen_to_history' ) && parent.Galaxy && parent.Galaxy.currHistoryPanel ) { this.listenTo( parent.Galaxy.currHistoryPanel.collection, 'change', function() { @@ -25,6 +22,21 @@ define( [ 'utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view this.$el.on( 'remove', function() { self._destroy() } ); }, + /** Allows tool form variation to update tool model */ + _update: function( callback ) { + var self = this; + callback = callback || this.model.get( 'buildmodel' ); + if ( callback ) { + this.deferred.reset(); + this.deferred.execute( function( process ) { + callback( process, self ); + process.then( function() { self._render() } ); + }); + } else { + this._render(); + } + }, + /** Wait for deferred build processes before removal */ _destroy: function() { var self = this; @@ -36,12 +48,12 @@ define( [ 'utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view }, /** Build form */ - _buildForm: function( options ) { + _render: function() { var self = this; - this.model.set( options ); + var options = this.model.attributes; this.model.set({ - title : options.title || '' + options.name + ' ' + options.description + ' (Galaxy Version ' + options.version + ')', - operations : !this.model.get( 'hide_operations' ) && this._operations(), + title : options.fixed_title || '' + options.name + ' ' + options.description + ' (Galaxy Version ' + options.version + ')', + operations : !options.hide_operations && this._operations(), onchange : function() { self.deferred.reset(); self.deferred.execute( function ( process ) { @@ -49,85 +61,16 @@ define( [ 'utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view }); } }); - this.model.get( 'customize' ) && this.model.get( 'customize' )( this ); this.render(); if ( !this.model.get( 'collapsible' ) ) { this.$el.append( $( '
    ' ).addClass( 'ui-margin-top-large' ).append( this._footer() ) ); } - }, - - /** Builds a new model through api call and recreates the entire form */ - _buildModel: function( process, new_options, hide_message ) { - var self = this; - var options = this.model.attributes; - options.version = new_options.version; - options.id = new_options.id; - - // build request url - var build_url = ''; - var build_data = {}; - var job_id = ''; - // When re-running a job the job_id is found in the new_options object. - // When re-running a job and requesting a new tool_version, - // the job_id is in the options object. - if ( new_options.job_id ) { - job_id = new_options.job_id; - } else if (options.job_id) { - job_id = options.job_id; - } - if ( job_id ) { - build_url = Galaxy.root + 'api/jobs/' + job_id + '/build_for_rerun'; - } else { - build_url = Galaxy.root + 'api/tools/' + options.id + '/build'; - build_data = $.extend( {}, Galaxy.params ); - build_data[ 'tool_id' ] && ( delete build_data[ 'tool_id' ] ); - } - options.version && ( build_data[ 'tool_version' ] = options.version ); - - // get initial model - Utils.get({ - url : build_url, - data : build_data, - success : function( data ) { - if( !data.display ) { - window.location = Galaxy.root; - return; - } - self._buildForm( data ); - !hide_message && self.message.update({ - status : 'success', - message : 'Now you are using \'' + options.name + '\' version ' + options.version + ', id \'' + options.id + '\'.', - persistent : false - }); - Galaxy.emit.debug('tool-form-base::_buildModel()', 'Initial tool model ready.', data); - process.resolve(); - }, - error : function( response, status ) { - var error_message = ( response && response.err_msg ) || 'Uncaught error.'; - if ( status == 401 ) { - window.location = Galaxy.root + 'user/login?' + $.param({ redirect : Galaxy.root + '?tool_id=' + options.id }); - } else if ( self.$el.is( ':empty' ) ) { - self.$el.prepend( ( new Ui.Message({ - message : error_message, - status : 'danger', - persistent : true, - large : true - }) ).$el ); - } else { - Galaxy.modal && Galaxy.modal.show({ - title : 'Tool request failed', - body : error_message, - buttons : { - 'Close' : function() { - Galaxy.modal.hide(); - } - } - }); - } - Galaxy.emit.debug( 'tool-form-base::_buildModel()', 'Initial tool model request failed.', response ); - process.reject(); - } + this.show_message && this.message.update({ + status : 'success', + message : 'Now you are using \'' + options.name + '\' version ' + options.version + ', id \'' + options.id + '\'.', + persistent : false }); + this.show_message = true; }, /** Create tool operation menu */ @@ -151,13 +94,9 @@ define( [ 'utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view icon : 'fa-cube', onclick : function() { // here we update the tool version (some tools encode the version also in the id) - var id = options.id.replace( options.version, this.version ); - var version = this.version; - // queue model request - self.deferred.reset(); - self.deferred.execute( function( process ) { - self._buildModel( process, { id : id, version : version } ) - }); + self.model.set( 'id', options.id.replace( options.version, this.version ) ); + self.model.set( 'version', this.version ); + self._update(); } }); } diff --git a/client/galaxy/scripts/mvc/tool/tool-form-composite.js b/client/galaxy/scripts/mvc/tool/tool-form-composite.js index 608109432c1a..156af19e47b0 100644 --- a/client/galaxy/scripts/mvc/tool/tool-form-composite.js +++ b/client/galaxy/scripts/mvc/tool/tool-form-composite.js @@ -45,7 +45,7 @@ define([ 'utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view' } step = Utils.merge( { index : i, - title : _.escape( title ), + fixed_title : _.escape( title ), icon : icon || '', help : null, citations : null, @@ -112,7 +112,7 @@ define([ 'utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view' var wp_count = 0; this.wp_inputs = {}; function _handleWorkflowParameter( value, callback ) { - var re = /\$\{(.+?)\}/g; + var re = /\$\{(.+?)\}/g, match; while ( match = re.exec( String( value ) ) ) { var wp_name = match[ 1 ]; callback( self.wp_inputs[ wp_name ] = self.wp_inputs[ wp_name ] || { @@ -304,7 +304,7 @@ define([ 'utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view' var is_simple_input = ([ 'data_input', 'data_collection_input' ]).indexOf( step.step_type ) != -1; _.each( step.inputs, function( input ) { input.flavor = 'module'; input.hide_label = is_simple_input; } ); form = new Form( Utils.merge({ - title : step.title, + title : step.fixed_title, onchange : function() { _.each( self.links[ step.index ], function( link ) { self._refreshStep( link ) } ) }, inputs : step.inputs && step.inputs.length > 0 ? step.inputs : [ { type: 'hidden', name: 'No options available.', ignore: null } ] }, step ) ); @@ -336,7 +336,7 @@ define([ 'utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view' new_value = { values: [] }; _.each( input.step_linked, function( source_step ) { if ( self._isDataStep( source_step ) ) { - value = self.forms[ source_step.index ].data.create().input; + var value = self.forms[ source_step.index ].data.create().input; value && _.each( value.values, function( v ) { new_value.values.push( v ) } ); } }); @@ -345,7 +345,7 @@ define([ 'utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view' } } else if ( input.wp_linked ) { new_value = input.value; - var re = /\$\{(.+?)\}/g; + var re = /\$\{(.+?)\}/g, match; while ( match = re.exec( input.value ) ) { var wp_field = self.wp_form.field_list[ self.wp_form.data.match( match[ 1 ] ) ]; var wp_value = wp_field && wp_field.value(); @@ -514,7 +514,7 @@ define([ 'utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view' /** Is data input module/step */ _isDataStep: function( steps ) { - lst = $.isArray( steps ) ? steps : [ steps ] ; + var lst = $.isArray( steps ) ? steps : [ steps ] ; for ( var i = 0; i < lst.length; i++ ) { var step = lst[ i ]; if ( !step || !step.step_type || !step.step_type.startsWith( 'data' ) ) { diff --git a/client/galaxy/scripts/mvc/tool/tool-form.js b/client/galaxy/scripts/mvc/tool/tool-form.js index 7a10c5eba74f..1c3117f0fd01 100644 --- a/client/galaxy/scripts/mvc/tool/tool-form.js +++ b/client/galaxy/scripts/mvc/tool/tool-form.js @@ -8,42 +8,64 @@ define([ 'utils/utils', 'mvc/ui/ui-misc', 'mvc/ui/ui-modal', 'mvc/tool/tool-form this.form = new ToolFormBase( Utils.merge({ listen_to_history : true, always_refresh : false, - customize : function( form ) { + buildmodel: function( process, form ) { var options = form.model.attributes; - // build execute button - options.buttons = { - execute: execute_btn = new Ui.Button({ - icon : 'fa-check', - tooltip : 'Execute: ' + options.name + ' (' + options.version + ')', - title : 'Execute', - cls : 'ui-button btn btn-primary', - floating : 'clear', - onclick : function() { - execute_btn.wait(); - form.portlet.disable(); - self.submit( options, function() { - execute_btn.unwait(); - form.portlet.enable(); - } ); - } - }) - } - // remap feature - if ( options.job_id && options.job_remap ) { - options.inputs.push({ - label : 'Resume dependencies from this job', - name : 'rerun_remap_job_id', - type : 'select', - display : 'radio', - ignore : '__ignore__', - value : '__ignore__', - options : [ [ 'Yes', options.job_id ], [ 'No', '__ignore__' ] ], - help : 'The previous run of this tool failed and other tools were waiting for it to finish successfully. Use this option to resume those tools using the new output(s) of this tool run.' - }); + + // build request url + var build_url = ''; + var build_data = {}; + var job_id = options.job_id; + if ( job_id ) { + build_url = Galaxy.root + 'api/jobs/' + job_id + '/build_for_rerun'; + } else { + build_url = Galaxy.root + 'api/tools/' + options.id + '/build'; + build_data = $.extend( {}, Galaxy.params ); + build_data[ 'tool_id' ] && ( delete build_data[ 'tool_id' ] ); } + options.version && ( build_data[ 'tool_version' ] = options.version ); + + // get initial model + Utils.get({ + url : build_url, + data : build_data, + success : function( data ) { + if( !data.display ) { + window.location = Galaxy.root; + return; + } + form.model.set( data ); + self._customize( form ); + Galaxy.emit.debug('tool-form-base::_buildModel()', 'Initial tool model ready.', data); + process.resolve(); + }, + error : function( response, status ) { + var error_message = ( response && response.err_msg ) || 'Uncaught error.'; + if ( status == 401 ) { + window.location = Galaxy.root + 'user/login?' + $.param({ redirect : Galaxy.root + '?tool_id=' + options.id }); + } else if ( form.$el.is( ':empty' ) ) { + form.$el.prepend( ( new Ui.Message({ + message : error_message, + status : 'danger', + persistent : true, + large : true + }) ).$el ); + } else { + Galaxy.modal && Galaxy.modal.show({ + title : 'Tool request failed', + body : error_message, + buttons : { + 'Close' : function() { + Galaxy.modal.hide(); + } + } + }); + } + Galaxy.emit.debug( 'tool-form-base::_buildModel()', 'Initial tool model request failed.', response ); + process.reject(); + } + }); }, postchange : function( process, form ) { - var self = this; var current_state = { tool_id : form.model.get( 'id' ), tool_version : form.model.get( 'version' ), @@ -73,6 +95,42 @@ define([ 'utils/utils', 'mvc/ui/ui-misc', 'mvc/ui/ui-modal', 'mvc/tool/tool-form this.$el.append( this.form.$el ); }, + _customize: function( form ) { + var self = this; + var options = form.model.attributes; + // build execute button + options.buttons = { + execute: execute_btn = new Ui.Button({ + icon : 'fa-check', + tooltip : 'Execute: ' + options.name + ' (' + options.version + ')', + title : 'Execute', + cls : 'btn btn-primary ui-clear-float', + wait_cls : 'btn btn-info ui-clear-float', + onclick : function() { + execute_btn.wait(); + form.portlet.disable(); + self.submit( options, function() { + execute_btn.unwait(); + form.portlet.enable(); + } ); + } + }) + } + // remap feature + if ( options.job_id && options.job_remap ) { + options.inputs.push({ + label : 'Resume dependencies from this job', + name : 'rerun_remap_job_id', + type : 'select', + display : 'radio', + ignore : '__ignore__', + value : '__ignore__', + options : [ [ 'Yes', options.job_id ], [ 'No', '__ignore__' ] ], + help : 'The previous run of this tool failed and other tools were waiting for it to finish successfully. Use this option to resume those tools using the new output(s) of this tool run.' + }); + } + }, + /** Submit a regular job. * @param{dict} options - Specifies tool id and version * @param{function} callback - Called when request has completed @@ -207,4 +265,4 @@ define([ 'utils/utils', 'mvc/ui/ui-misc', 'mvc/ui/ui-modal', 'mvc/tool/tool-form return { View: View }; -}); +}); \ No newline at end of file diff --git a/client/galaxy/scripts/mvc/tool/tool-genomespace.js b/client/galaxy/scripts/mvc/tool/tool-genomespace.js new file mode 100644 index 000000000000..8ca44dff9080 --- /dev/null +++ b/client/galaxy/scripts/mvc/tool/tool-genomespace.js @@ -0,0 +1,24 @@ +// Provides support for interacting with the GenomeSpace File Browser popup dialogue +define([], function() { + +// tool form templates +return { + openFileBrowser: function( options ) { + var GS_UI_URL = window.Galaxy.config.genomespace_ui_url; + var GS_UPLOAD_URL = GS_UI_URL + 'upload/loadUrlToGenomespace.html?getLocation=true' + + var newWin = window.open(GS_UPLOAD_URL, "GenomeSpace File Browser", "height=360px,width=600px"); + + successCalBack = options['successCallback']; + window.addEventListener( "message", function (e) { + successCalBack(e.data); + }, false); + + newWin.focus(); + + if (options['errorCallback'] != null) newWin.setCallbackOnGSUploadError = config['errorCallback']; + } + +}; + +}); \ No newline at end of file diff --git a/client/galaxy/scripts/mvc/toolshed/categories-view.js b/client/galaxy/scripts/mvc/toolshed/categories-view.js index b914e55fe3ac..b9226f8d5c4c 100644 --- a/client/galaxy/scripts/mvc/toolshed/categories-view.js +++ b/client/galaxy/scripts/mvc/toolshed/categories-view.js @@ -39,7 +39,7 @@ define(['mvc/toolshed/toolshed-model', 'mvc/toolshed/util'], function(toolshed_m var params = {term: request.term, tool_shed_url: shed_url}; $.post(base_url, params, function(data) { console.log(data); - result_list = toolshed_util.shedParser(data); + var result_list = toolshed_util.shedParser(data); response(result_list); }); }, diff --git a/client/galaxy/scripts/mvc/toolshed/repo-status-view.js b/client/galaxy/scripts/mvc/toolshed/repo-status-view.js index 183de64a53d9..0d8e381e87c9 100644 --- a/client/galaxy/scripts/mvc/toolshed/repo-status-view.js +++ b/client/galaxy/scripts/mvc/toolshed/repo-status-view.js @@ -16,7 +16,7 @@ define(['mvc/toolshed/toolshed-model', 'mvc/toolshed/util'], function(toolshed_m var all_done = true; _.some(self.model.models, function(repository) { repo_id = repository.get('id'); - repo_status = repository.get('status').toLowerCase(); + var repo_status = repository.get('status').toLowerCase(); if (terminal_states.indexOf(repo_status) === -1) { all_done = false; return true; diff --git a/client/galaxy/scripts/mvc/toolshed/repositories-view.js b/client/galaxy/scripts/mvc/toolshed/repositories-view.js index 795adfa04715..acf7f2fa7f30 100644 --- a/client/galaxy/scripts/mvc/toolshed/repositories-view.js +++ b/client/galaxy/scripts/mvc/toolshed/repositories-view.js @@ -32,7 +32,7 @@ define(['mvc/toolshed/toolshed-model', 'mvc/toolshed/util'], function(toolshed_m var base_url = Galaxy.root + 'api/tool_shed/search'; var params = {term: request.term, tool_shed_url: shed_url}; $.post(base_url, params, function(data) { - result_list = toolshed_util.shedParser(data); + var result_list = toolshed_util.shedParser(data); response(result_list); }); }, diff --git a/client/galaxy/scripts/mvc/toolshed/repository-queue-view.js b/client/galaxy/scripts/mvc/toolshed/repository-queue-view.js index 24193a63d2bc..509f0a869bbb 100644 --- a/client/galaxy/scripts/mvc/toolshed/repository-queue-view.js +++ b/client/galaxy/scripts/mvc/toolshed/repository-queue-view.js @@ -63,7 +63,7 @@ define(['mvc/toolshed/toolshed-model', 'mvc/toolshed/util'], function(toolshed_m if (queue_key === undefined) { queue_key = toolshed_util.queueKey(repository_metadata); } - repository_queue = JSON.parse(localStorage.repositories); + var repository_queue = JSON.parse(localStorage.repositories); if (repository_queue.hasOwnProperty(queue_key)) { delete repository_queue[queue_key]; localStorage.repositories = JSON.stringify(repository_queue); @@ -82,7 +82,7 @@ define(['mvc/toolshed/toolshed-model', 'mvc/toolshed/util'], function(toolshed_m }, loadFromQueue: function(queue_key) { - repository_queue = JSON.parse(localStorage.repositories); + var repository_queue = JSON.parse(localStorage.repositories); if (repository_queue.hasOwnProperty(queue_key)) { return repository_queue[queue_key]; } diff --git a/client/galaxy/scripts/mvc/toolshed/repository-view.js b/client/galaxy/scripts/mvc/toolshed/repository-view.js index 27403c54d027..9192988f16cd 100644 --- a/client/galaxy/scripts/mvc/toolshed/repository-view.js +++ b/client/galaxy/scripts/mvc/toolshed/repository-view.js @@ -19,7 +19,7 @@ define(['mvc/toolshed/toolshed-model', this.options = _.defaults(this.options || {}, this.defaults); this.model = new toolshed_model.RepositoryCollection(); this.listenTo(this.model, 'sync', this.render); - shed = params.tool_shed.replace(/\//g, '%2f'); + var shed = params.tool_shed.replace(/\//g, '%2f'); this.model.url += '?tool_shed_url=' + shed + '&repository_id=' + params.repository_id; this.model.tool_shed_url = params.tool_shed.replace(/%2f/g, '/'); this.model.tool_shed = shed; @@ -81,7 +81,7 @@ define(['mvc/toolshed/toolshed-model', params.tool_panel_section = JSON.stringify(that.panelSelect(params)); params.shed_tool_conf = $("select[name='shed_tool_conf']").find('option:selected').val() params.changeset = $('#changeset').find("option:selected").val(); - url = $('#repository_installation').attr('action'); + var url = $('#repository_installation').attr('action'); that.prepareInstall(params, url); }); $('#queue_install').on('click', function(ev) { @@ -107,8 +107,8 @@ define(['mvc/toolshed/toolshed-model', that.checkInstalled(repository_metadata); }); $('.tool_panel_section_picker').on('change', function() { - new_value = $(this).find('option:selected').val(); - default_tps = $('#tool_panel_section_select').find('option:selected').val(); + var new_value = $(this).find('option:selected').val(); + var default_tps = $('#tool_panel_section_select').find('option:selected').val(); if (new_value == default_tps) { $(this).attr('default', 'active'); } @@ -191,8 +191,8 @@ define(['mvc/toolshed/toolshed-model', params.new_tool_panel_section = $("#new_tool_panel_section").val(); } $('.tool_panel_section_picker').each(function() { - element_name = $(this).attr('name'); - tool_guid = $(this).attr('data-toolguid'); + var element_name = $(this).attr('name'); + var tool_guid = $(this).attr('data-toolguid'); if (element_name === 'tool_panel_section_id') { tool_panel_section[tool_guid] = { tool_panel_section: $(this).find("option:selected").val(), action: 'append' } } @@ -244,13 +244,13 @@ define(['mvc/toolshed/toolshed-model', prepareInstall: function(params, api_url) { var that = this; $.post(api_url, params, function(data) { - iri_parameters = JSON.parse(data); + var iri_parameters = JSON.parse(data); that.doInstall(iri_parameters); }); }, doInstall: function(params) { - controller_url = Galaxy.root + 'admin_toolshed/manage_repositories'; + var controller_url = Galaxy.root + 'admin_toolshed/manage_repositories'; var repositories = params.repositories; var new_route = 'status/r/' + repositories.join('|'); $.post(controller_url, params, function(data) { diff --git a/client/galaxy/scripts/mvc/toolshed/util.js b/client/galaxy/scripts/mvc/toolshed/util.js index ed052ecb8e81..143309f6df47 100644 --- a/client/galaxy/scripts/mvc/toolshed/util.js +++ b/client/galaxy/scripts/mvc/toolshed/util.js @@ -4,7 +4,7 @@ define([], function() { var shed_url = this.shed_url; var base_url = Galaxy.root + 'api/tool_shed/search'; $.get(base_url, {term: request.term, tool_shed_url: shed_url}, function(data) { - result_list = that.shedParser(data); + var result_list = that.shedParser(data); response(result_list); }); @@ -16,7 +16,7 @@ define([], function() { $.each(hits, function(hit) { var record = hits[hit]; var label = record.repository.name + ' by ' + record.repository.repo_owner_username + ': ' + record.repository.description; - result = {value: record.repository.id, label: label}; + var result = {value: record.repository.id, label: label}; results.push(result); }); return results; @@ -37,8 +37,8 @@ define([], function() { var queueLength = function() { if (localStorage.hasOwnProperty('repositories')) { - repo_queue = JSON.parse(localStorage.repositories); - queue_length = Object.keys(repo_queue).length; + var repo_queue = JSON.parse(localStorage.repositories); + var queue_length = Object.keys(repo_queue).length; return queue_length; } else { diff --git a/client/galaxy/scripts/mvc/toolshed/workflows-view.js b/client/galaxy/scripts/mvc/toolshed/workflows-view.js index bb47d9ba65a3..45ea8faacb9d 100644 --- a/client/galaxy/scripts/mvc/toolshed/workflows-view.js +++ b/client/galaxy/scripts/mvc/toolshed/workflows-view.js @@ -24,7 +24,7 @@ define(['mvc/toolshed/toolshed-model', 'mvc/toolshed/util'], function(toolshed_m }, bindEvents: function() { - var that = this; + var that = this, repository_id; $('.show_wf_repo').on('click', function() { var tool_ids = $(this).attr('data-toolids'); var toolshed = $(this).attr('data-shed'); diff --git a/client/galaxy/scripts/mvc/ui/ui-buttons.js b/client/galaxy/scripts/mvc/ui/ui-buttons.js index 03c053fb39c1..220b4c8c9c4b 100644 --- a/client/galaxy/scripts/mvc/ui/ui-buttons.js +++ b/client/galaxy/scripts/mvc/ui/ui-buttons.js @@ -6,7 +6,6 @@ define( [ 'utils/utils' ], function( Utils ) { this.model = options && options.model || new Backbone.Model({ id : Utils.uid(), title : '', - floating : 'right', icon : '', cls : 'btn btn-default', wait : false, @@ -31,7 +30,6 @@ define( [ 'utils/utils' ], function( Utils ) { .addClass( options.disabled && 'disabled' ) .attr( 'id', options.id ) .attr( 'disabled', options.disabled ) - .css( 'float', options.floating ) .off( 'click' ).on( 'click' , function() { $( '.tooltip' ).hide(); options.onclick && !self.disabled && options.onclick(); @@ -174,7 +172,6 @@ define( [ 'utils/utils' ], function( Utils ) { this.model = options && options.model || new Backbone.Model({ id : Utils.uid(), title : '', - floating : 'right', icon : '', cls : 'ui-button-icon', disabled : false @@ -193,7 +190,6 @@ define( [ 'utils/utils' ], function( Utils ) { .addClass( options.disabled && 'disabled' ) .attr( 'disabled', options.disabled ) .attr( 'id', options.id ) - .css( 'float', options.floating ) .off( 'click' ).on( 'click', function() { $( '.tooltip' ).hide(); !options.disabled && options.onclick && options.onclick(); @@ -212,7 +208,6 @@ define( [ 'utils/utils' ], function( Utils ) { this.model = options && options.model || new Backbone.Model({ id : '', title : '', - floating : 'right', pull : 'right', icon : null, onclick : null, @@ -239,8 +234,7 @@ define( [ 'utils/utils' ], function( Utils ) { .addClass( 'dropdown' ) .addClass( options.cls ) .attr( 'id', options.id ) - .css( { float : options.floating, - display : options.visible && this.collection.where( { visible: true } ).length > 0 ? 'block' : 'none' } ); + .css( { display : options.visible && this.collection.where( { visible: true } ).length > 0 ? 'block' : 'none' } ); this.$root.addClass( 'root button dropdown-toggle' ) .attr( 'data-toggle', 'dropdown' ) .tooltip( { title: options.tooltip, placement: 'bottom' } ) diff --git a/client/galaxy/scripts/mvc/ui/ui-drilldown.js b/client/galaxy/scripts/mvc/ui/ui-drilldown.js index 71627f2e6517..88f56957b5b8 100644 --- a/client/galaxy/scripts/mvc/ui/ui-drilldown.js +++ b/client/galaxy/scripts/mvc/ui/ui-drilldown.js @@ -53,7 +53,7 @@ var View = Options.BaseIcons.extend({ // recursive function which iterates through options function iterate ( $tmpl, options, header ) { header = header || []; - for ( i in options ) { + for (var i in options ) { var level = options[ i ]; var has_options = level.options && level.options.length > 0; var new_header = header.slice( 0 ); diff --git a/client/galaxy/scripts/mvc/ui/ui-list.js b/client/galaxy/scripts/mvc/ui/ui-list.js index bdc7d080d291..368d5ce170c0 100644 --- a/client/galaxy/scripts/mvc/ui/ui-list.js +++ b/client/galaxy/scripts/mvc/ui/ui-list.js @@ -25,7 +25,6 @@ var View = Backbone.View.extend({ // create insert new list element button this.button = new Ui.ButtonIcon({ icon : 'fa fa-sign-in', - floating : 'left', tooltip : 'Insert new ' + this.name, onclick : function() { self.add({ diff --git a/client/galaxy/scripts/mvc/ui/ui-popover.js b/client/galaxy/scripts/mvc/ui/ui-popover.js index 3227a1c4408d..7d8f013b4196 100644 --- a/client/galaxy/scripts/mvc/ui/ui-popover.js +++ b/client/galaxy/scripts/mvc/ui/ui-popover.js @@ -116,7 +116,8 @@ var View = Backbone.View.extend({ var container_position = $container.position(); // get position - var top = left = 0; + var top, left; + top = left = 0; if ([ 'top', 'bottom' ].indexOf( placement ) != -1) { left = container_position.left - width + ( container_width + width ) / 2; switch ( placement ) { diff --git a/client/galaxy/scripts/mvc/ui/ui-select-content.js b/client/galaxy/scripts/mvc/ui/ui-select-content.js index 274fbeb774fa..37844e9110ba 100644 --- a/client/galaxy/scripts/mvc/ui/ui-select-content.js +++ b/client/galaxy/scripts/mvc/ui/ui-select-content.js @@ -175,7 +175,7 @@ var View = Backbone.View.extend({ optional : self.model.get( 'optional' ), multiple : c.multiple, searchable : !c.multiple || ( data && data[ c.src ] && data[ c.src ].length > self.model.get( 'pagelimit' ) ), - selectall : false, + individual : true, error_text : 'No ' + ( extensions ? extensions + ' ' : '' ) + ( src_labels[ c.src ] || 'content' ) + ' available.', onchange : function() { self.trigger( 'change' ); diff --git a/client/galaxy/scripts/mvc/ui/ui-select-default.js b/client/galaxy/scripts/mvc/ui/ui-select-default.js index 4d0671344a52..4d14bc6936a1 100644 --- a/client/galaxy/scripts/mvc/ui/ui-select-default.js +++ b/client/galaxy/scripts/mvc/ui/ui-select-default.js @@ -20,7 +20,7 @@ var View = Backbone.View.extend({ disabled : false, onchange : function(){}, value : null, - selectall : true, + individual : false, pagesize : 20 }).set( options ); this.on( 'change', function() { self.model.get( 'onchange' ) && self.model.get( 'onchange' )( self.value() ) } ); @@ -94,7 +94,7 @@ var View = Backbone.View.extend({ }); } this.all_button = null; - if ( this.model.get( 'multiple' ) && this.model.get( 'selectall' ) ) { + if ( this.model.get( 'multiple' ) && !this.model.get( 'individual' ) ) { this.all_button = new Buttons.ButtonCheck({ onclick: function() { var new_value = []; @@ -315,7 +315,7 @@ var View = Backbone.View.extend({ } if ( this.model.get( 'searchable' ) ) { if ( $.isArray( new_value ) ) { - val = []; + var val = []; _.each( new_value, function( v ) { var d = _.findWhere( self.data2, { id: v } ); d && val.push( d ); diff --git a/client/galaxy/scripts/mvc/ui/ui-select-genomespace.js b/client/galaxy/scripts/mvc/ui/ui-select-genomespace.js new file mode 100644 index 000000000000..61e382f67829 --- /dev/null +++ b/client/galaxy/scripts/mvc/ui/ui-select-genomespace.js @@ -0,0 +1,96 @@ +// dependencies +define(['utils/utils', 'mvc/ui/ui-misc', 'mvc/tool/tool-genomespace'], + function(Utils, Ui, GenomespaceBrowser) { + +/** + * GenomeSpace file selector + */ +var View = Backbone.View.extend({ + // initialize + initialize : function(options) { + + // link this + var self = this; + + // create insert new list element button + this.browse_button = new Ui.ButtonIcon({ + title : 'Browse', + icon : 'fa fa-sign-in', + tooltip : 'Browse GenomeSpace', + onclick : function() { + self.browseGenomeSpace(); + } + }); + + // create genomespace filepath textbox + this.filename_textbox = new Ui.Input(); + + // create genomespace token textbox + this.token_textbox = new Ui.Input({ + type : 'password' + }); + + // create elements + this.setElement(this._template(options)); + this.$('.ui-gs-browse-button').append(this.browse_button.$el); + this.$('.ui-gs-filename-textbox').append(this.filename_textbox.$el); + this.$('.ui-gs-token-textbox').append(this.token_textbox.$el); + }, + + /** Browse GenomeSpace */ + browseGenomeSpace: function(options) { + var self = this; + GenomespaceBrowser.openFileBrowser({ + successCallback: function(data) { + self.value(data.destination + "^" + data.token); + } + }); + }, + + /** Main Template */ + _template: function(options) { + return '
    ' + + '
    ' + + '' + + '' + + '
    ' + + '
    ' + + '
    Token
    ' + + '' + + '
    ' + + '
    '; + }, + + /** Return/Set currently selected genomespace filename/token */ + value : function (new_value) { + // check if new_value is defined + if (new_value !== undefined) { + this._setValue(new_value); + } + else { + return this._getValue(); + } + }, + + // get value + _getValue: function() { + return this.filename_textbox.value() + + "^" + this.token_textbox.value(); + }, + + // set value + _setValue: function(new_value) { + if (new_value) { + values = new_value.split("^"); + this.filename_textbox.value(values[0]); + this.token_textbox.value(values[1]); + } + }, + +}); + +return { + View: View +} + +}); diff --git a/client/galaxy/scripts/mvc/upload/upload-ftp.js b/client/galaxy/scripts/mvc/upload/upload-ftp.js index 4e36750f55f5..b3547f82eee7 100644 --- a/client/galaxy/scripts/mvc/upload/upload-ftp.js +++ b/client/galaxy/scripts/mvc/upload/upload-ftp.js @@ -27,7 +27,7 @@ define( [ 'utils/utils' ], function( Utils ) { if ( ftp_files && ftp_files.length > 0 ) { this.$( '.upload-ftp-content' ).html( $( this._templateTable() ) ); var size = 0; - for ( index in ftp_files ) { + for (var index in ftp_files ) { this.rows.push( this._add( ftp_files[ index ] ) ); size += ftp_files[ index ].size; } @@ -39,7 +39,7 @@ define( [ 'utils/utils' ], function( Utils ) { this.$select_all = this.$( '.upload-selectall' ).addClass( this.options.class_add ); this.$select_all.on( 'click', function() { var add = self.$select_all.hasClass( self.options.class_add ); - for ( index in ftp_files ) { + for (var index in ftp_files ) { var ftp_file = ftp_files[ index ]; var model_index = self._find( ftp_file ); if( !model_index && add || model_index && !add ) { diff --git a/client/galaxy/scripts/mvc/upload/upload-view.js b/client/galaxy/scripts/mvc/upload/upload-view.js index e6a8a2dc839c..38e6b8329d17 100644 --- a/client/galaxy/scripts/mvc/upload/upload-view.js +++ b/client/galaxy/scripts/mvc/upload/upload-view.js @@ -48,7 +48,7 @@ function( Utils, Modal, Tabs, UploadButton, UploadViewDefault, UploadViewComposi Utils.get({ url : Galaxy.root + 'api/datatypes?extension_only=False', success : function( datatypes ) { - for ( key in datatypes ) { + for (var key in datatypes ) { self.list_extensions.push({ id : datatypes[ key ].extension, text : datatypes[ key ].extension, @@ -72,7 +72,7 @@ function( Utils, Modal, Tabs, UploadButton, UploadViewDefault, UploadViewComposi Utils.get({ url : Galaxy.root + 'api/genomes', success : function( genomes ) { - for ( key in genomes ) { + for (var key in genomes ) { self.list_genomes.push({ id : genomes[ key ][ 1 ], text : genomes[ key ][ 0 ] diff --git a/client/galaxy/scripts/mvc/user/user-custom-builds.js b/client/galaxy/scripts/mvc/user/user-custom-builds.js index b9b8438c882e..b3b82e8b2873 100644 --- a/client/galaxy/scripts/mvc/user/user-custom-builds.js +++ b/client/galaxy/scripts/mvc/user/user-custom-builds.js @@ -126,8 +126,7 @@ define( [ 'utils/utils', 'mvc/ui/ui-misc', 'mvc/form/form-view', 'mvc/ui/ui-tabl icon : 'fa-save', tooltip : 'Create new Build', title : 'Save', - cls : 'ui-button btn btn-primary', - floating : 'clear', + cls : 'btn btn-primary ui-clear-float', onclick : function() { var data = form.data.create(); if ( !data.id || !data.name ) { diff --git a/client/galaxy/scripts/mvc/user/user-preferences.js b/client/galaxy/scripts/mvc/user/user-preferences.js index 06a504e48cd0..62edb56cd431 100644 --- a/client/galaxy/scripts/mvc/user/user-preferences.js +++ b/client/galaxy/scripts/mvc/user/user-preferences.js @@ -1,5 +1,5 @@ /** User Preferences view */ -define( [ 'mvc/form/form-view', 'mvc/ui/ui-misc' ], function( Form, Ui ) { +define( [ 'mvc/form/form-view', 'mvc/ui/ui-misc', 'utils/query-string-parsing' ], function( Form, Ui, QueryStringParsing ) { /** Contains descriptive dictionaries describing user forms */ var Model = Backbone.Model.extend({ @@ -12,7 +12,8 @@ define( [ 'mvc/form/form-view', 'mvc/ui/ui-misc' ], function( Form, Ui ) { title : 'Manage information', description : 'Edit your email, addresses and custom parameters or change your username.', url : 'api/users/' + options.user_id + '/information/inputs', - icon : 'fa-user' + icon : 'fa-user', + redirect : 'user' }, 'password': { title : 'Change password', @@ -20,19 +21,22 @@ define( [ 'mvc/form/form-view', 'mvc/ui/ui-misc' ], function( Form, Ui ) { icon : 'fa-unlock-alt', url : 'api/users/' + options.user_id + '/password/inputs', submit_title : 'Save password', + redirect : 'user' }, 'communication': { title : 'Change communication settings', description : 'Enable or disable the communication feature to chat with other users.', url : 'api/users/' + options.user_id + '/communication/inputs', - icon : 'fa-comments-o' + icon : 'fa-comments-o', + redirect : 'user' }, 'permissions': { title : 'Set dataset permissions for new histories', description : 'Grant others default access to newly created histories. Changes made here will only affect histories created after these settings have been stored.', url : 'api/users/' + options.user_id + '/permissions/inputs', icon : 'fa-users', - submit_title : 'Save permissions' + submit_title : 'Save permissions', + redirect : 'user' }, 'api_key': { title : 'Manage API key', @@ -47,7 +51,8 @@ define( [ 'mvc/form/form-view', 'mvc/ui/ui-misc' ], function( Form, Ui ) { description : 'Customize your Toolbox by displaying or omitting sets of Tools.', url : 'api/users/' + options.user_id + '/toolbox_filters/inputs', icon : 'fa-filter', - submit_title : 'Save filters' + submit_title : 'Save filters', + redirect : 'user' }, 'openids': { title : 'Manage OpenIDs', @@ -65,14 +70,6 @@ define( [ 'mvc/form/form-view', 'mvc/ui/ui-misc' ], function( Form, Ui ) { window.location.href = Galaxy.root + 'custom_builds'; } }, - 'configure_menu': { - title : 'Configure workflow menu', - description : 'Configure your workflow items which appear in the Tool panel.', - icon : 'fa-cog', - onclick : function() { - window.location.href = Galaxy.root + 'workflow/configure_menu'; - } - }, 'logout': { title : 'Sign out', description : 'Click here to sign out of all sessions.', @@ -83,7 +80,7 @@ define( [ 'mvc/form/form-view', 'mvc/ui/ui-misc' ], function( Form, Ui ) { body : 'Do you want to continue and sign out of all active sessions?', buttons : { 'Cancel' : function() { Galaxy.modal.hide(); }, - 'Sign out' : function() { window.location.href = Galaxy.root + 'user/logout'; } + 'Sign out' : function() { window.location.href = Galaxy.root + 'user/logout?session_csrf_token=' + Galaxy.session_csrf_token; } } }); } @@ -97,7 +94,6 @@ define( [ 'mvc/form/form-view', 'mvc/ui/ui-misc' ], function( Form, Ui ) { initialize: function() { this.model = new Model(); - this.message = new Ui.Message(); this.setElement( '
    ' ); this.render(); }, @@ -107,10 +103,14 @@ define( [ 'mvc/form/form-view', 'mvc/ui/ui-misc' ], function( Form, Ui ) { var config = Galaxy.config; $.getJSON( Galaxy.root + 'api/users/' + Galaxy.user.id, function( data ) { self.$preferences = $( '
    ' ).addClass( 'ui-panel' ) - .append( self.message.$el ) .append( $( '

    ' ).append( 'User preferences' ) ) .append( $( '

    ' ).append( 'You are logged in as ' + _.escape( data.email ) + '.' ) ) .append( self.$table = $( '' ).addClass( 'ui-panel-table' ) ); + var message = QueryStringParsing.get( 'message' ); + var status = QueryStringParsing.get( 'status' ); + if( message && status ) { + self.$preferences.prepend( ( new Ui.Message( { message: message, status: status } ) ).$el ); + } if( !config.use_remote_user ) { self._addLink( 'information' ); self._addLink( 'password' ); @@ -127,8 +127,9 @@ define( [ 'mvc/form/form-view', 'mvc/ui/ui-misc' ], function( Form, Ui ) { if( config.enable_openid && !config.use_remote_user ) { self._addLink( 'openids' ); } - self._addLink( 'configure_menu' ); - self._addLink( 'logout' ); + if(Galaxy.session_csrf_token) { + self._addLink( 'logout' ); + } self.$preferences.append( self._templateFooter( data ) ); self.$el.empty().append( self.$preferences ); }); @@ -167,66 +168,8 @@ define( [ 'mvc/form/form-view', 'mvc/ui/ui-misc' ], function( Form, Ui ) { } }); - /** View of individual user forms */ - var Forms = Backbone.View.extend({ - - initialize: function( options ) { - this.model = new Model( options ); - this.page = this.model.get( options.form_id ); - this.setElement( '
    ' ); - this.render(); - }, - - render: function() { - var self = this; - $.ajax({ - url : Galaxy.root + this.page.url, - type : 'GET' - }).done( function( response ) { - var options = $.extend( {}, self.page, response ); - var form = new Form({ - title : options.title, - icon : options.icon, - inputs : options.inputs, - operations: { - 'submit': new Ui.ButtonIcon({ - tooltip : options.submit_tooltip, - title : options.submit_title || 'Save settings', - icon : options.submit_icon || 'fa-save', - onclick : function() { self._submit( form, options ) } - }) - } - }); - self.$el.empty().append( form.$el ); - }).fail( function( response ) { - self.$el.empty().append( new Ui.Message({ - message : 'Failed to load resource ' + self.page.url + '.', - status : 'danger', - persistent : true - }).$el ); - }); - }, - - _submit: function( form, options ) { - var self = this; - $.ajax( { - url : Galaxy.root + options.url, - data : JSON.stringify( form.data.create() ), - type : 'PUT', - contentType : 'application/json' - }).done( function( response ) { - form.data.matchModel( response, function ( input, input_id ) { - form.field_list[ input_id ].value( input.value ); - }); - form.message.update( { message: response.message, status: 'success' } ); - }).fail( function( response ) { - form.message.update( { message: response.responseJSON.err_msg, status: 'danger' } ); - }); - } - }); - return { View : View, - Forms : Forms + Model : Model }; }); diff --git a/client/galaxy/scripts/mvc/workflow/workflow-configure-menu.js b/client/galaxy/scripts/mvc/workflow/workflow-configure-menu.js deleted file mode 100644 index 4e262aa56f90..000000000000 --- a/client/galaxy/scripts/mvc/workflow/workflow-configure-menu.js +++ /dev/null @@ -1,153 +0,0 @@ -/** Configure Workflow Menu View */ -define( [], function() { - var View = Backbone.View.extend({ - - initialize: function( options ) { - this.setElement( '
    ' ); - this.render(); - }, - - render: function() { - var self = this; - $.getJSON( Galaxy.root + 'api/workflows/menu/', function( response ) { - var workflows = response.workflows, - ids_in_menu = response.ids_in_menu, - $el_config_worflow = null; - - // Add configure workflow header - self.$el.empty().append( self._templateConfigWorkflowHeader() ); - $el_config_worflow = self.$el.find( '.configure-workflows' ); - $el_config_worflow.append( self._templateActionButtons() ); - if( workflows.length > 0 ) { - $el_config_worflow.append( self._templateConfigureWorkflow( self, workflows, ids_in_menu ) ); - self.save_workflow_menu( self ); - self.make_checked( self, ids_in_menu ); - self.register_check_uncheck_all( self ); - } - else { - $el_config_worflow.append( self._templateNoWorkflow() ); - } - }); - }, - - /** Register check and uncheck all callbacks*/ - register_check_uncheck_all: function( self ) { - var $el_check_all = self.$el.find( '.check-all-wf' ), - $el_uncheck_all = self.$el.find( '.uncheck-all-wf' ); - - $el_check_all.click(function( e ) { - self.check_uncheck_all( self, true ); - }); - $el_uncheck_all.click(function( e ) { - self.check_uncheck_all( self, false ); - }); - }, - - /** Check or uncheck all workflows */ - check_uncheck_all: function( self, checked ) { - $.each(self.$el.find( '.wf-config-item' ), function() { - var wf_checkbox = $( this )[0]; - wf_checkbox.checked = checked; - }); - }, - - /** Make the worflows as checked if present in the menu */ - make_checked: function( self, ids_in_menu ) { - $.each(self.$el.find( '.wf-config-item' ), function() { - var wf_checkbox = $( this )[0]; - _.each( ids_in_menu, function( id ) { - if ( parseInt( wf_checkbox.value ) === id ) { - wf_checkbox.checked = true; - } - }); - }); - }, - - /** Save the changes for workflow menu */ - save_workflow_menu: function( self ) { - var $el_save_workflow_menu = self.$el.find( '.wf-save-menu' ); - $el_save_workflow_menu.click( function( e ) { - var ids = []; - $.each(self.$el.find( '.wf-config-item' ), function() { - var wf_checkbox = $( this )[0]; - if( wf_checkbox.checked || wf_checkbox.checked === 'true' ) { - ids.push( parseInt( wf_checkbox.value ) ); - } - }); - $.ajax({ - type: 'PUT', - url: Galaxy.root + 'api/workflows/menu/', - data: JSON.stringify( { 'workflow_ids': ids } ), - contentType : 'application/json' - }).done( function( response ) { - window.location = Galaxy.root + 'user'; - }); - }); - }, - - /** Template for actions buttons */ - _templateActionButtons: function() { - return ''; - }, - - /** Template for configure workflow table */ - _templateConfigureWorkflow: function( self, workflows, ids_in_menu ) { - var tableHtml = "", trHtml = ""; - tableHtml = tableHtml + '
    ' + - '' + - '' + - '' + - '' + - '' + - ''; - _.each( workflows, function( wf ) { - trHtml = trHtml + '' + - '' + - '' + - '' + - '' + - ''; - }); - tableHtml = tableHtml + '' + trHtml + '
    NameOwner# of StepsShow in menu
    ' + _.escape( wf.name ) +'' + ( wf.owner === Galaxy.user.attributes.username ? "You" : wf.owner ) +'' + wf.number_of_steps + '' + self._templateInputCheckbox( self, wf, ids_in_menu ) + '
    '; - tableHtml = tableHtml + '' + - 'Save' + - '' + - '' + - 'Back to User Preferences' + - ''; - return tableHtml; - }, - - /** Template for no workflow */ - _templateNoWorkflow: function() { - return '

    You do not have any accessible workflows.
    '; - }, - - /** Template for checkboxes */ - _templateInputCheckbox: function( self, wf ) { - return ''; - }, - - /** Template for main config workflow menu */ - _templateConfigWorkflowHeader: function() { - return '
    ' + - '
    ' + - '

    Configure workflow menu

    ' + - '
    '+ - '
    '; - } - }); - - return { - View : View - }; -}); diff --git a/client/galaxy/scripts/mvc/workflow/workflow-forms.js b/client/galaxy/scripts/mvc/workflow/workflow-forms.js index 1aed0dd189a9..7f70a4087a35 100644 --- a/client/galaxy/scripts/mvc/workflow/workflow-forms.js +++ b/client/galaxy/scripts/mvc/workflow/workflow-forms.js @@ -3,119 +3,198 @@ define( [ 'utils/utils', 'mvc/form/form-view', 'mvc/tool/tool-form-base' ], func /** Default form wrapper for non-tool modules in the workflow editor. */ var Default = Backbone.View.extend({ initialize: function( options ) { - this.form = new Form( options ); + var self = this; + var node = options.node; + this.form = new Form( Utils.merge( options, { + onchange: function() { + Utils.request({ + type : 'POST', + url : Galaxy.root + 'api/workflows/build_module', + data : { + id : node.id, + type : node.type, + content_id : node.content_id, + inputs : self.form.data.create() + }, + success : function( data ) { + node.update_field_data( data ); + } + } ); + } + } ) ); + _addLabelAnnotation( this.form ); + this.form.render(); } }); /** Tool form wrapper for the workflow editor. */ var Tool = Backbone.View.extend({ initialize: function( options ) { - var self = this; - this.workflow = options.workflow; - this.node = options.node; - if ( this.node ) { - this.post_job_actions = this.node.post_job_actions || {}; - Utils.deepeach( options.inputs, function( input ) { - if ( input.type ) { - if ( [ 'data', 'data_collection' ].indexOf( input.type ) != -1 ) { - input.type = 'hidden'; - input.info = 'Data input \'' + input.name + '\' (' + Utils.textify( input.extensions ) + ')'; - input.value = { '__class__': 'RuntimeValue' }; - } else if ( !input.fixed ) { - input.collapsible_value = { '__class__': 'RuntimeValue' }; - input.is_workflow = ( input.options && input.options.length == 0 ) || - ( [ 'integer', 'float' ].indexOf( input.type ) != -1 ); - } + var self = this; + var node = options.node; + this.form = new ToolFormBase( Utils.merge( options, { + text_enable : 'Set in Advance', + text_disable : 'Set at Runtime', + narrow : true, + initial_errors : true, + cls : 'ui-portlet-narrow', + initialmodel : function( process, form ) { + self._customize( form ); + process.resolve(); + }, + buildmodel : function( process, form ) { + form.model.get( 'postchange' )( process, form ); + }, + postchange : function( process, form ) { + var options = form.model.attributes; + var current_state = { + tool_id : options.id, + tool_version : options.version, + type : 'tool', + inputs : $.extend( true, {}, form.data.create() ) } - }); - Utils.deepeach( options.inputs, function( input ) { - input.type == 'conditional' && ( input.test_param.collapsible_value = undefined ); - }); - this._makeSections( options ); - this.form = new ToolFormBase( Utils.merge( options, { - text_enable : 'Set in Advance', - text_disable : 'Set at Runtime', - narrow : true, - initial_errors : true, - sustain_version : true, - cls : 'ui-portlet-narrow', - postchange : function( process, form ) { - var options = form.model.attributes; - var current_state = { - tool_id : options.id, - tool_version : options.version, - type : 'tool', - inputs : $.extend( true, {}, form.data.create() ) + Galaxy.emit.debug( 'tool-form-workflow::postchange()', 'Sending current state.', current_state ); + Utils.request({ + type : 'POST', + url : Galaxy.root + 'api/workflows/build_module', + data : current_state, + success : function( data ) { + form.model.set( data.config_form ); + self._customize( form ); + form.update( data.config_form ); + form.errors( data.config_form ); + // This hasn't modified the workflow, just returned + // module information for the tool to update the workflow + // state stored on the client with. User needs to save + // for this to take effect. + node.update_field_data( data ); + Galaxy.emit.debug( 'tool-form-workflow::postchange()', 'Received new model.', data ); + process.resolve(); + }, + error : function( response ) { + Galaxy.emit.debug( 'tool-form-workflow::postchange()', 'Refresh request failed.', response ); + process.reject(); } - Galaxy.emit.debug( 'tool-form-workflow::postchange()', 'Sending current state.', current_state ); - Utils.request({ - type : 'POST', - url : Galaxy.root + 'api/workflows/build_module', - data : current_state, - success : function( data ) { - form.update( data.config_form ); - form.errors( data.config_form ); - // This hasn't modified the workflow, just returned - // module information for the tool to update the workflow - // state stored on the client with. User needs to save - // for this to take effect. - self.node.update_field_data( data ); - Galaxy.emit.debug( 'tool-form-workflow::postchange()', 'Received new model.', data ); - process.resolve(); - }, - error : function( response ) { - Galaxy.emit.debug( 'tool-form-workflow::postchange()', 'Refresh request failed.', response ); - process.reject(); - } - }); - }, - })); - } else { - Galaxy.emit.debug('tool-form-workflow::initialize()', 'Node not found in workflow.'); - } + }); + } + })); }, - /** Builds all sub sections */ - _makeSections: function( options ){ - var inputs = options.inputs; - var datatypes = options.datatypes; - var output_id = this.node.output_terminals && Object.keys( this.node.output_terminals )[ 0 ]; - if ( output_id ) { - inputs.push({ - name : 'pja__' + output_id + '__EmailAction', - label : 'Email notification', - type : 'boolean', - value : String( Boolean( this.post_job_actions[ 'EmailAction' + output_id ] ) ), - ignore : 'false', - help : 'An email notification will be sent when the job has completed.', - payload : { - 'host' : window.location.host + _customize: function( form ) { + var options = form.model.attributes; + Utils.deepeach( options.inputs, function( input ) { + if ( input.type ) { + if ( [ 'data', 'data_collection' ].indexOf( input.type ) != -1 ) { + input.type = 'hidden'; + input.info = 'Data input \'' + input.name + '\' (' + Utils.textify( input.extensions ) + ')'; + input.value = { '__class__': 'RuntimeValue' }; + } else if ( !input.fixed ) { + input.collapsible_value = { '__class__': 'RuntimeValue' }; + input.is_workflow = ( input.options && input.options.length == 0 ) || + ( [ 'integer', 'float' ].indexOf( input.type ) != -1 ); + } + } + }); + Utils.deepeach( options.inputs, function( input ) { + input.type == 'conditional' && ( input.test_param.collapsible_value = undefined ); + }); + _addSections( form ); + _addLabelAnnotation( form ); + } + }); + + /** Augments the module form definition by adding label and annotation fields */ + function _addLabelAnnotation ( form ) { + var options = form.model.attributes; + var workflow = options.workflow; + var node = options.node; + options.inputs.unshift({ + type : 'text', + name : '__annotation', + label : 'Annotation', + fixed : true, + value : node.annotation, + area : true, + help : 'Add an annotation or notes to this step. Annotations are available when a workflow is viewed.' + }); + options.inputs.unshift({ + type : 'text', + name : '__label', + label : 'Label', + value : node.label, + help : 'Add a step label.', + fixed : true, + onchange: function( new_label ) { + var duplicate = false; + for ( var i in workflow.nodes ) { + var n = workflow.nodes[ i ]; + if ( n.label && n.label == new_label && n.id != node.id ) { + duplicate = true; + break; } - }); - inputs.push({ - name : 'pja__' + output_id + '__DeleteIntermediatesAction', - label : 'Output cleanup', - type : 'boolean', - value : String( Boolean( this.post_job_actions[ 'DeleteIntermediatesAction' + output_id ] ) ), - ignore : 'false', - help : 'Upon completion of this step, delete non-starred outputs from completed workflow steps if they are no longer required as inputs.' - }); - for ( var i in this.node.output_terminals ) { - inputs.push( this._makeSection( i, datatypes ) ); } + var input_id = form.data.match( '__label' ); + var input_element = form.element_list[ input_id ]; + input_element.model.set( 'error_text', duplicate && 'Duplicate label. Please fix this before saving the workflow.' ); + form.trigger( 'change' ); } - }, + }); + } + + /** Builds all sub sections */ + function _addSections( form ) { + var options = form.model.attributes; + var inputs = options.inputs; + var datatypes = options.datatypes; + var node = options.node; + var workflow = options.workflow; + var post_job_actions = node.post_job_actions; + var output_id = node.output_terminals && Object.keys( node.output_terminals )[ 0 ]; + + /** Visit input nodes and enrich by name/value pairs from server data */ + function visit( head, head_list ) { + head_list = head_list || []; + head_list.push( head ); + for ( var i in head.inputs ) { + var input = head.inputs[ i ]; + var action = input.action; + if ( action ) { + input.name = 'pja__' + output_id + '__' + input.action; + if ( input.pja_arg ) { + input.name += '__' + input.pja_arg; + } + if ( input.payload ) { + for ( var p_id in input.payload ) { + var p = input.payload[ p_id ]; + input.payload[ input.name + '__' + p_id ] = p; + delete p; + } + } + var d = post_job_actions[ input.action + output_id ]; + if ( d ) { + for ( var j in head_list ) { + head_list[ j ].expanded = true; + } + if ( input.pja_arg ) { + input.value = d.action_arguments && d.action_arguments[ input.pja_arg ] || input.value; + } else { + input.value = 'true'; + } + } + } + input.inputs && visit( input, head_list.slice( 0 ) ); + } + } /** Builds sub section with step actions/annotation */ - _makeSection: function( output_id, datatypes ){ - var self = this; + function _makeSection( output_id, datatypes ) { var extensions = []; var input_terminal_names = []; - for ( key in datatypes ) { + for (var key in datatypes ) { extensions.push( { 0 : datatypes[ key ], 1 : datatypes[ key ] } ); } - for ( key in this.node.input_terminals ){ - input_terminal_names.push( this.node.input_terminals[ key ].name ); + for ( key in node.input_terminals ){ + input_terminal_names.push( node.input_terminals[ key ].name ); } extensions.sort( function( a, b ) { return a.label > b.label ? 1 : a.label < b.label ? -1 : 0; @@ -132,6 +211,7 @@ define( [ 'utils/utils', 'mvc/form/form-view', 'mvc/tool/tool-form-base' ], func 0 : 'Leave unchanged', 1 : '__empty__' }); + var output; var input_config = { title : 'Configure Output: \'' + output_id + '\'', type : 'section', @@ -139,10 +219,10 @@ define( [ 'utils/utils', 'mvc/form/form-view', 'mvc/tool/tool-form-base' ], func inputs : [{ label : 'Label', type : 'text', - value : ( output = this.node.getWorkflowOutput( output_id ) ) && output.label || '', + value : ( output = node.getWorkflowOutput( output_id ) ) && output.label || '', help : 'This will provide a short name to describe the output - this must be unique across workflows.', onchange : function( new_value ) { - self.workflow.attemptUpdateOutputLabel( self.node, output_id, new_value ); + workflow.attemptUpdateOutputLabel( node, output_id, new_value ); } },{ action : 'RenameDatasetAction', @@ -164,11 +244,19 @@ define( [ 'utils/utils', 'mvc/form/form-view', 'mvc/tool/tool-form-base' ], func },{ action : 'TagDatasetAction', pja_arg : 'tags', - label : 'Tags', + label : 'Add Tags', type : 'text', value : '', ignore : '', help : 'This action will set tags for the dataset.' + },{ + action : 'RemoveTagDatasetAction', + pja_arg : 'tags', + label : 'Remove Tags', + type : 'text', + value : '', + ignore : '', + help : 'This action will remove tags for the dataset.' },{ title : 'Assign columns', type : 'section', @@ -212,45 +300,35 @@ define( [ 'utils/utils', 'mvc/form/form-view', 'mvc/tool/tool-form-base' ], func help : 'This action will set column assignments in the output dataset. Blank fields are ignored.' }] }; + visit( input_config ); + return input_config; + } - // visit input nodes and enrich by name/value pairs from server data - function visit ( head, head_list ) { - head_list = head_list || []; - head_list.push( head ); - for ( var i in head.inputs ) { - var input = head.inputs[ i ]; - var action = input.action; - if ( action ) { - input.name = 'pja__' + output_id + '__' + input.action; - if ( input.pja_arg ) { - input.name += '__' + input.pja_arg; - } - if ( input.payload ) { - for ( var p_id in input.payload ) { - var p = input.payload[ p_id ]; - input.payload[ input.name + '__' + p_id ] = p; - delete p; - } - } - var d = self.post_job_actions[ input.action + output_id ]; - if ( d ) { - for ( var j in head_list ) { - head_list[ j ].expanded = true; - } - if ( input.pja_arg ) { - input.value = d.action_arguments && d.action_arguments[ input.pja_arg ] || input.value; - } else { - input.value = 'true'; - } - } - } - input.inputs && visit( input, head_list.slice( 0 ) ); + if ( output_id ) { + inputs.push({ + name : 'pja__' + output_id + '__EmailAction', + label : 'Email notification', + type : 'boolean', + value : String( Boolean( post_job_actions[ 'EmailAction' + output_id ] ) ), + ignore : 'false', + help : 'An email notification will be sent when the job has completed.', + payload : { + 'host' : window.location.host } + }); + inputs.push({ + name : 'pja__' + output_id + '__DeleteIntermediatesAction', + label : 'Output cleanup', + type : 'boolean', + value : String( Boolean( post_job_actions[ 'DeleteIntermediatesAction' + output_id ] ) ), + ignore : 'false', + help : 'Upon completion of this step, delete non-starred outputs from completed workflow steps if they are no longer required as inputs.' + }); + for ( var i in node.output_terminals ) { + inputs.push( _makeSection( i, datatypes ) ); } - visit( input_config ); - return input_config; } - }); + } return { Default: Default, diff --git a/client/galaxy/scripts/mvc/workflow/workflow-manager.js b/client/galaxy/scripts/mvc/workflow/workflow-manager.js index 4e03d37b1319..feb4611e8229 100644 --- a/client/galaxy/scripts/mvc/workflow/workflow-manager.js +++ b/client/galaxy/scripts/mvc/workflow/workflow-manager.js @@ -182,15 +182,16 @@ function( Connector, Toastr ) { id : node.id, type : node.type, content_id : node.content_id, + tool_version : node.config_form.version, tool_state : node.tool_state, errors : node.errors, input_connections : input_connections, position : $(node.element).position(), - annotation: node.annotation, - post_job_actions: node.post_job_actions, - uuid: node.uuid, - label: node.label, - workflow_outputs: node.workflow_outputs + annotation : node.annotation, + post_job_actions : node.post_job_actions, + uuid : node.uuid, + label : node.label, + workflow_outputs : node.workflow_outputs }; nodes[ node.id ] = node_data; }); @@ -198,7 +199,7 @@ function( Connector, Toastr ) { }, from_simple : function ( data, initialImport_ ) { var initialImport = (initialImport_ === undefined) ? true : initialImport_; - wf = this; + var wf = this; var offset = 0; if( initialImport ) { wf.name = data.name; @@ -263,7 +264,7 @@ function( Connector, Toastr ) { $.each(node.output_terminals, function(ot_id, ot){ if(node.post_job_actions['HideDatasetAction'+ot.name] === undefined){ node.addWorkflowOutput(ot.name); - callout = $(node.element).find('.callout.'+ot.name); + var callout = $(node.element).find('.callout.'+ot.name); callout.find('img').attr('src', Galaxy.root + 'static/images/fugue/asterisk-small.png'); wf.has_changes = true; } @@ -337,10 +338,10 @@ function( Connector, Toastr ) { }); }); // Assemble order, tracking levels - node_ids_by_level = []; + var node_ids_by_level = []; while ( true ) { // Everything without a predecessor - level_parents = []; + var level_parents = []; for ( var pred_k in n_pred ) { if ( n_pred[ pred_k ] == 0 ) { level_parents.push( pred_k ); @@ -394,7 +395,7 @@ function( Connector, Toastr ) { ymin = Infinity, ymax = -Infinity, p; $.each( this.nodes, function( id, node ) { - e = $(node.element); + var e = $(node.element); p = e.position(); xmin = Math.min( xmin, p.left ); xmax = Math.max( xmax, p.left + e.width() ); @@ -410,7 +411,7 @@ function( Connector, Toastr ) { } function fix_delta( x, n ) { if ( x < n|| x > 3*n ) { - new_pos = ( Math.ceil( ( ( x % n ) ) / n ) + 1 ) * n; + var new_pos = ( Math.ceil( ( ( x % n ) ) / n ) + 1 ) * n; return ( - ( x - new_pos ) ); } return 0; diff --git a/client/galaxy/scripts/mvc/workflow/workflow-node.js b/client/galaxy/scripts/mvc/workflow/workflow-node.js index ed0ac71e1e6a..6beff7dcc419 100644 --- a/client/galaxy/scripts/mvc/workflow/workflow-node.js +++ b/client/galaxy/scripts/mvc/workflow/workflow-node.js @@ -154,6 +154,7 @@ define(['mvc/workflow/workflow-view-node'], function( NodeView ) { } this.name = data.name; this.config_form = data.config_form; + this.tool_version = this.config_form && this.config_form.version; this.tool_state = data.tool_state; this.errors = data.errors; this.tooltip = data.tooltip ? data.tooltip : ""; @@ -183,8 +184,55 @@ define(['mvc/workflow/workflow-view-node'], function( NodeView ) { update_field_data : function( data ) { var node = this; var nodeView = node.nodeView; + // remove unused output views and remove pre-existing output views from data.data_outputs, + // so that these are not added twice. + var unused_outputs = []; + // nodeView.outputViews contains pre-existing outputs, + // while data.data_output contains what should be displayed. + // Now we gather the unused outputs + $.each(nodeView.outputViews, function(i, output_view) { + var cur_name = output_view.output.name; + var data_names = data.data_outputs; + var cur_name_in_data_outputs = false; + _.each(data_names, function(data_name) { + if (data_name.name == cur_name) { + cur_name_in_data_outputs = true; + } + }); + if (cur_name_in_data_outputs === false) { + unused_outputs.push(cur_name) + } + }); + + // Remove the unused outputs + _.each(unused_outputs, function(unused_output) { + _.each(nodeView.outputViews[unused_output].terminalElement.terminal.connectors, function(x) { + if (x) { + x.destroy(); // Removes the noodle connectors + } + }); + nodeView.outputViews[unused_output].remove(); // removes the rendered output + delete nodeView.outputViews[unused_output]; // removes the reference to the output + delete node.output_terminals[unused_output]; // removes the output terminal + }); + $.each( node.workflow_outputs, function(i, wf_output){ + if (wf_output && !node.output_terminals[wf_output.output_name]) { + node.workflow_outputs.splice(i, 1); // removes output from list of workflow outputs + } + }); + $.each( data.data_outputs, function( i, output ) { + if (!nodeView.outputViews[output.name]) { + nodeView.addDataOutput(output); // add data output if it does not yet exist + } else { + // the output already exists, but the output formats may have changed. + // Therefore we update the datatypes and destroy invalid connections. + node.output_terminals[ output.name ].datatypes = output.extensions; + node.output_terminals[ output.name ].destroyInvalidConnections(); + } + }); this.tool_state = data.tool_state; this.config_form = data.config_form; + this.tool_version = this.config_form && this.config_form.version; this.errors = data.errors; this.annotation = data['annotation']; this.label = data.label; @@ -237,4 +285,4 @@ define(['mvc/workflow/workflow-view-node'], function( NodeView ) { } }); return Node; -}); \ No newline at end of file +}); diff --git a/client/galaxy/scripts/mvc/workflow/workflow-terminals.js b/client/galaxy/scripts/mvc/workflow/workflow-terminals.js index 8b244b6fa41d..45c2cf8f6296 100644 --- a/client/galaxy/scripts/mvc/workflow/workflow-terminals.js +++ b/client/galaxy/scripts/mvc/workflow/workflow-terminals.js @@ -4,6 +4,41 @@ define(['mvc/workflow/workflow-globals'], function( Globals ) { this.isCollection = true; this.rank = collectionType.split(":").length; } + + var NULL_COLLECTION_TYPE_DESCRIPTION = { + isCollection: false, + canMatch: function( other ) { return false; }, + canMapOver: function( other ) { + return false; + }, + toString: function() { + return "NullCollectionType[]"; + }, + append: function( otherCollectionType ) { + return otherCollectionType; + }, + equal: function( other ) { + return other === this; + } + }; + + var ANY_COLLECTION_TYPE_DESCRIPTION = { + isCollection: true, + canMatch: function( other ) { return NULL_COLLECTION_TYPE_DESCRIPTION !== other; }, + canMapOver: function( other ) { + return false; + }, + toString: function() { + return "AnyCollectionType[]"; + }, + append: function( otherCollectionType ) { + throw "Cannot append to ANY_COLLECTION_TYPE_DESCRIPTION"; + }, + equal: function( other ) { + return other === this; + } + }; + $.extend( CollectionTypeDescription.prototype, { append: function( otherCollectionTypeDescription ) { if( otherCollectionTypeDescription === NULL_COLLECTION_TYPE_DESCRIPTION ) { @@ -53,40 +88,6 @@ define(['mvc/workflow/workflow-globals'], function( Globals ) { } } ); - NULL_COLLECTION_TYPE_DESCRIPTION = { - isCollection: false, - canMatch: function( other ) { return false; }, - canMapOver: function( other ) { - return false; - }, - toString: function() { - return "NullCollectionType[]"; - }, - append: function( otherCollectionType ) { - return otherCollectionType; - }, - equal: function( other ) { - return other === this; - } - }; - - ANY_COLLECTION_TYPE_DESCRIPTION = { - isCollection: true, - canMatch: function( other ) { return NULL_COLLECTION_TYPE_DESCRIPTION !== other; }, - canMapOver: function( other ) { - return false; - }, - toString: function() { - return "AnyCollectionType[]"; - }, - append: function( otherCollectionType ) { - throw "Cannot append to ANY_COLLECTION_TYPE_DESCRIPTION"; - }, - equal: function( other ) { - return other === this; - } - }; - var TerminalMapping = Backbone.Model.extend( { initialize: function( attr ) { this.mapOver = attr.mapOver || NULL_COLLECTION_TYPE_DESCRIPTION; @@ -134,7 +135,7 @@ define(['mvc/workflow/workflow-globals'], function( Globals ) { }, destroyInvalidConnections: function( ) { _.each( this.connectors, function( connector ) { - connector.destroyIfInvalid(); + connector && connector.destroyIfInvalid(); } ); }, setMapOver : function( val ) { diff --git a/client/galaxy/scripts/mvc/workflow/workflow-view.js b/client/galaxy/scripts/mvc/workflow/workflow-view.js index c79870999b84..6934a9bc1eb4 100644 --- a/client/galaxy/scripts/mvc/workflow/workflow-view.js +++ b/client/galaxy/scripts/mvc/workflow/workflow-view.js @@ -64,7 +64,7 @@ define([ var close_editor = function() { self.workflow.check_changes_in_active_form(); if ( workflow && self.workflow.has_changes ) { - do_close = function() { + var do_close = function() { window.onbeforeunload = undefined; window.document.location = self.urls.workflow_index; }; @@ -227,7 +227,7 @@ define([ self.scroll_to_nodes(); self.canvas_manager.draw_overview(); // Determine if any parameters were 'upgraded' and provide message - upgrade_message = ""; + var upgrade_message = ""; _.each( data.steps, function( step, step_id ) { var details = ""; if ( step.errors ) { @@ -256,7 +256,7 @@ define([ "Save" : save_current_workflow, "Save As": workflow_save_as, "Run": function() { - window.location = self.urls.run_workflow; + window.location = Galaxy.root + "workflow/run?id=" + self.options.id; }, "Edit Attributes" : function() { self.workflow.clear_active_node() }, "Auto Re-layout": layout_editor, @@ -335,7 +335,7 @@ define([ } // On load, set the size to the pref stored in local storage if it exists - overview_size = $.jStorage.get("overview-size"); + var overview_size = $.jStorage.get("overview-size"); if (overview_size !== undefined) { $("#overview-border").css( { width: overview_size, @@ -468,7 +468,7 @@ define([ success: function( data ) { self.workflow.from_simple( data, false ); // Determine if any parameters were 'upgraded' and provide message - upgrade_message = ""; + var upgrade_message = ""; $.each( data.upgrade_messages, function( k, v ) { upgrade_message += ( "
  • Step " + ( parseInt(k, 10) + 1 ) + ": " + self.workflow.nodes[k].name + "
      "); $.each( v, function( i, vv ) { @@ -538,18 +538,18 @@ define([ // Add a new step to the workflow by tool id add_node_for_tool: function ( id, title ) { - node = this.workflow.create_node( 'tool', title, id ); + var node = this.workflow.create_node( 'tool', title, id ); this._moduleInitAjax(node, { type: "tool", tool_id: id, "_": "true" }); }, // Add a new step to the workflow by tool id add_node_for_subworkflow: function ( id, title ) { - node = this.workflow.create_node( 'subworkflow', title, id ); + var node = this.workflow.create_node( 'subworkflow', title, id ); this._moduleInitAjax(node, { type: "subworkflow", content_id: id, "_": "true" }); }, add_node_for_module: function ( type, title ) { - node = this.workflow.create_node( type, title ); + var node = this.workflow.create_node( type, title ); this._moduleInitAjax(node, { type: type, "_": "true" }); }, @@ -559,7 +559,7 @@ define([ var self = this; $("#pja_container").append( get_pja_form(pja, node) ); $("#pja_container>.toolForm:last>.toolFormTitle>.buttons").click(function (){ - action_to_rem = $(this).closest(".toolForm", ".action_tag").children(".action_tag:first").text(); + var action_to_rem = $(this).closest(".toolForm", ".action_tag").children(".action_tag:first").text(); $(this).closest(".toolForm").remove(); delete self.workflow.active_node.post_job_actions[action_to_rem]; self.workflow.active_form_has_changes = true; @@ -571,7 +571,7 @@ define([ }, display_file_list: function (node){ - addlist = ""; for (var out_terminal in node.output_terminals){ addlist += ""; } @@ -660,65 +660,18 @@ define([ var $container = $( '#' + cls ); if ( content && $container.find( '#' + id ).length == 0 ) { var $el = $( '
      ' ); - var form_wrapper = null; content.node = node; content.workflow = this.workflow; content.datatypes = this.datatypes; content.icon = WorkflowIcons[ node.type ]; content.cls = 'ui-portlet-narrow'; - content.inputs.unshift({ - type : 'text', - name : '__annotation', - label : 'Annotation', - fixed : true, - value : node.annotation, - area : true, - help : 'Add an annotation or notes to this step. Annotations are available when a workflow is viewed.' - }); - content.inputs.unshift({ - type : 'text', - name : '__label', - label : 'Label', - value : node.label, - help : 'Add a step label.', - fixed : true, - onchange: function( new_label ) { - var duplicate = false; - for ( var i in self.workflow.nodes ) { - var n = self.workflow.nodes[ i ]; - if ( n.label && n.label == new_label && n.id != node.id ) { - duplicate = true; - break; - } - } - var input_id = form_wrapper.form.data.match( '__label' ); - var input_element = form_wrapper.form.element_list[ input_id ]; - input_element.model.set( 'error_text', duplicate && 'Duplicate label. Please fix this before saving the workflow.' ); - form_wrapper.form.trigger( 'change' ); - } - }); - content.onchange = function() { - Utils.request({ - type : 'POST', - url : Galaxy.root + 'api/workflows/build_module', - data : { - id : node.id, - type : node.type, - content_id : node.content_id, - inputs : form_wrapper.form.data.create() - }, - success : function( data ) { - node.update_field_data( data ); - } - }); - }; - if ( node.type == 'tool' ) { - form_wrapper = new FormWrappers.Tool( content ); + if ( node ) { + var form_type = ( node.type == 'tool' ? 'Tool' : 'Default' ); + $el.append( ( new FormWrappers[ form_type ]( content ) ).form.$el ); + $container.append( $el ); } else { - form_wrapper = new FormWrappers.Default( content ); + Galaxy.emit.debug('workflow-view::initialize()', 'Node not found in workflow.'); } - $el.append( form_wrapper.form.$el ); - $container.append( $el ); } $( '.' + cls ).hide(); $container.find( '#' + id ).show(); diff --git a/client/galaxy/scripts/mvc/workflow/workflow.js b/client/galaxy/scripts/mvc/workflow/workflow.js index 35f4b07ff14f..988cabe61441 100644 --- a/client/galaxy/scripts/mvc/workflow/workflow.js +++ b/client/galaxy/scripts/mvc/workflow/workflow.js @@ -1,19 +1,17 @@ /** Workflow view */ -define( [ 'utils/utils' ], function( Utils ) { +define( [ 'utils/utils', 'mvc/ui/ui-misc' ], function( Utils, Ui ) { /** Build messages after user action */ - function build_messages( self ) { - var $el_message = self.$el.find( '.response-message' ), - status = Utils.getQueryString( 'status' ), - message = Utils.getQueryString( 'message' ); - - if( message && message !== null && message !== "" ) { - $el_message.addClass( status + 'message' ); - $el_message.html( '

      ' + _.escape( message ) + '

      ' ); - } - else { - $el_message.html(""); - } + function build_messages() { + var $el_message = this.$( '.response-message' ), + response = {}; + response = { + 'status': Utils.getQueryString( 'status' ), + 'message': _.escape( Utils.getQueryString( 'message' ) ), + 'persistent': true, + 'cls': Utils.getQueryString( 'status' ) + 'message' + }; + $el_message.empty().html( new Ui.Message( response ).$el ); } /** View of the main workflow list page */ @@ -32,8 +30,8 @@ define( [ 'utils/utils' ], function( Utils ) { // Add workflow header self.$el.empty().append( self._templateHeader() ); // Add user actions message if any - build_messages( self ); - $el_workflow = self.$el.find( '.user-workflows' ); + build_messages(); + $el_workflow = self.$( '.user-workflows' ); // Add the actions buttons $el_workflow.append( self._templateActionButtons() ); if( workflows.length > 0) { @@ -41,10 +39,11 @@ define( [ 'utils/utils' ], function( Utils ) { self.adjust_actiondropdown( $el_workflow ); // Register delete and run workflow events _.each( workflows, function( wf ) { - self.confirm_delete( self, wf ); + self.confirm_delete( wf ); }); + self.register_show_tool_menu(); // Register search workflow event - self.search_workflow( self, self.$el.find( '.search-wf' ), self.$el.find( '.workflow-search tr' ), min_query_length ); + self.search_workflow( self.$( '.search-wf' ), self.$( '.workflow-search tr' ), min_query_length ); } else { $el_workflow.append( self._templateNoWorkflow() ); @@ -52,10 +51,34 @@ define( [ 'utils/utils' ], function( Utils ) { }); }, + // Save the workflow as an item in Tool panel + register_show_tool_menu: function() { + var $el_checkboxes = this.$( '.show-in-tool-panel' ); + $el_checkboxes.on( 'click', function( e ) { + var ids = []; + // Look for all the checked checkboxes + for( var item = 0; item < $el_checkboxes.length; item++ ) { + var checkbox = $el_checkboxes[ item ]; + if( checkbox.checked ) { + ids.push( checkbox.value ); + } + } + // Save all the checked workflows + $.ajax({ + type: 'PUT', + url: Galaxy.root + 'api/workflows/menu/', + data: JSON.stringify( { 'workflow_ids': ids } ), + contentType : 'application/json' + }).done( function( response ) { + window.location = Galaxy.root + 'workflow'; + }); + }); + }, + /** Add confirm box before removing/unsharing workflow */ - confirm_delete: function( self, workflow ) { - var $el_wf_link = self.$el.find( '.link-confirm-' + workflow.id ), - $el_shared_wf_link = self.$el.find( '.link-confirm-shared-' + workflow.id ); + confirm_delete: function( workflow ) { + var $el_wf_link = this.$( '.link-confirm-' + workflow.id ), + $el_shared_wf_link = this.$( '.link-confirm-shared-' + workflow.id ); $el_wf_link.click( function() { return confirm( "Are you sure you want to delete workflow '" + workflow.name + "'?" ); }); @@ -65,7 +88,7 @@ define( [ 'utils/utils' ], function( Utils ) { }, /** Implement client side workflow search/filtering */ - search_workflow: function( self, $el_searchinput, $el_tabletr, min_querylen ) { + search_workflow: function( $el_searchinput, $el_tabletr, min_querylen ) { $el_searchinput.on( 'keyup', function () { var query = $( this ).val(); // Filter when query is at least 3 characters @@ -128,8 +151,10 @@ define( [ 'utils/utils' ], function( Utils ) { 'Owner' + '# of Steps' + 'Published' + + 'Show in tools panel' + ''; _.each( workflows, function( wf ) { + var checkbox_html = ''; trHtml = trHtml + '' + '' + '
  • ') + return "\n".join(rval) def regenerate_primary_file(self, dataset): """ cannot do this until we are setting metadata """ - log.debug( "Velvet log info %s" % 'JJ regenerate_primary_file') + log.debug("Velvet log info %s" % 'JJ regenerate_primary_file') gen_msg = '' try: efp = dataset.extra_files_path @@ -188,7 +188,7 @@ def regenerate_primary_file(self, dataset): log_content = f.read(1000) f.close() log_msg = re.sub('/\S*/', '', log_content) - log.debug( "Velveth log info %s" % log_msg) + log.debug("Velveth log info %s" % log_msg) paired_end_reads = re.search('-(short|long)Paired', log_msg) is not None dataset.metadata.paired_end_reads = paired_end_reads long_reads = re.search('-long', log_msg) is not None @@ -203,30 +203,30 @@ def regenerate_primary_file(self, dataset): if len(gen_msg) > 0: gen_msg = 'Uses: ' + gen_msg except: - log.debug( "Velveth could not read Log file in %s" % efp) - log.debug( "Velveth log info %s" % gen_msg) + log.debug("Velveth could not read Log file in %s" % efp) + log.debug("Velveth log info %s" % gen_msg) rval = ['Velvet Galaxy Composite Dataset

    '] # rval.append('

    Generated:

    %s

    ' %(re.sub('\n','
    ',log_msg))) rval.append('
    Generated:

    %s

    ' % (gen_msg)) rval.append('
    Velveth dataset:

      ') - for composite_name, composite_file in self.get_composite_files( dataset=dataset ).items(): + for composite_name, composite_file in self.get_composite_files(dataset=dataset).items(): fn = composite_name - log.debug( "Velvet log info %s %s %s" % ('JJ regenerate_primary_file', fn, composite_file)) + log.debug("Velvet log info %s %s %s" % ('JJ regenerate_primary_file', fn, composite_file)) if re.search('Log', fn) is None: opt_text = '' if composite_file.optional: opt_text = ' (optional)' if composite_file.get('description'): - rval.append( '
    • %s (%s)%s
    • ' % ( fn, fn, composite_file.get('description'), opt_text ) ) + rval.append('
    • %s (%s)%s
    • ' % (fn, fn, composite_file.get('description'), opt_text)) else: - rval.append( '
    • %s%s
    • ' % ( fn, fn, opt_text ) ) - rval.append( '
    ' ) + rval.append('
  • %s%s
  • ' % (fn, fn, opt_text)) + rval.append('
    ') with open(dataset.file_name, 'w') as f: - f.write("\n".join( rval )) + f.write("\n".join(rval)) f.write('\n') - def set_meta( self, dataset, **kwd ): - Html.set_meta( self, dataset, **kwd ) + def set_meta(self, dataset, **kwd): + Html.set_meta(self, dataset, **kwd) self.regenerate_primary_file(dataset) diff --git a/lib/galaxy/datatypes/binary.py b/lib/galaxy/datatypes/binary.py index 236d9ba3de2d..83139b65a080 100644 --- a/lib/galaxy/datatypes/binary.py +++ b/lib/galaxy/datatypes/binary.py @@ -10,10 +10,12 @@ import subprocess import tempfile import zipfile +from json import dumps import pysam from bx.seq.twobit import TWOBIT_MAGIC_NUMBER, TWOBIT_MAGIC_NUMBER_SWAP, TWOBIT_MAGIC_SIZE +from galaxy import util from galaxy.datatypes import metadata from galaxy.datatypes.metadata import DictParameter, ListParameter, MetadataElement, MetadataParameter from galaxy.util import FILENAME_VALID_CHARS, nice_size, sqlite, which @@ -25,7 +27,7 @@ # Currently these supported binary data types must be manually set on upload -class Binary( data.Data ): +class Binary(data.Data): """Binary data""" edam_format = "format_2333" sniffable_binary_formats = [] @@ -40,13 +42,13 @@ def register_unsniffable_binary_ext(ext): Binary.unsniffable_binary_formats.append(ext.lower()) @staticmethod - def is_sniffable_binary( filename ): + def is_sniffable_binary(filename): format_information = None for format in Binary.sniffable_binary_formats: - format_instance = format[ "class" ]() + format_instance = format["class"]() try: if format_instance.sniff(filename): - format_information = ( format["type"], format[ "ext" ] ) + format_information = (format["type"], format["ext"]) break except Exception: # Sniffer raised exception, could be any number of @@ -59,63 +61,63 @@ def is_sniffable_binary( filename ): def is_ext_unsniffable(ext): return ext in Binary.unsniffable_binary_formats - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): """Set the peek and blurb text""" if not dataset.dataset.purged: dataset.peek = 'binary data' - dataset.blurb = nice_size( dataset.get_size() ) + dataset.blurb = nice_size(dataset.get_size()) else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disk' - def get_mime( self ): + def get_mime(self): """Returns the mime type of the datatype""" return 'application/octet-stream' def display_data(self, trans, dataset, preview=False, filename=None, to_ext=None, **kwd): trans.response.set_content_type(dataset.get_mime()) - trans.log_event( "Display dataset id: %s" % str( dataset.id ) ) - trans.response.headers['Content-Length'] = int( os.stat( dataset.file_name ).st_size ) + trans.log_event("Display dataset id: %s" % str(dataset.id)) + trans.response.headers['Content-Length'] = int(os.stat(dataset.file_name).st_size) to_ext = dataset.extension fname = ''.join(c in FILENAME_VALID_CHARS and c or '_' for c in dataset.name)[0:150] - trans.response.set_content_type( "application/octet-stream" ) # force octet-stream so Safari doesn't append mime extensions to filename + trans.response.set_content_type("application/octet-stream") # force octet-stream so Safari doesn't append mime extensions to filename trans.response.headers["Content-Disposition"] = 'attachment; filename="Galaxy%s-[%s].%s"' % (dataset.hid, fname, to_ext) - return open( dataset.file_name ) + return open(dataset.file_name) -class Ab1( Binary ): +class Ab1(Binary): """Class describing an ab1 binary sequence file""" file_ext = "ab1" edam_format = "format_3000" edam_data = "data_0924" - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): if not dataset.dataset.purged: dataset.peek = "Binary ab1 sequence file" - dataset.blurb = nice_size( dataset.get_size() ) + dataset.blurb = nice_size(dataset.get_size()) else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disk' - def display_peek( self, dataset ): + def display_peek(self, dataset): try: return dataset.peek except: - return "Binary ab1 sequence file (%s)" % ( nice_size( dataset.get_size() ) ) + return "Binary ab1 sequence file (%s)" % (nice_size(dataset.get_size())) Binary.register_unsniffable_binary_ext("ab1") -class Idat( Binary ): +class Idat(Binary): """Binary data in idat format""" file_ext = "idat" edam_format = "format_2058" edam_data = "data_2603" - def sniff( self, filename ): + def sniff(self, filename): try: - header = open( filename, 'rb' ).read(4) + header = open(filename, 'rb').read(4) if header == b'IDAT': return True return False @@ -126,14 +128,14 @@ def sniff( self, filename ): Binary.register_sniffable_binary_format("idat", "idat", Idat) -class Cel( Binary ): +class Cel(Binary): """Binary data in CEL format.""" file_ext = "cel" edam_format = "format_1638" edam_data = "data_3110" - def sniff( self, filename ): + def sniff(self, filename): """ Try to guess if the file is a CEL file. @@ -147,7 +149,7 @@ def sniff( self, filename ): False """ try: - header = open( filename, 'rb' ).read(4) + header = open(filename, 'rb').read(4) if header == b';\x01\x00\x00': return True return False @@ -158,7 +160,7 @@ def sniff( self, filename ): Binary.register_sniffable_binary_format("cel", "cel", Cel) -class CompressedArchive( Binary ): +class CompressedArchive(Binary): """ Class describing an compressed binary file This class can be sublass'ed to implement archive filetypes that will not be unpacked by upload.py. @@ -166,50 +168,50 @@ class CompressedArchive( Binary ): file_ext = "compressed_archive" compressed = True - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): if not dataset.dataset.purged: dataset.peek = "Compressed binary file" - dataset.blurb = nice_size( dataset.get_size() ) + dataset.blurb = nice_size(dataset.get_size()) else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disk' - def display_peek( self, dataset ): + def display_peek(self, dataset): try: return dataset.peek except: - return "Compressed binary file (%s)" % ( nice_size( dataset.get_size() ) ) + return "Compressed binary file (%s)" % (nice_size(dataset.get_size())) Binary.register_unsniffable_binary_ext("compressed_archive") -class CompressedZipArchive( CompressedArchive ): +class CompressedZipArchive(CompressedArchive): """ Class describing an compressed binary file This class can be sublass'ed to implement archive filetypes that will not be unpacked by upload.py. """ file_ext = "zip" - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): if not dataset.dataset.purged: dataset.peek = "Compressed zip file" - dataset.blurb = nice_size( dataset.get_size() ) + dataset.blurb = nice_size(dataset.get_size()) else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disk' - def display_peek( self, dataset ): + def display_peek(self, dataset): try: return dataset.peek except: - return "Compressed zip file (%s)" % ( nice_size( dataset.get_size() ) ) + return "Compressed zip file (%s)" % (nice_size(dataset.get_size())) Binary.register_unsniffable_binary_ext("zip") -class GenericAsn1Binary( Binary ): +class GenericAsn1Binary(Binary): """Class for generic ASN.1 binary format""" file_ext = "asn1-binary" edam_format = "format_1966" @@ -220,23 +222,26 @@ class GenericAsn1Binary( Binary ): @dataproviders.decorators.has_dataproviders -class Bam( Binary ): +class Bam(Binary): """Class describing a BAM binary file""" edam_format = "format_2572" edam_data = "data_0863" file_ext = "bam" track_type = "ReadTrack" - data_sources = { "data": "bai", "index": "bigwig" } - - MetadataElement( name="bam_index", desc="BAM Index File", param=metadata.FileParameter, file_ext="bai", readonly=True, no_value=None, visible=False, optional=True ) - MetadataElement( name="bam_version", default=None, desc="BAM Version", param=MetadataParameter, readonly=True, visible=False, optional=True, no_value=None ) - MetadataElement( name="sort_order", default=None, desc="Sort Order", param=MetadataParameter, readonly=True, visible=False, optional=True, no_value=None ) - MetadataElement( name="read_groups", default=[], desc="Read Groups", param=MetadataParameter, readonly=True, visible=False, optional=True, no_value=[] ) - MetadataElement( name="reference_names", default=[], desc="Chromosome Names", param=MetadataParameter, readonly=True, visible=False, optional=True, no_value=[] ) - MetadataElement( name="reference_lengths", default=[], desc="Chromosome Lengths", param=MetadataParameter, readonly=True, visible=False, optional=True, no_value=[] ) - MetadataElement( name="bam_header", default={}, desc="Dictionary of BAM Headers", param=MetadataParameter, readonly=True, visible=False, optional=True, no_value={} ) - - def _get_samtools_version( self ): + data_sources = {"data": "bai", "index": "bigwig"} + + MetadataElement(name="bam_index", desc="BAM Index File", param=metadata.FileParameter, file_ext="bai", readonly=True, no_value=None, visible=False, optional=True) + MetadataElement(name="bam_version", default=None, desc="BAM Version", param=MetadataParameter, readonly=True, visible=False, optional=True, no_value=None) + MetadataElement(name="sort_order", default=None, desc="Sort Order", param=MetadataParameter, readonly=True, visible=False, optional=True, no_value=None) + MetadataElement(name="read_groups", default=[], desc="Read Groups", param=MetadataParameter, readonly=True, visible=False, optional=True, no_value=[]) + MetadataElement(name="reference_names", default=[], desc="Chromosome Names", param=MetadataParameter, readonly=True, visible=False, optional=True, no_value=[]) + MetadataElement(name="reference_lengths", default=[], desc="Chromosome Lengths", param=MetadataParameter, readonly=True, visible=False, optional=True, no_value=[]) + MetadataElement(name="bam_header", default={}, desc="Dictionary of BAM Headers", param=MetadataParameter, readonly=True, visible=False, optional=True, no_value={}) + MetadataElement(name="columns", default=12, desc="Number of columns", readonly=True, visible=False, no_value=0) + MetadataElement(name="column_types", default=['str', 'int', 'str', 'int', 'int', 'str', 'str', 'int', 'int', 'str', 'str', 'str'], desc="Column types", param=metadata.ColumnTypesParameter, readonly=True, visible=False, no_value=[]) + MetadataElement(name="column_names", default=['QNAME', 'FLAG', 'RNAME', 'POS', 'MAPQ', 'CIGAR', 'MRNM', 'MPOS', 'ISIZE', 'SEQ', 'QUAL', 'OPT'], desc="Column names", readonly=True, visible=False, optional=True, no_value=[]) + + def _get_samtools_version(self): version = '0.0.0' samtools_exec = which('samtools') if not samtools_exec: @@ -244,9 +249,9 @@ def _get_samtools_version( self ): raise Exception(message) # Get the version of samtools via --version-only, if available - p = subprocess.Popen( ['samtools', '--version-only'], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) + p = subprocess.Popen(['samtools', '--version-only'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) output, error = p.communicate() # --version-only is available @@ -255,10 +260,10 @@ def _get_samtools_version( self ): version = output.split('+')[0] return version - output = subprocess.Popen( [ 'samtools' ], stderr=subprocess.PIPE, stdout=subprocess.PIPE ).communicate()[1] - lines = output.split( '\n' ) + output = subprocess.Popen(['samtools'], stderr=subprocess.PIPE, stdout=subprocess.PIPE).communicate()[1] + lines = output.split('\n') for line in lines: - if line.lower().startswith( 'version' ): + if line.lower().startswith('version'): # Assuming line looks something like: version: 0.1.12a (r862) version = line.split()[1] break @@ -270,31 +275,31 @@ def merge(split_files, output_file): tmp_dir = tempfile.mkdtemp() stderr_name = tempfile.NamedTemporaryFile(dir=tmp_dir, prefix="bam_merge_stderr").name command = ["samtools", "merge", "-f", output_file] + split_files - proc = subprocess.Popen( args=command, stderr=open( stderr_name, 'wb' ) ) + proc = subprocess.Popen(args=command, stderr=open(stderr_name, 'wb')) exit_code = proc.wait() # Did merge succeed? stderr = open(stderr_name).read().strip() if stderr: if exit_code != 0: shutil.rmtree(tmp_dir) # clean up - raise Exception( "Error merging BAM files: %s" % stderr ) + raise Exception("Error merging BAM files: %s" % stderr) else: print(stderr) os.unlink(stderr_name) os.rmdir(tmp_dir) - def _is_coordinate_sorted( self, file_name ): + def _is_coordinate_sorted(self, file_name): """See if the input BAM file is sorted from the header information.""" - params = [ "samtools", "view", "-H", file_name ] - output = subprocess.Popen( params, stderr=subprocess.PIPE, stdout=subprocess.PIPE ).communicate()[0] + params = ["samtools", "view", "-H", file_name] + output = subprocess.Popen(params, stderr=subprocess.PIPE, stdout=subprocess.PIPE).communicate()[0] # find returns -1 if string is not found - return output.find( "SO:coordinate" ) != -1 or output.find( "SO:sorted" ) != -1 + return output.find("SO:coordinate") != -1 or output.find("SO:sorted") != -1 - def dataset_content_needs_grooming( self, file_name ): + def dataset_content_needs_grooming(self, file_name): """See if file_name is a sorted BAM file""" version = self._get_samtools_version() if version < '0.1.13': - return not self._is_coordinate_sorted( file_name ) + return not self._is_coordinate_sorted(file_name) else: # Samtools version 0.1.13 or newer produces an error condition when attempting to index an # unsorted bam file - see http://biostar.stackexchange.com/questions/5273/is-my-bam-file-sorted. @@ -309,36 +314,36 @@ def dataset_content_needs_grooming( self, file_name ): # upload tool / framework to allow setting metadata from directly within the tool itself, it should be # done generically so that all tools will have the ability. In testing, a 6.6 gb BAM file took 128 # seconds to index with samtools, and 45 minutes to sort, so indexing is relatively inexpensive. - if self._is_coordinate_sorted( file_name ): + if self._is_coordinate_sorted(file_name): return False - index_name = tempfile.NamedTemporaryFile( prefix="bam_index" ).name - stderr_name = tempfile.NamedTemporaryFile( prefix="bam_index_stderr" ).name - command = 'samtools index %s %s' % ( file_name, index_name ) - proc = subprocess.Popen( args=command, shell=True, stderr=open( stderr_name, 'wb' ) ) + index_name = tempfile.NamedTemporaryFile(prefix="bam_index").name + stderr_name = tempfile.NamedTemporaryFile(prefix="bam_index_stderr").name + command = 'samtools index %s %s' % (file_name, index_name) + proc = subprocess.Popen(args=command, shell=True, stderr=open(stderr_name, 'wb')) proc.wait() - stderr = open( stderr_name ).read().strip() + stderr = open(stderr_name).read().strip() if stderr: try: - os.unlink( index_name ) + os.unlink(index_name) except OSError: pass try: - os.unlink( stderr_name ) + os.unlink(stderr_name) except OSError: pass # Return True if unsorted error condition is found (find returns -1 if string is not found). - return stderr.find( "[bam_index_core] the alignment is not sorted" ) != -1 + return stderr.find("[bam_index_core] the alignment is not sorted") != -1 try: - os.unlink( index_name ) + os.unlink(index_name) except OSError: pass try: - os.unlink( stderr_name ) + os.unlink(stderr_name) except OSError: pass return False - def groom_dataset_content( self, file_name ): + def groom_dataset_content(self, file_name): """ Ensures that the Bam file contents are sorted. This function is called on an output dataset after the content is initially generated. @@ -350,108 +355,108 @@ def groom_dataset_content( self, file_name ): # This command may also create temporary files .%d.bam when the # whole alignment cannot be fitted into memory ( controlled by option -m ). # do this in a unique temp directory, because of possible .%d.bam temp files - if not self.dataset_content_needs_grooming( file_name ): + if not self.dataset_content_needs_grooming(file_name): # Don't re-sort if already sorted return tmp_dir = tempfile.mkdtemp() - tmp_sorted_dataset_file_name_prefix = os.path.join( tmp_dir, 'sorted' ) - stderr_name = tempfile.NamedTemporaryFile( dir=tmp_dir, prefix="bam_sort_stderr" ).name + tmp_sorted_dataset_file_name_prefix = os.path.join(tmp_dir, 'sorted') + stderr_name = tempfile.NamedTemporaryFile(dir=tmp_dir, prefix="bam_sort_stderr").name samtools_created_sorted_file_name = "%s.bam" % tmp_sorted_dataset_file_name_prefix # samtools accepts a prefix, not a filename, it always adds .bam to the prefix - command = "samtools sort %s %s" % ( file_name, tmp_sorted_dataset_file_name_prefix ) - proc = subprocess.Popen( args=command, shell=True, cwd=tmp_dir, stderr=open( stderr_name, 'wb' ) ) + command = "samtools sort %s %s" % (file_name, tmp_sorted_dataset_file_name_prefix) + proc = subprocess.Popen(args=command, shell=True, cwd=tmp_dir, stderr=open(stderr_name, 'wb')) exit_code = proc.wait() # Did sort succeed? - stderr = open( stderr_name ).read().strip() + stderr = open(stderr_name).read().strip() if stderr: if exit_code != 0: - shutil.rmtree( tmp_dir) # clean up - raise Exception( "Error Grooming BAM file contents: %s" % stderr ) + shutil.rmtree(tmp_dir) # clean up + raise Exception("Error Grooming BAM file contents: %s" % stderr) else: print(stderr) # Move samtools_created_sorted_file_name to our output dataset location - shutil.move( samtools_created_sorted_file_name, file_name ) + shutil.move(samtools_created_sorted_file_name, file_name) # Remove temp file and empty temporary directory - os.unlink( stderr_name ) - os.rmdir( tmp_dir ) + os.unlink(stderr_name) + os.rmdir(tmp_dir) - def init_meta( self, dataset, copy_from=None ): - Binary.init_meta( self, dataset, copy_from=copy_from ) + def init_meta(self, dataset, copy_from=None): + Binary.init_meta(self, dataset, copy_from=copy_from) - def set_meta( self, dataset, overwrite=True, **kwd ): + def set_meta(self, dataset, overwrite=True, **kwd): """ Creates the index for the BAM file. """ # These metadata values are not accessible by users, always overwrite index_file = dataset.metadata.bam_index if not index_file: - index_file = dataset.metadata.spec['bam_index'].param.new_file( dataset=dataset ) + index_file = dataset.metadata.spec['bam_index'].param.new_file(dataset=dataset) # Create the Bam index # $ samtools index # Usage: samtools index [] - stderr_name = tempfile.NamedTemporaryFile( prefix="bam_index_stderr" ).name - command = [ 'samtools', 'index', dataset.file_name, index_file.file_name ] - exit_code = subprocess.call( args=command, stderr=open( stderr_name, 'wb' ) ) + stderr_name = tempfile.NamedTemporaryFile(prefix="bam_index_stderr").name + command = ['samtools', 'index', dataset.file_name, index_file.file_name] + exit_code = subprocess.call(args=command, stderr=open(stderr_name, 'wb')) # Did index succeed? if exit_code == -6: # SIGABRT, most likely samtools 1.0+ which does not accept the index name parameter. - dataset_symlink = os.path.join( os.path.dirname( index_file.file_name ), - '__dataset_%d_%s' % ( dataset.id, os.path.basename( index_file.file_name ) ) ) - os.symlink( dataset.file_name, dataset_symlink ) + dataset_symlink = os.path.join(os.path.dirname(index_file.file_name), + '__dataset_%d_%s' % (dataset.id, os.path.basename(index_file.file_name))) + os.symlink(dataset.file_name, dataset_symlink) try: - command = [ 'samtools', 'index', dataset_symlink ] - exit_code = subprocess.call( args=command, stderr=open( stderr_name, 'wb' ) ) - shutil.move( dataset_symlink + '.bai', index_file.file_name ) + command = ['samtools', 'index', dataset_symlink] + exit_code = subprocess.call(args=command, stderr=open(stderr_name, 'wb')) + shutil.move(dataset_symlink + '.bai', index_file.file_name) except Exception as e: - open( stderr_name, 'ab+' ).write( 'Galaxy attempted to build the BAM index with samtools 1.0+ but failed: %s\n' % e) + open(stderr_name, 'ab+').write('Galaxy attempted to build the BAM index with samtools 1.0+ but failed: %s\n' % e) exit_code = 1 # Make sure an exception raised by shutil.move() is re-raised below finally: - os.unlink( dataset_symlink ) - stderr = open( stderr_name ).read().strip() + os.unlink(dataset_symlink) + stderr = open(stderr_name).read().strip() if stderr: if exit_code != 0: - os.unlink( stderr_name ) # clean up - raise Exception( "Error Setting BAM Metadata: %s" % stderr ) + os.unlink(stderr_name) # clean up + raise Exception("Error Setting BAM Metadata: %s" % stderr) else: print(stderr) dataset.metadata.bam_index = index_file # Remove temp file - os.unlink( stderr_name ) + os.unlink(stderr_name) # Now use pysam with BAI index to determine additional metadata try: - bam_file = pysam.AlignmentFile( dataset.file_name, mode='rb', index_filename=index_file.file_name ) - dataset.metadata.reference_names = list( bam_file.references ) - dataset.metadata.reference_lengths = list( bam_file.lengths ) + bam_file = pysam.AlignmentFile(dataset.file_name, mode='rb', index_filename=index_file.file_name) + dataset.metadata.reference_names = list(bam_file.references) + dataset.metadata.reference_lengths = list(bam_file.lengths) dataset.metadata.bam_header = bam_file.header - dataset.metadata.read_groups = [ read_group['ID'] for read_group in dataset.metadata.bam_header.get( 'RG', [] ) if 'ID' in read_group ] - dataset.metadata.sort_order = dataset.metadata.bam_header.get( 'HD', {} ).get( 'SO', None ) - dataset.metadata.bam_version = dataset.metadata.bam_header.get( 'HD', {} ).get( 'VN', None ) + dataset.metadata.read_groups = [read_group['ID'] for read_group in dataset.metadata.bam_header.get('RG', []) if 'ID' in read_group] + dataset.metadata.sort_order = dataset.metadata.bam_header.get('HD', {}).get('SO', None) + dataset.metadata.bam_version = dataset.metadata.bam_header.get('HD', {}).get('VN', None) except: # Per Dan, don't log here because doing so will cause datasets that # fail metadata to end in the error state pass - def sniff( self, filename ): + def sniff(self, filename): # BAM is compressed in the BGZF format, and must not be uncompressed in Galaxy. # The first 4 bytes of any bam file is 'BAM\1', and the file is binary. try: - header = gzip.open( filename ).read(4) + header = gzip.open(filename).read(4) if header == b'BAM\1': return True return False except: return False - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): if not dataset.dataset.purged: dataset.peek = "Binary bam alignments file" - dataset.blurb = nice_size( dataset.get_size() ) + dataset.blurb = nice_size(dataset.get_size()) else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disk' - def display_peek( self, dataset ): + def display_peek(self, dataset): try: return dataset.peek except: - return "Binary bam alignments file (%s)" % ( nice_size( dataset.get_size() ) ) + return "Binary bam alignments file (%s)" % (nice_size(dataset.get_size())) def to_archive(self, trans, dataset, name=""): rel_paths = [] @@ -462,35 +467,84 @@ def to_archive(self, trans, dataset, name=""): file_paths.append(dataset.metadata.bam_index.file_name) return zip(file_paths, rel_paths) + def get_chunk(self, trans, dataset, offset=0, ck_size=None): + index_file = dataset.metadata.bam_index + with pysam.AlignmentFile(dataset.file_name, "rb", index_filename=index_file.file_name) as bamfile: + ck_size = 300 # 300 lines + ck_data = "" + header_line_count = 0 + if offset == 0: + ck_data = bamfile.text.replace('\t', ' ') + header_line_count = bamfile.text.count('\n') + else: + bamfile.seek(offset) + for line_number, alignment in enumerate(bamfile) : + # return only Header lines if 'header_line_count' exceeds 'ck_size' + # FIXME: Can be problematic if bam has million lines of header + offset = bamfile.tell() + if (line_number + header_line_count) > ck_size: + break + else: + bamline = alignment.tostring(bamfile) + # Galaxy display each tag as separate column because 'tostring()' funcition put tabs in between each tag of tags column. + # Below code will remove spaces between each tag. + bamline_modified = ('\t').join(bamline.split()[:11] + [(' ').join(bamline.split()[11:])]) + ck_data = "%s\n%s" % (ck_data, bamline_modified) + return dumps({'ck_data': util.unicodify(ck_data), + 'offset': offset}) + + def display_data(self, trans, dataset, preview=False, filename=None, to_ext=None, offset=None, ck_size=None, **kwd): + preview = util.string_as_bool(preview) + if offset is not None: + return self.get_chunk(trans, dataset, offset, ck_size) + elif to_ext or not preview: + return super(Bam, self).display_data(trans, dataset, preview, filename, to_ext, **kwd) + else: + column_names = dataset.metadata.column_names + if not column_names: + column_names = [] + column_types = dataset.metadata.column_types + if not column_types: + column_types = [] + column_number = dataset.metadata.columns + if column_number is None: + column_number = 1 + return trans.fill_template("/dataset/tabular_chunked.mako", + dataset=dataset, + chunk=self.get_chunk(trans, dataset, 0), + column_number=column_number, + column_names=column_names, + column_types=column_types) + # ------------- Dataproviders # pipe through samtools view # ALSO: (as Sam) # bam does not use '#' to indicate comments/headers - we need to strip out those headers from the std. providers # TODO:?? seems like there should be an easier way to do/inherit this - metadata.comment_char? # TODO: incorporate samtools options to control output: regions first, then flags, etc. - @dataproviders.decorators.dataprovider_factory( 'line', dataproviders.line.FilteredLineDataProvider.settings ) - def line_dataprovider( self, dataset, **settings ): - samtools_source = dataproviders.dataset.SamtoolsDataProvider( dataset ) - settings[ 'comment_char' ] = '@' - return dataproviders.line.FilteredLineDataProvider( samtools_source, **settings ) - - @dataproviders.decorators.dataprovider_factory( 'regex-line', dataproviders.line.RegexLineDataProvider.settings ) - def regex_line_dataprovider( self, dataset, **settings ): - samtools_source = dataproviders.dataset.SamtoolsDataProvider( dataset ) - settings[ 'comment_char' ] = '@' - return dataproviders.line.RegexLineDataProvider( samtools_source, **settings ) - - @dataproviders.decorators.dataprovider_factory( 'column', dataproviders.column.ColumnarDataProvider.settings ) - def column_dataprovider( self, dataset, **settings ): - samtools_source = dataproviders.dataset.SamtoolsDataProvider( dataset ) - settings[ 'comment_char' ] = '@' - return dataproviders.column.ColumnarDataProvider( samtools_source, **settings ) - - @dataproviders.decorators.dataprovider_factory( 'dict', dataproviders.column.DictDataProvider.settings ) - def dict_dataprovider( self, dataset, **settings ): - samtools_source = dataproviders.dataset.SamtoolsDataProvider( dataset ) - settings[ 'comment_char' ] = '@' - return dataproviders.column.DictDataProvider( samtools_source, **settings ) + @dataproviders.decorators.dataprovider_factory('line', dataproviders.line.FilteredLineDataProvider.settings) + def line_dataprovider(self, dataset, **settings): + samtools_source = dataproviders.dataset.SamtoolsDataProvider(dataset) + settings['comment_char'] = '@' + return dataproviders.line.FilteredLineDataProvider(samtools_source, **settings) + + @dataproviders.decorators.dataprovider_factory('regex-line', dataproviders.line.RegexLineDataProvider.settings) + def regex_line_dataprovider(self, dataset, **settings): + samtools_source = dataproviders.dataset.SamtoolsDataProvider(dataset) + settings['comment_char'] = '@' + return dataproviders.line.RegexLineDataProvider(samtools_source, **settings) + + @dataproviders.decorators.dataprovider_factory('column', dataproviders.column.ColumnarDataProvider.settings) + def column_dataprovider(self, dataset, **settings): + samtools_source = dataproviders.dataset.SamtoolsDataProvider(dataset) + settings['comment_char'] = '@' + return dataproviders.column.ColumnarDataProvider(samtools_source, **settings) + + @dataproviders.decorators.dataprovider_factory('dict', dataproviders.column.DictDataProvider.settings) + def dict_dataprovider(self, dataset, **settings): + samtools_source = dataproviders.dataset.SamtoolsDataProvider(dataset) + settings['comment_char'] = '@' + return dataproviders.column.DictDataProvider(samtools_source, **settings) # these can't be used directly - may need BamColumn, BamDict (Bam metadata -> column/dict) # OR - see genomic_region_dataprovider @@ -504,21 +558,21 @@ def dict_dataprovider( self, dataset, **settings ): # settings[ 'comment_char' ] = '@' # return super( Sam, self ).dataset_dict_dataprovider( dataset, **settings ) - @dataproviders.decorators.dataprovider_factory( 'header', dataproviders.line.RegexLineDataProvider.settings ) - def header_dataprovider( self, dataset, **settings ): + @dataproviders.decorators.dataprovider_factory('header', dataproviders.line.RegexLineDataProvider.settings) + def header_dataprovider(self, dataset, **settings): # in this case we can use an option of samtools view to provide just what we need (w/o regex) - samtools_source = dataproviders.dataset.SamtoolsDataProvider( dataset, '-H' ) - return dataproviders.line.RegexLineDataProvider( samtools_source, **settings ) - - @dataproviders.decorators.dataprovider_factory( 'id-seq-qual', dataproviders.column.DictDataProvider.settings ) - def id_seq_qual_dataprovider( self, dataset, **settings ): - settings[ 'indeces' ] = [ 0, 9, 10 ] - settings[ 'column_types' ] = [ 'str', 'str', 'str' ] - settings[ 'column_names' ] = [ 'id', 'seq', 'qual' ] - return self.dict_dataprovider( dataset, **settings ) - - @dataproviders.decorators.dataprovider_factory( 'genomic-region', dataproviders.column.ColumnarDataProvider.settings ) - def genomic_region_dataprovider( self, dataset, **settings ): + samtools_source = dataproviders.dataset.SamtoolsDataProvider(dataset, '-H') + return dataproviders.line.RegexLineDataProvider(samtools_source, **settings) + + @dataproviders.decorators.dataprovider_factory('id-seq-qual', dataproviders.column.DictDataProvider.settings) + def id_seq_qual_dataprovider(self, dataset, **settings): + settings['indeces'] = [0, 9, 10] + settings['column_types'] = ['str', 'str', 'str'] + settings['column_names'] = ['id', 'seq', 'qual'] + return self.dict_dataprovider(dataset, **settings) + + @dataproviders.decorators.dataprovider_factory('genomic-region', dataproviders.column.ColumnarDataProvider.settings) + def genomic_region_dataprovider(self, dataset, **settings): # GenomicRegionDataProvider currently requires a dataset as source - may not be necc. # TODO:?? consider (at least) the possible use of a kwarg: metadata_source (def. to source.dataset), # or remove altogether... @@ -527,52 +581,52 @@ def genomic_region_dataprovider( self, dataset, **settings ): # 2, 3, 3, **settings ) # instead, set manually and use in-class column gen - settings[ 'indeces' ] = [ 2, 3, 3 ] - settings[ 'column_types' ] = [ 'str', 'int', 'int' ] - return self.column_dataprovider( dataset, **settings ) - - @dataproviders.decorators.dataprovider_factory( 'genomic-region-dict', dataproviders.column.DictDataProvider.settings ) - def genomic_region_dict_dataprovider( self, dataset, **settings ): - settings[ 'indeces' ] = [ 2, 3, 3 ] - settings[ 'column_types' ] = [ 'str', 'int', 'int' ] - settings[ 'column_names' ] = [ 'chrom', 'start', 'end' ] - return self.dict_dataprovider( dataset, **settings ) - - @dataproviders.decorators.dataprovider_factory( 'samtools' ) - def samtools_dataprovider( self, dataset, **settings ): + settings['indeces'] = [2, 3, 3] + settings['column_types'] = ['str', 'int', 'int'] + return self.column_dataprovider(dataset, **settings) + + @dataproviders.decorators.dataprovider_factory('genomic-region-dict', dataproviders.column.DictDataProvider.settings) + def genomic_region_dict_dataprovider(self, dataset, **settings): + settings['indeces'] = [2, 3, 3] + settings['column_types'] = ['str', 'int', 'int'] + settings['column_names'] = ['chrom', 'start', 'end'] + return self.dict_dataprovider(dataset, **settings) + + @dataproviders.decorators.dataprovider_factory('samtools') + def samtools_dataprovider(self, dataset, **settings): """Generic samtools interface - all options available through settings.""" - dataset_source = dataproviders.dataset.DatasetDataProvider( dataset ) - return dataproviders.dataset.SamtoolsDataProvider( dataset_source, **settings ) + dataset_source = dataproviders.dataset.DatasetDataProvider(dataset) + return dataproviders.dataset.SamtoolsDataProvider(dataset_source, **settings) Binary.register_sniffable_binary_format("bam", "bam", Bam) -class CRAM( Binary ): +class CRAM(Binary): file_ext = "cram" edam_format = "format_3462" edam_data = "format_0863" - MetadataElement( name="cram_version", default=None, desc="CRAM Version", param=MetadataParameter, readonly=True, visible=False, optional=False, no_value=None ) - MetadataElement( name="cram_index", desc="CRAM Index File", param=metadata.FileParameter, file_ext="crai", readonly=True, no_value=None, visible=False, optional=True ) + MetadataElement(name="cram_version", default=None, desc="CRAM Version", param=MetadataParameter, readonly=True, visible=False, optional=False, no_value=None) + MetadataElement(name="cram_index", desc="CRAM Index File", param=metadata.FileParameter, file_ext="crai", readonly=True, no_value=None, visible=False, optional=True) - def set_meta( self, dataset, overwrite=True, **kwd ): - major_version, minor_version = self.get_cram_version( dataset.file_name ) + def set_meta(self, dataset, overwrite=True, **kwd): + major_version, minor_version = self.get_cram_version(dataset.file_name) if major_version != -1: dataset.metadata.cram_version = str(major_version) + "." + str(minor_version) if not dataset.metadata.cram_index: - index_file = dataset.metadata.spec['cram_index'].param.new_file( dataset=dataset ) + index_file = dataset.metadata.spec['cram_index'].param.new_file(dataset=dataset) if self.set_index_file(dataset, index_file): dataset.metadata.cram_index = index_file - def get_cram_version( self, filename): + def get_cram_version(self, filename): try: - with open( filename, "rb") as fh: + with open(filename, "rb") as fh: header = fh.read(6) - return ord( header[4] ), ord( header[5] ) + return ord(header[4]), ord(header[5]) except Exception as exc: - log.warning( '%s, get_cram_version Exception: %s', self, exc ) + log.warning('%s, get_cram_version Exception: %s', self, exc) return -1, -1 def set_index_file(self, dataset, index_file): @@ -583,23 +637,23 @@ def set_index_file(self, dataset, index_file): # fixed in the dev branch: # xref: https://github.com/samtools/samtools/issues/199 - dataset_symlink = os.path.join( os.path.dirname( index_file.file_name ), '__dataset_%d_%s' % ( dataset.id, os.path.basename( index_file.file_name ) ) ) - os.symlink( dataset.file_name, dataset_symlink ) - pysam.index( dataset_symlink ) + dataset_symlink = os.path.join(os.path.dirname(index_file.file_name), '__dataset_%d_%s' % (dataset.id, os.path.basename(index_file.file_name))) + os.symlink(dataset.file_name, dataset_symlink) + pysam.index(dataset_symlink) tmp_index = dataset_symlink + ".crai" - if os.path.isfile( tmp_index ): - shutil.move( tmp_index, index_file.file_name ) + if os.path.isfile(tmp_index): + shutil.move(tmp_index, index_file.file_name) return index_file.file_name else: - os.unlink( dataset_symlink ) - log.warning( '%s, expected crai index not created for: %s', self, dataset.file_name ) + os.unlink(dataset_symlink) + log.warning('%s, expected crai index not created for: %s', self, dataset.file_name) return False except Exception as exc: - log.warning( '%s, set_index_file Exception: %s', self, exc ) + log.warning('%s, set_index_file Exception: %s', self, exc) return False - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): if not dataset.dataset.purged: dataset.peek = 'CRAM binary alignment file' dataset.blurb = 'binary data' @@ -607,9 +661,9 @@ def set_peek( self, dataset, is_multi_byte=False ): dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disk' - def sniff( self, filename ): + def sniff(self, filename): try: - header = open( filename, 'rb' ).read(4) + header = open(filename, 'rb').read(4) if header == b"CRAM": return True return False @@ -620,58 +674,58 @@ def sniff( self, filename ): Binary.register_sniffable_binary_format('cram', 'cram', CRAM) -class Bcf( Binary): +class Bcf(Binary): """Class describing a BCF file""" edam_format = "format_3020" edam_data = "data_3498" file_ext = "bcf" - MetadataElement( name="bcf_index", desc="BCF Index File", param=metadata.FileParameter, file_ext="csi", readonly=True, no_value=None, visible=False, optional=True ) + MetadataElement(name="bcf_index", desc="BCF Index File", param=metadata.FileParameter, file_ext="csi", readonly=True, no_value=None, visible=False, optional=True) - def sniff( self, filename ): + def sniff(self, filename): # BCF is compressed in the BGZF format, and must not be uncompressed in Galaxy. # The first 3 bytes of any bcf file is 'BCF', and the file is binary. try: - header = gzip.open( filename ).read(3) + header = gzip.open(filename).read(3) if header == b'BCF': return True return False except: return False - def set_meta( self, dataset, overwrite=True, **kwd ): + def set_meta(self, dataset, overwrite=True, **kwd): """ Creates the index for the BCF file. """ # These metadata values are not accessible by users, always overwrite index_file = dataset.metadata.bcf_index if not index_file: - index_file = dataset.metadata.spec['bcf_index'].param.new_file( dataset=dataset ) + index_file = dataset.metadata.spec['bcf_index'].param.new_file(dataset=dataset) # Create the bcf index # $ bcftools index # Usage: bcftools index - dataset_symlink = os.path.join( os.path.dirname( index_file.file_name ), - '__dataset_%d_%s' % ( dataset.id, os.path.basename( index_file.file_name ) ) ) - os.symlink( dataset.file_name, dataset_symlink ) + dataset_symlink = os.path.join(os.path.dirname(index_file.file_name), + '__dataset_%d_%s' % (dataset.id, os.path.basename(index_file.file_name))) + os.symlink(dataset.file_name, dataset_symlink) - stderr_name = tempfile.NamedTemporaryFile( prefix="bcf_index_stderr" ).name - command = [ 'bcftools', 'index', dataset_symlink ] + stderr_name = tempfile.NamedTemporaryFile(prefix="bcf_index_stderr").name + command = ['bcftools', 'index', dataset_symlink] try: - subprocess.check_call( args=command, stderr=open( stderr_name, 'wb' ) ) - shutil.move( dataset_symlink + '.csi', index_file.file_name ) # this will fail if bcftools < 1.0 is used, because it creates a .bci index file instead of .csi + subprocess.check_call(args=command, stderr=open(stderr_name, 'wb')) + shutil.move(dataset_symlink + '.csi', index_file.file_name) # this will fail if bcftools < 1.0 is used, because it creates a .bci index file instead of .csi except Exception as e: - stderr = open( stderr_name ).read().strip() + stderr = open(stderr_name).read().strip() raise Exception('Error setting BCF metadata: %s' % (stderr or str(e))) finally: # Remove temp file and symlink - os.remove( stderr_name ) - os.remove( dataset_symlink ) + os.remove(stderr_name) + os.remove(dataset_symlink) dataset.metadata.bcf_index = index_file Binary.register_sniffable_binary_format("bcf", "bcf", Bcf) -class H5( Binary ): +class H5(Binary): """ Class describing an HDF5 file @@ -686,92 +740,92 @@ class H5( Binary ): file_ext = "h5" edam_format = "format_3590" - def __init__( self, **kwd ): - Binary.__init__( self, **kwd ) + def __init__(self, **kwd): + Binary.__init__(self, **kwd) self._magic = binascii.unhexlify("894844460d0a1a0a") - def sniff( self, filename ): + def sniff(self, filename): # The first 8 bytes of any hdf5 file are 0x894844460d0a1a0a try: - header = open( filename, 'rb' ).read(8) + header = open(filename, 'rb').read(8) if header == self._magic: return True return False except: return False - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): if not dataset.dataset.purged: dataset.peek = "Binary HDF5 file" - dataset.blurb = nice_size( dataset.get_size() ) + dataset.blurb = nice_size(dataset.get_size()) else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disk' - def display_peek( self, dataset ): + def display_peek(self, dataset): try: return dataset.peek except: - return "Binary HDF5 file (%s)" % ( nice_size( dataset.get_size() ) ) + return "Binary HDF5 file (%s)" % (nice_size(dataset.get_size())) Binary.register_sniffable_binary_format("h5", "h5", H5) -class Scf( Binary ): +class Scf(Binary): """Class describing an scf binary sequence file""" edam_format = "format_1632" edam_data = "data_0924" file_ext = "scf" - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): if not dataset.dataset.purged: dataset.peek = "Binary scf sequence file" - dataset.blurb = nice_size( dataset.get_size() ) + dataset.blurb = nice_size(dataset.get_size()) else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disk' - def display_peek( self, dataset ): + def display_peek(self, dataset): try: return dataset.peek except: - return "Binary scf sequence file (%s)" % ( nice_size( dataset.get_size() ) ) + return "Binary scf sequence file (%s)" % (nice_size(dataset.get_size())) Binary.register_unsniffable_binary_ext("scf") -class Sff( Binary ): +class Sff(Binary): """ Standard Flowgram Format (SFF) """ edam_format = "format_3284" edam_data = "data_0924" file_ext = "sff" - def sniff( self, filename ): + def sniff(self, filename): # The first 4 bytes of any sff file is '.sff', and the file is binary. For details # about the format, see http://www.ncbi.nlm.nih.gov/Traces/trace.cgi?cmd=show&f=formats&m=doc&s=format try: - header = open( filename, 'rb' ).read(4) + header = open(filename, 'rb').read(4) if header == b'.sff': return True return False except: return False - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): if not dataset.dataset.purged: dataset.peek = "Binary sff file" - dataset.blurb = nice_size( dataset.get_size() ) + dataset.blurb = nice_size(dataset.get_size()) else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disk' - def display_peek( self, dataset ): + def display_peek(self, dataset): try: return dataset.peek except: - return "Binary sff file (%s)" % ( nice_size( dataset.get_size() ) ) + return "Binary sff file (%s)" % (nice_size(dataset.get_size())) Binary.register_sniffable_binary_format("sff", "sff", Sff) @@ -786,36 +840,36 @@ class BigWig(Binary): edam_format = "format_3006" edam_data = "data_3002" track_type = "LineTrack" - data_sources = { "data_standalone": "bigwig" } + data_sources = {"data_standalone": "bigwig"} - def __init__( self, **kwd ): - Binary.__init__( self, **kwd ) + def __init__(self, **kwd): + Binary.__init__(self, **kwd) self._magic = 0x888FFC26 self._name = "BigWig" - def _unpack( self, pattern, handle ): - return struct.unpack( pattern, handle.read( struct.calcsize( pattern ) ) ) + def _unpack(self, pattern, handle): + return struct.unpack(pattern, handle.read(struct.calcsize(pattern))) - def sniff( self, filename ): + def sniff(self, filename): try: - magic = self._unpack( "I", open( filename, 'rb' ) ) + magic = self._unpack("I", open(filename, 'rb')) return magic[0] == self._magic except: return False - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): if not dataset.dataset.purged: dataset.peek = "Binary UCSC %s file" % self._name - dataset.blurb = nice_size( dataset.get_size() ) + dataset.blurb = nice_size(dataset.get_size()) else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disk' - def display_peek( self, dataset ): + def display_peek(self, dataset): try: return dataset.peek except: - return "Binary UCSC %s file (%s)" % ( self._name, nice_size( dataset.get_size() ) ) + return "Binary UCSC %s file (%s)" % (self._name, nice_size(dataset.get_size())) Binary.register_sniffable_binary_format("bigwig", "bigwig", BigWig) @@ -825,10 +879,10 @@ class BigBed(BigWig): """BigBed support from UCSC.""" edam_format = "format_3004" edam_data = "data_3002" - data_sources = { "data_standalone": "bigbed" } + data_sources = {"data_standalone": "bigbed"} - def __init__( self, **kwd ): - Binary.__init__( self, **kwd ) + def __init__(self, **kwd): + Binary.__init__(self, **kwd) self._magic = 0x8789F2EB self._name = "BigBed" @@ -872,18 +926,18 @@ def display_peek(self, dataset): @dataproviders.decorators.has_dataproviders -class SQlite ( Binary ): +class SQlite (Binary): """Class describing a Sqlite database """ - MetadataElement( name="tables", default=[], param=ListParameter, desc="Database Tables", readonly=True, visible=True, no_value=[] ) - MetadataElement( name="table_columns", default={}, param=DictParameter, desc="Database Table Columns", readonly=True, visible=True, no_value={} ) - MetadataElement( name="table_row_count", default={}, param=DictParameter, desc="Database Table Row Count", readonly=True, visible=True, no_value={} ) + MetadataElement(name="tables", default=[], param=ListParameter, desc="Database Tables", readonly=True, visible=True, no_value=[]) + MetadataElement(name="table_columns", default={}, param=DictParameter, desc="Database Table Columns", readonly=True, visible=True, no_value={}) + MetadataElement(name="table_row_count", default={}, param=DictParameter, desc="Database Table Row Count", readonly=True, visible=True, no_value={}) file_ext = "sqlite" edam_format = "format_3621" - def init_meta( self, dataset, copy_from=None ): - Binary.init_meta( self, dataset, copy_from=copy_from ) + def init_meta(self, dataset, copy_from=None): + Binary.init_meta(self, dataset, copy_from=copy_from) - def set_meta( self, dataset, overwrite=True, **kwd ): + def set_meta(self, dataset, overwrite=True, **kwd): try: tables = [] columns = dict() @@ -900,20 +954,20 @@ def set_meta( self, dataset, overwrite=True, **kwd ): cols = [col[0] for col in cur.description] columns[table] = cols except Exception as exc: - log.warning( '%s, set_meta Exception: %s', self, exc ) + log.warning('%s, set_meta Exception: %s', self, exc) for table in tables: try: row_query = "SELECT count(*) FROM %s" % table rowcounts[table] = c.execute(row_query).fetchone()[0] except Exception as exc: - log.warning( '%s, set_meta Exception: %s', self, exc ) + log.warning('%s, set_meta Exception: %s', self, exc) dataset.metadata.tables = tables dataset.metadata.table_columns = columns dataset.metadata.table_row_count = rowcounts except Exception as exc: - log.warning( '%s, set_meta Exception: %s', self, exc ) + log.warning('%s, set_meta Exception: %s', self, exc) - def sniff( self, filename ): + def sniff(self, filename): # The first 16 bytes of any SQLite3 database file is 'SQLite format 3\0', and the file is binary. For details # about the format, see http://www.sqlite.org/fileformat.html try: @@ -924,7 +978,7 @@ def sniff( self, filename ): except: return False - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): if not dataset.dataset.purged: dataset.peek = "SQLite Database" lines = ['SQLite Database'] @@ -935,116 +989,116 @@ def set_peek( self, dataset, is_multi_byte=False ): except: continue dataset.peek = '\n'.join(lines) - dataset.blurb = nice_size( dataset.get_size() ) + dataset.blurb = nice_size(dataset.get_size()) else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disk' - def display_peek( self, dataset ): + def display_peek(self, dataset): try: return dataset.peek except: - return "SQLite Database (%s)" % ( nice_size( dataset.get_size() ) ) + return "SQLite Database (%s)" % (nice_size(dataset.get_size())) - @dataproviders.decorators.dataprovider_factory( 'sqlite', dataproviders.dataset.SQliteDataProvider.settings ) - def sqlite_dataprovider( self, dataset, **settings ): - dataset_source = dataproviders.dataset.DatasetDataProvider( dataset ) - return dataproviders.dataset.SQliteDataProvider( dataset_source, **settings ) + @dataproviders.decorators.dataprovider_factory('sqlite', dataproviders.dataset.SQliteDataProvider.settings) + def sqlite_dataprovider(self, dataset, **settings): + dataset_source = dataproviders.dataset.DatasetDataProvider(dataset) + return dataproviders.dataset.SQliteDataProvider(dataset_source, **settings) - @dataproviders.decorators.dataprovider_factory( 'sqlite-table', dataproviders.dataset.SQliteDataTableProvider.settings ) - def sqlite_datatableprovider( self, dataset, **settings ): - dataset_source = dataproviders.dataset.DatasetDataProvider( dataset ) - return dataproviders.dataset.SQliteDataTableProvider( dataset_source, **settings ) + @dataproviders.decorators.dataprovider_factory('sqlite-table', dataproviders.dataset.SQliteDataTableProvider.settings) + def sqlite_datatableprovider(self, dataset, **settings): + dataset_source = dataproviders.dataset.DatasetDataProvider(dataset) + return dataproviders.dataset.SQliteDataTableProvider(dataset_source, **settings) - @dataproviders.decorators.dataprovider_factory( 'sqlite-dict', dataproviders.dataset.SQliteDataDictProvider.settings ) - def sqlite_datadictprovider( self, dataset, **settings ): - dataset_source = dataproviders.dataset.DatasetDataProvider( dataset ) - return dataproviders.dataset.SQliteDataDictProvider( dataset_source, **settings ) + @dataproviders.decorators.dataprovider_factory('sqlite-dict', dataproviders.dataset.SQliteDataDictProvider.settings) + def sqlite_datadictprovider(self, dataset, **settings): + dataset_source = dataproviders.dataset.DatasetDataProvider(dataset) + return dataproviders.dataset.SQliteDataDictProvider(dataset_source, **settings) # Binary.register_sniffable_binary_format("sqlite", "sqlite", SQlite) -class GeminiSQLite( SQlite ): +class GeminiSQLite(SQlite): """Class describing a Gemini Sqlite database """ - MetadataElement( name="gemini_version", default='0.10.0', param=MetadataParameter, desc="Gemini Version", - readonly=True, visible=True, no_value='0.10.0' ) + MetadataElement(name="gemini_version", default='0.10.0', param=MetadataParameter, desc="Gemini Version", + readonly=True, visible=True, no_value='0.10.0') file_ext = "gemini.sqlite" edam_format = "format_3622" edam_data = "data_3498" - def set_meta( self, dataset, overwrite=True, **kwd ): - super( GeminiSQLite, self ).set_meta( dataset, overwrite=overwrite, **kwd ) + def set_meta(self, dataset, overwrite=True, **kwd): + super(GeminiSQLite, self).set_meta(dataset, overwrite=overwrite, **kwd) try: - conn = sqlite.connect( dataset.file_name ) + conn = sqlite.connect(dataset.file_name) c = conn.cursor() tables_query = "SELECT version FROM version" - result = c.execute( tables_query ).fetchall() + result = c.execute(tables_query).fetchall() for version, in result: dataset.metadata.gemini_version = version # TODO: Can/should we detect even more attributes, such as use of PED file, what was input annotation type, etc. except Exception as e: - log.warning( '%s, set_meta Exception: %s', self, e ) + log.warning('%s, set_meta Exception: %s', self, e) - def sniff( self, filename ): - if super( GeminiSQLite, self ).sniff( filename ): - gemini_table_names = [ "gene_detailed", "gene_summary", "resources", "sample_genotype_counts", "sample_genotypes", "samples", - "variant_impacts", "variants", "version" ] + def sniff(self, filename): + if super(GeminiSQLite, self).sniff(filename): + gemini_table_names = ["gene_detailed", "gene_summary", "resources", "sample_genotype_counts", "sample_genotypes", "samples", + "variant_impacts", "variants", "version"] try: - conn = sqlite.connect( filename ) + conn = sqlite.connect(filename) c = conn.cursor() tables_query = "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name" - result = c.execute( tables_query ).fetchall() + result = c.execute(tables_query).fetchall() result = [_[0] for _ in result] for table_name in gemini_table_names: if table_name not in result: return False return True except Exception as e: - log.warning( '%s, sniff Exception: %s', self, e ) + log.warning('%s, sniff Exception: %s', self, e) return False - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): if not dataset.dataset.purged: - dataset.peek = "Gemini SQLite Database, version %s" % ( dataset.metadata.gemini_version or 'unknown' ) - dataset.blurb = nice_size( dataset.get_size() ) + dataset.peek = "Gemini SQLite Database, version %s" % (dataset.metadata.gemini_version or 'unknown') + dataset.blurb = nice_size(dataset.get_size()) else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disk' - def display_peek( self, dataset ): + def display_peek(self, dataset): try: return dataset.peek except: - return "Gemini SQLite Database, version %s" % ( dataset.metadata.gemini_version or 'unknown' ) + return "Gemini SQLite Database, version %s" % (dataset.metadata.gemini_version or 'unknown') -class MzSQlite( SQlite ): +class MzSQlite(SQlite): """Class describing a Proteomics Sqlite database """ file_ext = "mz.sqlite" - def set_meta( self, dataset, overwrite=True, **kwd ): - super( MzSQlite, self ).set_meta( dataset, overwrite=overwrite, **kwd ) + def set_meta(self, dataset, overwrite=True, **kwd): + super(MzSQlite, self).set_meta(dataset, overwrite=overwrite, **kwd) - def sniff( self, filename ): - if super( MzSQlite, self ).sniff( filename ): + def sniff(self, filename): + if super(MzSQlite, self).sniff(filename): mz_table_names = ["DBSequence", "Modification", "Peaks", "Peptide", "PeptideEvidence", "Score", "SearchDatabase", "Source", "SpectraData", "Spectrum", "SpectrumIdentification"] try: - conn = sqlite.connect( filename ) + conn = sqlite.connect(filename) c = conn.cursor() tables_query = "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name" - result = c.execute( tables_query ).fetchall() + result = c.execute(tables_query).fetchall() result = [_[0] for _ in result] for table_name in mz_table_names: if table_name not in result: return False return True except Exception as e: - log.warning( '%s, sniff Exception: %s', self, e ) + log.warning('%s, sniff Exception: %s', self, e) return False -class IdpDB( SQlite ): +class IdpDB(SQlite): """ Class describing an IDPicker 3 idpDB (sqlite) database @@ -1058,44 +1112,44 @@ class IdpDB( SQlite ): """ file_ext = "idpdb" - def set_meta( self, dataset, overwrite=True, **kwd ): - super( IdpDB, self ).set_meta( dataset, overwrite=overwrite, **kwd ) + def set_meta(self, dataset, overwrite=True, **kwd): + super(IdpDB, self).set_meta(dataset, overwrite=overwrite, **kwd) - def sniff( self, filename ): - if super( IdpDB, self ).sniff( filename ): + def sniff(self, filename): + if super(IdpDB, self).sniff(filename): mz_table_names = ["About", "Analysis", "AnalysisParameter", "PeptideSpectrumMatch", "Spectrum", "SpectrumSource"] try: - conn = sqlite.connect( filename ) + conn = sqlite.connect(filename) c = conn.cursor() tables_query = "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name" - result = c.execute( tables_query ).fetchall() + result = c.execute(tables_query).fetchall() result = [_[0] for _ in result] for table_name in mz_table_names: if table_name not in result: return False return True except Exception as e: - log.warning( '%s, sniff Exception: %s', self, e ) + log.warning('%s, sniff Exception: %s', self, e) return False - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): if not dataset.dataset.purged: dataset.peek = "IDPickerDB SQLite file" - dataset.blurb = nice_size( dataset.get_size() ) + dataset.blurb = nice_size(dataset.get_size()) else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disk' - def display_peek( self, dataset ): + def display_peek(self, dataset): try: return dataset.peek except: - return "IDPickerDB SQLite file (%s)" % ( nice_size( dataset.get_size() ) ) + return "IDPickerDB SQLite file (%s)" % (nice_size(dataset.get_size())) -Binary.register_sniffable_binary_format( "gemini.sqlite", "gemini.sqlite", GeminiSQLite ) -Binary.register_sniffable_binary_format( "idpdb", "idpdb", IdpDB ) -Binary.register_sniffable_binary_format( "mz.sqlite", "mz.sqlite", MzSQlite ) +Binary.register_sniffable_binary_format("gemini.sqlite", "gemini.sqlite", GeminiSQLite) +Binary.register_sniffable_binary_format("idpdb", "idpdb", IdpDB) +Binary.register_sniffable_binary_format("mz.sqlite", "mz.sqlite", MzSQlite) # FIXME: We need to register specialized sqlite formats before sqlite, since register_sniffable_binary_format and is_sniffable_binary called in upload.py # ignores sniff order declared in datatypes_conf.xml Binary.register_sniffable_binary_format("sqlite", "sqlite", SQlite) @@ -1105,11 +1159,11 @@ class Xlsx(Binary): """Class for Excel 2007 (xlsx) files""" file_ext = "xlsx" - def sniff( self, filename ): + def sniff(self, filename): # Xlsx is compressed in zip format and must not be uncompressed in Galaxy. try: - if zipfile.is_zipfile( filename ): - tempzip = zipfile.ZipFile( filename ) + if zipfile.is_zipfile(filename): + tempzip = zipfile.ZipFile(filename) if "[Content_Types].xml" in tempzip.namelist() and tempzip.read("[Content_Types].xml").find(b'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml') != -1: return True return False @@ -1120,11 +1174,45 @@ def sniff( self, filename ): Binary.register_sniffable_binary_format("xlsx", "xlsx", Xlsx) -class Sra( Binary ): +class ExcelXls(Binary): + """Class describing an Excel (xls) file""" + file_ext = "excel.xls" + edam_format = "format_3468" + + def sniff(self, filename): + mime_type = subprocess.check_output("file --mime-type '{}'".format(filename), shell=True).rstrip() + if mime_type.find("application/vnd.ms-excel") != -1: + return True + else: + return False + + def get_mime(self): + """Returns the mime type of the datatype""" + return 'application/vnd.ms-excel' + + def set_peek(self, dataset, is_multi_byte=False): + if not dataset.dataset.purged: + dataset.peek = "Microsoft Excel XLS file" + dataset.blurb = data.nice_size(dataset.get_size()) + else: + dataset.peek = 'file does not exist' + dataset.blurb = 'file purged from disk' + + def display_peek(self, dataset): + try: + return dataset.peek + except: + return "Microsoft Excel XLS file (%s)" % (data.nice_size(dataset.get_size())) + + +Binary.register_sniffable_binary_format("excel.xls", "excel.xls", ExcelXls) + + +class Sra(Binary): """ Sequence Read Archive (SRA) datatype originally from mdshw5/sra-tools-galaxy""" file_ext = 'sra' - def sniff( self, filename ): + def sniff(self, filename): """ The first 8 bytes of any NCBI sra file is 'NCBI.sra', and the file is binary. For details about the format, see http://www.ncbi.nlm.nih.gov/books/n/helpsra/SRA_Overview_BK/#SRA_Overview_BK.4_SRA_Data_Structure """ @@ -1155,18 +1243,18 @@ def display_peek(self, dataset): Binary.register_sniffable_binary_format('sra', 'sra', Sra) -class RData( Binary ): +class RData(Binary): """Generic R Data file datatype implementation""" file_ext = 'rdata' - def sniff( self, filename ): + def sniff(self, filename): rdata_header = b'RDX2\nX\n' try: header = open(filename, 'rb').read(7) if header == rdata_header: return True - header = gzip.open( filename ).read(7) + header = gzip.open(filename).read(7) if header == rdata_header: return True except: @@ -1359,19 +1447,19 @@ def sniff(self, filename): OxliGraphLabels) -class SearchGuiArchive ( CompressedArchive ): +class SearchGuiArchive (CompressedArchive): """Class describing a SearchGUI archive """ - MetadataElement( name="searchgui_version", default='1.28.0', param=MetadataParameter, desc="SearchGui Version", - readonly=True, visible=True, no_value=None ) - MetadataElement( name="searchgui_major_version", default='1', param=MetadataParameter, desc="SearchGui Major Version", - readonly=True, visible=True, no_value=None ) + MetadataElement(name="searchgui_version", default='1.28.0', param=MetadataParameter, desc="SearchGui Version", + readonly=True, visible=True, no_value=None) + MetadataElement(name="searchgui_major_version", default='1', param=MetadataParameter, desc="SearchGui Major Version", + readonly=True, visible=True, no_value=None) file_ext = "searchgui_archive" - def set_meta( self, dataset, overwrite=True, **kwd ): - super( SearchGuiArchive, self ).set_meta( dataset, overwrite=overwrite, **kwd ) + def set_meta(self, dataset, overwrite=True, **kwd): + super(SearchGuiArchive, self).set_meta(dataset, overwrite=overwrite, **kwd) try: - if dataset and zipfile.is_zipfile( dataset.file_name ): - tempzip = zipfile.ZipFile( dataset.file_name ) + if dataset and zipfile.is_zipfile(dataset.file_name): + tempzip = zipfile.ZipFile(dataset.file_name) if 'searchgui.properties' in tempzip.namelist(): fh = tempzip.open('searchgui.properties') for line in fh: @@ -1382,60 +1470,60 @@ def set_meta( self, dataset, overwrite=True, **kwd ): fh.close() tempzip.close() except Exception as e: - log.warning( '%s, set_meta Exception: %s', self, e ) + log.warning('%s, set_meta Exception: %s', self, e) - def sniff( self, filename ): + def sniff(self, filename): try: - if filename and zipfile.is_zipfile( filename ): - tempzip = zipfile.ZipFile( filename, 'r' ) + if filename and zipfile.is_zipfile(filename): + tempzip = zipfile.ZipFile(filename, 'r') is_searchgui = 'searchgui.properties' in tempzip.namelist() tempzip.close() return is_searchgui except Exception as e: - log.warning( '%s, sniff Exception: %s', self, e ) + log.warning('%s, sniff Exception: %s', self, e) return False - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): if not dataset.dataset.purged: - dataset.peek = "SearchGUI Archive, version %s" % ( dataset.metadata.searchgui_version or 'unknown' ) - dataset.blurb = nice_size( dataset.get_size() ) + dataset.peek = "SearchGUI Archive, version %s" % (dataset.metadata.searchgui_version or 'unknown') + dataset.blurb = nice_size(dataset.get_size()) else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disk' - def display_peek( self, dataset ): + def display_peek(self, dataset): try: return dataset.peek except: - return "SearchGUI Archive, version %s" % ( dataset.metadata.searchgui_version or 'unknown' ) + return "SearchGUI Archive, version %s" % (dataset.metadata.searchgui_version or 'unknown') Binary.register_sniffable_binary_format("searchgui_archive", "searchgui_archive", SearchGuiArchive) -class NetCDF( Binary ): +class NetCDF(Binary): """Binary data in netCDF format""" file_ext = "netcdf" edam_format = "format_3650" edam_data = "data_0943" - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): if not dataset.dataset.purged: dataset.peek = "Binary netCDF file" - dataset.blurb = nice_size( dataset.get_size() ) + dataset.blurb = nice_size(dataset.get_size()) else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disk' - def display_peek( self, dataset ): + def display_peek(self, dataset): try: return dataset.peek except: - return "Binary netCDF file (%s)" % ( nice_size( dataset.get_size() ) ) + return "Binary netCDF file (%s)" % (nice_size(dataset.get_size())) - def sniff( self, filename ): + def sniff(self, filename): try: - with open( filename, 'rb' ) as f: + with open(filename, 'rb') as f: header = f.read(3) if header == b'CDF': return True @@ -1447,7 +1535,7 @@ def sniff( self, filename ): Binary.register_sniffable_binary_format("netcdf", "netcdf", NetCDF) -class DMND( Binary ): +class DMND(Binary): """ Class describing an DMND file >>> from galaxy.datatypes.sniff import get_test_fname @@ -1461,15 +1549,15 @@ class DMND( Binary ): file_ext = "dmnd" edam_format = "" - def __init__( self, **kwd ): - Binary.__init__( self, **kwd ) + def __init__(self, **kwd): + Binary.__init__(self, **kwd) self._magic = binascii.unhexlify("6d18ee15a4f84a02") - def sniff( self, filename ): + def sniff(self, filename): # The first 8 bytes of any dmnd file are 0x24af8a415ee186d try: - header = open( filename, 'rb' ).read(8) + header = open(filename, 'rb').read(8) if header == self._magic: return True return False diff --git a/lib/galaxy/datatypes/chrominfo.py b/lib/galaxy/datatypes/chrominfo.py index 179bf4a807e9..1b812160f9e5 100644 --- a/lib/galaxy/datatypes/chrominfo.py +++ b/lib/galaxy/datatypes/chrominfo.py @@ -5,7 +5,7 @@ from galaxy.datatypes.metadata import MetadataElement -class ChromInfo( galaxy.datatypes.tabular.Tabular ): +class ChromInfo(galaxy.datatypes.tabular.Tabular): file_ext = "len" - MetadataElement( name="chrom", default=1, desc="Chrom column", param=galaxy.datatypes.metadata.ColumnParameter ) - MetadataElement( name="length", default=2, desc="Length column", param=galaxy.datatypes.metadata.ColumnParameter ) + MetadataElement(name="chrom", default=1, desc="Chrom column", param=galaxy.datatypes.metadata.ColumnParameter) + MetadataElement(name="length", default=2, desc="Length column", param=galaxy.datatypes.metadata.ColumnParameter) diff --git a/lib/galaxy/datatypes/converters/bed_to_gff_converter.py b/lib/galaxy/datatypes/converters/bed_to_gff_converter.py index 82171549f046..ce6471677665 100644 --- a/lib/galaxy/datatypes/converters/bed_to_gff_converter.py +++ b/lib/galaxy/datatypes/converters/bed_to_gff_converter.py @@ -4,7 +4,7 @@ import sys -assert sys.version_info[:2] >= ( 2, 4 ) +assert sys.version_info[:2] >= (2, 4) def __main__(): @@ -12,17 +12,17 @@ def __main__(): output_name = sys.argv[2] skipped_lines = 0 first_skipped_line = 0 - out = open( output_name, 'w' ) - out.write( "##gff-version 2\n" ) - out.write( "##bed_to_gff_converter.py\n\n" ) + out = open(output_name, 'w') + out.write("##gff-version 2\n") + out.write("##bed_to_gff_converter.py\n\n") i = 0 - for i, line in enumerate( open( input_name ) ): + for i, line in enumerate(open(input_name)): complete_bed = False - line = line.rstrip( '\r\n' ) - if line and not line.startswith( '#' ) and not line.startswith( 'track' ) and not line.startswith( 'browser' ): + line = line.rstrip('\r\n') + if line and not line.startswith('#') and not line.startswith('track') and not line.startswith('browser'): try: - elems = line.split( '\t' ) - if len( elems ) == 12: + elems = line.split('\t') + if len(elems) == 12: complete_bed = True chrom = elems[0] if complete_bed: @@ -31,9 +31,9 @@ def __main__(): try: feature = elems[3] except: - feature = 'feature%d' % ( i + 1 ) - start = int( elems[1] ) + 1 - end = int( elems[2] ) + feature = 'feature%d' % (i + 1) + start = int(elems[1]) + 1 + end = int(elems[2]) try: score = elems[4] except: @@ -45,20 +45,20 @@ def __main__(): try: group = elems[3] except: - group = 'group%d' % ( i + 1 ) + group = 'group%d' % (i + 1) if complete_bed: - out.write( '%s\tbed2gff\t%s\t%d\t%d\t%s\t%s\t.\t%s %s;\n' % ( chrom, feature, start, end, score, strand, feature, group ) ) + out.write('%s\tbed2gff\t%s\t%d\t%d\t%s\t%s\t.\t%s %s;\n' % (chrom, feature, start, end, score, strand, feature, group)) else: - out.write( '%s\tbed2gff\t%s\t%d\t%d\t%s\t%s\t.\t%s;\n' % ( chrom, feature, start, end, score, strand, group ) ) + out.write('%s\tbed2gff\t%s\t%d\t%d\t%s\t%s\t.\t%s;\n' % (chrom, feature, start, end, score, strand, group)) if complete_bed: # We have all the info necessary to annotate exons for genes and mRNAs - block_count = int( elems[9] ) - block_sizes = elems[10].split( ',' ) - block_starts = elems[11].split( ',' ) - for j in range( block_count ): - exon_start = int( start ) + int( block_starts[j] ) - exon_end = exon_start + int( block_sizes[j] ) - 1 - out.write( '%s\tbed2gff\texon\t%d\t%d\t%s\t%s\t.\texon %s;\n' % ( chrom, exon_start, exon_end, score, strand, group ) ) + block_count = int(elems[9]) + block_sizes = elems[10].split(',') + block_starts = elems[11].split(',') + for j in range(block_count): + exon_start = int(start) + int(block_starts[j]) + exon_end = exon_start + int(block_sizes[j]) - 1 + out.write('%s\tbed2gff\texon\t%d\t%d\t%s\t%s\t.\texon %s;\n' % (chrom, exon_start, exon_end, score, strand, group)) except: skipped_lines += 1 if not first_skipped_line: @@ -68,9 +68,9 @@ def __main__(): if not first_skipped_line: first_skipped_line = i + 1 out.close() - info_msg = "%i lines converted to GFF version 2. " % ( i + 1 - skipped_lines ) + info_msg = "%i lines converted to GFF version 2. " % (i + 1 - skipped_lines) if skipped_lines > 0: - info_msg += "Skipped %d blank/comment/invalid lines starting with line #%d." % ( skipped_lines, first_skipped_line ) + info_msg += "Skipped %d blank/comment/invalid lines starting with line #%d." % (skipped_lines, first_skipped_line) print(info_msg) diff --git a/lib/galaxy/datatypes/converters/bedgraph_to_array_tree_converter.py b/lib/galaxy/datatypes/converters/bedgraph_to_array_tree_converter.py index 07021532737b..096b6a01254a 100644 --- a/lib/galaxy/datatypes/converters/bedgraph_to_array_tree_converter.py +++ b/lib/galaxy/datatypes/converters/bedgraph_to_array_tree_converter.py @@ -11,13 +11,13 @@ class BedGraphReader(Iterator): - def __init__( self, f ): + def __init__(self, f): self.f = f - def __iter__( self ): + def __iter__(self): return self - def __next__( self ): + def __next__(self): while True: line = self.f.readline() if not line: @@ -27,7 +27,7 @@ def __next__( self ): if line[0] == "#": continue if line[0].isalpha(): - if line.startswith( "track" ) or line.startswith( "browser" ): + if line.startswith("track") or line.startswith("browser"): continue feature = line.strip().split() @@ -42,15 +42,15 @@ def main(): input_fname = sys.argv[1] out_fname = sys.argv[2] - reader = BedGraphReader( open( input_fname ) ) + reader = BedGraphReader(open(input_fname)) # Fill array from reader - d = array_tree_dict_from_reader( reader, {}, block_size=BLOCK_SIZE ) + d = array_tree_dict_from_reader(reader, {}, block_size=BLOCK_SIZE) for array_tree in d.values(): array_tree.root.build_summary() - FileArrayTreeDict.dict_to_file( d, open( out_fname, "w" ) ) + FileArrayTreeDict.dict_to_file(d, open(out_fname, "w")) if __name__ == "__main__": diff --git a/lib/galaxy/datatypes/converters/bgzip.py b/lib/galaxy/datatypes/converters/bgzip.py index 3cd6062c9907..6e188b54bd99 100644 --- a/lib/galaxy/datatypes/converters/bgzip.py +++ b/lib/galaxy/datatypes/converters/bgzip.py @@ -15,10 +15,10 @@ def main(): # Read options, args. parser = optparse.OptionParser() - parser.add_option( '-c', '--chr-col', type='int', dest='chrom_col' ) - parser.add_option( '-s', '--start-col', type='int', dest='start_col' ) - parser.add_option( '-e', '--end-col', type='int', dest='end_col' ) - parser.add_option( '-P', '--preset', dest='preset' ) + parser.add_option('-c', '--chr-col', type='int', dest='chrom_col') + parser.add_option('-s', '--start-col', type='int', dest='start_col') + parser.add_option('-e', '--end-col', type='int', dest='end_col') + parser.add_option('-P', '--preset', dest='preset') (options, args) = parser.parse_args() input_fname, output_fname = args @@ -28,9 +28,9 @@ def main(): if options.chrom_col and options.start_col and options.end_col: sort_params = [ "sort", - "-k%(i)s,%(i)s" % { 'i': options.chrom_col }, - "-k%(i)i,%(i)in" % { 'i': options.start_col }, - "-k%(i)i,%(i)in" % { 'i': options.end_col } + "-k%(i)s,%(i)s" % {'i': options.chrom_col}, + "-k%(i)i,%(i)in" % {'i': options.start_col}, + "-k%(i)i,%(i)in" % {'i': options.end_col} ] elif options.preset == "bed": sort_params = ["sort", "-k1,1", "-k2,2n", "-k3,3n"] @@ -39,8 +39,8 @@ def main(): elif options.preset == "gff": sort_params = ["sort", "-s", "-k1,1", "-k4,4n"] # stable sort on start column # Skip any lines starting with "#" and "track" - grepped = subprocess.Popen(["grep", "-e", "^\"#\"", "-e", "^track", "-v", input_fname], stderr=subprocess.PIPE, stdout=subprocess.PIPE ) - after_sort = subprocess.Popen(sort_params, stdin=grepped.stdout, stderr=subprocess.PIPE, stdout=tmpfile ) + grepped = subprocess.Popen(["grep", "-e", "^\"#\"", "-e", "^track", "-v", input_fname], stderr=subprocess.PIPE, stdout=subprocess.PIPE) + after_sort = subprocess.Popen(sort_params, stdin=grepped.stdout, stderr=subprocess.PIPE, stdout=tmpfile) grepped.stdout.close() output, err = after_sort.communicate() diff --git a/lib/galaxy/datatypes/converters/fasta_to_len.py b/lib/galaxy/datatypes/converters/fasta_to_len.py index 9d8fbf4869fb..7c088d75e9f3 100644 --- a/lib/galaxy/datatypes/converters/fasta_to_len.py +++ b/lib/galaxy/datatypes/converters/fasta_to_len.py @@ -6,14 +6,14 @@ """ import sys -assert sys.version_info[:2] >= ( 2, 4 ) +assert sys.version_info[:2] >= (2, 4) -def compute_fasta_length( fasta_file, out_file, keep_first_char, keep_first_word=False ): +def compute_fasta_length(fasta_file, out_file, keep_first_char, keep_first_word=False): infile = fasta_file - out = open( out_file, 'w') - keep_first_char = int( keep_first_char ) + out = open(out_file, 'w') + keep_first_char = int(keep_first_char) fasta_title = '' seq_len = 0 @@ -26,15 +26,15 @@ def compute_fasta_length( fasta_file, out_file, keep_first_char, keep_first_word first_entry = True - for line in open( infile ): + for line in open(infile): line = line.strip() - if not line or line.startswith( '#' ): + if not line or line.startswith('#'): continue if line[0] == '>': if first_entry is False: if keep_first_word: fasta_title = fasta_title.split()[0] - out.write( "%s\t%d\n" % ( fasta_title[ 1:keep_first_char ], seq_len ) ) + out.write("%s\t%d\n" % (fasta_title[1:keep_first_char], seq_len)) else: first_entry = False fasta_title = line @@ -45,9 +45,9 @@ def compute_fasta_length( fasta_file, out_file, keep_first_char, keep_first_word # last fasta-entry if keep_first_word: fasta_title = fasta_title.split()[0] - out.write( "%s\t%d\n" % ( fasta_title[ 1:keep_first_char ], seq_len ) ) + out.write("%s\t%d\n" % (fasta_title[1:keep_first_char], seq_len)) out.close() if __name__ == "__main__" : - compute_fasta_length( sys.argv[1], sys.argv[2], sys.argv[3], True ) + compute_fasta_length(sys.argv[1], sys.argv[2], sys.argv[3], True) diff --git a/lib/galaxy/datatypes/converters/fastqsolexa_to_fasta_converter.py b/lib/galaxy/datatypes/converters/fastqsolexa_to_fasta_converter.py index 988fcb57cfa6..5b2e008b7132 100644 --- a/lib/galaxy/datatypes/converters/fastqsolexa_to_fasta_converter.py +++ b/lib/galaxy/datatypes/converters/fastqsolexa_to_fasta_converter.py @@ -15,36 +15,36 @@ """ import sys -assert sys.version_info[:2] >= ( 2, 4 ) +assert sys.version_info[:2] >= (2, 4) -def stop_err( msg ): - sys.stderr.write( "%s" % msg ) +def stop_err(msg): + sys.stderr.write("%s" % msg) sys.exit() def __main__(): infile_name = sys.argv[1] - outfile = open( sys.argv[2], 'w' ) + outfile = open(sys.argv[2], 'w') fastq_block_lines = 0 seq_title_startswith = '' - for i, line in enumerate( open( infile_name ) ): + for i, line in enumerate(open(infile_name)): line = line.rstrip() # eliminate trailing space and new line characters - if not line or line.startswith( '#' ): + if not line or line.startswith('#'): continue - fastq_block_lines = ( fastq_block_lines + 1 ) % 4 + fastq_block_lines = (fastq_block_lines + 1) % 4 line_startswith = line[0:1] if fastq_block_lines == 1: # line 1 is sequence title if not seq_title_startswith: seq_title_startswith = line_startswith if seq_title_startswith != line_startswith: - stop_err( 'Invalid fastqsolexa format at line %d: %s.' % ( i + 1, line ) ) - outfile.write( '>%s\n' % line[1:] ) + stop_err('Invalid fastqsolexa format at line %d: %s.' % (i + 1, line)) + outfile.write('>%s\n' % line[1:]) elif fastq_block_lines == 2: # line 2 is nucleotides - outfile.write( '%s\n' % line ) + outfile.write('%s\n' % line) else: pass diff --git a/lib/galaxy/datatypes/converters/fastqsolexa_to_qual_converter.py b/lib/galaxy/datatypes/converters/fastqsolexa_to_qual_converter.py index 2ac167256858..34ec1cbd67c0 100644 --- a/lib/galaxy/datatypes/converters/fastqsolexa_to_qual_converter.py +++ b/lib/galaxy/datatypes/converters/fastqsolexa_to_qual_converter.py @@ -15,52 +15,52 @@ """ import sys -assert sys.version_info[:2] >= ( 2, 4 ) +assert sys.version_info[:2] >= (2, 4) -def stop_err( msg ): - sys.stderr.write( "%s" % msg ) +def stop_err(msg): + sys.stderr.write("%s" % msg) sys.exit() def __main__(): infile_name = sys.argv[1] - outfile_score = open( sys.argv[2], 'w' ) + outfile_score = open(sys.argv[2], 'w') # datatype = sys.argv[3] qual_title_startswith = '' seq_title_startswith = '' default_coding_value = 64 fastq_block_lines = 0 - for i, line in enumerate( open( infile_name ) ): + for i, line in enumerate(open(infile_name)): line = line.rstrip() - if not line or line.startswith( '#' ): + if not line or line.startswith('#'): continue - fastq_block_lines = ( fastq_block_lines + 1 ) % 4 + fastq_block_lines = (fastq_block_lines + 1) % 4 line_startswith = line[0:1] if fastq_block_lines == 1: # first line is @title_of_seq if not seq_title_startswith: seq_title_startswith = line_startswith if line_startswith != seq_title_startswith: - stop_err( 'Invalid fastqsolexa format at line %d: %s.' % ( i + 1, line ) ) + stop_err('Invalid fastqsolexa format at line %d: %s.' % (i + 1, line)) read_title = line[1:] elif fastq_block_lines == 2: # second line is nucleotides - read_length = len( line ) + read_length = len(line) elif fastq_block_lines == 3: # third line is +title_of_qualityscore (might be skipped) if not qual_title_startswith: qual_title_startswith = line_startswith if line_startswith != qual_title_startswith: - stop_err( 'Invalid fastqsolexa format at line %d: %s.' % ( i + 1, line ) ) + stop_err('Invalid fastqsolexa format at line %d: %s.' % (i + 1, line)) quality_title = line[1:] if quality_title and read_title != quality_title: - stop_err( 'Invalid fastqsolexa format at line %d: sequence title "%s" differes from score title "%s".' % ( i + 1, read_title, quality_title ) ) + stop_err('Invalid fastqsolexa format at line %d: sequence title "%s" differes from score title "%s".' % (i + 1, read_title, quality_title)) if not quality_title: - outfile_score.write( '>%s\n' % read_title ) + outfile_score.write('>%s\n' % read_title) else: - outfile_score.write( '>%s\n' % line[1:] ) + outfile_score.write('>%s\n' % line[1:]) else: # fourth line is quality scores qual = '' @@ -69,7 +69,7 @@ def __main__(): val = line.split()[0] try: - int( val ) + int(val) fastq_integer = True except: fastq_integer = False @@ -78,18 +78,18 @@ def __main__(): qual = line else: # ascii - quality_score_length = len( line ) + quality_score_length = len(line) if quality_score_length == read_length + 1: - quality_score_startswith = ord( line[0:1] ) + quality_score_startswith = ord(line[0:1]) line = line[1:] elif quality_score_length == read_length: quality_score_startswith = default_coding_value else: - stop_err( 'Invalid fastqsolexa format at line %d: the number of quality scores ( %d ) is not the same as bases ( %d ).' % ( i + 1, quality_score_length, read_length ) ) - for j, char in enumerate( line ): - score = ord( char ) - quality_score_startswith # 64 - qual = "%s%s " % ( qual, str( score ) ) - outfile_score.write( '%s\n' % qual ) + stop_err('Invalid fastqsolexa format at line %d: the number of quality scores ( %d ) is not the same as bases ( %d ).' % (i + 1, quality_score_length, read_length)) + for j, char in enumerate(line): + score = ord(char) - quality_score_startswith # 64 + qual = "%s%s " % (qual, str(score)) + outfile_score.write('%s\n' % qual) outfile_score.close() diff --git a/lib/galaxy/datatypes/converters/gff_to_bed_converter.py b/lib/galaxy/datatypes/converters/gff_to_bed_converter.py index 788931866a36..60435054e347 100644 --- a/lib/galaxy/datatypes/converters/gff_to_bed_converter.py +++ b/lib/galaxy/datatypes/converters/gff_to_bed_converter.py @@ -3,7 +3,7 @@ import sys -assert sys.version_info[:2] >= ( 2, 4 ) +assert sys.version_info[:2] >= (2, 4) def __main__(): @@ -11,14 +11,14 @@ def __main__(): output_name = sys.argv[2] skipped_lines = 0 first_skipped_line = 0 - out = open( output_name, 'w' ) + out = open(output_name, 'w') i = 0 - for i, line in enumerate( open( input_name ) ): - line = line.rstrip( '\r\n' ) - if line and not line.startswith( '#' ): + for i, line in enumerate(open(input_name)): + line = line.rstrip('\r\n') + if line and not line.startswith('#'): try: - elems = line.split( '\t' ) - start = str( int( elems[3] ) - 1 ) + elems = line.split('\t') + start = str(int(elems[3]) - 1) strand = elems[6] if strand not in ['+', '-']: strand = '+' @@ -27,7 +27,7 @@ def __main__(): # # Replace any spaces in the name with underscores so UCSC will not complain name = elems[2].replace(" ", "_") - out.write( "%s\t%s\t%s\t%s\t0\t%s\n" % ( elems[0], start, elems[4], name, strand ) ) + out.write("%s\t%s\t%s\t%s\t0\t%s\n" % (elems[0], start, elems[4], name, strand)) except: skipped_lines += 1 if not first_skipped_line: @@ -37,9 +37,9 @@ def __main__(): if not first_skipped_line: first_skipped_line = i + 1 out.close() - info_msg = "%i lines converted to BED. " % ( i + 1 - skipped_lines ) + info_msg = "%i lines converted to BED. " % (i + 1 - skipped_lines) if skipped_lines > 0: - info_msg += "Skipped %d blank/comment/invalid lines starting with line #%d." % ( skipped_lines, first_skipped_line ) + info_msg += "Skipped %d blank/comment/invalid lines starting with line #%d." % (skipped_lines, first_skipped_line) print(info_msg) diff --git a/lib/galaxy/datatypes/converters/gff_to_interval_index_converter.py b/lib/galaxy/datatypes/converters/gff_to_interval_index_converter.py index c6b081b5de6f..6dda96b944f2 100644 --- a/lib/galaxy/datatypes/converters/gff_to_interval_index_converter.py +++ b/lib/galaxy/datatypes/converters/gff_to_interval_index_converter.py @@ -23,18 +23,18 @@ def main(): # Do conversion. index = Indexes() offset = 0 - reader_wrapper = GFFReaderWrapper( fileinput.FileInput( input_fname ), fix_strand=True ) - for feature in list( reader_wrapper ): + reader_wrapper = GFFReaderWrapper(fileinput.FileInput(input_fname), fix_strand=True) + for feature in list(reader_wrapper): # Add feature; index expects BED coordinates. - if isinstance( feature, GenomicInterval ): - convert_gff_coords_to_bed( feature ) - index.add( feature.chrom, feature.start, feature.end, offset ) + if isinstance(feature, GenomicInterval): + convert_gff_coords_to_bed(feature) + index.add(feature.chrom, feature.start, feature.end, offset) # Always increment offset, even if feature is not an interval and hence # not included in the index. offset += feature.raw_size - index.write( open(out_fname, "w") ) + index.write(open(out_fname, "w")) if __name__ == "__main__": diff --git a/lib/galaxy/datatypes/converters/interval_to_bed_converter.py b/lib/galaxy/datatypes/converters/interval_to_bed_converter.py index 36fec42571b5..48b0dd579ea0 100644 --- a/lib/galaxy/datatypes/converters/interval_to_bed_converter.py +++ b/lib/galaxy/datatypes/converters/interval_to_bed_converter.py @@ -6,11 +6,11 @@ import bx.intervals.io -assert sys.version_info[:2] >= ( 2, 4 ) +assert sys.version_info[:2] >= (2, 4) -def stop_err( msg ): - sys.stderr.write( msg ) +def stop_err(msg): + sys.stderr.write(msg) sys.exit() @@ -18,30 +18,30 @@ def __main__(): output_name = sys.argv[1] input_name = sys.argv[2] try: - chromCol = int( sys.argv[3] ) - 1 + chromCol = int(sys.argv[3]) - 1 except: - stop_err( "'%s' is an invalid chrom column, correct the column settings before attempting to convert the data format." % str( sys.argv[3] ) ) + stop_err("'%s' is an invalid chrom column, correct the column settings before attempting to convert the data format." % str(sys.argv[3])) try: - startCol = int( sys.argv[4] ) - 1 + startCol = int(sys.argv[4]) - 1 except: - stop_err( "'%s' is an invalid start column, correct the column settings before attempting to convert the data format." % str( sys.argv[4] ) ) + stop_err("'%s' is an invalid start column, correct the column settings before attempting to convert the data format." % str(sys.argv[4])) try: - endCol = int( sys.argv[5] ) - 1 + endCol = int(sys.argv[5]) - 1 except: - stop_err( "'%s' is an invalid end column, correct the column settings before attempting to convert the data format." % str( sys.argv[5] ) ) + stop_err("'%s' is an invalid end column, correct the column settings before attempting to convert the data format." % str(sys.argv[5])) try: - strandCol = int( sys.argv[6] ) - 1 + strandCol = int(sys.argv[6]) - 1 except: strandCol = -1 try: - nameCol = int( sys.argv[7] ) - 1 + nameCol = int(sys.argv[7]) - 1 except: nameCol = -1 skipped_lines = 0 first_skipped_line = 0 - out = open( output_name, 'w' ) + out = open(output_name, 'w') count = 0 - for count, region in enumerate( bx.intervals.io.NiceReaderWrapper( open( input_name, 'r' ), chrom_col=chromCol, start_col=startCol, end_col=endCol, strand_col=strandCol, fix_strand=True, return_header=False, return_comments=False ) ): + for count, region in enumerate(bx.intervals.io.NiceReaderWrapper(open(input_name, 'r'), chrom_col=chromCol, start_col=startCol, end_col=endCol, strand_col=strandCol, fix_strand=True, return_header=False, return_comments=False)): try: if nameCol >= 0: name = region.fields[nameCol] @@ -51,15 +51,15 @@ def __main__(): name = "region_%i" % count try: - out.write( "%s\t%i\t%i\t%s\t%i\t%s\n" % ( region.chrom, region.start, region.end, name, 0, region.strand ) ) + out.write("%s\t%i\t%i\t%s\t%i\t%s\n" % (region.chrom, region.start, region.end, name, 0, region.strand)) except: skipped_lines += 1 if not first_skipped_line: first_skipped_line = count + 1 out.close() - print("%i regions converted to BED." % ( count + 1 - skipped_lines )) + print("%i regions converted to BED." % (count + 1 - skipped_lines)) if skipped_lines > 0: - print("Skipped %d blank or invalid lines starting with line # %d." % ( skipped_lines, first_skipped_line )) + print("Skipped %d blank or invalid lines starting with line # %d." % (skipped_lines, first_skipped_line)) if __name__ == "__main__": diff --git a/lib/galaxy/datatypes/converters/interval_to_bedstrict_converter.py b/lib/galaxy/datatypes/converters/interval_to_bedstrict_converter.py index 8c34e384b430..04041cf575f7 100644 --- a/lib/galaxy/datatypes/converters/interval_to_bedstrict_converter.py +++ b/lib/galaxy/datatypes/converters/interval_to_bedstrict_converter.py @@ -6,33 +6,33 @@ import bx.intervals.io -assert sys.version_info[:2] >= ( 2, 4 ) +assert sys.version_info[:2] >= (2, 4) -def stop_err( msg ): - sys.stderr.write( msg ) +def stop_err(msg): + sys.stderr.write(msg) sys.exit() -def force_bed_field_count( fields, region_count, force_num_columns ): - if force_num_columns >= 4 and len( fields ) < 4: - fields.append( 'region_%i' % ( region_count ) ) - if force_num_columns >= 5 and len( fields ) < 5: - fields.append( '0' ) - if force_num_columns >= 6 and len( fields ) < 6: - fields.append( '+' ) - if force_num_columns >= 7 and len( fields ) < 7: - fields.append( fields[1] ) - if force_num_columns >= 8 and len( fields ) < 8: - fields.append( fields[2] ) - if force_num_columns >= 9 and len( fields ) < 9: - fields.append( '0' ) - if force_num_columns >= 10 and len( fields ) < 10: - fields.append( '0' ) - if force_num_columns >= 11 and len( fields ) < 11: - fields.append( ',' ) - if force_num_columns >= 12 and len( fields ) < 12: - fields.append( ',' ) +def force_bed_field_count(fields, region_count, force_num_columns): + if force_num_columns >= 4 and len(fields) < 4: + fields.append('region_%i' % (region_count)) + if force_num_columns >= 5 and len(fields) < 5: + fields.append('0') + if force_num_columns >= 6 and len(fields) < 6: + fields.append('+') + if force_num_columns >= 7 and len(fields) < 7: + fields.append(fields[1]) + if force_num_columns >= 8 and len(fields) < 8: + fields.append(fields[2]) + if force_num_columns >= 9 and len(fields) < 9: + fields.append('0') + if force_num_columns >= 10 and len(fields) < 10: + fields.append('0') + if force_num_columns >= 11 and len(fields) < 11: + fields.append(',') + if force_num_columns >= 12 and len(fields) < 12: + fields.append(',') return fields[:force_num_columns] @@ -40,23 +40,23 @@ def __main__(): output_name = sys.argv[1] input_name = sys.argv[2] try: - chromCol = int( sys.argv[3] ) - 1 + chromCol = int(sys.argv[3]) - 1 except: - stop_err( "'%s' is an invalid chrom column, correct the column settings before attempting to convert the data format." % str( sys.argv[3] ) ) + stop_err("'%s' is an invalid chrom column, correct the column settings before attempting to convert the data format." % str(sys.argv[3])) try: - startCol = int( sys.argv[4] ) - 1 + startCol = int(sys.argv[4]) - 1 except: - stop_err( "'%s' is an invalid start column, correct the column settings before attempting to convert the data format." % str( sys.argv[4] ) ) + stop_err("'%s' is an invalid start column, correct the column settings before attempting to convert the data format." % str(sys.argv[4])) try: - endCol = int( sys.argv[5] ) - 1 + endCol = int(sys.argv[5]) - 1 except: - stop_err( "'%s' is an invalid end column, correct the column settings before attempting to convert the data format." % str( sys.argv[5] ) ) + stop_err("'%s' is an invalid end column, correct the column settings before attempting to convert the data format." % str(sys.argv[5])) try: - strandCol = int( sys.argv[6] ) - 1 + strandCol = int(sys.argv[6]) - 1 except: strandCol = -1 try: - nameCol = int( sys.argv[7] ) - 1 + nameCol = int(sys.argv[7]) - 1 except: nameCol = -1 try: @@ -64,20 +64,20 @@ def __main__(): except: extension = 'interval' # default extension try: - force_num_columns = int( sys.argv[9] ) + force_num_columns = int(sys.argv[9]) except: force_num_columns = None skipped_lines = 0 first_skipped_line = None - out = open( output_name, 'w' ) + out = open(output_name, 'w') count = 0 # does file already conform to bed strict? # if so, we want to keep extended columns, otherwise we'll create a generic 6 column bed file strict_bed = True - if extension in [ 'bed', 'bedstrict', 'bed6', 'bed12' ] and ( chromCol, startCol, endCol) == ( 0, 1, 2) and ( nameCol < 0 or nameCol == 3 ) and ( strandCol < 0 or strandCol == 5 ): - for count, line in enumerate( open( input_name ) ): - line = line.rstrip( '\n\r' ) + if extension in ['bed', 'bedstrict', 'bed6', 'bed12'] and (chromCol, startCol, endCol) == (0, 1, 2) and (nameCol < 0 or nameCol == 3) and (strandCol < 0 or strandCol == 5): + for count, line in enumerate(open(input_name)): + line = line.rstrip('\n\r') if line == "" or line.startswith("#"): skipped_lines += 1 if first_skipped_line is None: @@ -85,43 +85,43 @@ def __main__(): continue fields = line.split('\t') try: - assert len( fields ) >= 3, 'A BED file requires at least 3 columns' # we can't fix this + assert len(fields) >= 3, 'A BED file requires at least 3 columns' # we can't fix this if len(fields) > 12: strict_bed = False break # name (fields[3]) can be anything, no verification needed - if len( fields ) > 4: - float( fields[4] ) # score - A score between 0 and 1000. If the track line useScore attribute is set to 1 for this annotation data set, the score value will determine the level of gray in which this feature is displayed (higher numbers = darker gray). - if len( fields ) > 5: - assert fields[5] in [ '+', '-' ], 'Invalid strand' # strand - Defines the strand - either '+' or '-'. - if len( fields ) > 6: - int( fields[6] ) # thickStart - The starting position at which the feature is drawn thickly (for example, the start codon in gene displays). - if len( fields ) > 7: - int( fields[7] ) # thickEnd - The ending position at which the feature is drawn thickly (for example, the stop codon in gene displays). - if len( fields ) > 8: + if len(fields) > 4: + float(fields[4]) # score - A score between 0 and 1000. If the track line useScore attribute is set to 1 for this annotation data set, the score value will determine the level of gray in which this feature is displayed (higher numbers = darker gray). + if len(fields) > 5: + assert fields[5] in ['+', '-'], 'Invalid strand' # strand - Defines the strand - either '+' or '-'. + if len(fields) > 6: + int(fields[6]) # thickStart - The starting position at which the feature is drawn thickly (for example, the start codon in gene displays). + if len(fields) > 7: + int(fields[7]) # thickEnd - The ending position at which the feature is drawn thickly (for example, the stop codon in gene displays). + if len(fields) > 8: if fields[8] != '0': # itemRgb - An RGB value of the form R,G,B (e.g. 255,0,0). If the track line itemRgb attribute is set to "On", this RBG value will determine the display color of the data contained in this BED line. NOTE: It is recommended that a simple color scheme (eight colors or less) be used with this attribute to avoid overwhelming the color resources of the Genome Browser and your Internet browser. - fields2 = fields[8].split( ',' ) - assert len( fields2 ) == 3, 'RGB value must be 0 or have length of 3' + fields2 = fields[8].split(',') + assert len(fields2) == 3, 'RGB value must be 0 or have length of 3' for field in fields2: - int( field ) # rgb values are integers - if len( fields ) > 9: - int( fields[9] ) # blockCount - The number of blocks (exons) in the BED line. - if len( fields ) > 10: + int(field) # rgb values are integers + if len(fields) > 9: + int(fields[9]) # blockCount - The number of blocks (exons) in the BED line. + if len(fields) > 10: if fields[10] != ',': # blockSizes - A comma-separated list of the block sizes. The number of items in this list should correspond to blockCount. - fields2 = fields[10].rstrip( "," ).split( "," ) # remove trailing comma and split on comma + fields2 = fields[10].rstrip(",").split(",") # remove trailing comma and split on comma for field in fields2: - int( field ) - if len( fields ) > 11: + int(field) + if len(fields) > 11: if fields[11] != ',': # blockStarts - A comma-separated list of block starts. All of the blockStart positions should be calculated relative to chromStart. The number of items in this list should correspond to blockCount. - fields2 = fields[11].rstrip( "," ).split( "," ) # remove trailing comma and split on comma + fields2 = fields[11].rstrip(",").split(",") # remove trailing comma and split on comma for field in fields2: - int( field ) + int(field) except: strict_bed = False break - if force_num_columns is not None and len( fields ) != force_num_columns: - line = '\t'.join( force_bed_field_count( fields, count, force_num_columns ) ) - out.write( "%s\n" % line ) + if force_num_columns is not None and len(fields) != force_num_columns: + line = '\t'.join(force_bed_field_count(fields, count, force_num_columns)) + out.write("%s\n" % line) else: strict_bed = False out.close() @@ -129,9 +129,9 @@ def __main__(): if not strict_bed: skipped_lines = 0 first_skipped_line = None - out = open( output_name, 'w' ) + out = open(output_name, 'w') count = 0 - for count, region in enumerate( bx.intervals.io.NiceReaderWrapper( open( input_name, 'r' ), chrom_col=chromCol, start_col=startCol, end_col=endCol, strand_col=strandCol, fix_strand=True, return_header=False, return_comments=False ) ): + for count, region in enumerate(bx.intervals.io.NiceReaderWrapper(open(input_name, 'r'), chrom_col=chromCol, start_col=startCol, end_col=endCol, strand_col=strandCol, fix_strand=True, return_header=False, return_comments=False)): try: if nameCol >= 0: name = region.fields[nameCol] @@ -141,17 +141,17 @@ def __main__(): name = "region_%i" % count try: fields = [str(item) for item in (region.chrom, region.start, region.end, name, 0, region.strand)] - if force_num_columns is not None and len( fields ) != force_num_columns: - fields = force_bed_field_count( fields, count, force_num_columns ) - out.write( "%s\n" % '\t'.join( fields ) ) + if force_num_columns is not None and len(fields) != force_num_columns: + fields = force_bed_field_count(fields, count, force_num_columns) + out.write("%s\n" % '\t'.join(fields)) except: skipped_lines += 1 if first_skipped_line is None: first_skipped_line = count + 1 out.close() - print("%i regions converted to BED." % ( count + 1 - skipped_lines )) + print("%i regions converted to BED." % (count + 1 - skipped_lines)) if skipped_lines > 0: - print("Skipped %d blank or invalid lines starting with line # %d." % ( skipped_lines, first_skipped_line )) + print("Skipped %d blank or invalid lines starting with line # %d." % (skipped_lines, first_skipped_line)) if __name__ == "__main__": diff --git a/lib/galaxy/datatypes/converters/interval_to_coverage.py b/lib/galaxy/datatypes/converters/interval_to_coverage.py index e58b3ec764ba..b0b8bb4695c1 100644 --- a/lib/galaxy/datatypes/converters/interval_to_coverage.py +++ b/lib/galaxy/datatypes/converters/interval_to_coverage.py @@ -25,7 +25,7 @@ 'reverseCol',) -def main( interval, coverage ): +def main(interval, coverage): """ Uses a sliding window of partitions to count coverages. Every interval record adds its start and end to the partitions. The result @@ -68,8 +68,8 @@ def main( interval, coverage ): forward_covs[index] += forward reverse_covs[index] += reverse partitions.insert(end_index, record.end) - forward_covs.insert(end_index, forward_covs[end_index - 1] - forward ) - reverse_covs.insert(end_index, reverse_covs[end_index - 1] - reverse ) + forward_covs.insert(end_index, forward_covs[end_index - 1] - forward) + reverse_covs.insert(end_index, reverse_covs[end_index - 1] - reverse) if partitions: for partition in range(0, start_index): @@ -94,8 +94,8 @@ def main( interval, coverage ): forward=forward, reverse=reverse) -class CoverageWriter( object ): - def __init__( self, out_stream=None, chromCol=0, positionCol=1, forwardCol=2, reverseCol=3 ): +class CoverageWriter(object): + def __init__(self, out_stream=None, chromCol=0, positionCol=1, forwardCol=2, reverseCol=3): self.out_stream = out_stream self.reverseCol = reverseCol self.nlines = 0 @@ -108,7 +108,7 @@ def __init__( self, out_stream=None, chromCol=0, positionCol=1, forwardCol=2, re else: self.template = "%(0)s\t%(1)s\t%(2)s\t%(3)s\n" % positions - def write(self, **kwargs ): + def write(self, **kwargs): if self.reverseCol < 0: kwargs['forward'] += kwargs['reverse'] posgen = kwargs['position'] @@ -122,7 +122,7 @@ def close(self): if __name__ == "__main__": - options, args = doc_optparse.parse( __doc__ ) + options, args = doc_optparse.parse(__doc__) try: chr_col_1, start_col_1, end_col_1, strand_col_1 = [int(x) - 1 for x in options.cols1.split(',')] chr_col_2, position_col_2, forward_col_2, reverse_col_2 = [int(x) - 1 for x in options.cols2.split(',')] @@ -136,16 +136,16 @@ def close(self): commandline = "sort -f -n -k %d -k %d -k %d -o %s %s" % (chr_col_1 + 1, start_col_1 + 1, end_col_1 + 1, temp_file.name, in_fname) subprocess.check_call(commandline, shell=True) - coverage = CoverageWriter( out_stream=open(out_fname, "a"), - chromCol=chr_col_2, positionCol=position_col_2, - forwardCol=forward_col_2, reverseCol=reverse_col_2, ) + coverage = CoverageWriter(out_stream=open(out_fname, "a"), + chromCol=chr_col_2, positionCol=position_col_2, + forwardCol=forward_col_2, reverseCol=reverse_col_2, ) temp_file.seek(0) - interval = io.NiceReaderWrapper( temp_file, - chrom_col=chr_col_1, - start_col=start_col_1, - end_col=end_col_1, - strand_col=strand_col_1, - fix_strand=True ) - main( interval, coverage ) + interval = io.NiceReaderWrapper(temp_file, + chrom_col=chr_col_1, + start_col=start_col_1, + end_col=end_col_1, + strand_col=strand_col_1, + fix_strand=True) + main(interval, coverage) temp_file.close() coverage.close() diff --git a/lib/galaxy/datatypes/converters/interval_to_fli.py b/lib/galaxy/datatypes/converters/interval_to_fli.py index 831efddc695f..1d5fd227846d 100644 --- a/lib/galaxy/datatypes/converters/interval_to_fli.py +++ b/lib/galaxy/datatypes/converters/interval_to_fli.py @@ -23,51 +23,51 @@ def main(): # Process arguments. parser = optparse.OptionParser() - parser.add_option( '-F', '--format', dest="input_format" ) + parser.add_option('-F', '--format', dest="input_format") (options, args) = parser.parse_args() in_fname, out_fname = args input_format = options.input_format.lower() # Create dict of name-location pairings. name_loc_dict = {} - if input_format in [ 'gff', 'gtf' ]: + if input_format in ['gff', 'gtf']: # GTF/GFF format # Create reader. if input_format == 'gff': - in_reader = GFFReaderWrapper( open( in_fname, 'r' ) ) + in_reader = GFFReaderWrapper(open(in_fname, 'r')) else: # input_format == 'gtf' - in_reader = read_unordered_gtf( open( in_fname, 'r' ) ) + in_reader = read_unordered_gtf(open(in_fname, 'r')) for feature in in_reader: - if isinstance( feature, Comment ): + if isinstance(feature, Comment): continue for name in feature.attributes: - val = feature.attributes[ name ] + val = feature.attributes[name] try: - float( val ) + float(val) continue except: - convert_gff_coords_to_bed( feature ) + convert_gff_coords_to_bed(feature) # Value is not a number, so it can be indexed. if val not in name_loc_dict: # Value is not in dictionary. - name_loc_dict[ val ] = { + name_loc_dict[val] = { 'contig': feature.chrom, 'start': feature.start, 'end': feature.end } else: # Value already in dictionary, so update dictionary. - loc = name_loc_dict[ val ] - if feature.start < loc[ 'start' ]: - loc[ 'start' ] = feature.start - if feature.end > loc[ 'end' ]: - loc[ 'end' ] = feature.end + loc = name_loc_dict[val] + if feature.start < loc['start']: + loc['start'] = feature.start + if feature.end > loc['end']: + loc['end'] = feature.end elif input_format == 'bed': # BED format. - for line in open( in_fname, 'r' ): + for line in open(in_fname, 'r'): # Ignore track lines. if line.startswith("track"): continue @@ -75,31 +75,31 @@ def main(): fields = line.split() # Ignore lines with no feature name. - if len( fields ) < 4: + if len(fields) < 4: continue # Process line - name_loc_dict[ fields[3] ] = { + name_loc_dict[fields[3]] = { 'contig': fields[0], - 'start': int( fields[1] ), - 'end': int( fields[2] ) + 'start': int(fields[1]), + 'end': int(fields[2]) } # Create sorted list of entries. - out = open( out_fname, 'w' ) + out = open(out_fname, 'w') max_len = 0 entries = [] - for name in sorted( name_loc_dict.keys() ): - loc = name_loc_dict[ name ] - entry = '%s\t%s\t%s' % ( name.lower(), name, '%s:%i-%i' % ( loc[ 'contig' ], loc[ 'start' ], loc[ 'end' ] ) ) - if len( entry ) > max_len: - max_len = len( entry ) - entries.append( entry ) + for name in sorted(name_loc_dict.keys()): + loc = name_loc_dict[name] + entry = '%s\t%s\t%s' % (name.lower(), name, '%s:%i-%i' % (loc['contig'], loc['start'], loc['end'])) + if len(entry) > max_len: + max_len = len(entry) + entries.append(entry) # Write padded entries. - out.write( str( max_len + 1 ).ljust( max_len ) + '\n' ) + out.write(str(max_len + 1).ljust(max_len) + '\n') for entry in entries: - out.write( entry.ljust( max_len ) + '\n' ) + out.write(entry.ljust(max_len) + '\n') out.close() diff --git a/lib/galaxy/datatypes/converters/interval_to_interval_index_converter.py b/lib/galaxy/datatypes/converters/interval_to_interval_index_converter.py index acdd493d0308..54dbd2d16ed3 100644 --- a/lib/galaxy/datatypes/converters/interval_to_interval_index_converter.py +++ b/lib/galaxy/datatypes/converters/interval_to_interval_index_converter.py @@ -19,9 +19,9 @@ def main(): # Read options, args. parser = optparse.OptionParser() - parser.add_option( '-c', '--chr-col', type='int', dest='chrom_col', default=1 ) - parser.add_option( '-s', '--start-col', type='int', dest='start_col', default=2 ) - parser.add_option( '-e', '--end-col', type='int', dest='end_col', default=3 ) + parser.add_option('-c', '--chr-col', type='int', dest='chrom_col', default=1) + parser.add_option('-s', '--start-col', type='int', dest='start_col', default=2) + parser.add_option('-e', '--end-col', type='int', dest='end_col', default=3) (options, args) = parser.parse_args() input_fname, output_fname = args @@ -38,13 +38,13 @@ def main(): if not feature or feature[0].startswith("track") or feature[0].startswith("#"): offset += len(line) continue - chrom = feature[ options.chrom_col ] - chrom_start = int( feature[ options.start_col ] ) - chrom_end = int( feature[ options.end_col ] ) - index.add( chrom, chrom_start, chrom_end, offset ) + chrom = feature[options.chrom_col] + chrom_start = int(feature[options.start_col]) + chrom_end = int(feature[options.end_col]) + index.add(chrom, chrom_start, chrom_end, offset) offset += len(line) - index.write( open(output_fname, "w") ) + index.write(open(output_fname, "w")) if __name__ == "__main__": diff --git a/lib/galaxy/datatypes/converters/interval_to_tabix_converter.py b/lib/galaxy/datatypes/converters/interval_to_tabix_converter.py index 487e3e9c7aa3..63ef4ff07b65 100644 --- a/lib/galaxy/datatypes/converters/interval_to_tabix_converter.py +++ b/lib/galaxy/datatypes/converters/interval_to_tabix_converter.py @@ -16,10 +16,10 @@ def main(): # Read options, args. parser = optparse.OptionParser() - parser.add_option( '-c', '--chr-col', type='int', dest='chrom_col' ) - parser.add_option( '-s', '--start-col', type='int', dest='start_col' ) - parser.add_option( '-e', '--end-col', type='int', dest='end_col' ) - parser.add_option( '-P', '--preset', dest='preset' ) + parser.add_option('-c', '--chr-col', type='int', dest='chrom_col') + parser.add_option('-s', '--start-col', type='int', dest='start_col') + parser.add_option('-e', '--end-col', type='int', dest='end_col') + parser.add_option('-P', '--preset', dest='preset') (options, args) = parser.parse_args() input_fname, index_fname, out_fname = args diff --git a/lib/galaxy/datatypes/converters/lped_to_fped_converter.py b/lib/galaxy/datatypes/converters/lped_to_fped_converter.py index 0f3175aa29fc..655219078c03 100644 --- a/lib/galaxy/datatypes/converters/lped_to_fped_converter.py +++ b/lib/galaxy/datatypes/converters/lped_to_fped_converter.py @@ -102,7 +102,7 @@ def main(): f.write(galhtmlprefix % prog) print('## Rgenetics: http://rgenetics.org Galaxy Tools %s %s' % (prog, timenow())) # becomes info f.write('
    ## Rgenetics: http://rgenetics.org Galaxy Tools %s %s\n
      ' % (prog, timenow())) - for i, data in enumerate( flist ): + for i, data in enumerate(flist): f.write('
    1. %s
    2. \n' % (os.path.split(data)[-1], os.path.split(data)[-1])) f.write("
    ") diff --git a/lib/galaxy/datatypes/converters/lped_to_pbed_converter.py b/lib/galaxy/datatypes/converters/lped_to_pbed_converter.py index 59ce6a2e8118..777259c3e0e7 100644 --- a/lib/galaxy/datatypes/converters/lped_to_pbed_converter.py +++ b/lib/galaxy/datatypes/converters/lped_to_pbed_converter.py @@ -104,7 +104,7 @@ def main(): s = '## Rgenetics: http://rgenetics.org Galaxy Tools %s %s' % (prog, timenow()) # becomes info print(s) f.write('
    %s\n
      ' % (s)) - for i, data in enumerate( flist ): + for i, data in enumerate(flist): f.write('
    1. %s
    2. \n' % (os.path.split(data)[-1], os.path.split(data)[-1])) f.write("
    ") diff --git a/lib/galaxy/datatypes/converters/maf_to_fasta_converter.py b/lib/galaxy/datatypes/converters/maf_to_fasta_converter.py index 4a6fa4774a3e..bd56734f3894 100644 --- a/lib/galaxy/datatypes/converters/maf_to_fasta_converter.py +++ b/lib/galaxy/datatypes/converters/maf_to_fasta_converter.py @@ -8,27 +8,27 @@ from galaxy.tools.util import maf_utilities -assert sys.version_info[:2] >= ( 2, 4 ) +assert sys.version_info[:2] >= (2, 4) def __main__(): output_name = sys.argv.pop(1) input_name = sys.argv.pop(1) - out = open( output_name, 'w' ) + out = open(output_name, 'w') count = 0 - for count, block in enumerate( bx.align.maf.Reader( open( input_name, 'r' ) ) ): + for count, block in enumerate(bx.align.maf.Reader(open(input_name, 'r'))): spec_counts = {} for c in block.components: - spec, chrom = maf_utilities.src_split( c.src ) + spec, chrom = maf_utilities.src_split(c.src) if spec not in spec_counts: - spec_counts[ spec ] = 0 + spec_counts[spec] = 0 else: - spec_counts[ spec ] += 1 - out.write( "%s\n" % maf_utilities.get_fasta_header( c, { 'block_index' : count, 'species' : spec, 'sequence_index' : spec_counts[ spec ] }, suffix="%s_%i_%i" % ( spec, count, spec_counts[ spec ] ) ) ) - out.write( "%s\n" % c.text ) - out.write( "\n" ) + spec_counts[spec] += 1 + out.write("%s\n" % maf_utilities.get_fasta_header(c, {'block_index' : count, 'species' : spec, 'sequence_index' : spec_counts[spec]}, suffix="%s_%i_%i" % (spec, count, spec_counts[spec]))) + out.write("%s\n" % c.text) + out.write("\n") out.close() - print("%i MAF blocks converted to FASTA." % ( count )) + print("%i MAF blocks converted to FASTA." % (count)) if __name__ == "__main__": diff --git a/lib/galaxy/datatypes/converters/maf_to_interval_converter.py b/lib/galaxy/datatypes/converters/maf_to_interval_converter.py index 3e68f2a252cf..7223653052b8 100644 --- a/lib/galaxy/datatypes/converters/maf_to_interval_converter.py +++ b/lib/galaxy/datatypes/converters/maf_to_interval_converter.py @@ -8,7 +8,7 @@ from galaxy.tools.util import maf_utilities -assert sys.version_info[:2] >= ( 2, 4 ) +assert sys.version_info[:2] >= (2, 4) def __main__(): @@ -18,17 +18,17 @@ def __main__(): out = open(output_name, 'w') count = 0 # write interval header line - out.write( "#chrom\tstart\tend\tstrand\n" ) + out.write("#chrom\tstart\tend\tstrand\n") try: - for block in bx.align.maf.Reader( open( input_name, 'r' ) ): - for c in maf_utilities.iter_components_by_src_start( block, species ): + for block in bx.align.maf.Reader(open(input_name, 'r')): + for c in maf_utilities.iter_components_by_src_start(block, species): if c is not None: - out.write( "%s\t%i\t%i\t%s\n" % ( maf_utilities.src_split( c.src )[-1], c.get_forward_strand_start(), c.get_forward_strand_end(), c.strand ) ) + out.write("%s\t%i\t%i\t%s\n" % (maf_utilities.src_split(c.src)[-1], c.get_forward_strand_start(), c.get_forward_strand_end(), c.strand)) count += 1 except Exception as e: print("There was a problem processing your input: %s" % e, file=sys.stderr) out.close() - print("%i MAF blocks converted to Genomic Intervals for species %s." % ( count, species )) + print("%i MAF blocks converted to Genomic Intervals for species %s." % (count, species)) if __name__ == "__main__": diff --git a/lib/galaxy/datatypes/converters/pbed_ldreduced_converter.py b/lib/galaxy/datatypes/converters/pbed_ldreduced_converter.py index f571befcd84f..86c4a4d051fc 100644 --- a/lib/galaxy/datatypes/converters/pbed_ldreduced_converter.py +++ b/lib/galaxy/datatypes/converters/pbed_ldreduced_converter.py @@ -55,7 +55,7 @@ def pruneLD(plinktasks=[], cd='./', vclbase=[]): def makeLDreduced(basename, infpath=None, outfpath=None, plinke='plink', forcerebuild=False, returnFname=False, - winsize="60", winmove="40", r2thresh="0.1" ): + winsize="60", winmove="40", r2thresh="0.1"): """ not there so make and leave in output dir for post job hook to copy back into input extra files path for next time """ outbase = os.path.join(outfpath, basename) @@ -105,7 +105,7 @@ def main(): s2 = 'Input %s, winsize=%s, winmove=%s, r2thresh=%s' % (base_name, winsize, winmove, r2thresh) print('%s %s' % (s1, s2)) f.write('
    %s\n%s\n
      ' % (s1, s2)) - for i, data in enumerate( flist ): + for i, data in enumerate(flist): f.write('
    1. %s
    2. \n' % (os.path.split(data)[-1], os.path.split(data)[-1])) f.write("
    ") diff --git a/lib/galaxy/datatypes/converters/pbed_to_lped_converter.py b/lib/galaxy/datatypes/converters/pbed_to_lped_converter.py index ad8cdc01d325..023c6ee5f30e 100644 --- a/lib/galaxy/datatypes/converters/pbed_to_lped_converter.py +++ b/lib/galaxy/datatypes/converters/pbed_to_lped_converter.py @@ -72,7 +72,7 @@ def main(): s = '## Rgenetics: http://bitbucket.org/rgalaxy Galaxy Tools %s %s' % (prog, timenow()) # becomes info print(s) f.write('
    %s\n
      ' % (s)) - for i, data in enumerate( flist ): + for i, data in enumerate(flist): f.write('
    1. %s
    2. \n' % (os.path.split(data)[-1], os.path.split(data)[-1])) f.write("
    ") diff --git a/lib/galaxy/datatypes/converters/picard_interval_list_to_bed6_converter.py b/lib/galaxy/datatypes/converters/picard_interval_list_to_bed6_converter.py index e9ebd89e8b14..f3783cceeb26 100644 --- a/lib/galaxy/datatypes/converters/picard_interval_list_to_bed6_converter.py +++ b/lib/galaxy/datatypes/converters/picard_interval_list_to_bed6_converter.py @@ -4,8 +4,8 @@ import sys -assert sys.version_info[:2] >= ( 2, 5 ) -HEADER_STARTS_WITH = ( '@' ) +assert sys.version_info[:2] >= (2, 5) +HEADER_STARTS_WITH = ('@') def __main__(): @@ -14,17 +14,17 @@ def __main__(): skipped_lines = 0 first_skipped_line = 0 header_lines = 0 - out = open( output_name, 'w' ) + out = open(output_name, 'w') i = 0 - for i, line in enumerate( open( input_name ) ): - line = line.rstrip( '\r\n' ) + for i, line in enumerate(open(input_name)): + line = line.rstrip('\r\n') if line: - if line.startswith( HEADER_STARTS_WITH ): + if line.startswith(HEADER_STARTS_WITH): header_lines += 1 else: try: - elems = line.split( '\t' ) - out.write( '%s\t%s\t%s\t%s\t0\t%s\n' % ( elems[0], int(elems[1]) - 1, elems[2], elems[4], elems[3] ) ) + elems = line.split('\t') + out.write('%s\t%s\t%s\t%s\t0\t%s\n' % (elems[0], int(elems[1]) - 1, elems[2], elems[4], elems[3])) except Exception as e: print(e) skipped_lines += 1 @@ -35,9 +35,9 @@ def __main__(): if not first_skipped_line: first_skipped_line = i + 1 out.close() - info_msg = "%i lines converted to BED. " % ( i + 1 - skipped_lines ) + info_msg = "%i lines converted to BED. " % (i + 1 - skipped_lines) if skipped_lines > 0: - info_msg += "Skipped %d blank/comment/invalid lines starting with line #%d." % ( skipped_lines, first_skipped_line ) + info_msg += "Skipped %d blank/comment/invalid lines starting with line #%d." % (skipped_lines, first_skipped_line) print(info_msg) diff --git a/lib/galaxy/datatypes/converters/pileup_to_interval_index_converter.py b/lib/galaxy/datatypes/converters/pileup_to_interval_index_converter.py index a8f6271a1ee1..02d781e1742f 100644 --- a/lib/galaxy/datatypes/converters/pileup_to_interval_index_converter.py +++ b/lib/galaxy/datatypes/converters/pileup_to_interval_index_converter.py @@ -22,14 +22,14 @@ def main(): # Do conversion. index = Indexes() offset = 0 - for line in open( input_fname, "r" ): - chrom, start = line.split()[ 0:2 ] + for line in open(input_fname, "r"): + chrom, start = line.split()[0:2] # Pileup format is 1-based. - start = int( start ) - 1 - index.add( chrom, start, start + 1, offset ) - offset += len( line ) + start = int(start) - 1 + index.add(chrom, start, start + 1, offset) + offset += len(line) - index.write( open(output_fname, "w") ) + index.write(open(output_fname, "w")) if __name__ == "__main__": diff --git a/lib/galaxy/datatypes/converters/sam_to_bam.py b/lib/galaxy/datatypes/converters/sam_to_bam.py index 804da49b90a5..dcc84b6c5181 100644 --- a/lib/galaxy/datatypes/converters/sam_to_bam.py +++ b/lib/galaxy/datatypes/converters/sam_to_bam.py @@ -16,9 +16,9 @@ CHUNK_SIZE = 2 ** 20 # 1mb -def cleanup_before_exit( tmp_dir ): - if tmp_dir and os.path.exists( tmp_dir ): - shutil.rmtree( tmp_dir ) +def cleanup_before_exit(tmp_dir): + if tmp_dir and os.path.exists(tmp_dir): + shutil.rmtree(tmp_dir) def cmd_exists(cmd): @@ -34,9 +34,9 @@ def _get_samtools_version(): if not cmd_exists('samtools'): raise Exception('This tool needs samtools, but it is not on PATH.') # Get the version of samtools via --version-only, if available - p = subprocess.Popen( ['samtools', '--version-only'], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) + p = subprocess.Popen(['samtools', '--version-only'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) output, error = p.communicate() # --version-only is available @@ -45,10 +45,10 @@ def _get_samtools_version(): version = output.split('+')[0] return version - output = subprocess.Popen( [ 'samtools' ], stderr=subprocess.PIPE, stdout=subprocess.PIPE ).communicate()[1] - lines = output.split( '\n' ) + output = subprocess.Popen(['samtools'], stderr=subprocess.PIPE, stdout=subprocess.PIPE).communicate()[1] + lines = output.split('\n') for line in lines: - if line.lower().startswith( 'version' ): + if line.lower().startswith('version'): # Assuming line looks something like: version: 0.1.12a (r862) version = line.split()[1] break @@ -60,56 +60,56 @@ def __main__(): parser = optparse.OptionParser() (options, args) = parser.parse_args() - assert len( args ) == 2, 'You must specify the input and output filenames' + assert len(args) == 2, 'You must specify the input and output filenames' input_filename, output_filename = args - tmp_dir = tempfile.mkdtemp( prefix='tmp-sam_to_bam_converter-' ) + tmp_dir = tempfile.mkdtemp(prefix='tmp-sam_to_bam_converter-') # convert to SAM - unsorted_bam_filename = os.path.join( tmp_dir, 'unsorted.bam' ) - unsorted_stderr_filename = os.path.join( tmp_dir, 'unsorted.stderr' ) - cmd = "samtools view -bS '%s' > '%s'" % ( input_filename, unsorted_bam_filename ) - proc = subprocess.Popen( args=cmd, stderr=open( unsorted_stderr_filename, 'wb' ), shell=True, cwd=tmp_dir ) + unsorted_bam_filename = os.path.join(tmp_dir, 'unsorted.bam') + unsorted_stderr_filename = os.path.join(tmp_dir, 'unsorted.stderr') + cmd = "samtools view -bS '%s' > '%s'" % (input_filename, unsorted_bam_filename) + proc = subprocess.Popen(args=cmd, stderr=open(unsorted_stderr_filename, 'wb'), shell=True, cwd=tmp_dir) return_code = proc.wait() if return_code: stderr_target = sys.stderr else: stderr_target = sys.stdout - stderr = open( unsorted_stderr_filename ) + stderr = open(unsorted_stderr_filename) while True: - chunk = stderr.read( CHUNK_SIZE ) + chunk = stderr.read(CHUNK_SIZE) if chunk: - stderr_target.write( chunk ) + stderr_target.write(chunk) else: break stderr.close() # sort sam, so indexing will not fail - sorted_stderr_filename = os.path.join( tmp_dir, 'sorted.stderr' ) - sorting_prefix = os.path.join( tmp_dir, 'sorted_bam' ) + sorted_stderr_filename = os.path.join(tmp_dir, 'sorted.stderr') + sorting_prefix = os.path.join(tmp_dir, 'sorted_bam') # samtools changed sort command arguments (starting from version 1.3) samtools_version = LooseVersion(_get_samtools_version()) if samtools_version < LooseVersion('1.0'): - cmd = "samtools sort -o '%s' '%s' > '%s'" % ( unsorted_bam_filename, sorting_prefix, output_filename ) + cmd = "samtools sort -o '%s' '%s' > '%s'" % (unsorted_bam_filename, sorting_prefix, output_filename) else: - cmd = "samtools sort -T '%s' '%s' > '%s'" % ( sorting_prefix, unsorted_bam_filename, output_filename ) - proc = subprocess.Popen( args=cmd, stderr=open( sorted_stderr_filename, 'wb' ), shell=True, cwd=tmp_dir ) + cmd = "samtools sort -T '%s' '%s' > '%s'" % (sorting_prefix, unsorted_bam_filename, output_filename) + proc = subprocess.Popen(args=cmd, stderr=open(sorted_stderr_filename, 'wb'), shell=True, cwd=tmp_dir) return_code = proc.wait() if return_code: stderr_target = sys.stderr else: stderr_target = sys.stdout - stderr = open( sorted_stderr_filename ) + stderr = open(sorted_stderr_filename) while True: - chunk = stderr.read( CHUNK_SIZE ) + chunk = stderr.read(CHUNK_SIZE) if chunk: - stderr_target.write( chunk ) + stderr_target.write(chunk) else: break stderr.close() - cleanup_before_exit( tmp_dir ) + cleanup_before_exit(tmp_dir) if __name__ == "__main__": diff --git a/lib/galaxy/datatypes/converters/tabular_to_dbnsfp.py b/lib/galaxy/datatypes/converters/tabular_to_dbnsfp.py index 4e1f909bc135..f8bef419a323 100644 --- a/lib/galaxy/datatypes/converters/tabular_to_dbnsfp.py +++ b/lib/galaxy/datatypes/converters/tabular_to_dbnsfp.py @@ -15,9 +15,9 @@ def main(): # Read options, args. usage = "Usage: %prog [options] tabular_input_file bgzip_output_file" parser = optparse.OptionParser(usage=usage) - parser.add_option( '-c', '--chr-col', type='int', default=0, dest='chrom_col' ) - parser.add_option( '-s', '--start-col', type='int', default=1, dest='start_col' ) - parser.add_option( '-e', '--end-col', type='int', default=1, dest='end_col' ) + parser.add_option('-c', '--chr-col', type='int', default=0, dest='chrom_col') + parser.add_option('-s', '--start-col', type='int', default=1, dest='start_col') + parser.add_option('-e', '--end-col', type='int', default=1, dest='end_col') (options, args) = parser.parse_args() if len(args) != 2: parser.print_usage() diff --git a/lib/galaxy/datatypes/converters/vcf_to_interval_index_converter.py b/lib/galaxy/datatypes/converters/vcf_to_interval_index_converter.py index 922c24f7ede7..a635db88aab8 100644 --- a/lib/galaxy/datatypes/converters/vcf_to_interval_index_converter.py +++ b/lib/galaxy/datatypes/converters/vcf_to_interval_index_converter.py @@ -18,15 +18,15 @@ def main(): # Do conversion. index = Indexes() - reader = galaxy_utils.sequence.vcf.Reader( open( in_file ) ) + reader = galaxy_utils.sequence.vcf.Reader(open(in_file)) offset = reader.metadata_len for vcf_line in reader: # VCF format provides a chrom and 1-based position for each variant. # IntervalIndex expects 0-based coordinates. - index.add( vcf_line.chrom, vcf_line.pos - 1, vcf_line.pos, offset ) - offset += len( vcf_line.raw_line ) + index.add(vcf_line.chrom, vcf_line.pos - 1, vcf_line.pos, offset) + offset += len(vcf_line.raw_line) - index.write( open( out_file, "w" ) ) + index.write(open(out_file, "w")) if __name__ == "__main__": diff --git a/lib/galaxy/datatypes/converters/wiggle_to_array_tree_converter.py b/lib/galaxy/datatypes/converters/wiggle_to_array_tree_converter.py index ca9056258c0a..6dc81144ea68 100644 --- a/lib/galaxy/datatypes/converters/wiggle_to_array_tree_converter.py +++ b/lib/galaxy/datatypes/converters/wiggle_to_array_tree_converter.py @@ -15,15 +15,15 @@ def main(): input_fname = sys.argv[1] out_fname = sys.argv[2] - reader = WiggleReader( open( input_fname ) ) + reader = WiggleReader(open(input_fname)) # Fill array from reader - d = array_tree_dict_from_reader( reader, {}, block_size=BLOCK_SIZE ) + d = array_tree_dict_from_reader(reader, {}, block_size=BLOCK_SIZE) for array_tree in d.values(): array_tree.root.build_summary() - FileArrayTreeDict.dict_to_file( d, open( out_fname, "w" ) ) + FileArrayTreeDict.dict_to_file(d, open(out_fname, "w")) if __name__ == "__main__": diff --git a/lib/galaxy/datatypes/converters/wiggle_to_simple_converter.py b/lib/galaxy/datatypes/converters/wiggle_to_simple_converter.py index 90c887cea125..4eab65b6b11a 100644 --- a/lib/galaxy/datatypes/converters/wiggle_to_simple_converter.py +++ b/lib/galaxy/datatypes/converters/wiggle_to_simple_converter.py @@ -17,32 +17,32 @@ ) -def stop_err( msg ): - sys.stderr.write( msg ) +def stop_err(msg): + sys.stderr.write(msg) sys.exit() def main(): - if len( sys.argv ) > 1: - in_file = open( sys.argv[1] ) + if len(sys.argv) > 1: + in_file = open(sys.argv[1]) else: - in_file = open( sys.stdin ) + in_file = open(sys.stdin) - if len( sys.argv ) > 2: - out_file = open( sys.argv[2], "w" ) + if len(sys.argv) > 2: + out_file = open(sys.argv[2], "w") else: out_file = sys.stdout try: - for fields in bx.wiggle.IntervalReader( UCSCOutWrapper( in_file ) ): - out_file.write( "%s\n" % "\t".join( map( str, fields ) ) ) + for fields in bx.wiggle.IntervalReader(UCSCOutWrapper(in_file)): + out_file.write("%s\n" % "\t".join(map(str, fields))) except UCSCLimitException: # Wiggle data was truncated, at the very least need to warn the user. print('Encountered message from UCSC: "Reached output limit of 100000 data values", so be aware your data was truncated.') except ValueError as e: in_file.close() out_file.close() - stop_err( str( e ) ) + stop_err(str(e)) in_file.close() out_file.close() diff --git a/lib/galaxy/datatypes/coverage.py b/lib/galaxy/datatypes/coverage.py index af70f8d8968d..50f7895bbcbd 100644 --- a/lib/galaxy/datatypes/coverage.py +++ b/lib/galaxy/datatypes/coverage.py @@ -13,20 +13,20 @@ log = logging.getLogger(__name__) -class LastzCoverage( Tabular ): +class LastzCoverage(Tabular): file_ext = "coverage" - MetadataElement( name="chromCol", default=1, desc="Chrom column", param=metadata.ColumnParameter ) - MetadataElement( name="positionCol", default=2, desc="Position column", param=metadata.ColumnParameter ) - MetadataElement( name="forwardCol", default=3, desc="Forward or aggregate read column", param=metadata.ColumnParameter ) - MetadataElement( name="reverseCol", desc="Optional reverse read column", param=metadata.ColumnParameter, optional=True, no_value=0 ) - MetadataElement( name="columns", default=3, desc="Number of columns", readonly=True, visible=False ) + MetadataElement(name="chromCol", default=1, desc="Chrom column", param=metadata.ColumnParameter) + MetadataElement(name="positionCol", default=2, desc="Position column", param=metadata.ColumnParameter) + MetadataElement(name="forwardCol", default=3, desc="Forward or aggregate read column", param=metadata.ColumnParameter) + MetadataElement(name="reverseCol", desc="Optional reverse read column", param=metadata.ColumnParameter, optional=True, no_value=0) + MetadataElement(name="columns", default=3, desc="Number of columns", readonly=True, visible=False) - def get_track_resolution( self, dataset, start, end): + def get_track_resolution(self, dataset, start, end): range = end - start # Determine appropriate resolution to plot ~1000 points - resolution = math.ceil( 10 ** math.ceil( math.log10( range / 1000 ) ) ) + resolution = math.ceil(10 ** math.ceil(math.log10(range / 1000))) # Restrict to valid range - resolution = min( resolution, 10000 ) - resolution = max( resolution, 1 ) + resolution = min(resolution, 10000) + resolution = max(resolution, 1) return resolution diff --git a/lib/galaxy/datatypes/data.py b/lib/galaxy/datatypes/data.py index dd0340128b6a..14b5ec0f0224 100644 --- a/lib/galaxy/datatypes/data.py +++ b/lib/galaxy/datatypes/data.py @@ -43,21 +43,21 @@ DOWNLOAD_FILENAME_PATTERN_COLLECTION_ELEMENT = "Galaxy${hdca_hid}-[${hdca_name}__${element_identifier}].${ext}" -class DataMeta( abc.ABCMeta ): +class DataMeta(abc.ABCMeta): """ Metaclass for Data class. Sets up metadata spec. """ - def __init__( cls, name, bases, dict_ ): + def __init__(cls, name, bases, dict_): cls.metadata_spec = metadata.MetadataSpecCollection() for base in bases: # loop through bases (class/types) of cls - if hasattr( base, "metadata_spec" ): # base of class Data (object) has no metadata - cls.metadata_spec.update( base.metadata_spec ) # add contents of metadata spec of base class to cls - metadata.Statement.process( cls ) + if hasattr(base, "metadata_spec"): # base of class Data (object) has no metadata + cls.metadata_spec.update(base.metadata_spec) # add contents of metadata spec of base class to cls + metadata.Statement.process(cls) @six.add_metaclass(DataMeta) @dataproviders.decorators.has_dataproviders -class Data( object ): +class Data(object): """ Base class for all datatypes. Implements basic interfaces as well as class methods for metadata. @@ -82,7 +82,7 @@ class Data( object ): metadata_spec = None # Add metadata elements - MetadataElement( name="dbkey", desc="Database/Build", default="?", param=metadata.DBKeyParameter, multiple=False, no_value="?" ) + MetadataElement(name="dbkey", desc="Database/Build", default="?", param=metadata.DBKeyParameter, multiple=False, no_value="?") # Stores the set of display applications, and viewing methods, supported by this datatype supported_display_apps = {} # If False, the peek is regenerated whenever a dataset of this type is copied @@ -129,7 +129,7 @@ def set_raw_data(self, dataset, data): os.write(fd, data) os.close(fd) - def get_raw_data( self, dataset ): + def get_raw_data(self, dataset): """Returns the full data. To stream it open the file_name and read/write as needed""" try: return open(dataset.file_name, 'rb').read(-1) @@ -137,15 +137,15 @@ def get_raw_data( self, dataset ): log.exception('%s reading a file that does not exist %s', self.__class__.__name__, dataset.file_name) return '' - def dataset_content_needs_grooming( self, file_name ): + def dataset_content_needs_grooming(self, file_name): """This function is called on an output dataset file after the content is initially generated.""" return False - def groom_dataset_content( self, file_name ): + def groom_dataset_content(self, file_name): """This function is called on an output dataset file if dataset_content_needs_grooming returns True.""" pass - def init_meta( self, dataset, copy_from=None ): + def init_meta(self, dataset, copy_from=None): # Metadata should be left mostly uninitialized. Dataset will # handle returning default values when metadata is not set. # copy_from allows metadata to be passed in that will be @@ -155,43 +155,43 @@ def init_meta( self, dataset, copy_from=None ): if copy_from: dataset.metadata = copy_from.metadata - def set_meta( self, dataset, overwrite=True, **kwd ): + def set_meta(self, dataset, overwrite=True, **kwd): """Unimplemented method, allows guessing of metadata from contents of file""" return True - def missing_meta( self, dataset, check=[], skip=[] ): + def missing_meta(self, dataset, check=[], skip=[]): """ Checks for empty metadata values, Returns True if non-optional metadata is missing Specifying a list of 'check' values will only check those names provided; when used, optionality is ignored Specifying a list of 'skip' items will return True even when a named metadata value is missing """ if check: - to_check = ( ( to_check, dataset.metadata.get( to_check ) ) for to_check in check ) + to_check = ((to_check, dataset.metadata.get(to_check)) for to_check in check) else: to_check = dataset.metadata.items() for key, value in to_check: - if key in skip or ( not check and dataset.metadata.spec[key].get( "optional" ) ): + if key in skip or (not check and dataset.metadata.spec[key].get("optional")): continue # we skip check for optional and nonrequested values here if not value: return True return False - def set_max_optional_metadata_filesize( self, max_value ): + def set_max_optional_metadata_filesize(self, max_value): try: - max_value = int( max_value ) + max_value = int(max_value) except: return self.__class__._max_optional_metadata_filesize = max_value - def get_max_optional_metadata_filesize( self ): + def get_max_optional_metadata_filesize(self): rval = self.__class__._max_optional_metadata_filesize if rval is None: return -1 return rval - max_optional_metadata_filesize = property( get_max_optional_metadata_filesize, set_max_optional_metadata_filesize ) + max_optional_metadata_filesize = property(get_max_optional_metadata_filesize, set_max_optional_metadata_filesize) - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): """Set the peek and blurb text""" if not dataset.dataset.purged: dataset.peek = '' @@ -200,7 +200,7 @@ def set_peek( self, dataset, is_multi_byte=False ): dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disk' - def display_peek(self, dataset ): + def display_peek(self, dataset): """Create HTML table, used for displaying peek""" out = [''] try: @@ -212,11 +212,11 @@ def display_peek(self, dataset ): line = line.strip() if not line: continue - out.append( '' % escape( unicodify( line, 'utf-8' ) ) ) - out.append( '
    %s
    ' ) - out = "".join( out ) + out.append('%s' % escape(unicodify(line, 'utf-8'))) + out.append('') + out = "".join(out) except Exception as exc: - out = "Can't create peek %s" % str( exc ) + out = "Can't create peek %s" % str(exc) return out def _archive_main_file(self, archive, display_name, data_filename): @@ -239,14 +239,14 @@ def _archive_main_file(self, archive, display_name, data_filename): messagetype = "error" return error, msg, messagetype - def _archive_composite_dataset( self, trans, data=None, **kwd ): + def _archive_composite_dataset(self, trans, data=None, **kwd): # save a composite object into a compressed archive for downloading - params = util.Params( kwd ) + params = util.Params(kwd) outfname = data.name[0:150] outfname = ''.join(c in FILENAME_VALID_CHARS and c or '_' for c in outfname) if params.do_action is None: params.do_action = 'zip' # default - msg = util.restore_text( params.get( 'msg', '' ) ) + msg = util.restore_text(params.get('msg', '')) if not data: msg = "You must select at least one dataset" else: @@ -255,17 +255,17 @@ def _archive_composite_dataset( self, trans, data=None, **kwd ): if params.do_action == 'zip': # Can't use mkstemp - the file must not exist first tmpd = tempfile.mkdtemp() - util.umask_fix_perms( tmpd, trans.app.config.umask, 0o777, trans.app.config.gid ) - tmpf = os.path.join( tmpd, 'library_download.' + params.do_action ) - archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED, True ) - archive.add = lambda x, y: archive.write( x, y.encode('CP437') ) + util.umask_fix_perms(tmpd, trans.app.config.umask, 0o777, trans.app.config.gid) + tmpf = os.path.join(tmpd, 'library_download.' + params.do_action) + archive = zipfile.ZipFile(tmpf, 'w', zipfile.ZIP_DEFLATED, True) + archive.add = lambda x, y: archive.write(x, y.encode('CP437')) elif params.do_action == 'tgz': - archive = util.streamball.StreamBall( 'w|gz' ) + archive = util.streamball.StreamBall('w|gz') elif params.do_action == 'tbz': - archive = util.streamball.StreamBall( 'w|bz2' ) + archive = util.streamball.StreamBall('w|bz2') except (OSError, zipfile.BadZipFile): error = True - log.exception( "Unable to create archive for download" ) + log.exception("Unable to create archive for download") msg = "Unable to create archive for %s for download, please report this error" % outfname if not error: ext = data.extension @@ -291,21 +291,21 @@ def _archive_composite_dataset( self, trans, data=None, **kwd ): if not error: if params.do_action == 'zip': archive.close() - tmpfh = open( tmpf ) + tmpfh = open(tmpf) # CANNOT clean up - unlink/rmdir was always failing because file handle retained to return - must rely on a cron job to clean up tmp - trans.response.set_content_type( "application/x-zip-compressed" ) - trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s.zip"' % outfname + trans.response.set_content_type("application/x-zip-compressed") + trans.response.headers["Content-Disposition"] = 'attachment; filename="%s.zip"' % outfname return tmpfh else: - trans.response.set_content_type( "application/x-tar" ) + trans.response.set_content_type("application/x-tar") outext = 'tgz' if params.do_action == 'tbz': outext = 'tbz' - trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s.%s"' % (outfname, outext) + trans.response.headers["Content-Disposition"] = 'attachment; filename="%s.%s"' % (outfname, outext) archive.wsgi_status = trans.response.wsgi_status() archive.wsgi_headeritems = trans.response.wsgi_headeritems() return archive.stream - return trans.show_error_message( msg ) + return trans.show_error_message(msg) def __archive_extra_files_path(self, extra_files_path): """Yield filepaths and relative filepaths for files in extra_files_path""" @@ -316,11 +316,11 @@ def __archive_extra_files_path(self, extra_files_path): yield fpath, rpath def _serve_raw(self, trans, dataset, to_ext, **kwd): - trans.response.headers['Content-Length'] = int( os.stat( dataset.file_name ).st_size ) - trans.response.set_content_type( "application/octet-stream" ) # force octet-stream so Safari doesn't append mime extensions to filename + trans.response.headers['Content-Length'] = int(os.stat(dataset.file_name).st_size) + trans.response.set_content_type("application/octet-stream") # force octet-stream so Safari doesn't append mime extensions to filename filename = self._download_filename(dataset, to_ext, hdca=kwd.get("hdca", None), element_identifier=kwd.get("element_identifier", None)) trans.response.headers["Content-Disposition"] = 'attachment; filename="%s"' % filename - return open( dataset.file_name ) + return open(dataset.file_name) def to_archive(self, trans, dataset, name=""): """ @@ -330,7 +330,7 @@ def to_archive(self, trans, dataset, name=""): :param name: archive name, in collection context corresponds to collection name(s) and element_identifier, joined by '/', e.g 'fastq_collection/sample1/forward' """ - composite_extensions = trans.app.datatypes_registry.get_composite_extensions( ) + composite_extensions = trans.app.datatypes_registry.get_composite_extensions() composite_extensions.append('html') # for archiving composite datatypes rel_paths = [] file_paths = [] @@ -356,18 +356,18 @@ def display_data(self, trans, data, preview=False, filename=None, to_ext=None, * providers?). """ # Relocate all composite datatype display to a common location. - composite_extensions = trans.app.datatypes_registry.get_composite_extensions( ) + composite_extensions = trans.app.datatypes_registry.get_composite_extensions() composite_extensions.append('html') # for archiving composite datatypes # Prevent IE8 from sniffing content type since we're explicit about it. This prevents intentionally text/plain # content from being rendered in the browser trans.response.headers['X-Content-Type-Options'] = 'nosniff' - if isinstance( data, six.string_types ): + if isinstance(data, six.string_types): return data if filename and filename != "index": # For files in extra_files_path file_path = trans.app.object_store.get_filename(data.dataset, extra_dir='dataset_%s_files' % data.dataset.id, alt_name=filename) - if os.path.exists( file_path ): - if os.path.isdir( file_path ): + if os.path.exists(file_path): + if os.path.isdir(file_path): tmp_fh = tempfile.NamedTemporaryFile(delete=False) tmp_file_name = tmp_fh.name dir_items = sorted(os.listdir(file_path)) @@ -390,36 +390,36 @@ def display_data(self, trans, data, preview=False, filename=None, to_ext=None, * tmp_fh.write('\n') tmp_fh.close() return open(tmp_file_name) - mime = mimetypes.guess_type( file_path )[0] + mime = mimetypes.guess_type(file_path)[0] if not mime: try: - mime = trans.app.datatypes_registry.get_mimetype_by_extension( ".".split( file_path )[-1] ) + mime = trans.app.datatypes_registry.get_mimetype_by_extension(".".split(file_path)[-1]) except: mime = "text/plain" - self._clean_and_set_mime_type( trans, mime ) - return open( file_path ) + self._clean_and_set_mime_type(trans, mime) + return open(file_path) else: - return paste.httpexceptions.HTTPNotFound( "Could not find '%s' on the extra files path %s." % ( filename, file_path ) ) - self._clean_and_set_mime_type( trans, data.get_mime() ) + return paste.httpexceptions.HTTPNotFound("Could not find '%s' on the extra files path %s." % (filename, file_path)) + self._clean_and_set_mime_type(trans, data.get_mime()) - trans.log_event( "Display dataset id: %s" % str( data.id ) ) + trans.log_event("Display dataset id: %s" % str(data.id)) from galaxy import datatypes # DBTODO REMOVE THIS AT REFACTOR if to_ext or isinstance(data.datatype, datatypes.binary.Binary): # Saving the file, or binary file if data.extension in composite_extensions: - return self._archive_composite_dataset( trans, data, **kwd ) + return self._archive_composite_dataset(trans, data, **kwd) else: - trans.response.headers['Content-Length'] = int( os.stat( data.file_name ).st_size ) + trans.response.headers['Content-Length'] = int(os.stat(data.file_name).st_size) filename = self._download_filename(data, to_ext, hdca=kwd.get("hdca", None), element_identifier=kwd.get("element_identifier", None)) - trans.response.set_content_type( "application/octet-stream" ) # force octet-stream so Safari doesn't append mime extensions to filename + trans.response.set_content_type("application/octet-stream") # force octet-stream so Safari doesn't append mime extensions to filename trans.response.headers["Content-Disposition"] = 'attachment; filename="%s"' % filename - return open( data.file_name ) - if not os.path.exists( data.file_name ): - raise paste.httpexceptions.HTTPNotFound( "File Not Found (%s)." % data.file_name ) + return open(data.file_name) + if not os.path.exists(data.file_name): + raise paste.httpexceptions.HTTPNotFound("File Not Found (%s)." % data.file_name) max_peek_size = 1000000 # 1 MB if isinstance(data.datatype, datatypes.text.Html): max_peek_size = 10000000 # 10 MB for html - preview = util.string_as_bool( preview ) - if not preview or isinstance(data.datatype, datatypes.images.Image) or os.stat( data.file_name ).st_size < max_peek_size: + preview = util.string_as_bool(preview) + if not preview or isinstance(data.datatype, datatypes.images.Image) or os.stat(data.file_name).st_size < max_peek_size: if trans.app.config.sanitize_all_html and trans.response.get_content_type() == "text/html": # Sanitize anytime we respond with plain text/html content. # Check to see if this dataset's parent job is whitelisted @@ -429,13 +429,13 @@ def display_data(self, trans, data, preview=False, filename=None, to_ext=None, * # This is returning to the browser, it needs to be encoded. # TODO Ideally this happens a layer higher, but this is a bad # issue affecting many tools - return sanitize_html(open( data.file_name ).read()).encode('utf-8') - return open( data.file_name ) + return sanitize_html(open(data.file_name).read()).encode('utf-8') + return open(data.file_name) else: - trans.response.set_content_type( "text/html" ) - return trans.stream_template_mako( "/dataset/large_file.mako", - truncated_data=open( data.file_name ).read(max_peek_size), - data=data) + trans.response.set_content_type("text/html") + return trans.stream_template_mako("/dataset/large_file.mako", + truncated_data=open(data.file_name).read(max_peek_size), + data=data) def _download_filename(self, dataset, to_ext, hdca=None, element_identifier=None): def escape(raw_identifier): @@ -465,7 +465,7 @@ def escape(raw_identifier): def display_name(self, dataset): """Returns formatted html of dataset name""" try: - return escape( unicodify( dataset.name, 'utf-8' ) ) + return escape(unicodify(dataset.name, 'utf-8')) except Exception: return "name unavailable" @@ -473,15 +473,15 @@ def display_info(self, dataset): """Returns formatted html of dataset info""" try: # Change new line chars to html - info = escape( dataset.info ) - if info.find( '\r\n' ) >= 0: - info = info.replace( '\r\n', '
    ' ) - if info.find( '\r' ) >= 0: - info = info.replace( '\r', '
    ' ) - if info.find( '\n' ) >= 0: - info = info.replace( '\n', '
    ' ) + info = escape(dataset.info) + if info.find('\r\n') >= 0: + info = info.replace('\r\n', '
    ') + if info.find('\r') >= 0: + info = info.replace('\r', '
    ') + if info.find('\n') >= 0: + info = info.replace('\n', '
    ') - info = unicodify( info, 'utf-8' ) + info = unicodify(info, 'utf-8') return info except: @@ -499,7 +499,7 @@ def get_mime(self): """Returns the mime type of the datatype""" return 'application/octet-stream' - def add_display_app( self, app_id, label, file_function, links_function ): + def add_display_app(self, app_id, label, file_function, links_function): """ Adds a display app to the datatype. app_id is a unique id @@ -516,23 +516,23 @@ def remove_display_app(self, app_id): try: del self.supported_display_apps[app_id] except: - log.exception('Tried to remove display app %s from datatype %s, but this display app is not declared.', type, self.__class__.__name__ ) + log.exception('Tried to remove display app %s from datatype %s, but this display app is not declared.', type, self.__class__.__name__) - def clear_display_apps( self ): + def clear_display_apps(self): self.supported_display_apps = {} - def add_display_application( self, display_application ): + def add_display_application(self, display_application): """New style display applications""" assert display_application.id not in self.display_applications, 'Attempted to add a display application twice' - self.display_applications[ display_application.id ] = display_application + self.display_applications[display_application.id] = display_application - def get_display_application( self, key, default=None ): - return self.display_applications.get( key, default ) + def get_display_application(self, key, default=None): + return self.display_applications.get(key, default) - def get_display_applications_by_dataset( self, dataset, trans ): + def get_display_applications_by_dataset(self, dataset, trans): rval = odict() for key, value in self.display_applications.items(): - value = value.filter_by_dataset( dataset, trans ) + value = value.filter_by_dataset(dataset, trans) if value.links: rval[key] = value return rval @@ -554,10 +554,10 @@ def as_display_type(self, dataset, type, **kwd): if type in self.get_display_types(): return getattr(self, self.supported_display_apps[type]['file_function'])(dataset, **kwd) except: - log.exception('Function %s is referred to in datatype %s for displaying as type %s, but is not accessible', self.supported_display_apps[type]['file_function'], self.__class__.__name__, type ) - return "This display type (%s) is not implemented for this datatype (%s)." % ( type, dataset.ext) + log.exception('Function %s is referred to in datatype %s for displaying as type %s, but is not accessible', self.supported_display_apps[type]['file_function'], self.__class__.__name__, type) + return "This display type (%s) is not implemented for this datatype (%s)." % (type, dataset.ext) - def get_display_links( self, dataset, type, app, base_url, target_frame='_blank', **kwd ): + def get_display_links(self, dataset, type, app, base_url, target_frame='_blank', **kwd): """ Returns a list of tuples of (name, link) for a particular display type. No check on 'access' permissions is done here - if you can view the dataset, you can also save it @@ -566,26 +566,26 @@ def get_display_links( self, dataset, type, app, base_url, target_frame='_blank' """ try: if app.config.enable_old_display_applications and type in self.get_display_types(): - return target_frame, getattr( self, self.supported_display_apps[type]['links_function'] )( dataset, type, app, base_url, **kwd ) + return target_frame, getattr(self, self.supported_display_apps[type]['links_function'])(dataset, type, app, base_url, **kwd) except: - log.exception( 'Function %s is referred to in datatype %s for generating links for type %s, but is not accessible', - self.supported_display_apps[type]['links_function'], self.__class__.__name__, type ) + log.exception('Function %s is referred to in datatype %s for generating links for type %s, but is not accessible', + self.supported_display_apps[type]['links_function'], self.__class__.__name__, type) return target_frame, [] def get_converter_types(self, original_dataset, datatypes_registry): """Returns available converters by type for this dataset""" return datatypes_registry.get_converters_by_datatype(original_dataset.ext) - def find_conversion_destination( self, dataset, accepted_formats, datatypes_registry, **kwd ): + def find_conversion_destination(self, dataset, accepted_formats, datatypes_registry, **kwd): """Returns ( target_ext, existing converted dataset )""" - return datatypes_registry.find_conversion_destination_for_dataset_by_extensions( dataset, accepted_formats, **kwd ) + return datatypes_registry.find_conversion_destination_for_dataset_by_extensions(dataset, accepted_formats, **kwd) def convert_dataset(self, trans, original_dataset, target_type, return_output=False, visible=True, deps=None, target_context=None, history=None): """This function adds a job to the queue to convert a dataset to another type. Returns a message about success/failure.""" - converter = trans.app.datatypes_registry.get_converter_by_target_type( original_dataset.ext, target_type ) + converter = trans.app.datatypes_registry.get_converter_by_target_type(original_dataset.ext, target_type) if converter is None: - raise Exception( "A converter does not exist for %s to %s." % ( original_dataset.ext, target_type ) ) + raise Exception("A converter does not exist for %s to %s." % (original_dataset.ext, target_type)) # Generate parameter dictionary params = {} # determine input parameter name and add to params @@ -598,14 +598,14 @@ def convert_dataset(self, trans, original_dataset, target_type, return_output=Fa # add potentially required/common internal tool parameters e.g. '__job_resource' if target_context: for key, value in target_context.items(): - if key.startswith( '__' ): - params[ key ] = value + if key.startswith('__'): + params[key] = value params[input_name] = original_dataset # Run converter, job is dispatched through Queue - converted_dataset = converter.execute( trans, incoming=params, set_output_hid=visible, history=history )[1] + converted_dataset = converter.execute(trans, incoming=params, set_output_hid=visible, history=history)[1] if len(params) > 0: - trans.log_event( "Converter params: %s" % (str(params)), tool_id=converter.id ) + trans.log_event("Converter params: %s" % (str(params)), tool_id=converter.id) if not visible: for value in converted_dataset.values(): value.visible = False @@ -616,77 +616,77 @@ def convert_dataset(self, trans, original_dataset, target_type, return_output=Fa # We need to clear associated files before we set metadata # so that as soon as metadata starts to be set, e.g. implicitly converted datasets are deleted and no longer available 'while' metadata is being set, not just after # We'll also clear after setting metadata, for backwards compatibility - def after_setting_metadata( self, dataset ): + def after_setting_metadata(self, dataset): """This function is called on the dataset after metadata is set.""" - dataset.clear_associated_files( metadata_safe=True ) + dataset.clear_associated_files(metadata_safe=True) - def before_setting_metadata( self, dataset ): + def before_setting_metadata(self, dataset): """This function is called on the dataset before metadata is set.""" - dataset.clear_associated_files( metadata_safe=True ) - - def __new_composite_file( self, name, optional=False, mimetype=None, description=None, substitute_name_with_metadata=None, is_binary=False, to_posix_lines=True, space_to_tab=False, **kwds ): - kwds[ 'name' ] = name - kwds[ 'optional' ] = optional - kwds[ 'mimetype' ] = mimetype - kwds[ 'description' ] = description - kwds[ 'substitute_name_with_metadata' ] = substitute_name_with_metadata - kwds[ 'is_binary' ] = is_binary - kwds[ 'to_posix_lines' ] = to_posix_lines - kwds[ 'space_to_tab' ] = space_to_tab - return Bunch( **kwds ) - - def add_composite_file( self, name, **kwds ): + dataset.clear_associated_files(metadata_safe=True) + + def __new_composite_file(self, name, optional=False, mimetype=None, description=None, substitute_name_with_metadata=None, is_binary=False, to_posix_lines=True, space_to_tab=False, **kwds): + kwds['name'] = name + kwds['optional'] = optional + kwds['mimetype'] = mimetype + kwds['description'] = description + kwds['substitute_name_with_metadata'] = substitute_name_with_metadata + kwds['is_binary'] = is_binary + kwds['to_posix_lines'] = to_posix_lines + kwds['space_to_tab'] = space_to_tab + return Bunch(**kwds) + + def add_composite_file(self, name, **kwds): # self.composite_files = self.composite_files.copy() - self.composite_files[ name ] = self.__new_composite_file( name, **kwds ) + self.composite_files[name] = self.__new_composite_file(name, **kwds) - def __substitute_composite_key( self, key, composite_file, dataset=None ): + def __substitute_composite_key(self, key, composite_file, dataset=None): if composite_file.substitute_name_with_metadata: if dataset: - meta_value = str( dataset.metadata.get( composite_file.substitute_name_with_metadata ) ) + meta_value = str(dataset.metadata.get(composite_file.substitute_name_with_metadata)) else: meta_value = self.spec[composite_file.substitute_name_with_metadata].default return key % meta_value return key @property - def writable_files( self, dataset=None ): + def writable_files(self, dataset=None): files = odict() if self.composite_type != 'auto_primary_file': - files[ self.primary_file_name ] = self.__new_composite_file( self.primary_file_name ) - for key, value in self.get_composite_files( dataset=dataset ).items(): - files[ key ] = value + files[self.primary_file_name] = self.__new_composite_file(self.primary_file_name) + for key, value in self.get_composite_files(dataset=dataset).items(): + files[key] = value return files - def get_composite_files( self, dataset=None ): - def substitute_composite_key( key, composite_file ): + def get_composite_files(self, dataset=None): + def substitute_composite_key(key, composite_file): if composite_file.substitute_name_with_metadata: if dataset: - meta_value = str( dataset.metadata.get( composite_file.substitute_name_with_metadata ) ) + meta_value = str(dataset.metadata.get(composite_file.substitute_name_with_metadata)) else: - meta_value = self.metadata_spec[ composite_file.substitute_name_with_metadata ].default + meta_value = self.metadata_spec[composite_file.substitute_name_with_metadata].default return key % meta_value return key files = odict() for key, value in self.composite_files.items(): - files[ substitute_composite_key( key, value ) ] = value + files[substitute_composite_key(key, value)] = value return files - def generate_primary_file( self, dataset=None ): - raise Exception( "generate_primary_file is not implemented for this datatype." ) + def generate_primary_file(self, dataset=None): + raise Exception("generate_primary_file is not implemented for this datatype.") @property def has_resolution(self): return False - def matches_any( self, target_datatypes ): + def matches_any(self, target_datatypes): """ Check if this datatype is of any of the target_datatypes or is a subtype thereof. """ - datatype_classes = tuple( datatype if isclass( datatype ) else datatype.__class__ for datatype in target_datatypes ) - return isinstance( self, datatype_classes ) + datatype_classes = tuple(datatype if isclass(datatype) else datatype.__class__ for datatype in target_datatypes) + return isinstance(self, datatype_classes) - def merge( split_files, output_file): + def merge(split_files, output_file): """ Merge files with copy.copyfileobj() will not hit the max argument limitation of cat. gz and bz2 files are also working. @@ -703,61 +703,61 @@ def merge( split_files, output_file): merge = staticmethod(merge) - def get_visualizations( self, dataset ): + def get_visualizations(self, dataset): """ Returns a list of visualizations for datatype. """ if self.track_type: - return [ 'trackster', 'circster' ] + return ['trackster', 'circster'] return [] # ------------- Dataproviders - def has_dataprovider( self, data_format ): + def has_dataprovider(self, data_format): """ Returns True if `data_format` is available in `dataproviders`. """ return data_format in self.dataproviders - def dataprovider( self, dataset, data_format, **settings ): + def dataprovider(self, dataset, data_format, **settings): """ Base dataprovider factory for all datatypes that returns the proper provider for the given `data_format` or raises a `NoProviderAvailable`. """ - if self.has_dataprovider( data_format ): - return self.dataproviders[ data_format ]( self, dataset, **settings ) - raise dataproviders.exceptions.NoProviderAvailable( self, data_format ) + if self.has_dataprovider(data_format): + return self.dataproviders[data_format](self, dataset, **settings) + raise dataproviders.exceptions.NoProviderAvailable(self, data_format) - @dataproviders.decorators.dataprovider_factory( 'base' ) - def base_dataprovider( self, dataset, **settings ): - dataset_source = dataproviders.dataset.DatasetDataProvider( dataset ) - return dataproviders.base.DataProvider( dataset_source, **settings ) + @dataproviders.decorators.dataprovider_factory('base') + def base_dataprovider(self, dataset, **settings): + dataset_source = dataproviders.dataset.DatasetDataProvider(dataset) + return dataproviders.base.DataProvider(dataset_source, **settings) - @dataproviders.decorators.dataprovider_factory( 'chunk', dataproviders.chunk.ChunkDataProvider.settings ) - def chunk_dataprovider( self, dataset, **settings ): - dataset_source = dataproviders.dataset.DatasetDataProvider( dataset ) - return dataproviders.chunk.ChunkDataProvider( dataset_source, **settings ) + @dataproviders.decorators.dataprovider_factory('chunk', dataproviders.chunk.ChunkDataProvider.settings) + def chunk_dataprovider(self, dataset, **settings): + dataset_source = dataproviders.dataset.DatasetDataProvider(dataset) + return dataproviders.chunk.ChunkDataProvider(dataset_source, **settings) - @dataproviders.decorators.dataprovider_factory( 'chunk64', dataproviders.chunk.Base64ChunkDataProvider.settings ) - def chunk64_dataprovider( self, dataset, **settings ): - dataset_source = dataproviders.dataset.DatasetDataProvider( dataset ) - return dataproviders.chunk.Base64ChunkDataProvider( dataset_source, **settings ) + @dataproviders.decorators.dataprovider_factory('chunk64', dataproviders.chunk.Base64ChunkDataProvider.settings) + def chunk64_dataprovider(self, dataset, **settings): + dataset_source = dataproviders.dataset.DatasetDataProvider(dataset) + return dataproviders.chunk.Base64ChunkDataProvider(dataset_source, **settings) def _clean_and_set_mime_type(self, trans, mime): if mime.lower() in XSS_VULNERABLE_MIME_TYPES: - if not getattr( trans.app.config, "serve_xss_vulnerable_mimetypes", True ): + if not getattr(trans.app.config, "serve_xss_vulnerable_mimetypes", True): mime = DEFAULT_MIME_TYPE - trans.response.set_content_type( mime ) + trans.response.set_content_type(mime) @dataproviders.decorators.has_dataproviders -class Text( Data ): +class Text(Data): edam_format = "format_2330" file_ext = 'txt' line_class = 'line' # Add metadata elements - MetadataElement( name="data_lines", default=0, desc="Number of data lines", readonly=True, optional=True, visible=False, no_value=0 ) + MetadataElement(name="data_lines", default=0, desc="Number of data lines", readonly=True, optional=True, visible=False, no_value=0) def write_from_stream(self, dataset, stream): """Writes data from a stream""" @@ -787,24 +787,24 @@ def set_raw_data(self, dataset, data): line = line.strip() + '\n' fp.write(line) fp.close() - os.remove( temp_name ) + os.remove(temp_name) def get_mime(self): """Returns the mime type of the datatype""" return 'text/plain' - def set_meta( self, dataset, **kwd ): + def set_meta(self, dataset, **kwd): """ Set the number of lines of data in dataset. """ dataset.metadata.data_lines = self.count_data_lines(dataset) - def estimate_file_lines( self, dataset ): + def estimate_file_lines(self, dataset): """ Perform a rough estimate by extrapolating number of lines from a small read. """ sample_size = 1048576 - dataset_fh = open( dataset.file_name ) + dataset_fh = open(dataset.file_name) dataset_read = dataset_fh.read(sample_size) dataset_fh.close() sample_lines = dataset_read.count('\n') @@ -817,23 +817,23 @@ def count_data_lines(self, dataset): skipping all blank lines and comments. """ data_lines = 0 - for line in open( dataset.file_name ): + for line in open(dataset.file_name): line = line.strip() - if line and not line.startswith( '#' ): + if line and not line.startswith('#'): data_lines += 1 return data_lines - def set_peek( self, dataset, line_count=None, is_multi_byte=False, WIDTH=256, skipchars=None, line_wrap=True ): + def set_peek(self, dataset, line_count=None, is_multi_byte=False, WIDTH=256, skipchars=None, line_wrap=True): """ Set the peek. This method is used by various subclasses of Text. """ if not dataset.dataset.purged: # The file must exist on disk for the get_file_peek() method - dataset.peek = get_file_peek( dataset.file_name, is_multi_byte=is_multi_byte, WIDTH=WIDTH, skipchars=skipchars, line_wrap=line_wrap ) + dataset.peek = get_file_peek(dataset.file_name, is_multi_byte=is_multi_byte, WIDTH=WIDTH, skipchars=skipchars, line_wrap=line_wrap) if line_count is None: # See if line_count is stored in the metadata if dataset.metadata.data_lines: - dataset.blurb = "%s %s" % ( util.commaify( str(dataset.metadata.data_lines) ), inflector.cond_plural(dataset.metadata.data_lines, self.line_class) ) + dataset.blurb = "%s %s" % (util.commaify(str(dataset.metadata.data_lines)), inflector.cond_plural(dataset.metadata.data_lines, self.line_class)) else: # Number of lines is not known ( this should not happen ), and auto-detect is # needed to set metadata @@ -842,17 +842,17 @@ def set_peek( self, dataset, line_count=None, is_multi_byte=False, WIDTH=256, sk # Small dataset, recount all lines and reset peek afterward. lc = self.count_data_lines(dataset) dataset.metadata.data_lines = lc - dataset.blurb = "%s %s" % ( util.commaify( str(lc) ), inflector.cond_plural(lc, self.line_class) ) + dataset.blurb = "%s %s" % (util.commaify(str(lc)), inflector.cond_plural(lc, self.line_class)) else: est_lines = self.estimate_file_lines(dataset) - dataset.blurb = "~%s %s" % ( util.commaify(util.roundify(str(est_lines))), inflector.cond_plural(est_lines, self.line_class) ) + dataset.blurb = "~%s %s" % (util.commaify(util.roundify(str(est_lines))), inflector.cond_plural(est_lines, self.line_class)) else: - dataset.blurb = "%s %s" % ( util.commaify( str(line_count) ), inflector.cond_plural(line_count, self.line_class) ) + dataset.blurb = "%s %s" % (util.commaify(str(line_count)), inflector.cond_plural(line_count, self.line_class)) else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disk' - def split( cls, input_datasets, subdir_generator_function, split_params): + def split(cls, input_datasets, subdir_generator_function, split_params): """ Split the input files by line. """ @@ -929,33 +929,33 @@ def _file_len(fname): split = classmethod(split) # ------------- Dataproviders - @dataproviders.decorators.dataprovider_factory( 'line', dataproviders.line.FilteredLineDataProvider.settings ) - def line_dataprovider( self, dataset, **settings ): + @dataproviders.decorators.dataprovider_factory('line', dataproviders.line.FilteredLineDataProvider.settings) + def line_dataprovider(self, dataset, **settings): """ Returns an iterator over the dataset's lines (that have been stripped) optionally excluding blank lines and lines that start with a comment character. """ - dataset_source = dataproviders.dataset.DatasetDataProvider( dataset ) - return dataproviders.line.FilteredLineDataProvider( dataset_source, **settings ) + dataset_source = dataproviders.dataset.DatasetDataProvider(dataset) + return dataproviders.line.FilteredLineDataProvider(dataset_source, **settings) - @dataproviders.decorators.dataprovider_factory( 'regex-line', dataproviders.line.RegexLineDataProvider.settings ) - def regex_line_dataprovider( self, dataset, **settings ): + @dataproviders.decorators.dataprovider_factory('regex-line', dataproviders.line.RegexLineDataProvider.settings) + def regex_line_dataprovider(self, dataset, **settings): """ Returns an iterator over the dataset's lines optionally including/excluding lines that match one or more regex filters. """ - dataset_source = dataproviders.dataset.DatasetDataProvider( dataset ) - return dataproviders.line.RegexLineDataProvider( dataset_source, **settings ) + dataset_source = dataproviders.dataset.DatasetDataProvider(dataset) + return dataproviders.line.RegexLineDataProvider(dataset_source, **settings) -class GenericAsn1( Text ): +class GenericAsn1(Text): """Class for generic ASN.1 text format""" edam_data = "data_0849" edam_format = "format_1966" file_ext = 'asn1' -class LineCount( Text ): +class LineCount(Text): """ Dataset contains a single line with a single integer that denotes the line count for a related dataset. Used for custom builds. @@ -963,7 +963,7 @@ class LineCount( Text ): pass -class Newick( Text ): +class Newick(Text): """New Hampshire/Newick Format""" edam_data = "data_0872" edam_format = "format_1910" @@ -971,24 +971,24 @@ class Newick( Text ): def __init__(self, **kwd): """Initialize foobar datatype""" - Text.__init__( self, **kwd ) + Text.__init__(self, **kwd) - def init_meta( self, dataset, copy_from=None ): - Text.init_meta( self, dataset, copy_from=copy_from ) + def init_meta(self, dataset, copy_from=None): + Text.init_meta(self, dataset, copy_from=copy_from) - def sniff( self, filename ): + def sniff(self, filename): """ Returning false as the newick format is too general and cannot be sniffed.""" return False - def get_visualizations( self, dataset ): + def get_visualizations(self, dataset): """ Returns a list of visualizations for datatype. """ - return [ 'phyloviz' ] + return ['phyloviz'] -class Nexus( Text ): +class Nexus(Text): """Nexus format as used By Paup, Mr Bayes, etc""" edam_data = "data_0872" edam_format = "format_1912" @@ -996,14 +996,14 @@ class Nexus( Text ): def __init__(self, **kwd): """Initialize foobar datatype""" - Text.__init__( self, **kwd ) + Text.__init__(self, **kwd) - def init_meta( self, dataset, copy_from=None ): - Text.init_meta( self, dataset, copy_from=copy_from ) + def init_meta(self, dataset, copy_from=None): + Text.init_meta(self, dataset, copy_from=copy_from) - def sniff( self, filename ): + def sniff(self, filename): """All Nexus Files Simply puts a '#NEXUS' in its first line""" - f = open( filename, "r" ) + f = open(filename, "r") firstline = f.readline().upper() f.close() @@ -1012,12 +1012,12 @@ def sniff( self, filename ): else: return False - def get_visualizations( self, dataset ): + def get_visualizations(self, dataset): """ Returns a list of visualizations for datatype. """ - return [ 'phyloviz' ] + return ['phyloviz'] # ------------- Utility methods -------------- @@ -1028,14 +1028,14 @@ def get_visualizations( self, dataset ): nice_size = util.nice_size -def get_test_fname( fname ): +def get_test_fname(fname): """Returns test data filename""" path = os.path.dirname(__file__) - full_path = os.path.join( path, 'test', fname ) + full_path = os.path.join(path, 'test', fname) return full_path -def get_file_peek( file_name, is_multi_byte=False, WIDTH=256, LINE_COUNT=5, skipchars=None, line_wrap=True ): +def get_file_peek(file_name, is_multi_byte=False, WIDTH=256, LINE_COUNT=5, skipchars=None, line_wrap=True): """ Returns the first LINE_COUNT lines wrapped to WIDTH @@ -1054,13 +1054,13 @@ def get_file_peek( file_name, is_multi_byte=False, WIDTH=256, LINE_COUNT=5, skip count = 0 file_type = None data_checked = False - with compression_utils.get_fileobj( file_name, "U" ) as temp: + with compression_utils.get_fileobj(file_name, "U") as temp: while count < LINE_COUNT: - line = temp.readline( WIDTH ) + line = temp.readline(WIDTH) if line and not is_multi_byte and not data_checked: # See if we have a compressed or binary file for char in line: - if ord( char ) > 128: + if ord(char) > 128: file_type = 'binary' break data_checked = True @@ -1076,17 +1076,17 @@ def get_file_peek( file_name, is_multi_byte=False, WIDTH=256, LINE_COUNT=5, skip break skip_line = False for skipchar in skipchars: - if line.startswith( skipchar ): + if line.startswith(skipchar): skip_line = True break if not skip_line: - lines.append( line ) + lines.append(line) count += 1 if file_type == 'binary': text = "%s file" % file_type else: try: - text = util.unicodify( '\n'.join( lines ) ) + text = util.unicodify('\n'.join(lines)) except UnicodeDecodeError: text = "binary/unknown file" return text diff --git a/lib/galaxy/datatypes/dataproviders/base.py b/lib/galaxy/datatypes/dataproviders/base.py index 7e8bce05cb17..6fb3fb5850b8 100644 --- a/lib/galaxy/datatypes/dataproviders/base.py +++ b/lib/galaxy/datatypes/dataproviders/base.py @@ -14,7 +14,7 @@ from . import exceptions -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) _TODO = """ hooks into datatypes (define providers inside datatype modules) as factories @@ -39,7 +39,7 @@ def stop( self ): self.endpoint = source.tell(); raise StopIteration() # ----------------------------------------------------------------------------- base classes -class HasSettings( type ): +class HasSettings(type): """ Metaclass for data providers that allows defining and inheriting a dictionary named 'settings'. @@ -48,24 +48,24 @@ class HasSettings( type ): passed to class `__init__` functions so they can be parsed from a query string. """ # yeah - this is all too acrobatic - def __new__( cls, name, base_classes, attributes ): + def __new__(cls, name, base_classes, attributes): settings = {} # get settings defined in base classes for base_class in base_classes: - base_settings = getattr( base_class, 'settings', None ) + base_settings = getattr(base_class, 'settings', None) if base_settings: - settings.update( base_settings ) + settings.update(base_settings) # get settings defined in this class - new_settings = attributes.pop( 'settings', None ) + new_settings = attributes.pop('settings', None) if new_settings: - settings.update( new_settings ) - attributes[ 'settings' ] = settings - return type.__new__( cls, name, base_classes, attributes ) + settings.update(new_settings) + attributes['settings'] = settings + return type.__new__(cls, name, base_classes, attributes) # ----------------------------------------------------------------------------- base classes @six.add_metaclass(HasSettings) -class DataProvider( six.Iterator ): +class DataProvider(six.Iterator): """ Base class for all data providers. Data providers: (a) have a source (which must be another file-like object) @@ -78,15 +78,15 @@ class DataProvider( six.Iterator ): # empty in this base class settings = {} - def __init__( self, source, **kwargs ): + def __init__(self, source, **kwargs): """ :param source: the source that this iterator will loop over. (Should implement the iterable interface and ideally have the context manager interface as well) """ - self.source = self.validate_source( source ) + self.source = self.validate_source(source) - def validate_source( self, source ): + def validate_source(self, source): """ Is this a valid source for this provider? @@ -94,76 +94,76 @@ def validate_source( self, source ): Meant to be overridden in subclasses. """ - if not source or not hasattr( source, '__iter__' ): + if not source or not hasattr(source, '__iter__'): # that's by no means a thorough check - raise exceptions.InvalidDataProviderSource( source ) + raise exceptions.InvalidDataProviderSource(source) return source # TODO: (this might cause problems later...) # TODO: some providers (such as chunk's seek and read) rely on this... remove - def __getattr__( self, name ): + def __getattr__(self, name): if name == 'source': # if we're inside this fn, source hasn't been set - provide some safety just for this attr return None # otherwise, try to get the attr from the source - allows us to get things like provider.encoding, etc. - if hasattr( self.source, name ): - return getattr( self.source, name ) + if hasattr(self.source, name): + return getattr(self.source, name) # raise the proper error - return self.__getattribute__( name ) + return self.__getattribute__(name) # write methods should not be allowed - def truncate( self, size ): - raise NotImplementedError( 'Write methods are purposely disabled' ) + def truncate(self, size): + raise NotImplementedError('Write methods are purposely disabled') - def write( self, string ): - raise NotImplementedError( 'Write methods are purposely disabled' ) + def write(self, string): + raise NotImplementedError('Write methods are purposely disabled') - def writelines( self, sequence ): - raise NotImplementedError( 'Write methods are purposely disabled' ) + def writelines(self, sequence): + raise NotImplementedError('Write methods are purposely disabled') # TODO: route read methods through next? # def readline( self ): # return self.next() - def readlines( self ): - return [ line for line in self ] + def readlines(self): + return [line for line in self] # iterator interface - def __iter__( self ): + def __iter__(self): # it's generators all the way up, Timmy with self: for datum in self.source: yield datum - def __next__( self ): + def __next__(self): return next(self.source) # context manager interface - def __enter__( self ): + def __enter__(self): # make the source's context manager interface optional - if hasattr( self.source, '__enter__' ): + if hasattr(self.source, '__enter__'): self.source.__enter__() return self - def __exit__( self, *args ): + def __exit__(self, *args): # make the source's context manager interface optional, call on source if there - if hasattr( self.source, '__exit__' ): - self.source.__exit__( *args ) + if hasattr(self.source, '__exit__'): + self.source.__exit__(*args) # alternately, call close() - elif hasattr( self.source, 'close' ): + elif hasattr(self.source, 'close'): self.source.close() - def __str__( self ): + def __str__(self): """ String representation for easier debugging. Will call `__str__` on its source so this will display piped dataproviders. """ # we need to protect against recursion (in __getattr__) if self.source hasn't been set - source_str = str( self.source ) if hasattr( self, 'source' ) else '' - return '%s(%s)' % ( self.__class__.__name__, str( source_str ) ) + source_str = str(self.source) if hasattr(self, 'source') else '' + return '%s(%s)' % (self.__class__.__name__, str(source_str)) -class FilteredDataProvider( DataProvider ): +class FilteredDataProvider(DataProvider): """ Passes each datum through a filter function and yields it if that function returns a non-`None` value. @@ -176,13 +176,13 @@ class FilteredDataProvider( DataProvider ): # not useful here - we don't want functions over the query string # settings.update({ 'filter_fn': 'function' }) - def __init__( self, source, filter_fn=None, **kwargs ): + def __init__(self, source, filter_fn=None, **kwargs): """ :param filter_fn: a lambda or function that will be passed a datum and return either the (optionally modified) datum or None. """ - super( FilteredDataProvider, self ).__init__( source, **kwargs ) - self.filter_fn = filter_fn if hasattr( filter_fn, '__call__' ) else None + super(FilteredDataProvider, self).__init__(source, **kwargs) + self.filter_fn = filter_fn if hasattr(filter_fn, '__call__') else None # count how many data we got from the source self.num_data_read = 0 # how many valid data have we gotten from the source @@ -191,18 +191,18 @@ def __init__( self, source, filter_fn=None, **kwargs ): # how many lines have been provided/output self.num_data_returned = 0 - def __iter__( self ): - parent_gen = super( FilteredDataProvider, self ).__iter__() + def __iter__(self): + parent_gen = super(FilteredDataProvider, self).__iter__() for datum in parent_gen: self.num_data_read += 1 - datum = self.filter( datum ) + datum = self.filter(datum) if datum is not None: self.num_valid_data_read += 1 self.num_data_returned += 1 yield datum # TODO: may want to squash this into DataProvider - def filter( self, datum ): + def filter(self, datum): """ When given a datum from the provider's source, return None if the datum 'does not pass' the filter or is invalid. Return the datum if it's valid. @@ -213,12 +213,12 @@ def filter( self, datum ): Meant to be overridden. """ if self.filter_fn: - return self.filter_fn( datum ) + return self.filter_fn(datum) # also can be overriden entirely return datum -class LimitedOffsetDataProvider( FilteredDataProvider ): +class LimitedOffsetDataProvider(FilteredDataProvider): """ A provider that uses the counters from FilteredDataProvider to limit the number of data and/or skip `offset` number of data before providing. @@ -232,23 +232,23 @@ class LimitedOffsetDataProvider( FilteredDataProvider ): } # TODO: may want to squash this into DataProvider - def __init__( self, source, offset=0, limit=None, **kwargs ): + def __init__(self, source, offset=0, limit=None, **kwargs): """ :param offset: the number of data to skip before providing. :param limit: the final number of data to provide. """ - super( LimitedOffsetDataProvider, self ).__init__( source, **kwargs ) + super(LimitedOffsetDataProvider, self).__init__(source, **kwargs) # how many valid data to skip before we start outputing data - must be positive # (diff to support neg. indeces - must be pos.) - self.offset = max( offset, 0 ) + self.offset = max(offset, 0) # how many valid data to return - must be positive (None indicates no limit) self.limit = limit if self.limit is not None: - self.limit = max( self.limit, 0 ) + self.limit = max(self.limit, 0) - def __iter__( self ): + def __iter__(self): """ Iterate over the source until `num_valid_data_read` is greater than `offset`, begin providing datat, and stop when `num_data_returned` @@ -258,7 +258,7 @@ def __iter__( self ): return yield - parent_gen = super( LimitedOffsetDataProvider, self ).__iter__() + parent_gen = super(LimitedOffsetDataProvider, self).__iter__() for datum in parent_gen: self.num_data_returned -= 1 # print 'self.num_data_returned:', self.num_data_returned @@ -284,20 +284,21 @@ def __iter__( self ): # self.curr_line_num = new_curr_line_num -class MultiSourceDataProvider( DataProvider ): +class MultiSourceDataProvider(DataProvider): """ A provider that iterates over a list of given sources and provides data from one after another. An iterator over iterators. """ - def __init__( self, source_list, **kwargs ): + + def __init__(self, source_list, **kwargs): """ :param source_list: an iterator of iterables """ - self.source_list = deque( source_list ) + self.source_list = deque(source_list) - def __iter__( self ): + def __iter__(self): """ Iterate over the source_list, then iterate over the data in each source. @@ -308,10 +309,10 @@ def __iter__( self ): if not source: continue try: - self.source = self.validate_source( source ) + self.source = self.validate_source(source) except exceptions.InvalidDataProviderSource: continue - parent_gen = super( MultiSourceDataProvider, self ).__iter__() + parent_gen = super(MultiSourceDataProvider, self).__iter__() for datum in parent_gen: yield datum diff --git a/lib/galaxy/datatypes/dataproviders/chunk.py b/lib/galaxy/datatypes/dataproviders/chunk.py index dc7a1b62739f..ac3c529e730c 100644 --- a/lib/galaxy/datatypes/dataproviders/chunk.py +++ b/lib/galaxy/datatypes/dataproviders/chunk.py @@ -13,10 +13,10 @@ exceptions ) -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class ChunkDataProvider( base.DataProvider ): +class ChunkDataProvider(base.DataProvider): """ Data provider that yields chunks of data from its file. @@ -31,7 +31,7 @@ class ChunkDataProvider( base.DataProvider ): # TODO: subclass from LimitedOffsetDataProvider? # see web/framework/base.iterate_file, util/__init__.file_reader, and datatypes.tabular - def __init__( self, source, chunk_index=0, chunk_size=DEFAULT_CHUNK_SIZE, **kwargs ): + def __init__(self, source, chunk_index=0, chunk_size=DEFAULT_CHUNK_SIZE, **kwargs): """ :param chunk_index: if a source can be divided into N number of `chunk_size` sections, this is the index of which section to @@ -39,29 +39,29 @@ def __init__( self, source, chunk_index=0, chunk_size=DEFAULT_CHUNK_SIZE, **kwar :param chunk_size: how large are the desired chunks to return (gen. in bytes). """ - super( ChunkDataProvider, self ).__init__( source, **kwargs ) - self.chunk_size = int( chunk_size ) - self.chunk_pos = int( chunk_index ) * self.chunk_size + super(ChunkDataProvider, self).__init__(source, **kwargs) + self.chunk_size = int(chunk_size) + self.chunk_pos = int(chunk_index) * self.chunk_size - def validate_source( self, source ): + def validate_source(self, source): """ Does the given source have both the methods `seek` and `read`? :raises InvalidDataProviderSource: if not. """ - source = super( ChunkDataProvider, self ).validate_source( source ) - if( ( not hasattr( source, 'seek' ) ) or ( not hasattr( source, 'read' ) ) ): - raise exceptions.InvalidDataProviderSource( source ) + source = super(ChunkDataProvider, self).validate_source(source) + if((not hasattr(source, 'seek')) or (not hasattr(source, 'read'))): + raise exceptions.InvalidDataProviderSource(source) return source - def __iter__( self ): + def __iter__(self): # not reeeally an iterator per se self.__enter__() - self.source.seek( self.chunk_pos, os.SEEK_SET ) - chunk = self.encode( self.source.read( self.chunk_size ) ) + self.source.seek(self.chunk_pos, os.SEEK_SET) + chunk = self.encode(self.source.read(self.chunk_size)) yield chunk self.__exit__() - def encode( self, chunk ): + def encode(self, chunk): """ Called on the chunk before returning. @@ -70,12 +70,13 @@ def encode( self, chunk ): return chunk -class Base64ChunkDataProvider( ChunkDataProvider ): +class Base64ChunkDataProvider(ChunkDataProvider): """ Data provider that yields chunks of base64 encoded data from its file. """ - def encode( self, chunk ): + + def encode(self, chunk): """ Return chunks encoded in base 64. """ - return base64.b64encode( chunk ) + return base64.b64encode(chunk) diff --git a/lib/galaxy/datatypes/dataproviders/column.py b/lib/galaxy/datatypes/dataproviders/column.py index 70d3a794662c..a8248fab5629 100644 --- a/lib/galaxy/datatypes/dataproviders/column.py +++ b/lib/galaxy/datatypes/dataproviders/column.py @@ -16,11 +16,11 @@ - see existing visualizations/dataprovider/basic.ColumnDataProvider """ -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) # ----------------------------------------------------------------------------- base classes -class ColumnarDataProvider( line.RegexLineDataProvider ): +class ColumnarDataProvider(line.RegexLineDataProvider): """ Data provider that provide a list of columns from the lines of its source. @@ -41,9 +41,9 @@ class ColumnarDataProvider( line.RegexLineDataProvider ): 'filters' : 'list:str' } - def __init__( self, source, indeces=None, - column_count=None, column_types=None, parsers=None, parse_columns=True, - deliminator='\t', filters=None, **kwargs ): + def __init__(self, source, indeces=None, + column_count=None, column_types=None, parsers=None, parse_columns=True, + deliminator='\t', filters=None, **kwargs): """ :param indeces: a list of indeces of columns to gather from each row Optional: will default to `None`. @@ -82,7 +82,7 @@ def __init__( self, source, indeces=None, params (limit, offset, etc.) are also applicable here. """ # TODO: other columnar formats: csv, etc. - super( ColumnarDataProvider, self ).__init__( source, **kwargs ) + super(ColumnarDataProvider, self).__init__(source, **kwargs) # IMPLICIT: if no indeces, column_count, or column_types passed: return all columns self.selected_column_indeces = indeces @@ -91,12 +91,12 @@ def __init__( self, source, indeces=None, # if no column count given, try to infer from indeces or column_types if not self.column_count: if self.selected_column_indeces: - self.column_count = len( self.selected_column_indeces ) + self.column_count = len(self.selected_column_indeces) elif self.column_types: - self.column_count = len( self.column_types ) + self.column_count = len(self.column_types) # if no indeces given, infer from column_count if not self.selected_column_indeces and self.column_count: - self.selected_column_indeces = list( range( self.column_count ) ) + self.selected_column_indeces = list(range(self.column_count)) self.deliminator = deliminator @@ -105,35 +105,35 @@ def __init__( self, source, indeces=None, if parse_columns: self.parsers = self.get_default_parsers() # overwrite with user desired parsers - self.parsers.update( parsers or {} ) + self.parsers.update(parsers or {}) filters = filters or [] self.column_filters = [] for filter_ in filters: - parsed = self.parse_filter( filter_ ) + parsed = self.parse_filter(filter_) # TODO: might be better to error on bad filter/None here - if callable( parsed ): - self.column_filters.append( parsed ) + if callable(parsed): + self.column_filters.append(parsed) - def parse_filter( self, filter_param_str ): - split = filter_param_str.split( '-', 2 ) - if not len( split ) >= 3: + def parse_filter(self, filter_param_str): + split = filter_param_str.split('-', 2) + if not len(split) >= 3: return None column, op, val = split # better checking v. len and indeces - column = int( column ) - if column > len( self.column_types ): + column = int(column) + if column > len(self.column_types): return None - if self.column_types[ column ] in ( 'float', 'int' ): - return self.create_numeric_filter( column, op, val ) - if self.column_types[ column ] in ( 'str' ): - return self.create_string_filter( column, op, val ) - if self.column_types[ column ] in ( 'list' ): - return self.create_list_filter( column, op, val ) + if self.column_types[column] in ('float', 'int'): + return self.create_numeric_filter(column, op, val) + if self.column_types[column] in ('str'): + return self.create_string_filter(column, op, val) + if self.column_types[column] in ('list'): + return self.create_list_filter(column, op, val) return None - def create_numeric_filter( self, column, op, val ): + def create_numeric_filter(self, column, op, val): """ Return an anonymous filter function that will be passed the array of parsed columns. Return None if no filter function can be @@ -152,7 +152,7 @@ def create_numeric_filter( self, column, op, val ): `val` is cast as float here and will return None if there's a parsing error. """ try: - val = float( val ) + val = float(val) except ValueError: return None if 'lt' == op: @@ -169,7 +169,7 @@ def create_numeric_filter( self, column, op, val ): return lambda d: d[column] > val return None - def create_string_filter( self, column, op, val ): + def create_string_filter(self, column, op, val): """ Return an anonymous filter function that will be passed the array of parsed columns. Return None if no filter function can be @@ -187,12 +187,12 @@ def create_string_filter( self, column, op, val ): elif 'has' == op: return lambda d: val in d[column] elif 're' == op: - val = unquote_plus( val ) - val = re.compile( val ) - return lambda d: val.match( d[column] ) is not None + val = unquote_plus(val) + val = re.compile(val) + return lambda d: val.match(d[column]) is not None return None - def create_list_filter( self, column, op, val ): + def create_list_filter(self, column, op, val): """ Return an anonymous filter function that will be passed the array of parsed columns. Return None if no filter function can be @@ -205,13 +205,13 @@ def create_list_filter( self, column, op, val ): - has: the list in the column contains the sublist `val` """ if 'eq' == op: - val = self.parse_value( val, 'list' ) + val = self.parse_value(val, 'list') return lambda d: d[column] == val elif 'has' == op: return lambda d: val in d[column] return None - def get_default_parsers( self ): + def get_default_parsers(self): """ Return parser dictionary keyed for each columnar type (as defined in datatypes). @@ -249,40 +249,40 @@ def get_default_parsers( self ): # 'gffstrand': # -, +, ?, or '.' for None, etc. } - def filter( self, line ): - line = super( ColumnarDataProvider, self ).filter( line ) + def filter(self, line): + line = super(ColumnarDataProvider, self).filter(line) if line is None: return line - columns = self.parse_columns_from_line( line ) - return self.filter_by_columns( columns ) + columns = self.parse_columns_from_line(line) + return self.filter_by_columns(columns) - def parse_columns_from_line( self, line ): + def parse_columns_from_line(self, line): """ Returns a list of the desired, parsed columns. :param line: the line to parse :type line: str """ # TODO: too much going on in this loop - the above should all be precomputed AMAP... - all_columns = line.split( self.deliminator ) + all_columns = line.split(self.deliminator) # if no indeces were passed to init, return all columns - selected_indeces = self.selected_column_indeces or list( range( len( all_columns ) ) ) + selected_indeces = self.selected_column_indeces or list(range(len(all_columns))) parsed_columns = [] - for parser_index, column_index in enumerate( selected_indeces ): - parsed_columns.append( self.parse_column_at_index( all_columns, parser_index, column_index ) ) + for parser_index, column_index in enumerate(selected_indeces): + parsed_columns.append(self.parse_column_at_index(all_columns, parser_index, column_index)) return parsed_columns - def parse_column_at_index( self, columns, parser_index, index ): + def parse_column_at_index(self, columns, parser_index, index): """ Get the column type for the parser from `self.column_types` or `None` if the type is unavailable. """ try: - return self.parse_value( columns[ index ], self.get_column_type( parser_index ) ) + return self.parse_value(columns[index], self.get_column_type(parser_index)) # if a selected index is not within columns, return None except IndexError: return None - def parse_value( self, val, type ): + def parse_value(self, val, type): """ Attempt to parse and return the given value based on the given type. @@ -295,7 +295,7 @@ def parse_value( self, val, type ): if type == 'str' or type is None: return val try: - return self.parsers[ type ]( val ) + return self.parsers[type](val) except KeyError: # no parser - return as string pass @@ -304,7 +304,7 @@ def parse_value( self, val, type ): return None return val - def get_column_type( self, index ): + def get_column_type(self, index): """ Get the column type for the parser from `self.column_types` or `None` if the type is unavailable. @@ -312,18 +312,18 @@ def get_column_type( self, index ): :returns: string name of type (e.g. 'float', 'int', etc.) """ try: - return self.column_types[ index ] + return self.column_types[index] except IndexError: return None - def filter_by_columns( self, columns ): + def filter_by_columns(self, columns): for filter_fn in self.column_filters: - if not filter_fn( columns ): + if not filter_fn(columns): return None return columns -class DictDataProvider( ColumnarDataProvider ): +class DictDataProvider(ColumnarDataProvider): """ Data provider that zips column_names and columns from the source's contents into a dictionary. @@ -338,7 +338,7 @@ class DictDataProvider( ColumnarDataProvider ): 'column_names' : 'list:str', } - def __init__( self, source, column_names=None, **kwargs ): + def __init__(self, source, column_names=None, **kwargs): """ :param column_names: an ordered list of strings that will be used as the keys for each column in the returned dictionaries. @@ -347,11 +347,11 @@ def __init__( self, source, column_names=None, **kwargs ): :type column_names: """ # TODO: allow passing in a map instead of name->index { 'name1': index1, ... } - super( DictDataProvider, self ).__init__( source, **kwargs ) + super(DictDataProvider, self).__init__(source, **kwargs) self.column_names = column_names or [] - def __iter__( self ): - parent_gen = super( DictDataProvider, self ).__iter__() + def __iter__(self): + parent_gen = super(DictDataProvider, self).__iter__() for column_values in parent_gen: - map = dict( zip( self.column_names, column_values ) ) + map = dict(zip(self.column_names, column_values)) yield map diff --git a/lib/galaxy/datatypes/dataproviders/dataset.py b/lib/galaxy/datatypes/dataproviders/dataset.py index 3076e0218b0a..2d77ccc2de5a 100644 --- a/lib/galaxy/datatypes/dataproviders/dataset.py +++ b/lib/galaxy/datatypes/dataproviders/dataset.py @@ -30,11 +30,11 @@ change SamtoolsDataProvider to use pysam """ -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) # ----------------------------------------------------------------------------- base for using a Glx dataset -class DatasetDataProvider( base.DataProvider ): +class DatasetDataProvider(base.DataProvider): """ Class that uses the file contents and/or metadata from a Galaxy DatasetInstance as its source. @@ -44,7 +44,8 @@ class DatasetDataProvider( base.DataProvider ): and conv. methods for using dataset metadata to set up and control how the data is provided. """ - def __init__( self, dataset, **kwargs ): + + def __init__(self, dataset, **kwargs): """ :param dataset: the Galaxy dataset whose file will be the source :type dataset: model.DatasetInstance @@ -53,11 +54,11 @@ def __init__( self, dataset, **kwargs ): self.dataset = dataset # this dataset file is obviously the source # TODO: this might be a good place to interface with the object_store... - super( DatasetDataProvider, self ).__init__( open( dataset.file_name, 'rb' ) ) + super(DatasetDataProvider, self).__init__(open(dataset.file_name, 'rb')) # TODO: this is a bit of a mess @classmethod - def get_column_metadata_from_dataset( cls, dataset ): + def get_column_metadata_from_dataset(cls, dataset): """ Convenience class method to get column metadata from a dataset. @@ -66,12 +67,12 @@ def get_column_metadata_from_dataset( cls, dataset ): """ # re-map keys to fit ColumnarProvider.__init__ kwargs params = {} - params[ 'column_count' ] = dataset.metadata.columns - params[ 'column_types' ] = dataset.metadata.column_types - params[ 'column_names' ] = dataset.metadata.column_names or getattr( dataset.datatype, 'column_names', None ) + params['column_count'] = dataset.metadata.columns + params['column_types'] = dataset.metadata.column_types + params['column_names'] = dataset.metadata.column_names or getattr(dataset.datatype, 'column_names', None) return params - def get_metadata_column_types( self, indeces=None ): + def get_metadata_column_types(self, indeces=None): """ Return the list of `column_types` for this dataset or `None` if unavailable. @@ -79,19 +80,19 @@ def get_metadata_column_types( self, indeces=None ): Optional: defaults to None (return all types) :type indeces: list of ints """ - metadata_column_types = ( self.dataset.metadata.column_types or - getattr( self.dataset.datatype, 'column_types', None ) or None ) + metadata_column_types = (self.dataset.metadata.column_types or + getattr(self.dataset.datatype, 'column_types', None) or None) if not metadata_column_types: return metadata_column_types if indeces: column_types = [] for index in indeces: - column_type = metadata_column_types[ index ] if index < len( metadata_column_types ) else None - column_types.append( column_type ) + column_type = metadata_column_types[index] if index < len(metadata_column_types) else None + column_types.append(column_type) return column_types return metadata_column_types - def get_metadata_column_names( self, indeces=None ): + def get_metadata_column_names(self, indeces=None): """ Return the list of `column_names` for this dataset or `None` if unavailable. @@ -99,20 +100,20 @@ def get_metadata_column_names( self, indeces=None ): Optional: defaults to None (return all names) :type indeces: list of ints """ - metadata_column_names = ( self.dataset.metadata.column_names or - getattr( self.dataset.datatype, 'column_names', None ) or None ) + metadata_column_names = (self.dataset.metadata.column_names or + getattr(self.dataset.datatype, 'column_names', None) or None) if not metadata_column_names: return metadata_column_names if indeces: column_names = [] for index in indeces: - column_type = metadata_column_names[ index ] if index < len( metadata_column_names ) else None - column_names.append( column_type ) + column_type = metadata_column_names[index] if index < len(metadata_column_names) else None + column_names.append(column_type) return column_names return metadata_column_names # TODO: merge the next two - def get_indeces_by_column_names( self, list_of_column_names ): + def get_indeces_by_column_names(self, list_of_column_names): """ Return the list of column indeces when given a list of column_names. @@ -122,27 +123,27 @@ def get_indeces_by_column_names( self, list_of_column_names ): :raises KeyError: if column_names are not found :raises ValueError: if an entry in list_of_column_names is not in column_names """ - metadata_column_names = ( self.dataset.metadata.column_names or - getattr( self.dataset.datatype, 'column_names', None ) or None ) + metadata_column_names = (self.dataset.metadata.column_names or + getattr(self.dataset.datatype, 'column_names', None) or None) if not metadata_column_names: - raise KeyError( 'No column_names found for ' + - 'datatype: %s, dataset: %s' % ( str( self.dataset.datatype ), str( self.dataset ) ) ) + raise KeyError('No column_names found for ' + + 'datatype: %s, dataset: %s' % (str(self.dataset.datatype), str(self.dataset))) indeces = [] # if indeces and column_names: # pull using indeces and re-name with given names - no need to alter (does as super would) # pass for column_name in list_of_column_names: - indeces.append( metadata_column_names.index( column_name ) ) + indeces.append(metadata_column_names.index(column_name)) return indeces - def get_metadata_column_index_by_name( self, name ): + def get_metadata_column_index_by_name(self, name): """ Return the 1-base index of a sources column with the given `name`. """ # metadata columns are 1-based indeces - column = getattr( self.dataset.metadata, name ) - return ( column - 1 ) if ( isinstance( column, int ) and column > 0 ) else None + column = getattr(self.dataset.metadata, name) + return (column - 1) if (isinstance(column, int) and column > 0) else None - def get_genomic_region_indeces( self, check=False ): + def get_genomic_region_indeces(self, check=False): """ Return a list of column indeces for 'chromCol', 'startCol', 'endCol' from a source representing a genomic region. @@ -152,26 +153,27 @@ def get_genomic_region_indeces( self, check=False ): :raises ValueError: if check is `True` and one or more indeces were not found. :returns: list of column indeces for the named columns. """ - region_column_names = ( 'chromCol', 'startCol', 'endCol' ) - region_indices = [ self.get_metadata_column_index_by_name( name ) for name in region_column_names ] - if check and not all( _ is not None for _ in region_indices ): - raise ValueError( "Could not determine proper column indices for chrom, start, end: %s" % ( str( region_indices ) ) ) + region_column_names = ('chromCol', 'startCol', 'endCol') + region_indices = [self.get_metadata_column_index_by_name(name) for name in region_column_names] + if check and not all(_ is not None for _ in region_indices): + raise ValueError("Could not determine proper column indices for chrom, start, end: %s" % (str(region_indices))) return region_indices -class ConvertedDatasetDataProvider( DatasetDataProvider ): +class ConvertedDatasetDataProvider(DatasetDataProvider): """ Class that uses the file contents of a dataset after conversion to a different format. """ - def __init__( self, dataset, **kwargs ): - raise NotImplementedError( 'Abstract class' ) + + def __init__(self, dataset, **kwargs): + raise NotImplementedError('Abstract class') self.original_dataset = dataset - self.converted_dataset = self.convert_dataset( dataset, **kwargs ) - super( ConvertedDatasetDataProvider, self ).__init__( self.converted_dataset, **kwargs ) + self.converted_dataset = self.convert_dataset(dataset, **kwargs) + super(ConvertedDatasetDataProvider, self).__init__(self.converted_dataset, **kwargs) # NOTE: now self.converted_dataset == self.dataset - def convert_dataset( self, dataset, **kwargs ): + def convert_dataset(self, dataset, **kwargs): """ Convert the given dataset in some way. """ @@ -179,13 +181,14 @@ def convert_dataset( self, dataset, **kwargs ): # ----------------------------------------------------------------------------- uses metadata for settings -class DatasetColumnarDataProvider( column.ColumnarDataProvider ): +class DatasetColumnarDataProvider(column.ColumnarDataProvider): """ Data provider that uses a DatasetDataProvider as its source and the dataset's metadata to buuild settings for the ColumnarDataProvider it's inherited from. """ - def __init__( self, dataset, **kwargs ): + + def __init__(self, dataset, **kwargs): """ All kwargs are inherited from ColumnarDataProvider. .. seealso:: column.ColumnarDataProvider @@ -195,20 +198,21 @@ def __init__( self, dataset, **kwargs ): If any kwarg is given, it will override and be used in place of any metadata available. """ - dataset_source = DatasetDataProvider( dataset ) - if not kwargs.get( 'column_types', None ): - indeces = kwargs.get( 'indeces', None ) - kwargs[ 'column_types' ] = dataset_source.get_metadata_column_types( indeces=indeces ) - super( DatasetColumnarDataProvider, self ).__init__( dataset_source, **kwargs ) + dataset_source = DatasetDataProvider(dataset) + if not kwargs.get('column_types', None): + indeces = kwargs.get('indeces', None) + kwargs['column_types'] = dataset_source.get_metadata_column_types(indeces=indeces) + super(DatasetColumnarDataProvider, self).__init__(dataset_source, **kwargs) -class DatasetDictDataProvider( column.DictDataProvider ): +class DatasetDictDataProvider(column.DictDataProvider): """ Data provider that uses a DatasetDataProvider as its source and the dataset's metadata to buuild settings for the DictDataProvider it's inherited from. """ - def __init__( self, dataset, **kwargs ): + + def __init__(self, dataset, **kwargs): """ All kwargs are inherited from DictDataProvider. .. seealso:: column.DictDataProvider @@ -227,34 +231,34 @@ def __init__( self, dataset, **kwargs ): | Names NOT given | pull indeces, name w/ meta | pull all, name w/meta | +=================+-------------------------------+-----------------------+ """ - dataset_source = DatasetDataProvider( dataset ) + dataset_source = DatasetDataProvider(dataset) # TODO: getting too complicated - simplify at some lvl, somehow # if no column_types given, get column_types from indeces (or all if indeces == None) - indeces = kwargs.get( 'indeces', None ) - column_names = kwargs.get( 'column_names', None ) + indeces = kwargs.get('indeces', None) + column_names = kwargs.get('column_names', None) if not indeces and column_names: # pull columns by name - indeces = kwargs[ 'indeces' ] = dataset_source.get_indeces_by_column_names( column_names ) + indeces = kwargs['indeces'] = dataset_source.get_indeces_by_column_names(column_names) elif indeces and not column_names: # pull using indeces, name with meta - column_names = kwargs[ 'column_names' ] = dataset_source.get_metadata_column_names( indeces=indeces ) + column_names = kwargs['column_names'] = dataset_source.get_metadata_column_names(indeces=indeces) elif not indeces and not column_names: # pull all indeces and name using metadata - column_names = kwargs[ 'column_names' ] = dataset_source.get_metadata_column_names( indeces=indeces ) + column_names = kwargs['column_names'] = dataset_source.get_metadata_column_names(indeces=indeces) # if no column_types given, use metadata column_types - if not kwargs.get( 'column_types', None ): - kwargs[ 'column_types' ] = dataset_source.get_metadata_column_types( indeces=indeces ) + if not kwargs.get('column_types', None): + kwargs['column_types'] = dataset_source.get_metadata_column_types(indeces=indeces) - super( DatasetDictDataProvider, self ).__init__( dataset_source, **kwargs ) + super(DatasetDictDataProvider, self).__init__(dataset_source, **kwargs) # ----------------------------------------------------------------------------- provides a bio-relevant datum -class GenomicRegionDataProvider( column.ColumnarDataProvider ): +class GenomicRegionDataProvider(column.ColumnarDataProvider): """ Data provider that parses chromosome, start, and end data from a file using the datasets metadata settings. @@ -265,7 +269,7 @@ class GenomicRegionDataProvider( column.ColumnarDataProvider ): 'chrom', 'start', 'end'. """ # dictionary keys when named_columns=True - COLUMN_NAMES = [ 'chrom', 'start', 'end' ] + COLUMN_NAMES = ['chrom', 'start', 'end'] settings = { 'chrom_column' : 'int', 'start_column' : 'int', @@ -273,7 +277,7 @@ class GenomicRegionDataProvider( column.ColumnarDataProvider ): 'named_columns' : 'bool', } - def __init__( self, dataset, chrom_column=None, start_column=None, end_column=None, named_columns=False, **kwargs ): + def __init__(self, dataset, chrom_column=None, start_column=None, end_column=None, named_columns=False, **kwargs): """ :param dataset: the Galaxy dataset whose file will be the source :type dataset: model.DatasetInstance @@ -291,41 +295,41 @@ def __init__( self, dataset, chrom_column=None, start_column=None, end_column=No :type named_columns: bool """ # TODO: allow passing in a string format e.g. "{chrom}:{start}-{end}" - dataset_source = DatasetDataProvider( dataset ) + dataset_source = DatasetDataProvider(dataset) if chrom_column is None: - chrom_column = dataset_source.get_metadata_column_index_by_name( 'chromCol' ) + chrom_column = dataset_source.get_metadata_column_index_by_name('chromCol') if start_column is None: - start_column = dataset_source.get_metadata_column_index_by_name( 'startCol' ) + start_column = dataset_source.get_metadata_column_index_by_name('startCol') if end_column is None: - end_column = dataset_source.get_metadata_column_index_by_name( 'endCol' ) - indeces = [ chrom_column, start_column, end_column ] - if not all( _ is not None for _ in indeces ): - raise ValueError( "Could not determine proper column indeces for" + - " chrom, start, end: %s" % ( str( indeces ) ) ) - kwargs.update({ 'indeces' : indeces }) + end_column = dataset_source.get_metadata_column_index_by_name('endCol') + indeces = [chrom_column, start_column, end_column] + if not all(_ is not None for _ in indeces): + raise ValueError("Could not determine proper column indeces for" + + " chrom, start, end: %s" % (str(indeces))) + kwargs.update({'indeces' : indeces}) - if not kwargs.get( 'column_types', None ): - kwargs.update({ 'column_types' : dataset_source.get_metadata_column_types( indeces=indeces ) }) + if not kwargs.get('column_types', None): + kwargs.update({'column_types' : dataset_source.get_metadata_column_types(indeces=indeces)}) self.named_columns = named_columns if self.named_columns: self.column_names = self.COLUMN_NAMES - super( GenomicRegionDataProvider, self ).__init__( dataset_source, **kwargs ) + super(GenomicRegionDataProvider, self).__init__(dataset_source, **kwargs) - def __iter__( self ): - parent_gen = super( GenomicRegionDataProvider, self ).__iter__() + def __iter__(self): + parent_gen = super(GenomicRegionDataProvider, self).__iter__() for column_values in parent_gen: if self.named_columns: - yield dict( zip( self.column_names, column_values ) ) + yield dict(zip(self.column_names, column_values)) else: yield column_values # TODO: this optionally provides the same data as the above and makes GenomicRegionDataProvider redundant # GenomicRegionDataProvider is a better name, tho -class IntervalDataProvider( column.ColumnarDataProvider ): +class IntervalDataProvider(column.ColumnarDataProvider): """ Data provider that parses chromosome, start, and end data (as well as strand and name if set in the metadata) using the dataset's metadata settings. @@ -333,7 +337,7 @@ class IntervalDataProvider( column.ColumnarDataProvider ): If `named_columns` is true, will return dictionaries with the keys 'chrom', 'start', 'end' (and 'strand' and 'name' if available). """ - COLUMN_NAMES = [ 'chrom', 'start', 'end', 'strand', 'name' ] + COLUMN_NAMES = ['chrom', 'start', 'end', 'strand', 'name'] settings = { 'chrom_column' : 'int', 'start_column' : 'int', @@ -343,8 +347,8 @@ class IntervalDataProvider( column.ColumnarDataProvider ): 'named_columns' : 'bool', } - def __init__( self, dataset, chrom_column=None, start_column=None, end_column=None, - strand_column=None, name_column=None, named_columns=False, **kwargs ): + def __init__(self, dataset, chrom_column=None, start_column=None, end_column=None, + strand_column=None, name_column=None, named_columns=False, **kwargs): """ :param dataset: the Galaxy dataset whose file will be the source :type dataset: model.DatasetInstance @@ -355,58 +359,58 @@ def __init__( self, dataset, chrom_column=None, start_column=None, end_column=No :type named_columns: bool """ # TODO: allow passing in a string format e.g. "{chrom}:{start}-{end}" - dataset_source = DatasetDataProvider( dataset ) + dataset_source = DatasetDataProvider(dataset) # get genomic indeces and add strand and name self.column_names = [] indeces = [] # TODO: this is sort of involved and oogly if chrom_column is None: - chrom_column = dataset_source.get_metadata_column_index_by_name( 'chromCol' ) + chrom_column = dataset_source.get_metadata_column_index_by_name('chromCol') if chrom_column is not None: - self.column_names.append( 'chrom' ) - indeces.append( chrom_column ) + self.column_names.append('chrom') + indeces.append(chrom_column) if start_column is None: - start_column = dataset_source.get_metadata_column_index_by_name( 'startCol' ) + start_column = dataset_source.get_metadata_column_index_by_name('startCol') if start_column is not None: - self.column_names.append( 'start' ) - indeces.append( start_column ) + self.column_names.append('start') + indeces.append(start_column) if end_column is None: - end_column = dataset_source.get_metadata_column_index_by_name( 'endCol' ) + end_column = dataset_source.get_metadata_column_index_by_name('endCol') if end_column is not None: - self.column_names.append( 'end' ) - indeces.append( end_column ) + self.column_names.append('end') + indeces.append(end_column) if strand_column is None: - strand_column = dataset_source.get_metadata_column_index_by_name( 'strandCol' ) + strand_column = dataset_source.get_metadata_column_index_by_name('strandCol') if strand_column is not None: - self.column_names.append( 'strand' ) - indeces.append( strand_column ) + self.column_names.append('strand') + indeces.append(strand_column) if name_column is None: - name_column = dataset_source.get_metadata_column_index_by_name( 'nameCol' ) + name_column = dataset_source.get_metadata_column_index_by_name('nameCol') if name_column is not None: - self.column_names.append( 'name' ) - indeces.append( name_column ) + self.column_names.append('name') + indeces.append(name_column) - kwargs.update({ 'indeces' : indeces }) - if not kwargs.get( 'column_types', None ): - kwargs.update({ 'column_types' : dataset_source.get_metadata_column_types( indeces=indeces ) }) + kwargs.update({'indeces' : indeces}) + if not kwargs.get('column_types', None): + kwargs.update({'column_types' : dataset_source.get_metadata_column_types(indeces=indeces)}) self.named_columns = named_columns - super( IntervalDataProvider, self ).__init__( dataset_source, **kwargs ) + super(IntervalDataProvider, self).__init__(dataset_source, **kwargs) - def __iter__( self ): - parent_gen = super( IntervalDataProvider, self ).__iter__() + def __iter__(self): + parent_gen = super(IntervalDataProvider, self).__iter__() for column_values in parent_gen: if self.named_columns: - yield dict( zip( self.column_names, column_values ) ) + yield dict(zip(self.column_names, column_values)) else: yield column_values # TODO: ideally with these next two - you'd allow pulling some region from the sequence # WITHOUT reading the entire seq into memory - possibly apply some version of limit/offset -class FastaDataProvider( base.FilteredDataProvider ): +class FastaDataProvider(base.FilteredDataProvider): """ Class that returns fasta format data in a list of maps of the form:: @@ -419,20 +423,20 @@ class FastaDataProvider( base.FilteredDataProvider ): 'ids' : 'list:str', } - def __init__( self, source, ids=None, **kwargs ): + def __init__(self, source, ids=None, **kwargs): """ :param ids: optionally return only ids (and sequences) that are in this list. Optional: defaults to None (provide all ids) :type ids: list or None """ - source = bx_seq.fasta.FastaReader( source ) + source = bx_seq.fasta.FastaReader(source) # TODO: validate is a fasta - super( FastaDataProvider, self ).__init__( source, **kwargs ) + super(FastaDataProvider, self).__init__(source, **kwargs) self.ids = ids # how to do ids? - def __iter__( self ): - parent_gen = super( FastaDataProvider, self ).__iter__() + def __iter__(self): + parent_gen = super(FastaDataProvider, self).__iter__() for fasta_record in parent_gen: yield { 'id' : fasta_record.name, @@ -440,7 +444,7 @@ def __iter__( self ): } -class TwoBitFastaDataProvider( DatasetDataProvider ): +class TwoBitFastaDataProvider(DatasetDataProvider): """ Class that returns fasta format data in a list of maps of the form:: @@ -453,38 +457,38 @@ class TwoBitFastaDataProvider( DatasetDataProvider ): 'ids' : 'list:str', } - def __init__( self, source, ids=None, **kwargs ): + def __init__(self, source, ids=None, **kwargs): """ :param ids: optionally return only ids (and sequences) that are in this list. Optional: defaults to None (provide all ids) :type ids: list or None """ - source = bx_seq.twobit.TwoBitFile( source ) + source = bx_seq.twobit.TwoBitFile(source) # TODO: validate is a 2bit - super( FastaDataProvider, self ).__init__( source, **kwargs ) + super(FastaDataProvider, self).__init__(source, **kwargs) # could do in order provided with twobit self.ids = ids or self.source.keys() - def __iter__( self ): + def __iter__(self): for id_ in self.ids: yield { 'id': id_, - 'seq': self.source[ id_ ] + 'seq': self.source[id_] } # TODO: -class WiggleDataProvider( base.LimitedOffsetDataProvider ): +class WiggleDataProvider(base.LimitedOffsetDataProvider): """ Class that returns chrom, pos, data from a wiggle source. """ - COLUMN_NAMES = [ 'chrom', 'pos', 'value' ] + COLUMN_NAMES = ['chrom', 'pos', 'value'] settings = { 'named_columns' : 'bool', 'column_names' : 'list:str', } - def __init__( self, source, named_columns=False, column_names=None, **kwargs ): + def __init__(self, source, named_columns=False, column_names=None, **kwargs): """ :param named_columns: optionally return dictionaries keying each column with 'chrom', 'start', 'end', 'strand', or 'name'. @@ -500,33 +504,33 @@ def __init__( self, source, named_columns=False, column_names=None, **kwargs ): # TODO: validate is a wig # still good to maintain a ref to the raw source bc Reader won't self.raw_source = source - self.parser = bx_wig.Reader( source ) - super( WiggleDataProvider, self ).__init__( self.parser, **kwargs ) + self.parser = bx_wig.Reader(source) + super(WiggleDataProvider, self).__init__(self.parser, **kwargs) self.named_columns = named_columns self.column_names = column_names or self.COLUMN_NAMES - def __iter__( self ): - parent_gen = super( WiggleDataProvider, self ).__iter__() + def __iter__(self): + parent_gen = super(WiggleDataProvider, self).__iter__() for three_tuple in parent_gen: if self.named_columns: - yield dict( zip( self.column_names, three_tuple ) ) + yield dict(zip(self.column_names, three_tuple)) else: # list is not strictly necessary - but consistent - yield list( three_tuple ) + yield list(three_tuple) -class BigWigDataProvider( base.LimitedOffsetDataProvider ): +class BigWigDataProvider(base.LimitedOffsetDataProvider): """ Class that returns chrom, pos, data from a wiggle source. """ - COLUMN_NAMES = [ 'chrom', 'pos', 'value' ] + COLUMN_NAMES = ['chrom', 'pos', 'value'] settings = { 'named_columns' : 'bool', 'column_names' : 'list:str', } - def __init__( self, source, chrom, start, end, named_columns=False, column_names=None, **kwargs ): + def __init__(self, source, chrom, start, end, named_columns=False, column_names=None, **kwargs): """ :param chrom: which chromosome within the bigbed file to extract data for :type chrom: str @@ -546,28 +550,28 @@ def __init__( self, source, chrom, start, end, named_columns=False, column_names be as short as the number of column names provided. :type column_names: """ - raise NotImplementedError( 'Work in progress' ) + raise NotImplementedError('Work in progress') # TODO: validate is a wig # still good to maintain a ref to the raw source bc Reader won't self.raw_source = source - self.parser = bx_bbi.bigwig_file.BigWigFile( source ) - super( BigWigDataProvider, self ).__init__( self.parser, **kwargs ) + self.parser = bx_bbi.bigwig_file.BigWigFile(source) + super(BigWigDataProvider, self).__init__(self.parser, **kwargs) self.named_columns = named_columns self.column_names = column_names or self.COLUMN_NAMES - def __iter__( self ): - parent_gen = super( BigWigDataProvider, self ).__iter__() + def __iter__(self): + parent_gen = super(BigWigDataProvider, self).__iter__() for three_tuple in parent_gen: if self.named_columns: - yield dict( zip( self.column_names, three_tuple ) ) + yield dict(zip(self.column_names, three_tuple)) else: # list is not strictly necessary - but consistent - yield list( three_tuple ) + yield list(three_tuple) # ----------------------------------------------------------------------------- binary, external conversion or tool -class DatasetSubprocessDataProvider( external.SubprocessDataProvider ): +class DatasetSubprocessDataProvider(external.SubprocessDataProvider): """ Create a source from running a subprocess on a dataset's file. @@ -575,17 +579,18 @@ class DatasetSubprocessDataProvider( external.SubprocessDataProvider ): for the process). """ # TODO: below should be a subclass of this and not RegexSubprocess - def __init__( self, dataset, *args, **kwargs ): + + def __init__(self, dataset, *args, **kwargs): """ :param args: the list of strings used to build commands. :type args: variadic function args """ - raise NotImplementedError( 'Abstract class' ) - super( DatasetSubprocessDataProvider, self ).__init__( *args, **kwargs ) + raise NotImplementedError('Abstract class') + super(DatasetSubprocessDataProvider, self).__init__(*args, **kwargs) self.dataset = dataset -class SamtoolsDataProvider( line.RegexLineDataProvider ): +class SamtoolsDataProvider(line.RegexLineDataProvider): """ Data provider that uses samtools on a Sam or Bam file as its source. @@ -597,7 +602,7 @@ class SamtoolsDataProvider( line.RegexLineDataProvider ): FLAGS_W_ARGS = 'fFqlrs' VALID_FLAGS = FLAGS_WO_ARGS + FLAGS_W_ARGS - def __init__( self, dataset, options_string='', options_dict=None, regions=None, **kwargs ): + def __init__(self, dataset, options_string='', options_dict=None, regions=None, **kwargs): """ :param options_string: samtools options in string form (flags separated by spaces) @@ -617,29 +622,29 @@ def __init__( self, dataset, options_string='', options_dict=None, regions=None, options_dict = options_dict or {} # ensure regions are strings - regions = [ str( r ) for r in regions ] if regions else [] + regions = [str(r) for r in regions] if regions else [] # TODO: view only for now # TODO: not properly using overriding super's validate_opts, command here subcommand = 'view' # TODO:?? do we need a path to samtools? - subproc_args = self.build_command_list( subcommand, options_string, options_dict, regions ) + subproc_args = self.build_command_list(subcommand, options_string, options_dict, regions) # TODO: the composition/inheritance here doesn't make a lot sense - subproc_provider = external.SubprocessDataProvider( *subproc_args ) - super( SamtoolsDataProvider, self ).__init__( subproc_provider, **kwargs ) + subproc_provider = external.SubprocessDataProvider(*subproc_args) + super(SamtoolsDataProvider, self).__init__(subproc_provider, **kwargs) - def build_command_list( self, subcommand, options_string, options_dict, regions ): + def build_command_list(self, subcommand, options_string, options_dict, regions): """ Convert all init args to list form. """ - command = [ 'samtools', subcommand ] + command = ['samtools', subcommand] # add options and switches, input file, regions list (if any) - command.extend( self.to_options_list( options_string, options_dict ) ) - command.append( self.dataset.file_name ) - command.extend( regions ) + command.extend(self.to_options_list(options_string, options_dict)) + command.append(self.dataset.file_name) + command.extend(regions) return command - def to_options_list( self, options_string, options_dict ): + def to_options_list(self, options_string, options_dict): """ Convert both options_string and options_dict to list form while filtering out non-'valid' options. @@ -648,26 +653,26 @@ def to_options_list( self, options_string, options_dict ): # strip out any user supplied bash switch formating -> string of option chars # then compress to single option string of unique, VALID flags with prefixed bash switch char '-' - options_string = options_string.strip( '- ' ) - validated_flag_list = set([ flag for flag in options_string if flag in self.FLAGS_WO_ARGS ]) + options_string = options_string.strip('- ') + validated_flag_list = set([flag for flag in options_string if flag in self.FLAGS_WO_ARGS]) # if sam add -S # TODO: not the best test in the world... - if( ( self.dataset.ext == 'sam' ) and - ( 'S' not in validated_flag_list ) ): - validated_flag_list.append( 'S' ) + if((self.dataset.ext == 'sam') and + ('S' not in validated_flag_list)): + validated_flag_list.append('S') if validated_flag_list: - opt_list.append( '-' + ''.join( validated_flag_list ) ) + opt_list.append('-' + ''.join(validated_flag_list)) for flag, arg in options_dict.items(): if flag in self.FLAGS_W_ARGS: - opt_list.extend([ '-' + flag, str( arg ) ]) + opt_list.extend(['-' + flag, str(arg)]) return opt_list @classmethod - def extract_options_from_dict( cls, dictionary ): + def extract_options_from_dict(cls, dictionary): """ Separrates valid samtools key/value pair options from a dictionary and returns both as a 2-tuple. @@ -678,37 +683,39 @@ def extract_options_from_dict( cls, dictionary ): new_kwargs = {} for key, value in dictionary.items(): if key in cls.FLAGS_W_ARGS: - options_dict[ key ] = value + options_dict[key] = value else: - new_kwargs[ key ] = value + new_kwargs[key] = value return options_dict, new_kwargs -class BcftoolsDataProvider( line.RegexLineDataProvider ): +class BcftoolsDataProvider(line.RegexLineDataProvider): """ Data provider that uses an bcftools on a bcf (or vcf?) file as its source. This can be piped through other providers (column, map, genome region, etc.). """ - def __init__( self, dataset, **kwargs ): + + def __init__(self, dataset, **kwargs): # TODO: as samtools raise NotImplementedError() - super( BcftoolsDataProvider, self ).__init__( dataset, **kwargs ) + super(BcftoolsDataProvider, self).__init__(dataset, **kwargs) -class BGzipTabixDataProvider( base.DataProvider ): +class BGzipTabixDataProvider(base.DataProvider): """ Data provider that uses an g(un)zip on a file as its source. This can be piped through other providers (column, map, genome region, etc.). """ - def __init__( self, dataset, **kwargs ): + + def __init__(self, dataset, **kwargs): # TODO: as samtools - need more info on output format raise NotImplementedError() - super( BGzipTabixDataProvider, self ).__init__( dataset, **kwargs ) + super(BGzipTabixDataProvider, self).__init__(dataset, **kwargs) -class SQliteDataProvider( base.DataProvider ): +class SQliteDataProvider(base.DataProvider): """ Data provider that uses a sqlite database file as its source. @@ -718,12 +725,12 @@ class SQliteDataProvider( base.DataProvider ): 'query': 'str' } - def __init__( self, source, query=None, **kwargs ): + def __init__(self, source, query=None, **kwargs): self.query = query self.connection = sqlite.connect(source.dataset.file_name) - super( SQliteDataProvider, self ).__init__( source, **kwargs ) + super(SQliteDataProvider, self).__init__(source, **kwargs) - def __iter__( self ): + def __iter__(self): if (self.query is not None) and sqlite.is_read_only_query(self.query): for row in self.connection.cursor().execute(self.query): yield row @@ -731,7 +738,7 @@ def __iter__( self ): yield -class SQliteDataTableProvider( base.DataProvider ): +class SQliteDataTableProvider(base.DataProvider): """ Data provider that uses a sqlite database file as its source. Allows any query to be run and returns the resulting rows as arrays of arrays @@ -742,14 +749,14 @@ class SQliteDataTableProvider( base.DataProvider ): 'limit': 'int' } - def __init__( self, source, query=None, headers=False, limit=sys.maxsize, **kwargs ): + def __init__(self, source, query=None, headers=False, limit=sys.maxsize, **kwargs): self.query = query self.headers = headers self.limit = limit self.connection = sqlite.connect(source.dataset.file_name) - super( SQliteDataTableProvider, self ).__init__( source, **kwargs ) + super(SQliteDataTableProvider, self).__init__(source, **kwargs) - def __iter__( self ): + def __iter__(self): if (self.query is not None) and sqlite.is_read_only_query(self.query): cur = self.connection.cursor() results = cur.execute(self.query) @@ -763,7 +770,7 @@ def __iter__( self ): yield -class SQliteDataDictProvider( base.DataProvider ): +class SQliteDataDictProvider(base.DataProvider): """ Data provider that uses a sqlite database file as its source. Allows any query to be run and returns the resulting rows as arrays of dicts @@ -772,12 +779,12 @@ class SQliteDataDictProvider( base.DataProvider ): 'query': 'str' } - def __init__( self, source, query=None, **kwargs ): + def __init__(self, source, query=None, **kwargs): self.query = query self.connection = sqlite.connect(source.dataset.file_name) - super( SQliteDataDictProvider, self ).__init__( source, **kwargs ) + super(SQliteDataDictProvider, self).__init__(source, **kwargs) - def __iter__( self ): + def __iter__(self): if (self.query is not None) and sqlite.is_read_only_query(self.query): cur = self.connection.cursor() for row in cur.execute(self.query): diff --git a/lib/galaxy/datatypes/dataproviders/decorators.py b/lib/galaxy/datatypes/dataproviders/decorators.py index 93cfec133969..15f5a79f77ed 100644 --- a/lib/galaxy/datatypes/dataproviders/decorators.py +++ b/lib/galaxy/datatypes/dataproviders/decorators.py @@ -20,13 +20,13 @@ from six.moves.urllib.parse import unquote -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) _DATAPROVIDER_CLASS_MAP_KEY = 'dataproviders' _DATAPROVIDER_METHOD_NAME_KEY = '_dataprovider_name' -def has_dataproviders( cls ): +def has_dataproviders(cls): """ Wraps a class (generally a Datatype), finds methods within that have been decorated with `@dataprovider` and adds them, by their name, to a map @@ -53,30 +53,30 @@ def provide_some_bler( self, dataset, **settings ): my_setting='blah', ... ) """ # init the class dataproviders map if necc. - if not hasattr( cls, _DATAPROVIDER_CLASS_MAP_KEY ): - setattr( cls, _DATAPROVIDER_CLASS_MAP_KEY, {} ) + if not hasattr(cls, _DATAPROVIDER_CLASS_MAP_KEY): + setattr(cls, _DATAPROVIDER_CLASS_MAP_KEY, {}) else: # need to deepcopy or subclasses will modify super.dataproviders as well - existing_dataproviders = getattr( cls, _DATAPROVIDER_CLASS_MAP_KEY ) - copied_dataproviders = copy.deepcopy( existing_dataproviders ) - setattr( cls, _DATAPROVIDER_CLASS_MAP_KEY, copied_dataproviders ) + existing_dataproviders = getattr(cls, _DATAPROVIDER_CLASS_MAP_KEY) + copied_dataproviders = copy.deepcopy(existing_dataproviders) + setattr(cls, _DATAPROVIDER_CLASS_MAP_KEY, copied_dataproviders) - dataproviders = getattr( cls, _DATAPROVIDER_CLASS_MAP_KEY ) + dataproviders = getattr(cls, _DATAPROVIDER_CLASS_MAP_KEY) # scan for methods with dataprovider names and add them to the map # note: this has a 'cascading' effect # where it's possible to override a super's provider with a sub's for attr_key, attr_value in cls.__dict__.items(): # can't use isinstance( attr_value, MethodType ) bc of wrapping - if( ( callable( attr_value ) ) and - ( not attr_key.startswith( "__" ) ) and - ( getattr( attr_value, _DATAPROVIDER_METHOD_NAME_KEY, None ) ) ): - name = getattr( attr_value, _DATAPROVIDER_METHOD_NAME_KEY ) - dataproviders[ name ] = attr_value + if((callable(attr_value)) and + (not attr_key.startswith("__")) and + (getattr(attr_value, _DATAPROVIDER_METHOD_NAME_KEY, None))): + name = getattr(attr_value, _DATAPROVIDER_METHOD_NAME_KEY) + dataproviders[name] = attr_value return cls -def dataprovider_factory( name, settings=None ): +def dataprovider_factory(name, settings=None): """ Wraps a class method and marks it as a dataprovider factory and creates a function to parse query strings to __init__ arguments as the @@ -98,40 +98,40 @@ def dataprovider_factory( name, settings=None ): # callable like: # settings_dict = dataproviders[ provider_name ].parse_query_string_settings( query_kwargs ) # TODO: ugh - overly complicated but the best I could think of - def parse_query_string_settings( query_kwargs ): - return _parse_query_string_settings( query_kwargs, settings ) + def parse_query_string_settings(query_kwargs): + return _parse_query_string_settings(query_kwargs, settings) - def named_dataprovider_factory( func ): - setattr( func, _DATAPROVIDER_METHOD_NAME_KEY, name ) + def named_dataprovider_factory(func): + setattr(func, _DATAPROVIDER_METHOD_NAME_KEY, name) - setattr( func, 'parse_query_string_settings', parse_query_string_settings ) - setattr( func, 'settings', settings ) + setattr(func, 'parse_query_string_settings', parse_query_string_settings) + setattr(func, 'settings', settings) # TODO: I want a way to inherit settings from the previous provider( this_name ) instead of defining over and over - @wraps( func ) - def wrapped_dataprovider_factory( self, *args, **kwargs ): - return func( self, *args, **kwargs ) + @wraps(func) + def wrapped_dataprovider_factory(self, *args, **kwargs): + return func(self, *args, **kwargs) return wrapped_dataprovider_factory return named_dataprovider_factory -def _parse_query_string_settings( query_kwargs, settings=None ): +def _parse_query_string_settings(query_kwargs, settings=None): """ Parse the values in `query_kwargs` from strings to the proper types listed in the same key in `settings`. """ # TODO: this was a relatively late addition: review and re-think - def list_from_query_string( s ): + def list_from_query_string(s): # assume csv - return s.split( ',' ) + return s.split(',') parsers = { 'int' : int, 'float' : float, 'bool' : bool, - 'list:str' : lambda s: list_from_query_string( s ), - 'list:escaped' : lambda s: [ unquote( e ) for e in list_from_query_string( s ) ], - 'list:int' : lambda s: [ int( i ) for i in list_from_query_string( s ) ], + 'list:str' : lambda s: list_from_query_string(s), + 'list:escaped' : lambda s: [unquote(e) for e in list_from_query_string(s)], + 'list:int' : lambda s: [int(i) for i in list_from_query_string(s)], } settings = settings or {} # yay! yet another set of query string parsers! <-- sarcasm @@ -141,13 +141,13 @@ def list_from_query_string( s ): for key in settings: if key in query_kwargs: # TODO: this would be the place to sanitize any strings - query_value = query_kwargs[ key ] - needed_type = settings[ key ] + query_value = query_kwargs[key] + needed_type = settings[key] if needed_type != 'str': try: - query_kwargs[ key ] = parsers[ needed_type ]( query_value ) - except ( KeyError, ValueError ): - del query_kwargs[ key ] + query_kwargs[key] = parsers[needed_type](query_value) + except (KeyError, ValueError): + del query_kwargs[key] # TODO:?? do we want to remove query_kwarg entries NOT in settings? return query_kwargs diff --git a/lib/galaxy/datatypes/dataproviders/exceptions.py b/lib/galaxy/datatypes/dataproviders/exceptions.py index c9e8e1dbcf36..2c7906c4da56 100644 --- a/lib/galaxy/datatypes/dataproviders/exceptions.py +++ b/lib/galaxy/datatypes/dataproviders/exceptions.py @@ -3,16 +3,17 @@ """ -class InvalidDataProviderSource( TypeError ): +class InvalidDataProviderSource(TypeError): """ Raised when a unusable source is passed to a provider. """ - def __init__( self, source=None, msg='' ): - msg = msg or 'Invalid source for provider: %s' % ( source ) - super( InvalidDataProviderSource, self ).__init__( msg ) + def __init__(self, source=None, msg=''): + msg = msg or 'Invalid source for provider: %s' % (source) + super(InvalidDataProviderSource, self).__init__(msg) -class NoProviderAvailable( TypeError ): + +class NoProviderAvailable(TypeError): """ Raised when no provider is found for the given `format_requested`. @@ -25,10 +26,11 @@ class NoProviderAvailable( TypeError ): Meant to be used within a class that builds dataproviders (e.g. a Datatype) """ - def __init__( self, factory_source, format_requested=None, msg='' ): + + def __init__(self, factory_source, format_requested=None, msg=''): self.factory_source = factory_source self.format_requested = format_requested - msg = msg or 'No provider available in factory_source "%s" for format requested' % ( str( factory_source ) ) + msg = msg or 'No provider available in factory_source "%s" for format requested' % (str(factory_source)) if self.format_requested: - msg += ': "%s"' % ( self.format_requested ) - super( NoProviderAvailable, self ).__init__( msg ) + msg += ': "%s"' % (self.format_requested) + super(NoProviderAvailable, self).__init__(msg) diff --git a/lib/galaxy/datatypes/dataproviders/external.py b/lib/galaxy/datatypes/dataproviders/external.py index 81751e5c9635..888fe2a3905c 100644 --- a/lib/galaxy/datatypes/dataproviders/external.py +++ b/lib/galaxy/datatypes/dataproviders/external.py @@ -21,81 +21,83 @@ admin: admin server log rgx/stats, ps aux """ -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) # ----------------------------------------------------------------------------- server subprocess / external prog -class SubprocessDataProvider( base.DataProvider ): +class SubprocessDataProvider(base.DataProvider): """ Data provider that uses the output from an intermediate program and subprocess as its data source. """ # TODO: need better ways of checking returncode, stderr for errors and raising - def __init__( self, *args, **kwargs ): + + def __init__(self, *args, **kwargs): """ :param args: the list of strings used to build commands. :type args: variadic function args """ self.exit_code = None command_list = args - self.popen = self.subprocess( *command_list, **kwargs ) + self.popen = self.subprocess(*command_list, **kwargs) # TODO:?? not communicate()? - super( SubprocessDataProvider, self ).__init__( self.popen.stdout ) + super(SubprocessDataProvider, self).__init__(self.popen.stdout) self.exit_code = self.popen.poll() # NOTE: there's little protection here v. sending a ';' and a dangerous command here # but...we're all adults here, right? ...RIGHT?! - def subprocess( self, *command_list, **kwargs ): + def subprocess(self, *command_list, **kwargs): """ :param args: the list of strings used as commands. :type args: variadic function args """ try: # how expensive is this? - popen = subprocess.Popen( command_list, stderr=subprocess.PIPE, stdout=subprocess.PIPE ) - log.info( 'opened subrocess (%s), PID: %s' % ( str( command_list ), str( popen.pid ) ) ) + popen = subprocess.Popen(command_list, stderr=subprocess.PIPE, stdout=subprocess.PIPE) + log.info('opened subrocess (%s), PID: %s' % (str(command_list), str(popen.pid))) except OSError as os_err: - command_str = ' '.join( self.command ) - raise OSError( ' '.join([ str( os_err ), ':', command_str ]) ) + command_str = ' '.join(self.command) + raise OSError(' '.join([str(os_err), ':', command_str])) return popen - def __exit__( self, *args ): + def __exit__(self, *args): # poll the subrocess for an exit code self.exit_code = self.popen.poll() - log.info( '%s.__exit__, exit_code: %s' % ( str( self ), str( self.exit_code ) ) ) - return super( SubprocessDataProvider, self ).__exit__( *args ) + log.info('%s.__exit__, exit_code: %s' % (str(self), str(self.exit_code))) + return super(SubprocessDataProvider, self).__exit__(*args) - def __str__( self ): + def __str__(self): # provide the pid and current return code source_str = '' - if hasattr( self, 'popen' ): - source_str = '%s:%s' % ( str( self.popen.pid ), str( self.popen.poll() ) ) - return '%s(%s)' % ( self.__class__.__name__, str( source_str ) ) + if hasattr(self, 'popen'): + source_str = '%s:%s' % (str(self.popen.pid), str(self.popen.poll())) + return '%s(%s)' % (self.__class__.__name__, str(source_str)) -class RegexSubprocessDataProvider( line.RegexLineDataProvider ): +class RegexSubprocessDataProvider(line.RegexLineDataProvider): """ RegexLineDataProvider that uses a SubprocessDataProvider as its data source. """ # this is a conv. class and not really all that necc... - def __init__( self, *args, **kwargs ): + + def __init__(self, *args, **kwargs): # using subprocess as proxy data source in filtered line prov. - subproc_provider = SubprocessDataProvider( *args ) - super( RegexSubprocessDataProvider, self ).__init__( subproc_provider, **kwargs ) + subproc_provider = SubprocessDataProvider(*args) + super(RegexSubprocessDataProvider, self).__init__(subproc_provider, **kwargs) # ----------------------------------------------------------------------------- other apis -class URLDataProvider( base.DataProvider ): +class URLDataProvider(base.DataProvider): """ Data provider that uses the contents of a URL for its data source. This can be piped through other providers (column, map, genome region, etc.). """ - VALID_METHODS = ( 'GET', 'POST' ) + VALID_METHODS = ('GET', 'POST') - def __init__( self, url, method='GET', data=None, **kwargs ): + def __init__(self, url, method='GET', data=None, **kwargs): """ :param url: the base URL to open. :param method: the HTTP method to use. @@ -108,59 +110,61 @@ def __init__( self, url, method='GET', data=None, **kwargs ): self.method = method self.data = data or {} - encoded_data = urlencode( self.data ) + encoded_data = urlencode(self.data) if method == 'GET': - self.url += '?%s' % ( encoded_data ) - opened = urlopen( url ) + self.url += '?%s' % (encoded_data) + opened = urlopen(url) elif method == 'POST': - opened = urlopen( url, encoded_data ) + opened = urlopen(url, encoded_data) else: - raise ValueError( 'Not a valid method: %s' % ( method ) ) + raise ValueError('Not a valid method: %s' % (method)) - super( URLDataProvider, self ).__init__( opened, **kwargs ) + super(URLDataProvider, self).__init__(opened, **kwargs) # NOTE: the request object is now accessible as self.source - def __enter__( self ): + def __enter__(self): pass - def __exit__( self, *args ): + def __exit__(self, *args): self.source.close() # ----------------------------------------------------------------------------- generic compression -class GzipDataProvider( base.DataProvider ): +class GzipDataProvider(base.DataProvider): """ Data provider that uses g(un)zip on a file as its source. This can be piped through other providers (column, map, genome region, etc.). """ - def __init__( self, source, **kwargs ): - unzipped = gzip.GzipFile( source, 'rb' ) - super( GzipDataProvider, self ).__init__( unzipped, **kwargs ) + + def __init__(self, source, **kwargs): + unzipped = gzip.GzipFile(source, 'rb') + super(GzipDataProvider, self).__init__(unzipped, **kwargs) # NOTE: the GzipFile is now accessible in self.source # ----------------------------------------------------------------------------- intermediate tempfile -class TempfileDataProvider( base.DataProvider ): +class TempfileDataProvider(base.DataProvider): """ Writes the data from the given source to a temp file, allowing it to be used as a source where a file_name is needed (e.g. as a parameter to a command line tool: samtools view -t ) """ - def __init__( self, source, **kwargs ): + + def __init__(self, source, **kwargs): # TODO: raise NotImplementedError() # write the file here self.create_file - super( TempfileDataProvider, self ).__init__( self.tmp_file, **kwargs ) + super(TempfileDataProvider, self).__init__(self.tmp_file, **kwargs) - def create_file( self ): + def create_file(self): self.tmp_file = tempfile.NamedTemporaryFile() return self.tmp_file - def write_to_file( self ): - parent_gen = super( TempfileDataProvider, self ).__iter__() - with open( self.tmp_file, 'w' ) as open_file: + def write_to_file(self): + parent_gen = super(TempfileDataProvider, self).__iter__() + with open(self.tmp_file, 'w') as open_file: for datum in parent_gen: - open_file.write( datum + '\n' ) + open_file.write(datum + '\n') diff --git a/lib/galaxy/datatypes/dataproviders/hierarchy.py b/lib/galaxy/datatypes/dataproviders/hierarchy.py index 6626f5c19a28..e5e76134abfe 100644 --- a/lib/galaxy/datatypes/dataproviders/hierarchy.py +++ b/lib/galaxy/datatypes/dataproviders/hierarchy.py @@ -12,24 +12,25 @@ _TODO = """ """ -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) # ----------------------------------------------------------------------------- hierarchal/tree data providers -class HierarchalDataProvider( line.BlockDataProvider ): +class HierarchalDataProvider(line.BlockDataProvider): """ Class that uses formats where a datum may have a parent or children data. e.g. XML, HTML, GFF3, Phylogenetic """ - def __init__( self, source, **kwargs ): + + def __init__(self, source, **kwargs): # TODO: (and defer to better (than I can write) parsers for each subtype) - super( HierarchalDataProvider, self ).__init__( source, **kwargs ) + super(HierarchalDataProvider, self).__init__(source, **kwargs) # ----------------------------------------------------------------------------- xml -class XMLDataProvider( HierarchalDataProvider ): +class XMLDataProvider(HierarchalDataProvider): """ Data provider that converts selected XML elements to dictionaries. """ @@ -39,10 +40,10 @@ class XMLDataProvider( HierarchalDataProvider ): 'selector' : 'str', # urlencoded 'max_depth' : 'int', } - ITERPARSE_ALL_EVENTS = ( 'start', 'end', 'start-ns', 'end-ns' ) + ITERPARSE_ALL_EVENTS = ('start', 'end', 'start-ns', 'end-ns') # TODO: move appropo into super - def __init__( self, source, selector=None, max_depth=None, **kwargs ): + def __init__(self, source, selector=None, max_depth=None, **kwargs): """ :param selector: some partial string in the desired tags to return :param max_depth: the number of generations of descendents to return @@ -51,9 +52,9 @@ def __init__( self, source, selector=None, max_depth=None, **kwargs ): self.max_depth = max_depth self.namespaces = {} - super( XMLDataProvider, self ).__init__( source, **kwargs ) + super(XMLDataProvider, self).__init__(source, **kwargs) - def matches_selector( self, element, selector=None ): + def matches_selector(self, element, selector=None): """ Returns true if the ``element`` matches the ``selector``. @@ -66,10 +67,10 @@ def matches_selector( self, element, selector=None ): # TODO: add more flexibility here w/o re-implementing xpath # TODO: fails with '#' - browser thinks it's an anchor - use urlencode # TODO: need removal/replacement of etree namespacing here - then move to string match - return bool( ( selector is None ) or - ( isinstance( element, Element ) and selector in element.tag ) ) + return bool((selector is None) or + (isinstance(element, Element) and selector in element.tag)) - def element_as_dict( self, element ): + def element_as_dict(self, element): """ Converts an XML element (its text, tag, and attributes) to dictionary form. @@ -80,54 +81,54 @@ def element_as_dict( self, element ): 'tag' : element.tag, 'text' : element.text.strip() if element.text else None, # needs shallow copy to protect v. element.clear() - 'attrib' : dict( element.attrib ) + 'attrib' : dict(element.attrib) } - def get_children( self, element, max_depth=None ): + def get_children(self, element, max_depth=None): """ Yield all children of element (and their children - recursively) in dictionary form. :param element: an XML ``Element`` :param max_depth: the number of generations of descendents to return """ - if not isinstance( max_depth, int ) or max_depth >= 1: + if not isinstance(max_depth, int) or max_depth >= 1: for child in element: - child_data = self.element_as_dict( child ) + child_data = self.element_as_dict(child) - next_depth = max_depth - 1 if isinstance( max_depth, int ) else None - grand_children = list( self.get_children( child, next_depth ) ) + next_depth = max_depth - 1 if isinstance(max_depth, int) else None + grand_children = list(self.get_children(child, next_depth)) if grand_children: - child_data[ 'children' ] = grand_children + child_data['children'] = grand_children yield child_data - def __iter__( self ): - context = iterparse( self.source, events=self.ITERPARSE_ALL_EVENTS ) - context = iter( context ) + def __iter__(self): + context = iterparse(self.source, events=self.ITERPARSE_ALL_EVENTS) + context = iter(context) selected_element = None for event, element in context: if event == 'start-ns': ns, uri = element - self.namespaces[ ns ] = uri + self.namespaces[ns] = uri elif event == 'start': - if( ( selected_element is None ) and - ( self.matches_selector( element, self.selector ) ) ): + if((selected_element is None) and + (self.matches_selector(element, self.selector))): # start tag of selected element - wait for 'end' to emit/yield selected_element = element elif event == 'end': - if( ( selected_element is not None ) and ( element == selected_element ) ): + if((selected_element is not None) and (element == selected_element)): self.num_valid_data_read += 1 # offset if self.num_valid_data_read > self.offset: # convert to dict and yield - selected_element_dict = self.element_as_dict( selected_element ) - children = list( self.get_children( selected_element, self.max_depth ) ) + selected_element_dict = self.element_as_dict(selected_element) + children = list(self.get_children(selected_element, self.max_depth)) if children: - selected_element_dict[ 'children' ] = children + selected_element_dict['children'] = children yield selected_element_dict # limit diff --git a/lib/galaxy/datatypes/dataproviders/line.py b/lib/galaxy/datatypes/dataproviders/line.py index 652bd408d677..e9244ebca54a 100644 --- a/lib/galaxy/datatypes/dataproviders/line.py +++ b/lib/galaxy/datatypes/dataproviders/line.py @@ -7,7 +7,7 @@ from . import base -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) _TODO = """ line offsets (skip to some place in a file) needs to work more efficiently than simply iterating till we're there @@ -17,7 +17,7 @@ def stop( self ): self.endpoint = source.tell(); raise StopIteration() """ -class FilteredLineDataProvider( base.LimitedOffsetDataProvider ): +class FilteredLineDataProvider(base.LimitedOffsetDataProvider): """ Data provider that yields lines of data from its source allowing optional control over which line to start on and how many lines @@ -31,8 +31,8 @@ class FilteredLineDataProvider( base.LimitedOffsetDataProvider ): 'comment_char' : 'str', } - def __init__( self, source, strip_lines=True, strip_newlines=False, provide_blank=False, - comment_char=DEFAULT_COMMENT_CHAR, **kwargs ): + def __init__(self, source, strip_lines=True, strip_newlines=False, provide_blank=False, + comment_char=DEFAULT_COMMENT_CHAR, **kwargs): """ :param strip_lines: remove whitespace from the beginning an ending of each line (or not). @@ -53,13 +53,13 @@ def __init__( self, source, strip_lines=True, strip_newlines=False, provide_blan Optional: defaults to '#' :type comment_char: str """ - super( FilteredLineDataProvider, self ).__init__( source, **kwargs ) + super(FilteredLineDataProvider, self).__init__(source, **kwargs) self.strip_lines = strip_lines self.strip_newlines = strip_newlines self.provide_blank = provide_blank self.comment_char = comment_char - def filter( self, line ): + def filter(self, line): """ Determines whether to provide line or not. @@ -72,16 +72,16 @@ def filter( self, line ): if self.strip_lines: line = line.strip() elif self.strip_newlines: - line = line.strip( '\n' ) + line = line.strip('\n') if not self.provide_blank and line == '': return None - elif self.comment_char and line.startswith( self.comment_char ): + elif self.comment_char and line.startswith(self.comment_char): return None - return super( FilteredLineDataProvider, self ).filter( line ) + return super(FilteredLineDataProvider, self).filter(line) -class RegexLineDataProvider( FilteredLineDataProvider ): +class RegexLineDataProvider(FilteredLineDataProvider): """ Data provider that yields only those lines of data from its source that do (or do not when `invert` is True) match one or more of the given list @@ -95,7 +95,7 @@ class RegexLineDataProvider( FilteredLineDataProvider ): 'invert' : 'bool', } - def __init__( self, source, regex_list=None, invert=False, **kwargs ): + def __init__(self, source, regex_list=None, invert=False, **kwargs): """ :param regex_list: list of strings or regular expression strings that will be `match`ed to each line @@ -106,22 +106,22 @@ def __init__( self, source, regex_list=None, invert=False, **kwargs ): Optional: defaults to False :type invert: bool """ - super( RegexLineDataProvider, self ).__init__( source, **kwargs ) + super(RegexLineDataProvider, self).__init__(source, **kwargs) - self.regex_list = regex_list if isinstance( regex_list, list ) else [] - self.compiled_regex_list = [ re.compile( regex ) for regex in self.regex_list ] + self.regex_list = regex_list if isinstance(regex_list, list) else [] + self.compiled_regex_list = [re.compile(regex) for regex in self.regex_list] self.invert = invert # NOTE: no support for flags - def filter( self, line ): + def filter(self, line): # NOTE: filter_fn will occur BEFORE any matching - line = super( RegexLineDataProvider, self ).filter( line ) + line = super(RegexLineDataProvider, self).filter(line) if line is not None and self.compiled_regex_list: - line = self.filter_by_regex( line ) + line = self.filter_by_regex(line) return line - def filter_by_regex( self, line ): - matches = any([ regex.match( line ) for regex in self.compiled_regex_list ]) + def filter_by_regex(self, line): + matches = any([regex.match(line) for regex in self.compiled_regex_list]) if self.invert: return line if not matches else None return line if matches else None @@ -129,7 +129,7 @@ def filter_by_regex( self, line ): # ============================================================================= MICELLAINEOUS OR UNIMPLEMENTED # ----------------------------------------------------------------------------- block data providers -class BlockDataProvider( base.LimitedOffsetDataProvider ): +class BlockDataProvider(base.LimitedOffsetDataProvider): """ Class that uses formats where multiple lines combine to describe a single datum. The data output will be a list of either map/dicts or sub-arrays. @@ -139,7 +139,8 @@ class BlockDataProvider( base.LimitedOffsetDataProvider ): e.g. Fasta, GenBank, MAF, hg log Note: mem intensive (gathers list of lines before output) """ - def __init__( self, source, new_block_delim_fn=None, block_filter_fn=None, **kwargs ): + + def __init__(self, source, new_block_delim_fn=None, block_filter_fn=None, **kwargs): """ :param new_block_delim_fn: T/F function to determine whether a given line is the start of a new block. @@ -152,27 +153,27 @@ def __init__( self, source, new_block_delim_fn=None, block_filter_fn=None, **kwa """ # composition - not inheritance # TODO: not a fan of this: - ( filter_fn, limit, offset ) = ( kwargs.pop( 'filter_fn', None ), - kwargs.pop( 'limit', None ), kwargs.pop( 'offset', 0 ) ) - line_provider = FilteredLineDataProvider( source, **kwargs ) - super( BlockDataProvider, self ).__init__( line_provider, filter_fn=filter_fn, limit=limit, offset=offset ) + (filter_fn, limit, offset) = (kwargs.pop('filter_fn', None), + kwargs.pop('limit', None), kwargs.pop('offset', 0)) + line_provider = FilteredLineDataProvider(source, **kwargs) + super(BlockDataProvider, self).__init__(line_provider, filter_fn=filter_fn, limit=limit, offset=offset) self.new_block_delim_fn = new_block_delim_fn self.block_filter_fn = block_filter_fn self.init_new_block() - def init_new_block( self ): + def init_new_block(self): """ Set up internal data for next block. """ # called in __init__ and after yielding the prev. block self.block_lines = collections.deque([]) - def __iter__( self ): + def __iter__(self): """ Overridden to provide last block. """ - parent_gen = super( BlockDataProvider, self ).__iter__() + parent_gen = super(BlockDataProvider, self).__iter__() for block in parent_gen: yield block @@ -181,7 +182,7 @@ def __iter__( self ): self.num_data_returned += 1 yield last_block - def filter( self, line ): + def filter(self, line): """ Line filter here being used to aggregate/assemble lines into a block and determine whether the line indicates a new block. @@ -190,32 +191,32 @@ def filter( self, line ): :type line: str :returns: a block or `None` """ - line = super( BlockDataProvider, self ).filter( line ) + line = super(BlockDataProvider, self).filter(line) # TODO: HACK self.num_data_read -= 1 if line is None: return None block_to_return = None - if self.is_new_block( line ): + if self.is_new_block(line): # if we're already in a block, return the prev. block and add the line to a new block if self.block_lines: block_to_return = self.assemble_current_block() - block_to_return = self.filter_block( block_to_return ) + block_to_return = self.filter_block(block_to_return) self.num_data_read += 1 self.init_new_block() - self.add_line_to_block( line ) + self.add_line_to_block(line) return block_to_return - def is_new_block( self, line ): + def is_new_block(self, line): """ Returns True if the given line indicates the start of a new block (and the current block should be provided) or False if not. """ if self.new_block_delim_fn: - return self.new_block_delim_fn( line ) + return self.new_block_delim_fn(line) return True # NOTE: @@ -223,7 +224,7 @@ def is_new_block( self, line ): # some formats rely on having access to multiple lines to make sensible data # So, building the block from the lines can happen in either: # add_line_to_block AND/OR assemble_current_block - def add_line_to_block( self, line ): + def add_line_to_block(self, line): """ Integrate the given line into the current block. @@ -232,28 +233,28 @@ def add_line_to_block( self, line ): # here either: # consume the line (using it to add attrs to self.block) # save the line (appending to self.block_lines) for use in assemble_current_block - self.block_lines.append( line ) + self.block_lines.append(line) - def assemble_current_block( self ): + def assemble_current_block(self): """ Build the current data into a block. Called per block (just before providing). """ # empty block_lines and assemble block - return list( ( self.block_lines.popleft() for i in range( len( self.block_lines ) ) ) ) + return list((self.block_lines.popleft() for i in range(len(self.block_lines)))) - def filter_block( self, block ): + def filter_block(self, block): """ Is the current block a valid/desired datum. Called per block (just before providing). """ if self.block_filter_fn: - return self.block_filter_fn( block ) + return self.block_filter_fn(block) return block - def handle_last_block( self ): + def handle_last_block(self): """ Handle any blocks remaining after the main loop. """ @@ -263,7 +264,7 @@ def handle_last_block( self ): last_block = self.assemble_current_block() self.num_data_read += 1 - last_block = self.filter_block( last_block ) + last_block = self.filter_block(last_block) if last_block is not None: self.num_valid_data_read += 1 diff --git a/lib/galaxy/datatypes/display_applications/application.py b/lib/galaxy/datatypes/display_applications/application.py index 0dfc3dd014fe..1759241b73d8 100644 --- a/lib/galaxy/datatypes/display_applications/application.py +++ b/lib/galaxy/datatypes/display_applications/application.py @@ -20,31 +20,31 @@ ) from .util import encode_dataset_user -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) # Any basic functions that we want to provide as a basic part of parameter dict should be added to this dict -BASE_PARAMS = { 'qp': quote_plus, 'url_for': url_for } +BASE_PARAMS = {'qp': quote_plus, 'url_for': url_for} -class DisplayApplicationLink( object ): +class DisplayApplicationLink(object): @classmethod - def from_elem( cls, elem, display_application, other_values=None ): - rval = DisplayApplicationLink( display_application ) - rval.id = elem.get( 'id', None ) + def from_elem(cls, elem, display_application, other_values=None): + rval = DisplayApplicationLink(display_application) + rval.id = elem.get('id', None) assert rval.id, 'Link elements require a id.' - rval.name = elem.get( 'name', rval.id ) - rval.url = elem.find( 'url' ) + rval.name = elem.get('name', rval.id) + rval.url = elem.find('url') assert rval.url is not None, 'A url element must be provided for link elements.' rval.other_values = other_values - rval.filters = elem.findall( 'filter' ) - for param_elem in elem.findall( 'param' ): - param = DisplayApplicationParameter.from_elem( param_elem, rval ) + rval.filters = elem.findall('filter') + for param_elem in elem.findall('param'): + param = DisplayApplicationParameter.from_elem(param_elem, rval) assert param, 'Unable to load parameter from element: %s' % param_elem - rval.parameters[ param.name ] = param - rval.url_param_name_map[ param.url ] = param.name + rval.parameters[param.name] = param + rval.url_param_name_map[param.url] = param.name return rval - def __init__( self, display_application ): + def __init__(self, display_application): self.display_application = display_application self.parameters = odict() # parameters are populated in order, allowing lower listed ones to have values of higher listed ones self.url_param_name_map = {} @@ -52,176 +52,176 @@ def __init__( self, display_application ): self.id = None self.name = None - def get_display_url( self, data, trans ): - dataset_hash, user_hash = encode_dataset_user( trans, data, None ) - return url_for( controller='dataset', - action="display_application", - dataset_id=dataset_hash, - user_id=user_hash, - app_name=quote_plus( self.display_application.id ), - link_name=quote_plus( self.id ), - app_action=None ) - - def get_inital_values( self, data, trans ): + def get_display_url(self, data, trans): + dataset_hash, user_hash = encode_dataset_user(trans, data, None) + return url_for(controller='dataset', + action="display_application", + dataset_id=dataset_hash, + user_id=user_hash, + app_name=quote_plus(self.display_application.id), + link_name=quote_plus(self.id), + app_action=None) + + def get_inital_values(self, data, trans): if self.other_values: - rval = odict( self.other_values ) + rval = odict(self.other_values) else: rval = odict() - rval.update( { 'BASE_URL': trans.request.base, 'APP': trans.app } ) # trans automatically appears as a response, need to add properties of trans that we want here + rval.update({'BASE_URL': trans.request.base, 'APP': trans.app}) # trans automatically appears as a response, need to add properties of trans that we want here for key, value in BASE_PARAMS.items(): # add helper functions/variables - rval[ key ] = value - rval[ DEFAULT_DATASET_NAME ] = data # always have the display dataset name available + rval[key] = value + rval[DEFAULT_DATASET_NAME] = data # always have the display dataset name available return rval - def build_parameter_dict( self, data, dataset_hash, user_hash, trans, app_kwds ): - other_values = self.get_inital_values( data, trans ) - other_values[ 'DATASET_HASH' ] = dataset_hash - other_values[ 'USER_HASH' ] = user_hash + def build_parameter_dict(self, data, dataset_hash, user_hash, trans, app_kwds): + other_values = self.get_inital_values(data, trans) + other_values['DATASET_HASH'] = dataset_hash + other_values['USER_HASH'] = user_hash ready = True for name, param in self.parameters.items(): assert name not in other_values, "The display parameter '%s' has been defined more than once." % name - if param.ready( other_values ): + if param.ready(other_values): if name in app_kwds and param.allow_override: - other_values[ name ] = app_kwds[ name ] + other_values[name] = app_kwds[name] else: - other_values[ name ] = param.get_value( other_values, dataset_hash, user_hash, trans ) # subsequent params can rely on this value + other_values[name] = param.get_value(other_values, dataset_hash, user_hash, trans) # subsequent params can rely on this value else: ready = False - other_values[ name ] = param.get_value( other_values, dataset_hash, user_hash, trans ) # subsequent params can rely on this value - if other_values[ name ] is None: + other_values[name] = param.get_value(other_values, dataset_hash, user_hash, trans) # subsequent params can rely on this value + if other_values[name] is None: # Need to stop here, next params may need this value to determine its own value return False, other_values return ready, other_values - def filter_by_dataset( self, data, trans ): - context = self.get_inital_values( data, trans ) + def filter_by_dataset(self, data, trans): + context = self.get_inital_values(data, trans) for filter_elem in self.filters: - if fill_template( filter_elem.text, context=context ) != filter_elem.get( 'value', 'True' ): + if fill_template(filter_elem.text, context=context) != filter_elem.get('value', 'True'): return False return True -class DynamicDisplayApplicationBuilder( object ): +class DynamicDisplayApplicationBuilder(object): - def __init__( self, elem, display_application, build_sites ): + def __init__(self, elem, display_application, build_sites): filename = None data_table = None - if elem.get( 'site_type', None ) is not None: - filename = build_sites.get( elem.get( 'site_type' ) ) + if elem.get('site_type', None) is not None: + filename = build_sites.get(elem.get('site_type')) else: - filename = elem.get( 'from_file', None ) + filename = elem.get('from_file', None) if filename is None: - data_table_name = elem.get( 'from_data_table', None ) + data_table_name = elem.get('from_data_table', None) if data_table_name: - data_table = display_application.app.tool_data_tables.get( data_table_name, None ) + data_table = display_application.app.tool_data_tables.get(data_table_name, None) assert data_table is not None, 'Unable to find data table named "%s".' % data_table_name assert filename is not None or data_table is not None, 'Filename or data Table is required for dynamic_links.' - skip_startswith = elem.get( 'skip_startswith', None ) - separator = elem.get( 'separator', '\t' ) - id_col = elem.get( 'id', None ) + skip_startswith = elem.get('skip_startswith', None) + separator = elem.get('separator', '\t') + id_col = elem.get('id', None) try: - id_col = int( id_col ) + id_col = int(id_col) except: if data_table: if id_col is None: - id_col = data_table.columns.get( 'id', None ) + id_col = data_table.columns.get('id', None) if id_col is None: - id_col = data_table.columns.get( 'value', None ) + id_col = data_table.columns.get('value', None) try: - id_col = int( id_col ) + id_col = int(id_col) except: # id is set to a string or None, use column by that name if available - id_col = data_table.columns.get( id_col, None ) - id_col = int( id_col ) - name_col = elem.get( 'name', None ) + id_col = data_table.columns.get(id_col, None) + id_col = int(id_col) + name_col = elem.get('name', None) try: - name_col = int( name_col ) + name_col = int(name_col) except: if data_table: if name_col is None: - name_col = data_table.columns.get( 'name', None ) + name_col = data_table.columns.get('name', None) else: - name_col = data_table.columns.get( name_col, None ) + name_col = data_table.columns.get(name_col, None) else: name_col = None if name_col is None: name_col = id_col - max_col = max( id_col, name_col ) + max_col = max(id_col, name_col) dynamic_params = {} if data_table is not None: - max_col = max( [ max_col ] + list(data_table.columns.values()) ) + max_col = max([max_col] + list(data_table.columns.values())) for key, value in data_table.columns.items(): - dynamic_params[key] = { 'column': value, 'split': False, 'separator': ',' } - for dynamic_param in elem.findall( 'dynamic_param' ): - name = dynamic_param.get( 'name' ) - value = int( dynamic_param.get( 'value' ) ) - split = string_as_bool( dynamic_param.get( 'split', False ) ) - param_separator = dynamic_param.get( 'separator', ',' ) - max_col = max( max_col, value ) - dynamic_params[name] = { 'column': value, 'split': split, 'separator': param_separator } + dynamic_params[key] = {'column': value, 'split': False, 'separator': ','} + for dynamic_param in elem.findall('dynamic_param'): + name = dynamic_param.get('name') + value = int(dynamic_param.get('value')) + split = string_as_bool(dynamic_param.get('split', False)) + param_separator = dynamic_param.get('separator', ',') + max_col = max(max_col, value) + dynamic_params[name] = {'column': value, 'split': split, 'separator': param_separator} if filename: - data_iter = open( filename ) + data_iter = open(filename) elif data_table: version, data_iter = data_table.get_version_fields() - display_application.add_data_table_watch( data_table.name, version ) + display_application.add_data_table_watch(data_table.name, version) links = [] for line in data_iter: - if isinstance( line, string_types ): - if not skip_startswith or not line.startswith( skip_startswith ): - line = line.rstrip( '\n\r' ) + if isinstance(line, string_types): + if not skip_startswith or not line.startswith(skip_startswith): + line = line.rstrip('\n\r') if not line: continue - fields = line.split( separator ) + fields = line.split(separator) else: continue else: fields = line - if len( fields ) > max_col: - new_elem = deepcopy( elem ) - new_elem.set( 'id', fields[id_col] ) - new_elem.set( 'name', fields[name_col] ) + if len(fields) > max_col: + new_elem = deepcopy(elem) + new_elem.set('id', fields[id_col]) + new_elem.set('name', fields[name_col]) dynamic_values = {} for key, attributes in dynamic_params.items(): - value = fields[ attributes[ 'column' ] ] + value = fields[attributes['column']] if attributes['split']: - value = value.split( attributes['separator'] ) + value = value.split(attributes['separator']) dynamic_values[key] = value # now populate - links.append( DisplayApplicationLink.from_elem( new_elem, display_application, other_values=dynamic_values ) ) + links.append(DisplayApplicationLink.from_elem(new_elem, display_application, other_values=dynamic_values)) else: - log.warning( 'Invalid dynamic display application link specified in %s: "%s"' % ( filename, line ) ) + log.warning('Invalid dynamic display application link specified in %s: "%s"' % (filename, line)) self.links = links - def __iter__( self ): - return iter( self.links ) + def __iter__(self): + return iter(self.links) -class PopulatedDisplayApplicationLink( object ): - def __init__( self, display_application_link, data, dataset_hash, user_hash, trans, app_kwds ): +class PopulatedDisplayApplicationLink(object): + def __init__(self, display_application_link, data, dataset_hash, user_hash, trans, app_kwds): self.link = display_application_link self.data = data self.dataset_hash = dataset_hash self.user_hash = user_hash self.trans = trans - self.ready, self.parameters = self.link.build_parameter_dict( self.data, self.dataset_hash, self.user_hash, trans, app_kwds ) + self.ready, self.parameters = self.link.build_parameter_dict(self.data, self.dataset_hash, self.user_hash, trans, app_kwds) - def display_ready( self ): + def display_ready(self): return self.ready - def get_param_value( self, name ): + def get_param_value(self, name): value = None if self.ready: - value = self.parameters.get( name, None ) + value = self.parameters.get(name, None) assert value, 'Unknown parameter requested' return value - def preparing_display( self ): + def preparing_display(self): if not self.ready: - return self.link.parameters[ list(self.parameters.keys())[-1] ].is_preparing( self.parameters ) + return self.link.parameters[list(self.parameters.keys())[-1]].is_preparing(self.parameters) return False - def prepare_display( self ): + def prepare_display(self): rval = [] found_last = False if not self.ready and not self.preparing_display(): @@ -229,55 +229,55 @@ def prepare_display( self ): for name, param in self.link.parameters.items(): if found_last or list(other_values.keys())[-1] == name: # found last parameter to be populated found_last = True - value = param.prepare( other_values, self.dataset_hash, self.user_hash, self.trans ) - rval.append( { 'name': name, 'value': value, 'param': param } ) - other_values[ name ] = value + value = param.prepare(other_values, self.dataset_hash, self.user_hash, self.trans) + rval.append({'name': name, 'value': value, 'param': param}) + other_values[name] = value if value is None: # We can go no further until we have a value for this parameter return rval return rval - def get_prepare_steps( self, datasets_only=True ): + def get_prepare_steps(self, datasets_only=True): rval = [] for name, param in self.link.parameters.items(): - if datasets_only and not isinstance( param, DisplayApplicationDataParameter ): + if datasets_only and not isinstance(param, DisplayApplicationDataParameter): continue - value = self.parameters.get( name, None ) - rval.append( { 'name': name, 'value': value, 'param': param, 'ready': param.ready( self.parameters ) } ) + value = self.parameters.get(name, None) + rval.append({'name': name, 'value': value, 'param': param, 'ready': param.ready(self.parameters)}) return rval - def display_url( self ): + def display_url(self): assert self.display_ready(), 'Display is not yet ready, cannot generate display link' - return fill_template( self.link.url.text, context=self.parameters ) + return fill_template(self.link.url.text, context=self.parameters) - def get_param_name_by_url( self, url ): + def get_param_name_by_url(self, url): for name, parameter in self.link.parameters.items(): - if parameter.build_url( self.parameters ) == url: + if parameter.build_url(self.parameters) == url: return name - raise ValueError( "Unknown URL parameter name provided: %s" % url ) + raise ValueError("Unknown URL parameter name provided: %s" % url) -class DisplayApplication( object ): +class DisplayApplication(object): @classmethod - def from_file( cls, filename, app ): - return cls.from_elem( parse_xml( filename ).getroot(), app, filename=filename ) + def from_file(cls, filename, app): + return cls.from_elem(parse_xml(filename).getroot(), app, filename=filename) @classmethod - def from_elem( cls, elem, app, filename=None ): - att_dict = cls._get_attributes_from_elem( elem ) - rval = DisplayApplication( att_dict['id'], att_dict['name'], app, att_dict['version'], filename=filename, elem=elem ) - rval._load_links_from_elem( elem ) + def from_elem(cls, elem, app, filename=None): + att_dict = cls._get_attributes_from_elem(elem) + rval = DisplayApplication(att_dict['id'], att_dict['name'], app, att_dict['version'], filename=filename, elem=elem) + rval._load_links_from_elem(elem) return rval @classmethod - def _get_attributes_from_elem( cls, elem ): - display_id = elem.get( 'id', None ) + def _get_attributes_from_elem(cls, elem): + display_id = elem.get('id', None) assert display_id, "ID tag is required for a Display Application" - name = elem.get( 'name', display_id ) - version = elem.get( 'version', None ) - return dict( id=display_id, name=name, version=version ) + name = elem.get('name', display_id) + version = elem.get('version', None) + return dict(id=display_id, name=name, version=version) - def __init__( self, display_id, name, app, version=None, filename=None, elem=None ): + def __init__(self, display_id, name, app, version=None, filename=None, elem=None): self.id = display_id self.name = name self.app = app @@ -289,63 +289,63 @@ def __init__( self, display_id, name, app, version=None, filename=None, elem=Non self._elem = elem self._data_table_versions = {} - def _load_links_from_elem( self, elem ): - for link_elem in elem.findall( 'link' ): - link = DisplayApplicationLink.from_elem( link_elem, self ) + def _load_links_from_elem(self, elem): + for link_elem in elem.findall('link'): + link = DisplayApplicationLink.from_elem(link_elem, self) if link: - self.links[ link.id ] = link + self.links[link.id] = link try: - for dynamic_links in elem.findall( 'dynamic_links' ): - for link in DynamicDisplayApplicationBuilder( dynamic_links, self, self.app.datatypes_registry.build_sites ): - self.links[ link.id ] = link + for dynamic_links in elem.findall('dynamic_links'): + for link in DynamicDisplayApplicationBuilder(dynamic_links, self, self.app.datatypes_registry.build_sites): + self.links[link.id] = link except Exception as e: - log.error( "Error loading a set of Dynamic Display Application links: %s", e ) + log.error("Error loading a set of Dynamic Display Application links: %s", e) - def get_link( self, link_name, data, dataset_hash, user_hash, trans, app_kwds ): + def get_link(self, link_name, data, dataset_hash, user_hash, trans, app_kwds): # returns a link object with data knowledge to generate links self._check_and_reload() - return PopulatedDisplayApplicationLink( self.links[ link_name ], data, dataset_hash, user_hash, trans, app_kwds ) + return PopulatedDisplayApplicationLink(self.links[link_name], data, dataset_hash, user_hash, trans, app_kwds) - def filter_by_dataset( self, data, trans ): + def filter_by_dataset(self, data, trans): self._check_and_reload() - filtered = DisplayApplication( self.id, self.name, self.app, version=self.version ) + filtered = DisplayApplication(self.id, self.name, self.app, version=self.version) for link_name, link_value in self.links.items(): - if link_value.filter_by_dataset( data, trans ): + if link_value.filter_by_dataset(data, trans): filtered.links[link_name] = link_value return filtered - def reload( self ): + def reload(self): if self._filename: - elem = parse_xml( self._filename ).getroot() + elem = parse_xml(self._filename).getroot() elif self._elem: elem = self._elem else: - raise Exception( "Unable to reload DisplayApplication %s." % ( self.name ) ) + raise Exception("Unable to reload DisplayApplication %s." % (self.name)) # All toolshed-specific attributes added by e.g the registry will remain - attr_dict = self._get_attributes_from_elem( elem ) + attr_dict = self._get_attributes_from_elem(elem) # We will not allow changing the id at this time (we'll need to fix several mappings upstream to handle this case) - assert attr_dict.get( 'id' ) == self.id, ValueError( "You cannot reload a Display application where the ID has changed. You will need to restart the server instead." ) + assert attr_dict.get('id') == self.id, ValueError("You cannot reload a Display application where the ID has changed. You will need to restart the server instead.") # clear old links self.links = {} # clear data table versions: self._data_table_versions = {} # Set new attributes for key, value in attr_dict.items(): - setattr( self, key, value ) + setattr(self, key, value) # Load new links - self._load_links_from_elem( elem ) + self._load_links_from_elem(elem) return self - def add_data_table_watch( self, table_name, version=None ): - self._data_table_versions[ table_name ] = version + def add_data_table_watch(self, table_name, version=None): + self._data_table_versions[table_name] = version - def _requires_reload( self ): + def _requires_reload(self): for key, value in self._data_table_versions.items(): - table = self.app.tool_data_tables.get( key, None ) - if table and not table.is_current_version( value ): + table = self.app.tool_data_tables.get(key, None) + if table and not table.is_current_version(value): return True return False - def _check_and_reload( self ): + def _check_and_reload(self): if self._requires_reload(): self.reload() diff --git a/lib/galaxy/datatypes/display_applications/parameters.py b/lib/galaxy/datatypes/display_applications/parameters.py index 97e0b4d27d3b..40e5a77c3794 100644 --- a/lib/galaxy/datatypes/display_applications/parameters.py +++ b/lib/galaxy/datatypes/display_applications/parameters.py @@ -11,241 +11,241 @@ DEFAULT_DATASET_NAME = 'dataset' -class DisplayApplicationParameter( object ): +class DisplayApplicationParameter(object): """ Abstract Class for Display Application Parameters """ type = None @classmethod - def from_elem( cls, elem, link ): - param_type = elem.get( 'type', None ) + def from_elem(cls, elem, link): + param_type = elem.get('type', None) assert param_type, 'DisplayApplicationParameter requires a type' - return parameter_type_to_class[ param_type ]( elem, link ) + return parameter_type_to_class[param_type](elem, link) - def __init__( self, elem, link ): - self.name = elem.get( 'name', None ) + def __init__(self, elem, link): + self.name = elem.get('name', None) assert self.name, 'DisplayApplicationParameter requires a name' self.link = link - self.url = elem.get( 'url', self.name ) # name used in url for display purposes defaults to name; e.g. want the form of file.ext, where a '.' is not allowed as python variable name/keyword - self.mime_type = elem.get( 'mimetype', None ) - self.guess_mime_type = string_as_bool( elem.get( 'guess_mimetype', 'False' ) ) - self.viewable = string_as_bool( elem.get( 'viewable', 'False' ) ) # only allow these to be viewed via direct url when explicitly set to viewable - self.strip = string_as_bool( elem.get( 'strip', 'False' ) ) - self.strip_https = string_as_bool( elem.get( 'strip_https', 'False' ) ) - self.allow_override = string_as_bool( elem.get( 'allow_override', 'False' ) ) # Passing query param app_= to dataset controller allows override if this is true. - - def get_value( self, other_values, dataset_hash, user_hash, trans ): + self.url = elem.get('url', self.name) # name used in url for display purposes defaults to name; e.g. want the form of file.ext, where a '.' is not allowed as python variable name/keyword + self.mime_type = elem.get('mimetype', None) + self.guess_mime_type = string_as_bool(elem.get('guess_mimetype', 'False')) + self.viewable = string_as_bool(elem.get('viewable', 'False')) # only allow these to be viewed via direct url when explicitly set to viewable + self.strip = string_as_bool(elem.get('strip', 'False')) + self.strip_https = string_as_bool(elem.get('strip_https', 'False')) + self.allow_override = string_as_bool(elem.get('allow_override', 'False')) # Passing query param app_= to dataset controller allows override if this is true. + + def get_value(self, other_values, dataset_hash, user_hash, trans): raise Exception('get_value() is unimplemented for DisplayApplicationDataParameter') - def prepare( self, other_values, dataset_hash, user_hash, trans ): - return self.get_value( other_values, dataset_hash, user_hash, trans ) + def prepare(self, other_values, dataset_hash, user_hash, trans): + return self.get_value(other_values, dataset_hash, user_hash, trans) - def ready( self, other_values ): + def ready(self, other_values): return True - def is_preparing( self, other_values ): + def is_preparing(self, other_values): return False - def build_url( self, other_values ): - return fill_template( self.url, context=other_values ) + def build_url(self, other_values): + return fill_template(self.url, context=other_values) -class DisplayApplicationDataParameter( DisplayApplicationParameter ): +class DisplayApplicationDataParameter(DisplayApplicationParameter): """ Parameter that returns a file_name containing the requested content """ type = 'data' - def __init__( self, elem, link ): - DisplayApplicationParameter.__init__( self, elem, link ) - self.extensions = elem.get( 'format', None ) + def __init__(self, elem, link): + DisplayApplicationParameter.__init__(self, elem, link) + self.extensions = elem.get('format', None) if self.extensions: - self.extensions = self.extensions.split( "," ) - self.metadata = elem.get( 'metadata', None ) - self.allow_extra_files_access = string_as_bool( elem.get( 'allow_extra_files_access', 'False' ) ) - self.dataset = elem.get( 'dataset', DEFAULT_DATASET_NAME ) # 'dataset' is default name assigned to dataset to be displayed - assert not ( self.extensions and self.metadata ), 'A format or a metadata can be defined for a DisplayApplicationParameter, but not both.' - assert not ( self.allow_extra_files_access and self.metadata ), 'allow_extra_files_access or metadata can be defined for a DisplayApplicationParameter, but not both.' - self.viewable = string_as_bool( elem.get( 'viewable', 'True' ) ) # data params should be viewable - self.force_url_param = string_as_bool( elem.get( 'force_url_param', 'False' ) ) - self.force_conversion = string_as_bool( elem.get( 'force_conversion', 'False' ) ) + self.extensions = self.extensions.split(",") + self.metadata = elem.get('metadata', None) + self.allow_extra_files_access = string_as_bool(elem.get('allow_extra_files_access', 'False')) + self.dataset = elem.get('dataset', DEFAULT_DATASET_NAME) # 'dataset' is default name assigned to dataset to be displayed + assert not (self.extensions and self.metadata), 'A format or a metadata can be defined for a DisplayApplicationParameter, but not both.' + assert not (self.allow_extra_files_access and self.metadata), 'allow_extra_files_access or metadata can be defined for a DisplayApplicationParameter, but not both.' + self.viewable = string_as_bool(elem.get('viewable', 'True')) # data params should be viewable + self.force_url_param = string_as_bool(elem.get('force_url_param', 'False')) + self.force_conversion = string_as_bool(elem.get('force_conversion', 'False')) @property - def formats( self ): + def formats(self): if self.extensions: - return tuple( map( type, map( self.link.display_application.app.datatypes_registry.get_datatype_by_extension, self.extensions ) ) ) + return tuple(map(type, map(self.link.display_application.app.datatypes_registry.get_datatype_by_extension, self.extensions))) return None - def _get_dataset_like_object( self, other_values ): + def _get_dataset_like_object(self, other_values): # this returned object has file_name, state, and states attributes equivalent to a DatasetAssociation - data = other_values.get( self.dataset, None ) + data = other_values.get(self.dataset, None) assert data, 'Base dataset could not be found in values provided to DisplayApplicationDataParameter' - if isinstance( data, DisplayDataValueWrapper ): + if isinstance(data, DisplayDataValueWrapper): data = data.value if self.metadata: - rval = getattr( data.metadata, self.metadata, None ) - assert rval, 'Unknown metadata name (%s) provided for dataset type (%s).' % ( self.metadata, data.datatype.__class__.name ) - return Bunch( file_name=rval.file_name, state=data.state, states=data.states, extension='data' ) - elif self.extensions and ( self.force_conversion or not isinstance( data.datatype, self.formats ) ): + rval = getattr(data.metadata, self.metadata, None) + assert rval, 'Unknown metadata name (%s) provided for dataset type (%s).' % (self.metadata, data.datatype.__class__.name) + return Bunch(file_name=rval.file_name, state=data.state, states=data.states, extension='data') + elif self.extensions and (self.force_conversion or not isinstance(data.datatype, self.formats)): for ext in self.extensions: - rval = data.get_converted_files_by_type( ext ) + rval = data.get_converted_files_by_type(ext) if rval: return rval - assert data.find_conversion_destination( self.formats )[0] is not None, "No conversion path found for data param: %s" % self.name + assert data.find_conversion_destination(self.formats)[0] is not None, "No conversion path found for data param: %s" % self.name return None return data - def get_value( self, other_values, dataset_hash, user_hash, trans ): - data = self._get_dataset_like_object( other_values ) + def get_value(self, other_values, dataset_hash, user_hash, trans): + data = self._get_dataset_like_object(other_values) if data: - return DisplayDataValueWrapper( data, self, other_values, dataset_hash, user_hash, trans ) + return DisplayDataValueWrapper(data, self, other_values, dataset_hash, user_hash, trans) return None - def prepare( self, other_values, dataset_hash, user_hash, trans ): - data = self._get_dataset_like_object( other_values ) + def prepare(self, other_values, dataset_hash, user_hash, trans): + data = self._get_dataset_like_object(other_values) if not data and self.formats: - data = other_values.get( self.dataset, None ) - trans.sa_session.refresh( data ) + data = other_values.get(self.dataset, None) + trans.sa_session.refresh(data) # start conversion # FIXME: Much of this is copied (more than once...); should be some abstract method elsewhere called from here # find target ext - target_ext, converted_dataset = data.find_conversion_destination( self.formats, converter_safe=True ) + target_ext, converted_dataset = data.find_conversion_destination(self.formats, converter_safe=True) if target_ext and not converted_dataset: - if isinstance( data, DisplayDataValueWrapper ): + if isinstance(data, DisplayDataValueWrapper): data = data.value - new_data = next(iter(data.datatype.convert_dataset( trans, data, target_ext, return_output=True, visible=False ).values())) + new_data = next(iter(data.datatype.convert_dataset(trans, data, target_ext, return_output=True, visible=False).values())) new_data.hid = data.hid new_data.name = data.name - trans.sa_session.add( new_data ) - assoc = trans.app.model.ImplicitlyConvertedDatasetAssociation( parent=data, file_type=target_ext, dataset=new_data, metadata_safe=False ) - trans.sa_session.add( assoc ) + trans.sa_session.add(new_data) + assoc = trans.app.model.ImplicitlyConvertedDatasetAssociation(parent=data, file_type=target_ext, dataset=new_data, metadata_safe=False) + trans.sa_session.add(assoc) trans.sa_session.flush() elif converted_dataset and converted_dataset.state == converted_dataset.states.ERROR: - raise Exception( "Dataset conversion failed for data parameter: %s" % self.name ) - return self.get_value( other_values, dataset_hash, user_hash, trans ) + raise Exception("Dataset conversion failed for data parameter: %s" % self.name) + return self.get_value(other_values, dataset_hash, user_hash, trans) - def is_preparing( self, other_values ): - value = self._get_dataset_like_object( other_values ) - if value and value.state in ( value.states.NEW, value.states.UPLOAD, value.states.QUEUED, value.states.RUNNING ): + def is_preparing(self, other_values): + value = self._get_dataset_like_object(other_values) + if value and value.state in (value.states.NEW, value.states.UPLOAD, value.states.QUEUED, value.states.RUNNING): return True return False - def ready( self, other_values ): - value = self._get_dataset_like_object( other_values ) + def ready(self, other_values): + value = self._get_dataset_like_object(other_values) if value: if value.state == value.states.OK: return True elif value.state == value.states.ERROR: - raise Exception( 'A data display parameter is in the error state: %s' % ( self.name ) ) + raise Exception('A data display parameter is in the error state: %s' % (self.name)) return False -class DisplayApplicationTemplateParameter( DisplayApplicationParameter ): +class DisplayApplicationTemplateParameter(DisplayApplicationParameter): """ Parameter that returns a string containing the requested content """ type = 'template' - def __init__( self, elem, link ): - DisplayApplicationParameter.__init__( self, elem, link ) + def __init__(self, elem, link): + DisplayApplicationParameter.__init__(self, elem, link) self.text = elem.text or '' - def get_value( self, other_values, dataset_hash, user_hash, trans ): - value = fill_template( self.text, context=other_values ) + def get_value(self, other_values, dataset_hash, user_hash, trans): + value = fill_template(self.text, context=other_values) if self.strip: value = value.strip() - return DisplayParameterValueWrapper( value, self, other_values, dataset_hash, user_hash, trans ) + return DisplayParameterValueWrapper(value, self, other_values, dataset_hash, user_hash, trans) -parameter_type_to_class = { DisplayApplicationDataParameter.type: DisplayApplicationDataParameter, - DisplayApplicationTemplateParameter.type: DisplayApplicationTemplateParameter } +parameter_type_to_class = {DisplayApplicationDataParameter.type: DisplayApplicationDataParameter, + DisplayApplicationTemplateParameter.type: DisplayApplicationTemplateParameter} -class DisplayParameterValueWrapper( object ): +class DisplayParameterValueWrapper(object): ACTION_NAME = 'param' - def __init__( self, value, parameter, other_values, dataset_hash, user_hash, trans ): + def __init__(self, value, parameter, other_values, dataset_hash, user_hash, trans): self.value = value self.parameter = parameter self.other_values = other_values self.trans = trans self._dataset_hash = dataset_hash self._user_hash = user_hash - self._url = self.parameter.build_url( self.other_values ) + self._url = self.parameter.build_url(self.other_values) - def __str__( self ): - return str( self.value ) + def __str__(self): + return str(self.value) - def mime_type( self, action_param_extra=None ): + def mime_type(self, action_param_extra=None): if self.parameter.mime_type is not None: return self.parameter.mime_type if self.parameter.guess_mime_type: - mime, encoding = mimetypes.guess_type( self._url ) + mime, encoding = mimetypes.guess_type(self._url) if not mime: - mime = self.trans.app.datatypes_registry.get_mimetype_by_extension( ".".split( self._url )[ -1 ], None ) + mime = self.trans.app.datatypes_registry.get_mimetype_by_extension(".".split(self._url)[-1], None) if mime: return mime return 'text/plain' @property - def url( self ): + def url(self): base_url = self.trans.request.base - if self.parameter.strip_https and base_url[ : 5].lower() == 'https': - base_url = "http%s" % base_url[ 5: ] - return "%s%s" % ( base_url, - url_for( controller='dataset', - action="display_application", - dataset_id=self._dataset_hash, - user_id=self._user_hash, - app_name=quote_plus( self.parameter.link.display_application.id ), - link_name=quote_plus( self.parameter.link.id ), - app_action=self.action_name, - action_param=self._url ) ) + if self.parameter.strip_https and base_url[: 5].lower() == 'https': + base_url = "http%s" % base_url[5:] + return "%s%s" % (base_url, + url_for(controller='dataset', + action="display_application", + dataset_id=self._dataset_hash, + user_id=self._user_hash, + app_name=quote_plus(self.parameter.link.display_application.id), + link_name=quote_plus(self.parameter.link.id), + app_action=self.action_name, + action_param=self._url)) @property - def action_name( self ): + def action_name(self): return self.ACTION_NAME @property - def qp( self ): + def qp(self): # returns quoted str contents - return self.other_values[ 'qp' ]( str( self ) ) + return self.other_values['qp'](str(self)) - def __getattr__( self, key ): - return getattr( self.value, key ) + def __getattr__(self, key): + return getattr(self.value, key) -class DisplayDataValueWrapper( DisplayParameterValueWrapper ): +class DisplayDataValueWrapper(DisplayParameterValueWrapper): ACTION_NAME = 'data' - def __str__( self ): + def __str__(self): # string of data param is filename - return str( self.value.file_name ) + return str(self.value.file_name) - def mime_type( self, action_param_extra=None ): + def mime_type(self, action_param_extra=None): if self.parameter.mime_type is not None: return self.parameter.mime_type if self.parameter.guess_mime_type: if action_param_extra: - mime, encoding = mimetypes.guess_type( action_param_extra ) + mime, encoding = mimetypes.guess_type(action_param_extra) else: - mime, encoding = mimetypes.guess_type( self._url ) + mime, encoding = mimetypes.guess_type(self._url) if not mime: if action_param_extra: - mime = self.trans.app.datatypes_registry.get_mimetype_by_extension( ".".split( action_param_extra )[ -1 ], None ) + mime = self.trans.app.datatypes_registry.get_mimetype_by_extension(".".split(action_param_extra)[-1], None) if not mime: - mime = self.trans.app.datatypes_registry.get_mimetype_by_extension( ".".split( self._url )[ -1 ], None ) + mime = self.trans.app.datatypes_registry.get_mimetype_by_extension(".".split(self._url)[-1], None) if mime: return mime - if hasattr( self.value, 'get_mime' ): + if hasattr(self.value, 'get_mime'): return self.value.get_mime() - return self.other_values[ DEFAULT_DATASET_NAME ].get_mime() + return self.other_values[DEFAULT_DATASET_NAME].get_mime() @property - def action_name( self ): + def action_name(self): if self.parameter.force_url_param: - return super( DisplayParameterValueWrapper, self ).action_name + return super(DisplayParameterValueWrapper, self).action_name return self.ACTION_NAME @property - def qp( self ): + def qp(self): # returns quoted url contents - return self.other_values[ 'qp' ]( self.url ) + return self.other_values['qp'](self.url) diff --git a/lib/galaxy/datatypes/display_applications/util.py b/lib/galaxy/datatypes/display_applications/util.py index 2aec95440aa9..f71eb5ed6390 100644 --- a/lib/galaxy/datatypes/display_applications/util.py +++ b/lib/galaxy/datatypes/display_applications/util.py @@ -1,32 +1,32 @@ from Crypto.Cipher import Blowfish -def encode_dataset_user( trans, dataset, user ): +def encode_dataset_user(trans, dataset, user): # encode dataset id as usual # encode user id using the dataset create time as the key - dataset_hash = trans.security.encode_id( dataset.id ) + dataset_hash = trans.security.encode_id(dataset.id) if user is None: user_hash = 'None' else: - user_hash = str( user.id ) + user_hash = str(user.id) # Pad to a multiple of 8 with leading "!" - user_hash = ( "!" * ( 8 - len( user_hash ) % 8 ) ) + user_hash - cipher = Blowfish.new( str( dataset.create_time ) ) - user_hash = cipher.encrypt( user_hash ).encode( 'hex' ) + user_hash = ("!" * (8 - len(user_hash) % 8)) + user_hash + cipher = Blowfish.new(str(dataset.create_time)) + user_hash = cipher.encrypt(user_hash).encode('hex') return dataset_hash, user_hash -def decode_dataset_user( trans, dataset_hash, user_hash ): +def decode_dataset_user(trans, dataset_hash, user_hash): # decode dataset id as usual # decode user id using the dataset create time as the key - dataset_id = trans.security.decode_id( dataset_hash ) - dataset = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( dataset_id ) + dataset_id = trans.security.decode_id(dataset_hash) + dataset = trans.sa_session.query(trans.app.model.HistoryDatasetAssociation).get(dataset_id) assert dataset, "Bad Dataset id provided to decode_dataset_user" - if user_hash in [ None, 'None' ]: + if user_hash in [None, 'None']: user = None else: - cipher = Blowfish.new( str( dataset.create_time ) ) - user_id = cipher.decrypt( user_hash.decode( 'hex' ) ).lstrip( "!" ) - user = trans.sa_session.query( trans.app.model.User ).get( int( user_id ) ) + cipher = Blowfish.new(str(dataset.create_time)) + user_id = cipher.decrypt(user_hash.decode('hex')).lstrip("!") + user = trans.sa_session.query(trans.app.model.User).get(int(user_id)) assert user, "A Bad user id was passed to decode_dataset_user" return dataset, user diff --git a/lib/galaxy/datatypes/genetics.py b/lib/galaxy/datatypes/genetics.py index ee5b5980739d..22833fd50ade 100644 --- a/lib/galaxy/datatypes/genetics.py +++ b/lib/galaxy/datatypes/genetics.py @@ -33,14 +33,14 @@ VALID_GENOME_GRAPH_MARKERS = re.compile('^(chr.*|RH.*|rs.*|SNP_.*|CN.*|A_.*)') -class GenomeGraphs( Tabular ): +class GenomeGraphs(Tabular): """ Tab delimited data containing a marker id and any number of numeric values """ - MetadataElement( name="markerCol", default=1, desc="Marker ID column", param=metadata.ColumnParameter ) - MetadataElement( name="columns", default=3, desc="Number of columns", readonly=True ) - MetadataElement( name="column_types", default=[], desc="Column types", readonly=True, visible=False ) + MetadataElement(name="markerCol", default=1, desc="Marker ID column", param=metadata.ColumnParameter) + MetadataElement(name="columns", default=3, desc="Number of columns", readonly=True) + MetadataElement(name="column_types", default=[], desc="Column types", readonly=True, visible=False) file_ext = 'gg' def __init__(self, **kwd): @@ -48,10 +48,10 @@ def __init__(self, **kwd): Initialize gg datatype, by adding UCSC display apps """ Tabular.__init__(self, **kwd) - self.add_display_app( 'ucsc', 'Genome Graph', 'as_ucsc_display_file', 'ucsc_links' ) + self.add_display_app('ucsc', 'Genome Graph', 'as_ucsc_display_file', 'ucsc_links') def set_meta(self, dataset, **kwd): - Tabular.set_meta( self, dataset, **kwd) + Tabular.set_meta(self, dataset, **kwd) dataset.metadata.markerCol = 1 header = open(dataset.file_name, 'r').readlines()[0].strip().split('\t') dataset.metadata.columns = len(header) @@ -60,13 +60,13 @@ def set_meta(self, dataset, **kwd): dataset.metadata.column_types = t return True - def as_ucsc_display_file( self, dataset, **kwd ): + def as_ucsc_display_file(self, dataset, **kwd): """ Returns file """ return open(dataset.file_name, 'r') - def ucsc_links( self, dataset, type, app, base_url ): + def ucsc_links(self, dataset, type, app, base_url): """ from the ever-helpful angie hinrichs angie@soe.ucsc.edu a genome graphs call looks like this @@ -90,15 +90,15 @@ def ucsc_links( self, dataset, type, app, base_url ): for site_name, site_url in app.datatypes_registry.get_legacy_sites_by_build('ucsc', dataset.dbkey): if site_name in app.datatypes_registry.get_display_sites('ucsc'): site_url = site_url.replace('/hgTracks?', '/hgGenome?') # for genome graphs - internal_url = "%s" % url_for( controller='dataset', - dataset_id=dataset.id, - action='display_at', - filename='ucsc_' + site_name ) - display_url = "%s%s/display_as?id=%i&display_app=%s&authz_method=display_at" % (base_url, url_for( controller='root' ), dataset.id, type) - display_url = quote_plus( display_url ) + internal_url = "%s" % url_for(controller='dataset', + dataset_id=dataset.id, + action='display_at', + filename='ucsc_' + site_name) + display_url = "%s%s/display_as?id=%i&display_app=%s&authz_method=display_at" % (base_url, url_for(controller='root'), dataset.id, type) + display_url = quote_plus(display_url) # was display_url = quote_plus( "%s/display_as?id=%i&display_app=%s" % (base_url, dataset.id, type) ) # redirect_url = quote_plus( "%sdb=%s&position=%s:%s-%s&hgt.customText=%%s" % (site_url, dataset.dbkey, chrom, start, stop) ) - sl = ["%sdb=%s" % (site_url, dataset.dbkey ), ] + sl = ["%sdb=%s" % (site_url, dataset.dbkey), ] # sl.append("&hgt.customText=%s") sl.append("&hgGenome_dataSetName=%s&hgGenome_dataSetDescription=%s" % (dataset.name, 'GalaxyGG_data')) sl.append("&hgGenome_formatType=best guess&hgGenome_markerType=best guess") @@ -108,11 +108,11 @@ def ucsc_links( self, dataset, type, app, base_url ): s = ''.join(sl) s = quote_plus(s) redirect_url = s - link = '%s?redirect_url=%s&display_url=%s' % ( internal_url, redirect_url, display_url ) - ret_val.append( (site_name, link) ) + link = '%s?redirect_url=%s&display_url=%s' % (internal_url, redirect_url, display_url) + ret_val.append((site_name, link)) return ret_val - def make_html_table( self, dataset, skipchars=[] ): + def make_html_table(self, dataset, skipchars=[]): """ Create HTML table, used for displaying peek """ @@ -129,23 +129,23 @@ def make_html_table( self, dataset, skipchars=[] ): except: hasheader = 1 # Generate column header - out.append( '' ) + out.append('') if hasheader: - for i, name in enumerate(d[0].split() ): - out.append( '%s.%s' % ( i + 1, name ) ) + for i, name in enumerate(d[0].split()): + out.append('%s.%s' % (i + 1, name)) d.pop(0) out.append('') for row in d: out.append('') out.append(''.join(['%s' % x for x in row.split()])) out.append('') - out.append( '' ) - out = "".join( out ) + out.append('') + out = "".join(out) except Exception as exc: out = "Can't create peek %s" % exc return out - def validate( self, dataset ): + def validate(self, dataset): """ Validate a gg file - all numeric after header row """ @@ -164,7 +164,7 @@ def validate( self, dataset ): errors.append('row %d, %s' % (' '.join(badvals))) return errors - def sniff( self, filename ): + def sniff(self, filename): """ Determines whether the file is in gg format @@ -214,12 +214,12 @@ def __init__(self, **kwd): """ Initialize featurelistt datatype """ - Tabular.__init__( self, **kwd ) + Tabular.__init__(self, **kwd) self.column_names = [] - def display_peek( self, dataset ): + def display_peek(self, dataset): """Returns formated html of peek""" - return self.make_html_table( dataset, column_names=self.column_names ) + return self.make_html_table(dataset, column_names=self.column_names) def get_mime(self): """Returns the mime type of the datatype""" @@ -240,7 +240,7 @@ def __init__(self, **kwd): """ Initialize samplelist datatype """ - rgTabList.__init__( self, **kwd ) + rgTabList.__init__(self, **kwd) self.column_names[0] = 'FID' self.column_names[1] = 'IID' # this is what Plink wants as at 2009 @@ -254,7 +254,7 @@ def sniff(self, filename): return False -class rgFeatureList( rgTabList ): +class rgFeatureList(rgTabList): """ for featureid lists of exclusions or inclusions in the clean tool output from QC eg low maf, high missingness, bad hwe in controls, excess mendel errors,... @@ -265,7 +265,7 @@ class rgFeatureList( rgTabList ): def __init__(self, **kwd): """Initialize featurelist datatype""" - rgTabList.__init__( self, **kwd ) + rgTabList.__init__(self, **kwd) for i, s in enumerate(['#FeatureId', 'Chr', 'Genpos', 'Mappos']): self.column_names[i] = s @@ -277,27 +277,27 @@ class Rgenetics(Html): stored in extra files path """ - MetadataElement( name="base_name", desc="base name for all transformed versions of this genetic dataset", default='RgeneticsData', - readonly=True, set_in_upload=True) + MetadataElement(name="base_name", desc="base name for all transformed versions of this genetic dataset", default='RgeneticsData', + readonly=True, set_in_upload=True) composite_type = 'auto_primary_file' allow_datatype_change = False file_ext = 'rgenetics' - def generate_primary_file( self, dataset=None ): + def generate_primary_file(self, dataset=None): rval = ['Rgenetics Galaxy Composite Dataset

    '] rval.append('

    This composite dataset is composed of the following files:

      ') - for composite_name, composite_file in self.get_composite_files( dataset=dataset ).items(): + for composite_name, composite_file in self.get_composite_files(dataset=dataset).items(): fn = composite_name opt_text = '' if composite_file.optional: opt_text = ' (optional)' if composite_file.get('description'): - rval.append( '
    • %s (%s)%s
    • ' % ( fn, fn, composite_file.get('description'), opt_text ) ) + rval.append('
    • %s (%s)%s
    • ' % (fn, fn, composite_file.get('description'), opt_text)) else: - rval.append( '
    • %s%s
    • ' % ( fn, fn, opt_text ) ) - rval.append( '
    ' ) - return "\n".join( rval ) + rval.append('
  • %s%s
  • ' % (fn, fn, opt_text)) + rval.append('
    ') + return "\n".join(rval) def regenerate_primary_file(self, dataset): """ @@ -309,23 +309,22 @@ def regenerate_primary_file(self, dataset): for i, fname in enumerate(flist): sfname = os.path.split(fname)[-1] f, e = os.path.splitext(fname) - rval.append( '
  • %s
  • ' % ( sfname, sfname) ) - rval.append( '' ) + rval.append('
  • %s
  • ' % (sfname, sfname)) + rval.append('') with open(dataset.file_name, 'w') as f: - f.write("\n".join( rval )) + f.write("\n".join(rval)) f.write('\n') def get_mime(self): """Returns the mime type of the datatype""" return 'text/html' - def set_meta( self, dataset, **kwd ): - + def set_meta(self, dataset, **kwd): """ for lped/pbed eg """ - Html.set_meta( self, dataset, **kwd ) + Html.set_meta(self, dataset, **kwd) if not kwd.get('overwrite'): if verbose: gal_Log.debug('@@@ rgenetics set_meta called with overwrite = False') @@ -360,10 +359,10 @@ class SNPMatrix(Rgenetics): """ file_ext = "snpmatrix" - def set_peek( self, dataset, **kwd ): + def set_peek(self, dataset, **kwd): if not dataset.dataset.purged: dataset.peek = "Binary RGenetics file" - dataset.blurb = nice_size( dataset.get_size() ) + dataset.blurb = nice_size(dataset.get_size()) else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disk' @@ -386,16 +385,16 @@ class Lped(Rgenetics): """ file_ext = "lped" - def __init__( self, **kwd ): + def __init__(self, **kwd): Rgenetics.__init__(self, **kwd) - self.add_composite_file( '%s.ped', - description='Pedigree File', - substitute_name_with_metadata='base_name', - is_binary=False ) - self.add_composite_file( '%s.map', - description='Map File', - substitute_name_with_metadata='base_name', - is_binary=False ) + self.add_composite_file('%s.ped', + description='Pedigree File', + substitute_name_with_metadata='base_name', + is_binary=False) + self.add_composite_file('%s.map', + description='Map File', + substitute_name_with_metadata='base_name', + is_binary=False) class Pphe(Rgenetics): @@ -404,12 +403,12 @@ class Pphe(Rgenetics): """ file_ext = "pphe" - def __init__( self, **kwd ): + def __init__(self, **kwd): Rgenetics.__init__(self, **kwd) - self.add_composite_file( '%s.pphe', - description='Plink Phenotype File', - substitute_name_with_metadata='base_name', - is_binary=False ) + self.add_composite_file('%s.pphe', + description='Plink Phenotype File', + substitute_name_with_metadata='base_name', + is_binary=False) class Fphe(Rgenetics): @@ -419,11 +418,11 @@ class Fphe(Rgenetics): """ file_ext = "fphe" - def __init__( self, **kwd ): + def __init__(self, **kwd): Rgenetics.__init__(self, **kwd) - self.add_composite_file( '%s.fphe', - description='FBAT Phenotype File', - substitute_name_with_metadata='base_name' ) + self.add_composite_file('%s.fphe', + description='FBAT Phenotype File', + substitute_name_with_metadata='base_name') class Phe(Rgenetics): @@ -432,12 +431,12 @@ class Phe(Rgenetics): """ file_ext = "phe" - def __init__( self, **kwd ): + def __init__(self, **kwd): Rgenetics.__init__(self, **kwd) - self.add_composite_file( '%s.phe', - description='Phenotype File', - substitute_name_with_metadata='base_name', - is_binary=False ) + self.add_composite_file('%s.phe', + description='Phenotype File', + substitute_name_with_metadata='base_name', + is_binary=False) class Fped(Rgenetics): @@ -447,11 +446,11 @@ class Fped(Rgenetics): """ file_ext = "fped" - def __init__( self, **kwd ): + def __init__(self, **kwd): Rgenetics.__init__(self, **kwd) - self.add_composite_file( '%s.fped', description='FBAT format pedfile', - substitute_name_with_metadata='base_name', - is_binary=False ) + self.add_composite_file('%s.fped', description='FBAT format pedfile', + substitute_name_with_metadata='base_name', + is_binary=False) class Pbed(Rgenetics): @@ -460,11 +459,11 @@ class Pbed(Rgenetics): """ file_ext = "pbed" - def __init__( self, **kwd ): + def __init__(self, **kwd): Rgenetics.__init__(self, **kwd) - self.add_composite_file( '%s.bim', substitute_name_with_metadata='base_name', is_binary=False ) - self.add_composite_file( '%s.bed', substitute_name_with_metadata='base_name', is_binary=True ) - self.add_composite_file( '%s.fam', substitute_name_with_metadata='base_name', is_binary=False ) + self.add_composite_file('%s.bim', substitute_name_with_metadata='base_name', is_binary=False) + self.add_composite_file('%s.bed', substitute_name_with_metadata='base_name', is_binary=True) + self.add_composite_file('%s.fam', substitute_name_with_metadata='base_name', is_binary=False) class ldIndep(Rgenetics): @@ -475,11 +474,11 @@ class ldIndep(Rgenetics): """ file_ext = "ldreduced" - def __init__( self, **kwd ): + def __init__(self, **kwd): Rgenetics.__init__(self, **kwd) - self.add_composite_file( '%s.bim', substitute_name_with_metadata='base_name', is_binary=False ) - self.add_composite_file( '%s.bed', substitute_name_with_metadata='base_name', is_binary=True ) - self.add_composite_file( '%s.fam', substitute_name_with_metadata='base_name', is_binary=False ) + self.add_composite_file('%s.bim', substitute_name_with_metadata='base_name', is_binary=False) + self.add_composite_file('%s.bed', substitute_name_with_metadata='base_name', is_binary=True) + self.add_composite_file('%s.fam', substitute_name_with_metadata='base_name', is_binary=False) class Eigenstratgeno(Rgenetics): @@ -490,11 +489,11 @@ class Eigenstratgeno(Rgenetics): """ file_ext = "eigenstratgeno" - def __init__( self, **kwd ): + def __init__(self, **kwd): Rgenetics.__init__(self, **kwd) - self.add_composite_file( '%s.eigenstratgeno', substitute_name_with_metadata='base_name', is_binary=False ) - self.add_composite_file( '%s.ind', substitute_name_with_metadata='base_name', is_binary=False ) - self.add_composite_file( '%s.map', substitute_name_with_metadata='base_name', is_binary=False ) + self.add_composite_file('%s.eigenstratgeno', substitute_name_with_metadata='base_name', is_binary=False) + self.add_composite_file('%s.ind', substitute_name_with_metadata='base_name', is_binary=False) + self.add_composite_file('%s.map', substitute_name_with_metadata='base_name', is_binary=False) class Eigenstratpca(Rgenetics): @@ -504,10 +503,10 @@ class Eigenstratpca(Rgenetics): """ file_ext = "eigenstratpca" - def __init__( self, **kwd ): + def __init__(self, **kwd): Rgenetics.__init__(self, **kwd) - self.add_composite_file( '%s.eigenstratpca', - description='Eigenstrat PCA file', substitute_name_with_metadata='base_name' ) + self.add_composite_file('%s.eigenstratpca', + description='Eigenstrat PCA file', substitute_name_with_metadata='base_name') class Snptest(Rgenetics): @@ -524,30 +523,30 @@ class Pheno(Tabular): file_ext = 'pheno' -class RexpBase( Html ): +class RexpBase(Html): """ base class for BioC data structures in Galaxy must be constructed with the pheno data in place since that goes into the metadata for each instance """ - MetadataElement( name="columns", default=0, desc="Number of columns", visible=True ) - MetadataElement( name="column_names", default=[], desc="Column names", visible=True ) + MetadataElement(name="columns", default=0, desc="Number of columns", visible=True) + MetadataElement(name="column_names", default=[], desc="Column names", visible=True) MetadataElement(name="pheCols", default=[], desc="Select list for potentially interesting variables", visible=True) - MetadataElement( name="base_name", - desc="base name for all transformed versions of this expression dataset", default='rexpression', set_in_upload=True) - MetadataElement( name="pheno_path", desc="Path to phenotype data for this experiment", default="rexpression.pheno", visible=True) + MetadataElement(name="base_name", + desc="base name for all transformed versions of this expression dataset", default='rexpression', set_in_upload=True) + MetadataElement(name="pheno_path", desc="Path to phenotype data for this experiment", default="rexpression.pheno", visible=True) file_ext = 'rexpbase' html_table = None is_binary = True composite_type = 'auto_primary_file' allow_datatype_change = False - def __init__( self, **kwd ): + def __init__(self, **kwd): Html.__init__(self, **kwd) - self.add_composite_file( '%s.pheno', description='Phenodata tab text file', - substitute_name_with_metadata='base_name', is_binary=False) + self.add_composite_file('%s.pheno', description='Phenodata tab text file', + substitute_name_with_metadata='base_name', is_binary=False) - def generate_primary_file( self, dataset=None ): + def generate_primary_file(self, dataset=None): """ This is called only at upload to write the html file cannot rename the datasets here - they come with the default unfortunately @@ -617,10 +616,10 @@ def get_phecols(self, phenolist=[], maxConc=20): del useConc[i] # get rid of concordance del useCols[i] # and usecols entry for i, conc in enumerate(useConc): # these are all unique columns for the design matrix - ccounts = sorted((conc.get(code, 0), code) for code in conc.keys()) # decorate - cc = [(x[1], x[0]) for x in ccounts] # list of code count tuples - codeDetails = (head[useCols[i]], cc) # ('foo',[('a',3),('b',11),..]) - listCol.append(codeDetails) + ccounts = sorted((conc.get(code, 0), code) for code in conc.keys()) # decorate + cc = [(x[1], x[0]) for x in ccounts] # list of code count tuples + codeDetails = (head[useCols[i]], cc) # ('foo',[('a',3),('b',11),..]) + listCol.append(codeDetails) if len(listCol) > 0: res = listCol # metadata.pheCols becomes [('bar;22,zot;113','foo'), ...] @@ -647,7 +646,7 @@ def get_pheno(self, dataset): p = [] return '\n'.join(p) - def set_peek( self, dataset, **kwd ): + def set_peek(self, dataset, **kwd): """ expects a .pheno file in the extra_files_dir - ugh note that R is weird and does not include the row.name in @@ -664,7 +663,7 @@ def set_peek( self, dataset, **kwd ): dataset.peek = 'file does not exist\n' dataset.blurb = 'file purged from disk' - def get_peek( self, dataset ): + def get_peek(self, dataset): """ expects a .pheno file in the extra_files_dir - ugh """ @@ -695,18 +694,17 @@ def regenerate_primary_file(self, dataset): rval = ['Files for Composite Dataset %s

    Comprises the following files:

      ' % (bn)] for i, fname in enumerate(flist): sfname = os.path.split(fname)[-1] - rval.append( '
    • %s' % ( sfname, sfname ) ) - rval.append( '
    ' ) + rval.append('
  • %s' % (sfname, sfname)) + rval.append('') with open(dataset.file_name, 'w') as f: - f.write("\n".join( rval )) + f.write("\n".join(rval)) f.write('\n') - def init_meta( self, dataset, copy_from=None ): + def init_meta(self, dataset, copy_from=None): if copy_from: dataset.metadata = copy_from.metadata - def set_meta( self, dataset, **kwd ): - + def set_meta(self, dataset, **kwd): """ NOTE we apply the tabular machinary to the phenodata extracted from a BioC eSet or affybatch. @@ -756,7 +754,7 @@ def set_meta( self, dataset, **kwd ): dataset.blurb = 'R loadable BioC expression object for the Rexpression Galaxy toolkit' return True - def make_html_table( self, pp='nothing supplied from peek\n'): + def make_html_table(self, pp='nothing supplied from peek\n'): """ Create HTML table, used for displaying peek """ @@ -775,13 +773,13 @@ def make_html_table( self, pp='nothing supplied from peek\n'): orow.insert(0, '') orow.append('') out.append(''.join(orow)) - out.append( '' ) - out = "\n".join( out ) + out.append('') + out = "\n".join(out) except Exception as exc: - out = "Can't create html table %s" % str( exc ) + out = "Can't create html table %s" % str(exc) return out - def display_peek( self, dataset ): + def display_peek(self, dataset): """ Returns formatted html of peek """ @@ -789,44 +787,44 @@ def display_peek( self, dataset ): return out -class Affybatch( RexpBase ): +class Affybatch(RexpBase): """ derived class for BioC data structures in Galaxy """ file_ext = "affybatch" - def __init__( self, **kwd ): + def __init__(self, **kwd): RexpBase.__init__(self, **kwd) - self.add_composite_file( '%s.affybatch', - description='AffyBatch R object saved to file', - substitute_name_with_metadata='base_name', is_binary=True ) + self.add_composite_file('%s.affybatch', + description='AffyBatch R object saved to file', + substitute_name_with_metadata='base_name', is_binary=True) -class Eset( RexpBase ): +class Eset(RexpBase): """ derived class for BioC data structures in Galaxy """ file_ext = "eset" - def __init__( self, **kwd ): + def __init__(self, **kwd): RexpBase.__init__(self, **kwd) - self.add_composite_file( '%s.eset', - description='ESet R object saved to file', - substitute_name_with_metadata='base_name', is_binary=True ) + self.add_composite_file('%s.eset', + description='ESet R object saved to file', + substitute_name_with_metadata='base_name', is_binary=True) -class MAlist( RexpBase ): +class MAlist(RexpBase): """ derived class for BioC data structures in Galaxy """ file_ext = "malist" - def __init__( self, **kwd ): + def __init__(self, **kwd): RexpBase.__init__(self, **kwd) - self.add_composite_file( '%s.malist', - description='MAlist R object saved to file', - substitute_name_with_metadata='base_name', is_binary=True ) + self.add_composite_file('%s.malist', + description='MAlist R object saved to file', + substitute_name_with_metadata='base_name', is_binary=True) if __name__ == '__main__': diff --git a/lib/galaxy/datatypes/graph.py b/lib/galaxy/datatypes/graph.py index 913cec78a0a3..6e7ca5e9afe3 100644 --- a/lib/galaxy/datatypes/graph.py +++ b/lib/galaxy/datatypes/graph.py @@ -12,53 +12,53 @@ xml ) -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) @dataproviders.decorators.has_dataproviders -class Xgmml( xml.GenericXml ): +class Xgmml(xml.GenericXml): """ XGMML graph format (http://wiki.cytoscape.org/Cytoscape_User_Manual/Network_Formats). """ file_ext = "xgmml" - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): """ Set the peek and blurb text """ if not dataset.dataset.purged: - dataset.peek = data.get_file_peek( dataset.file_name, is_multi_byte=is_multi_byte ) + dataset.peek = data.get_file_peek(dataset.file_name, is_multi_byte=is_multi_byte) dataset.blurb = 'XGMML data' else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disk' - def sniff( self, filename ): + def sniff(self, filename): """ Returns false and the user must manually set. """ return False @staticmethod - def merge( split_files, output_file ): + def merge(split_files, output_file): """ Merging multiple XML files is non-trivial and must be done in subclasses. """ - if len( split_files ) > 1: - raise NotImplementedError( "Merging multiple XML files is non-trivial " + - "and must be implemented for each XML type" ) + if len(split_files) > 1: + raise NotImplementedError("Merging multiple XML files is non-trivial " + + "and must be implemented for each XML type") # For one file only, use base class method (move/copy) - data.Text.merge( split_files, output_file ) + data.Text.merge(split_files, output_file) - @dataproviders.decorators.dataprovider_factory( 'node-edge', dataproviders.hierarchy.XMLDataProvider.settings ) - def node_edge_dataprovider( self, dataset, **settings ): - dataset_source = dataproviders.dataset.DatasetDataProvider( dataset ) - return XGMMLGraphDataProvider( dataset_source, **settings ) + @dataproviders.decorators.dataprovider_factory('node-edge', dataproviders.hierarchy.XMLDataProvider.settings) + def node_edge_dataprovider(self, dataset, **settings): + dataset_source = dataproviders.dataset.DatasetDataProvider(dataset) + return XGMMLGraphDataProvider(dataset_source, **settings) @dataproviders.decorators.has_dataproviders -class Sif( tabular.Tabular ): +class Sif(tabular.Tabular): """ SIF graph format (http://wiki.cytoscape.org/Cytoscape_User_Manual/Network_Formats). @@ -69,35 +69,35 @@ class Sif( tabular.Tabular ): """ file_ext = "sif" - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): """ Set the peek and blurb text """ if not dataset.dataset.purged: - dataset.peek = data.get_file_peek( dataset.file_name, is_multi_byte=is_multi_byte ) + dataset.peek = data.get_file_peek(dataset.file_name, is_multi_byte=is_multi_byte) dataset.blurb = 'SIF data' else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disk' - def sniff( self, filename ): + def sniff(self, filename): """ Returns false and the user must manually set. """ return False @staticmethod - def merge( split_files, output_file ): - data.Text.merge( split_files, output_file ) + def merge(split_files, output_file): + data.Text.merge(split_files, output_file) - @dataproviders.decorators.dataprovider_factory( 'node-edge', dataproviders.column.ColumnarDataProvider.settings ) - def node_edge_dataprovider( self, dataset, **settings ): - dataset_source = dataproviders.dataset.DatasetDataProvider( dataset ) - return SIFGraphDataProvider( dataset_source, **settings ) + @dataproviders.decorators.dataprovider_factory('node-edge', dataproviders.column.ColumnarDataProvider.settings) + def node_edge_dataprovider(self, dataset, **settings): + dataset_source = dataproviders.dataset.DatasetDataProvider(dataset) + return SIFGraphDataProvider(dataset_source, **settings) # ----------------------------------------------------------------------------- graph specific data providers -class XGMMLGraphDataProvider( dataproviders.hierarchy.XMLDataProvider ): +class XGMMLGraphDataProvider(dataproviders.hierarchy.XMLDataProvider): """ Provide two lists: nodes, edges:: @@ -106,31 +106,32 @@ class XGMMLGraphDataProvider( dataproviders.hierarchy.XMLDataProvider ): 'edges': contains objects of the form: { 'source' : , 'target': , 'data': } """ - def __iter__( self ): + + def __iter__(self): # use simple graph to store nodes and links, later providing them as a dict # essentially this is a form of aggregation graph = simplegraph.SimpleGraph() - parent_gen = super( XGMMLGraphDataProvider, self ).__iter__() + parent_gen = super(XGMMLGraphDataProvider, self).__iter__() for graph_elem in parent_gen: if 'children' not in graph_elem: continue - for elem in graph_elem[ 'children' ]: + for elem in graph_elem['children']: # use endswith to work around Elementtree namespaces - if elem[ 'tag' ].endswith( 'node' ): - node_id = elem[ 'attrib' ][ 'id' ] + if elem['tag'].endswith('node'): + node_id = elem['attrib']['id'] # pass the entire, parsed xml element as the data - graph.add_node( node_id, **elem ) + graph.add_node(node_id, **elem) - elif elem[ 'tag' ].endswith( 'edge' ): - source_id = elem[ 'attrib' ][ 'source' ] - target_id = elem[ 'attrib' ][ 'target' ] - graph.add_edge( source_id, target_id, **elem ) + elif elem['tag'].endswith('edge'): + source_id = elem['attrib']['source'] + target_id = elem['attrib']['target'] + graph.add_edge(source_id, target_id, **elem) yield graph.as_dict() -class SIFGraphDataProvider( dataproviders.column.ColumnarDataProvider ): +class SIFGraphDataProvider(dataproviders.column.ColumnarDataProvider): """ Provide two lists: nodes, edges:: @@ -139,24 +140,25 @@ class SIFGraphDataProvider( dataproviders.column.ColumnarDataProvider ): 'edges': contains objects of the form: { 'source' : , 'target': , 'data': } """ - def __iter__( self ): + + def __iter__(self): # use simple graph to store nodes and links, later providing them as a dict # essentially this is a form of aggregation graph = simplegraph.SimpleGraph() # SIF is tabular with the source, link-type, and all targets in the columns - parent_gen = super( SIFGraphDataProvider, self ).__iter__() + parent_gen = super(SIFGraphDataProvider, self).__iter__() for columns in parent_gen: if columns: source_id = columns[0] # there's no extra data for nodes (or links) in the examples I've seen - graph.add_node( source_id ) + graph.add_node(source_id) # targets are the (variadic) remaining columns - if len( columns ) >= 3: + if len(columns) >= 3: relation = columns[1] targets = columns[2:] for target_id in targets: - graph.add_node( target_id ) - graph.add_edge( source_id, target_id, type=relation ) + graph.add_node(target_id) + graph.add_edge(source_id, target_id, type=relation) yield graph.as_dict() diff --git a/lib/galaxy/datatypes/images.py b/lib/galaxy/datatypes/images.py index 5df60919a1dc..f961df3ea78c 100644 --- a/lib/galaxy/datatypes/images.py +++ b/lib/galaxy/datatypes/images.py @@ -26,7 +26,7 @@ # to our main public instance. -class Image( data.Data ): +class Image(data.Data): """Class describing an image""" edam_data = 'data_2968' edam_format = "format_3547" @@ -36,20 +36,20 @@ def __init__(self, **kwd): super(Image, self).__init__(**kwd) self.image_formats = [self.file_ext.upper()] - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): if not dataset.dataset.purged: dataset.peek = 'Image in %s format' % dataset.extension - dataset.blurb = nice_size( dataset.get_size() ) + dataset.blurb = nice_size(dataset.get_size()) else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disk' - def sniff( self, filename ): + def sniff(self, filename): """Determine if the file is in this format""" - return check_image_type( filename, self.image_formats ) + return check_image_type(filename, self.image_formats) -class Jpg( Image ): +class Jpg(Image): edam_format = "format_3579" file_ext = "jpg" @@ -58,103 +58,103 @@ def __init__(self, **kwd): self.image_formats = ['JPEG'] -class Png( Image ): +class Png(Image): edam_format = "format_3603" file_ext = "png" -class Tiff( Image ): +class Tiff(Image): edam_format = "format_3591" file_ext = "tiff" -class Hamamatsu( Image ): +class Hamamatsu(Image): file_ext = "vms" -class Mirax( Image ): +class Mirax(Image): file_ext = "mrxs" -class Sakura( Image ): +class Sakura(Image): file_ext = "svslide" -class Nrrd( Image ): +class Nrrd(Image): file_ext = "nrrd" -class Bmp( Image ): +class Bmp(Image): edam_format = "format_3592" file_ext = "bmp" -class Gif( Image ): +class Gif(Image): edam_format = "format_3467" file_ext = "gif" -class Im( Image ): +class Im(Image): edam_format = "format_3593" file_ext = "im" -class Pcd( Image ): +class Pcd(Image): edam_format = "format_3594" file_ext = "pcd" -class Pcx( Image ): +class Pcx(Image): edam_format = "format_3595" file_ext = "pcx" -class Ppm( Image ): +class Ppm(Image): edam_format = "format_3596" file_ext = "ppm" -class Psd( Image ): +class Psd(Image): edam_format = "format_3597" file_ext = "psd" -class Xbm( Image ): +class Xbm(Image): edam_format = "format_3598" file_ext = "xbm" -class Xpm( Image ): +class Xpm(Image): edam_format = "format_3599" file_ext = "xpm" -class Rgb( Image ): +class Rgb(Image): edam_format = "format_3600" file_ext = "rgb" -class Pbm( Image ): +class Pbm(Image): edam_format = "format_3601" file_ext = "pbm" -class Pgm( Image ): +class Pgm(Image): edam_format = "format_3602" file_ext = "pgm" -class Eps( Image ): +class Eps(Image): edam_format = "format_3466" file_ext = "eps" -class Rast( Image ): +class Rast(Image): edam_format = "format_3605" file_ext = "rast" -class Pdf( Image ): +class Pdf(Image): edam_format = "format_3508" file_ext = "pdf" @@ -173,47 +173,47 @@ def sniff(self, filename): Binary.register_sniffable_binary_format("pdf", "pdf", Pdf) -def create_applet_tag_peek( class_name, archive, params ): +def create_applet_tag_peek(class_name, archive, params): text = """ - """ % ( class_name, archive ) + """ % (class_name, archive) for name, value in params.items(): - text += """""" % ( name, value ) + text += """""" % (name, value) text += """ - """ % ( class_name, archive ) + """ % (class_name, archive) for name, value in params.items(): - text += """""" % ( name, value ) + text += """""" % (name, value) text += """
    You must install and enable Java in your browser in order to access this applet.
    """ return """

    %s

    """ % text -class Gmaj( data.Data ): +class Gmaj(data.Data): """Class describing a GMAJ Applet""" edam_format = "format_3547" file_ext = "gmaj.zip" copy_safe_peek = False - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): if not dataset.dataset.purged: - if hasattr( dataset, 'history_id' ): + if hasattr(dataset, 'history_id'): params = { "bundle": "display?id=%s&tofile=yes&toext=.zip" % dataset.id, "buttonlabel": "Launch GMAJ", "nobutton": "false", "urlpause": "100", "debug": "false", - "posturl": "history_add_to?%s" % "&".join( "%s=%s" % ( x[0], quote_plus( str( x[1] ) ) ) for x in [ ( 'copy_access_from', dataset.id), ( 'history_id', dataset.history_id ), ( 'ext', 'maf' ), ( 'name', 'GMAJ Output on data %s' % dataset.hid ), ( 'info', 'Added by GMAJ' ), ( 'dbkey', dataset.dbkey ) ] ) + "posturl": "history_add_to?%s" % "&".join("%s=%s" % (x[0], quote_plus(str(x[1]))) for x in [('copy_access_from', dataset.id), ('history_id', dataset.history_id), ('ext', 'maf'), ('name', 'GMAJ Output on data %s' % dataset.hid), ('info', 'Added by GMAJ'), ('dbkey', dataset.dbkey)]) } class_name = "edu.psu.bx.gmaj.MajApplet.class" archive = "/static/gmaj/gmaj.jar" - dataset.peek = create_applet_tag_peek( class_name, archive, params ) + dataset.peek = create_applet_tag_peek(class_name, archive, params) dataset.blurb = 'GMAJ Multiple Alignment Viewer' else: dataset.peek = "After you add this item to your history, you will be able to launch the GMAJ applet." @@ -238,7 +238,7 @@ def sniff(self, filename): correctly sniffed, but the files can be uploaded (they'll be sniffed as 'txt'). This sniff function is here to provide an example of a sniffer for a zip file. """ - if not zipfile.is_zipfile( filename ): + if not zipfile.is_zipfile(filename): return False contains_gmaj_file = False zip_file = zipfile.ZipFile(filename, "r") @@ -252,29 +252,29 @@ def sniff(self, filename): return True -class Html( HtmlFromText ): +class Html(HtmlFromText): """Deprecated class. This class should not be used anymore, but the galaxy.datatypes.text:Html one. This is for backwards compatibilities only.""" -class Laj( data.Text ): +class Laj(data.Text): """Class describing a LAJ Applet""" file_ext = "laj" copy_safe_peek = False - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): if not dataset.dataset.purged: - if hasattr( dataset, 'history_id' ): + if hasattr(dataset, 'history_id'): params = { "alignfile1": "display?id=%s" % dataset.id, "buttonlabel": "Launch LAJ", "title": "LAJ in Galaxy", - "posturl": quote_plus( "history_add_to?%s" % "&".join( "%s=%s" % ( key, value ) for key, value in { 'history_id': dataset.history_id, 'ext': 'lav', 'name': 'LAJ Output', 'info': 'Added by LAJ', 'dbkey': dataset.dbkey, 'copy_access_from': dataset.id }.items() ) ), + "posturl": quote_plus("history_add_to?%s" % "&".join("%s=%s" % (key, value) for key, value in {'history_id': dataset.history_id, 'ext': 'lav', 'name': 'LAJ Output', 'info': 'Added by LAJ', 'dbkey': dataset.dbkey, 'copy_access_from': dataset.id}.items())), "noseq": "true" } class_name = "edu.psu.cse.bio.laj.LajApplet.class" archive = "/static/laj/laj.jar" - dataset.peek = create_applet_tag_peek( class_name, archive, params ) + dataset.peek = create_applet_tag_peek(class_name, archive, params) else: dataset.peek = "After you add this item to your history, you will be able to launch the LAJ applet." dataset.blurb = 'LAJ Multiple Alignment Viewer' diff --git a/lib/galaxy/datatypes/interval.py b/lib/galaxy/datatypes/interval.py index 658d767af3ab..9d9a86a1c017 100644 --- a/lib/galaxy/datatypes/interval.py +++ b/lib/galaxy/datatypes/interval.py @@ -13,7 +13,10 @@ from galaxy import util from galaxy.datatypes import metadata from galaxy.datatypes.metadata import MetadataElement -from galaxy.datatypes.sniff import get_headers +from galaxy.datatypes.sniff import ( + get_headers, + iter_headers +) from galaxy.datatypes.tabular import Tabular from galaxy.datatypes.util.gff_util import parse_gff3_attributes, parse_gff_attributes from galaxy.web import url_for @@ -28,11 +31,11 @@ # Contains the meta columns and the words that map to it; list aliases on the # right side of the : in decreasing order of priority alias_spec = { - 'chromCol' : [ 'chrom', 'CHROMOSOME', 'CHROM', 'Chromosome Name' ], - 'startCol' : [ 'start', 'START', 'chromStart', 'txStart', 'Start Position (bp)' ], - 'endCol' : [ 'end', 'END', 'STOP', 'chromEnd', 'txEnd', 'End Position (bp)' ], - 'strandCol' : [ 'strand', 'STRAND', 'Strand' ], - 'nameCol' : [ 'name', 'NAME', 'Name', 'name2', 'NAME2', 'Name2', 'Ensembl Gene ID', 'Ensembl Transcript ID', 'Ensembl Peptide ID' ] + 'chromCol' : ['chrom', 'CHROMOSOME', 'CHROM', 'Chromosome Name'], + 'startCol' : ['start', 'START', 'chromStart', 'txStart', 'Start Position (bp)'], + 'endCol' : ['end', 'END', 'STOP', 'chromEnd', 'txEnd', 'End Position (bp)'], + 'strandCol' : ['strand', 'STRAND', 'Strand'], + 'nameCol' : ['name', 'NAME', 'Name', 'name2', 'NAME2', 'Name2', 'Ensembl Gene ID', 'Ensembl Transcript ID', 'Ensembl Peptide ID'] } # a little faster lookup @@ -49,70 +52,70 @@ @dataproviders.decorators.has_dataproviders -class Interval( Tabular ): +class Interval(Tabular): """Tab delimited data containing interval information""" edam_data = "data_3002" edam_format = "format_3475" file_ext = "interval" line_class = "region" track_type = "FeatureTrack" - data_sources = { "data": "tabix", "index": "bigwig" } + data_sources = {"data": "tabix", "index": "bigwig"} """Add metadata elements""" - MetadataElement( name="chromCol", default=1, desc="Chrom column", param=metadata.ColumnParameter ) - MetadataElement( name="startCol", default=2, desc="Start column", param=metadata.ColumnParameter ) - MetadataElement( name="endCol", default=3, desc="End column", param=metadata.ColumnParameter ) - MetadataElement( name="strandCol", desc="Strand column (click box & select)", param=metadata.ColumnParameter, optional=True, no_value=0 ) - MetadataElement( name="nameCol", desc="Name/Identifier column (click box & select)", param=metadata.ColumnParameter, optional=True, no_value=0 ) - MetadataElement( name="columns", default=3, desc="Number of columns", readonly=True, visible=False ) + MetadataElement(name="chromCol", default=1, desc="Chrom column", param=metadata.ColumnParameter) + MetadataElement(name="startCol", default=2, desc="Start column", param=metadata.ColumnParameter) + MetadataElement(name="endCol", default=3, desc="End column", param=metadata.ColumnParameter) + MetadataElement(name="strandCol", desc="Strand column (click box & select)", param=metadata.ColumnParameter, optional=True, no_value=0) + MetadataElement(name="nameCol", desc="Name/Identifier column (click box & select)", param=metadata.ColumnParameter, optional=True, no_value=0) + MetadataElement(name="columns", default=3, desc="Number of columns", readonly=True, visible=False) def __init__(self, **kwd): """Initialize interval datatype, by adding UCSC display apps""" Tabular.__init__(self, **kwd) - self.add_display_app( 'ucsc', 'display at UCSC', 'as_ucsc_display_file', 'ucsc_links' ) + self.add_display_app('ucsc', 'display at UCSC', 'as_ucsc_display_file', 'ucsc_links') - def init_meta( self, dataset, copy_from=None ): - Tabular.init_meta( self, dataset, copy_from=copy_from ) + def init_meta(self, dataset, copy_from=None): + Tabular.init_meta(self, dataset, copy_from=copy_from) - def set_meta( self, dataset, overwrite=True, first_line_is_header=False, **kwd ): + def set_meta(self, dataset, overwrite=True, first_line_is_header=False, **kwd): """Tries to guess from the line the location number of the column for the chromosome, region start-end and strand""" - Tabular.set_meta( self, dataset, overwrite=overwrite, skip=0 ) + Tabular.set_meta(self, dataset, overwrite=overwrite, skip=0) if dataset.has_data(): empty_line_count = 0 num_check_lines = 100 # only check up to this many non empty lines - for i, line in enumerate( open( dataset.file_name ) ): - line = line.rstrip( '\r\n' ) + for i, line in enumerate(open(dataset.file_name)): + line = line.rstrip('\r\n') if line: - if ( first_line_is_header or line[0] == '#' ): - self.init_meta( dataset ) - line = line.strip( '#' ) - elems = line.split( '\t' ) + if (first_line_is_header or line[0] == '#'): + self.init_meta(dataset) + line = line.strip('#') + elems = line.split('\t') for meta_name, header_list in alias_spec.items(): for header_val in header_list: if header_val in elems: # found highest priority header to meta_name - setattr( dataset.metadata, meta_name, elems.index( header_val ) + 1 ) + setattr(dataset.metadata, meta_name, elems.index(header_val) + 1) break # next meta_name break # Our metadata is set, so break out of the outer loop else: # Header lines in Interval files are optional. For example, BED is Interval but has no header. # We'll make a best guess at the location of the metadata columns. metadata_is_set = False - elems = line.split( '\t' ) - if len( elems ) > 2: + elems = line.split('\t') + if len(elems) > 2: for str in data.col1_startswith: - if line.lower().startswith( str ): - if overwrite or not dataset.metadata.element_is_set( 'chromCol' ): + if line.lower().startswith(str): + if overwrite or not dataset.metadata.element_is_set('chromCol'): dataset.metadata.chromCol = 1 try: - int( elems[1] ) - if overwrite or not dataset.metadata.element_is_set( 'startCol' ): + int(elems[1]) + if overwrite or not dataset.metadata.element_is_set('startCol'): dataset.metadata.startCol = 2 except: pass # Metadata default will be used try: - int( elems[2] ) - if overwrite or not dataset.metadata.element_is_set( 'endCol' ): + int(elems[2]) + if overwrite or not dataset.metadata.element_is_set('endCol'): dataset.metadata.endCol = 3 except: pass # Metadata default will be used @@ -124,20 +127,20 @@ def set_meta( self, dataset, overwrite=True, first_line_is_header=False, **kwd ) # except: # if overwrite or not dataset.metadata.element_is_set( 'nameCol' ): # dataset.metadata.nameCol = 4 - if len( elems ) < 6 or elems[5] not in data.valid_strand: - if overwrite or not dataset.metadata.element_is_set( 'strandCol' ): + if len(elems) < 6 or elems[5] not in data.valid_strand: + if overwrite or not dataset.metadata.element_is_set('strandCol'): dataset.metadata.strandCol = 0 else: - if overwrite or not dataset.metadata.element_is_set( 'strandCol' ): + if overwrite or not dataset.metadata.element_is_set('strandCol'): dataset.metadata.strandCol = 6 metadata_is_set = True break - if metadata_is_set or ( i - empty_line_count ) > num_check_lines: + if metadata_is_set or (i - empty_line_count) > num_check_lines: break # Our metadata is set or we examined 100 non-empty lines, so break out of the outer loop else: empty_line_count += 1 - def displayable( self, dataset ): + def displayable(self, dataset): try: return dataset.has_data() \ and dataset.state == dataset.states.OK \ @@ -149,41 +152,41 @@ def displayable( self, dataset ): except: return False - def get_estimated_display_viewport( self, dataset, chrom_col=None, start_col=None, end_col=None ): + def get_estimated_display_viewport(self, dataset, chrom_col=None, start_col=None, end_col=None): """Return a chrom, start, stop tuple for viewing a file.""" viewport_feature_count = 100 # viewport should check at least 100 features; excludes comment lines - max_line_count = max( viewport_feature_count, 500 ) # maximum number of lines to check; includes comment lines - if not self.displayable( dataset ): - return ( None, None, None ) + max_line_count = max(viewport_feature_count, 500) # maximum number of lines to check; includes comment lines + if not self.displayable(dataset): + return (None, None, None) try: # If column indexes were not passwed, determine from metadata if chrom_col is None: - chrom_col = int( dataset.metadata.chromCol ) - 1 + chrom_col = int(dataset.metadata.chromCol) - 1 if start_col is None: - start_col = int( dataset.metadata.startCol ) - 1 + start_col = int(dataset.metadata.startCol) - 1 if end_col is None: - end_col = int( dataset.metadata.endCol ) - 1 + end_col = int(dataset.metadata.endCol) - 1 # Scan lines of file to find a reasonable chromosome and range chrom = None start = sys.maxsize end = 0 - max_col = max( chrom_col, start_col, end_col ) - fh = open( dataset.file_name ) + max_col = max(chrom_col, start_col, end_col) + fh = open(dataset.file_name) while True: - line = fh.readline( VIEWPORT_READLINE_BUFFER_SIZE ) + line = fh.readline(VIEWPORT_READLINE_BUFFER_SIZE) # Stop if at end of file if not line: break # Skip comment lines - if not line.startswith( '#' ): + if not line.startswith('#'): try: - fields = line.rstrip().split( '\t' ) - if len( fields ) > max_col: - if chrom is None or chrom == fields[ chrom_col ]: - start = min( start, int( fields[ start_col ] ) ) - end = max( end, int( fields[ end_col ] ) ) + fields = line.rstrip().split('\t') + if len(fields) > max_col: + if chrom is None or chrom == fields[chrom_col]: + start = min(start, int(fields[start_col])) + end = max(end, int(fields[end_col])) # Set chrom last, in case start and end are not integers - chrom = fields[ chrom_col ] + chrom = fields[chrom_col] viewport_feature_count -= 1 except Exception: # Most likely a non-integer field has been encountered @@ -192,9 +195,9 @@ def get_estimated_display_viewport( self, dataset, chrom_col=None, start_col=Non pass # Make sure we are at the next new line readline_count = VIEWPORT_MAX_READS_PER_LINE - while line.rstrip( '\n\r' ) == line: - assert readline_count > 0, Exception( 'Viewport readline count exceeded for dataset %s.' % dataset.id ) - line = fh.readline( VIEWPORT_READLINE_BUFFER_SIZE ) + while line.rstrip('\n\r') == line: + assert readline_count > 0, Exception('Viewport readline count exceeded for dataset %s.' % dataset.id) + line = fh.readline(VIEWPORT_READLINE_BUFFER_SIZE) if not line: break # EOF readline_count -= 1 @@ -203,76 +206,76 @@ def get_estimated_display_viewport( self, dataset, chrom_col=None, start_col=Non # exceeded viewport or total line count to check break if chrom is not None: - return ( chrom, str( start ), str( end ) ) # Necessary to return strings? + return (chrom, str(start), str(end)) # Necessary to return strings? except Exception: # Unexpected error, possibly missing metadata - log.exception( "Exception caught attempting to generate viewport for dataset '%d'", dataset.id ) - return ( None, None, None ) + log.exception("Exception caught attempting to generate viewport for dataset '%d'", dataset.id) + return (None, None, None) - def as_ucsc_display_file( self, dataset, **kwd ): + def as_ucsc_display_file(self, dataset, **kwd): """Returns file contents with only the bed data""" fd, temp_name = tempfile.mkstemp() c, s, e, t, n = dataset.metadata.chromCol, dataset.metadata.startCol, dataset.metadata.endCol, dataset.metadata.strandCol or 0, dataset.metadata.nameCol or 0 c, s, e, t, n = int(c) - 1, int(s) - 1, int(e) - 1, int(t) - 1, int(n) - 1 if t >= 0: # strand column (should) exists - for i, elems in enumerate( util.file_iter(dataset.file_name) ): + for i, elems in enumerate(util.file_iter(dataset.file_name)): strand = "+" name = "region_%i" % i - if n >= 0 and n < len( elems ): + if n >= 0 and n < len(elems): name = elems[n] if t < len(elems): strand = elems[t] - tmp = [ elems[c], elems[s], elems[e], name, '0', strand ] - os.write(fd, '%s\n' % '\t'.join(tmp) ) + tmp = [elems[c], elems[s], elems[e], name, '0', strand] + os.write(fd, '%s\n' % '\t'.join(tmp)) elif n >= 0: # name column (should) exists - for i, elems in enumerate( util.file_iter(dataset.file_name) ): + for i, elems in enumerate(util.file_iter(dataset.file_name)): name = "region_%i" % i - if n >= 0 and n < len( elems ): + if n >= 0 and n < len(elems): name = elems[n] - tmp = [ elems[c], elems[s], elems[e], name ] - os.write(fd, '%s\n' % '\t'.join(tmp) ) + tmp = [elems[c], elems[s], elems[e], name] + os.write(fd, '%s\n' % '\t'.join(tmp)) else: for elems in util.file_iter(dataset.file_name): - tmp = [ elems[c], elems[s], elems[e] ] - os.write(fd, '%s\n' % '\t'.join(tmp) ) + tmp = [elems[c], elems[s], elems[e]] + os.write(fd, '%s\n' % '\t'.join(tmp)) os.close(fd) return open(temp_name) - def display_peek( self, dataset ): + def display_peek(self, dataset): """Returns formated html of peek""" - return self.make_html_table( dataset, column_parameter_alias={'chromCol': 'Chrom', 'startCol': 'Start', 'endCol': 'End', 'strandCol': 'Strand', 'nameCol': 'Name'} ) + return self.make_html_table(dataset, column_parameter_alias={'chromCol': 'Chrom', 'startCol': 'Start', 'endCol': 'End', 'strandCol': 'Strand', 'nameCol': 'Name'}) - def ucsc_links( self, dataset, type, app, base_url ): + def ucsc_links(self, dataset, type, app, base_url): """ Generate links to UCSC genome browser sites based on the dbkey and content of dataset. """ # Filter UCSC sites to only those that are supported by this build and # enabled. - valid_sites = [ ( name, url ) - for name, url in app.datatypes_registry.get_legacy_sites_by_build('ucsc', dataset.dbkey ) - if name in app.datatypes_registry.get_display_sites('ucsc') ] + valid_sites = [(name, url) + for name, url in app.datatypes_registry.get_legacy_sites_by_build('ucsc', dataset.dbkey) + if name in app.datatypes_registry.get_display_sites('ucsc')] if not valid_sites: return [] # If there are any valid sites, we need to generate the estimated # viewport - chrom, start, stop = self.get_estimated_display_viewport( dataset ) + chrom, start, stop = self.get_estimated_display_viewport(dataset) if chrom is None: return [] # Accumulate links for valid sites ret_val = [] for site_name, site_url in valid_sites: - internal_url = url_for( controller='dataset', dataset_id=dataset.id, - action='display_at', filename='ucsc_' + site_name ) - display_url = quote_plus( "%s%s/display_as?id=%i&display_app=%s&authz_method=display_at" % - (base_url, url_for( controller='root' ), dataset.id, type) ) - redirect_url = quote_plus( "%sdb=%s&position=%s:%s-%s&hgt.customText=%%s" % - (site_url, dataset.dbkey, chrom, start, stop ) ) - link = '%s?redirect_url=%s&display_url=%s' % ( internal_url, redirect_url, display_url ) - ret_val.append( ( site_name, link ) ) + internal_url = url_for(controller='dataset', dataset_id=dataset.id, + action='display_at', filename='ucsc_' + site_name) + display_url = quote_plus("%s%s/display_as?id=%i&display_app=%s&authz_method=display_at" % + (base_url, url_for(controller='root'), dataset.id, type)) + redirect_url = quote_plus("%sdb=%s&position=%s:%s-%s&hgt.customText=%%s" % + (site_url, dataset.dbkey, chrom, start, stop)) + link = '%s?redirect_url=%s&display_url=%s' % (internal_url, redirect_url, display_url) + ret_val.append((site_name, link)) return ret_val - def validate( self, dataset ): + def validate(self, dataset): """Validate an interval file using the bx GenomicIntervalReader""" errors = list() c, s, e, t = dataset.metadata.chromCol, dataset.metadata.startCol, dataset.metadata.endCol, dataset.metadata.strandCol @@ -294,11 +297,11 @@ def validate( self, dataset ): infile.close() return errors - def repair_methods( self, dataset ): + def repair_methods(self, dataset): """Return options for removing errors along with a description""" return [("lines", "Remove erroneous lines")] - def sniff( self, filename ): + def sniff(self, filename): """ Checks for 'intervalness' @@ -313,12 +316,12 @@ def sniff( self, filename ): >>> Interval().sniff( fname ) True """ - headers = get_headers( filename, '\t', comment_designator='#' ) try: """ If we got here, we already know the file is_column_based and is not bed, so we'll just look for some valid data. """ + headers = iter_headers(filename, '\t', comment_designator='#') for hdr in headers: if hdr: if len(hdr) < 3: @@ -326,108 +329,108 @@ def sniff( self, filename ): try: # Assume chrom start and end are in column positions 1 and 2 # respectively ( for 0 based columns ) - int( hdr[1] ) - int( hdr[2] ) + int(hdr[1]) + int(hdr[2]) except: return False return True except: return False - def get_track_resolution( self, dataset, start, end): + def get_track_resolution(self, dataset, start, end): return None # ------------- Dataproviders - @dataproviders.decorators.dataprovider_factory( 'genomic-region', - dataproviders.dataset.GenomicRegionDataProvider.settings ) - def genomic_region_dataprovider( self, dataset, **settings ): - return dataproviders.dataset.GenomicRegionDataProvider( dataset, **settings ) - - @dataproviders.decorators.dataprovider_factory( 'genomic-region-dict', - dataproviders.dataset.GenomicRegionDataProvider.settings ) - def genomic_region_dict_dataprovider( self, dataset, **settings ): - settings[ 'named_columns' ] = True - return self.genomic_region_dataprovider( dataset, **settings ) - - @dataproviders.decorators.dataprovider_factory( 'interval', - dataproviders.dataset.IntervalDataProvider.settings ) - def interval_dataprovider( self, dataset, **settings ): - return dataproviders.dataset.IntervalDataProvider( dataset, **settings ) - - @dataproviders.decorators.dataprovider_factory( 'interval-dict', - dataproviders.dataset.IntervalDataProvider.settings ) - def interval_dict_dataprovider( self, dataset, **settings ): - settings[ 'named_columns' ] = True - return self.interval_dataprovider( dataset, **settings ) - - -class BedGraph( Interval ): + @dataproviders.decorators.dataprovider_factory('genomic-region', + dataproviders.dataset.GenomicRegionDataProvider.settings) + def genomic_region_dataprovider(self, dataset, **settings): + return dataproviders.dataset.GenomicRegionDataProvider(dataset, **settings) + + @dataproviders.decorators.dataprovider_factory('genomic-region-dict', + dataproviders.dataset.GenomicRegionDataProvider.settings) + def genomic_region_dict_dataprovider(self, dataset, **settings): + settings['named_columns'] = True + return self.genomic_region_dataprovider(dataset, **settings) + + @dataproviders.decorators.dataprovider_factory('interval', + dataproviders.dataset.IntervalDataProvider.settings) + def interval_dataprovider(self, dataset, **settings): + return dataproviders.dataset.IntervalDataProvider(dataset, **settings) + + @dataproviders.decorators.dataprovider_factory('interval-dict', + dataproviders.dataset.IntervalDataProvider.settings) + def interval_dict_dataprovider(self, dataset, **settings): + settings['named_columns'] = True + return self.interval_dataprovider(dataset, **settings) + + +class BedGraph(Interval): """Tab delimited chrom/start/end/datavalue dataset""" edam_format = "format_3583" file_ext = "bedgraph" track_type = "LineTrack" - data_sources = { "data": "bigwig", "index": "bigwig" } + data_sources = {"data": "bigwig", "index": "bigwig"} - def as_ucsc_display_file( self, dataset, **kwd ): + def as_ucsc_display_file(self, dataset, **kwd): """ Returns file contents as is with no modifications. TODO: this is a functional stub and will need to be enhanced moving forward to provide additional support for bedgraph. """ - return open( dataset.file_name ) + return open(dataset.file_name) - def get_estimated_display_viewport( self, dataset, chrom_col=0, start_col=1, end_col=2 ): + def get_estimated_display_viewport(self, dataset, chrom_col=0, start_col=1, end_col=2): """ Set viewport based on dataset's first 100 lines. """ - return Interval.get_estimated_display_viewport( self, dataset, chrom_col=chrom_col, start_col=start_col, end_col=end_col ) + return Interval.get_estimated_display_viewport(self, dataset, chrom_col=chrom_col, start_col=start_col, end_col=end_col) -class Bed( Interval ): +class Bed(Interval): """Tab delimited data in BED format""" edam_format = "format_3003" file_ext = "bed" - data_sources = { "data": "tabix", "index": "bigwig", "feature_search": "fli" } + data_sources = {"data": "tabix", "index": "bigwig", "feature_search": "fli"} track_type = Interval.track_type - column_names = [ 'Chrom', 'Start', 'End', 'Name', 'Score', 'Strand', 'ThickStart', 'ThickEnd', 'ItemRGB', 'BlockCount', 'BlockSizes', 'BlockStarts' ] + column_names = ['Chrom', 'Start', 'End', 'Name', 'Score', 'Strand', 'ThickStart', 'ThickEnd', 'ItemRGB', 'BlockCount', 'BlockSizes', 'BlockStarts'] """Add metadata elements""" - MetadataElement( name="chromCol", default=1, desc="Chrom column", param=metadata.ColumnParameter ) - MetadataElement( name="startCol", default=2, desc="Start column", param=metadata.ColumnParameter ) - MetadataElement( name="endCol", default=3, desc="End column", param=metadata.ColumnParameter ) - MetadataElement( name="strandCol", desc="Strand column (click box & select)", param=metadata.ColumnParameter, optional=True, no_value=0 ) - MetadataElement( name="columns", default=3, desc="Number of columns", readonly=True, visible=False ) - MetadataElement( name="viz_filter_cols", desc="Score column for visualization", default=[4], param=metadata.ColumnParameter, optional=True, multiple=True ) + MetadataElement(name="chromCol", default=1, desc="Chrom column", param=metadata.ColumnParameter) + MetadataElement(name="startCol", default=2, desc="Start column", param=metadata.ColumnParameter) + MetadataElement(name="endCol", default=3, desc="End column", param=metadata.ColumnParameter) + MetadataElement(name="strandCol", desc="Strand column (click box & select)", param=metadata.ColumnParameter, optional=True, no_value=0) + MetadataElement(name="columns", default=3, desc="Number of columns", readonly=True, visible=False) + MetadataElement(name="viz_filter_cols", desc="Score column for visualization", default=[4], param=metadata.ColumnParameter, optional=True, multiple=True) # do we need to repeat these? they are the same as should be inherited from interval type - def set_meta( self, dataset, overwrite=True, **kwd ): + def set_meta(self, dataset, overwrite=True, **kwd): """Sets the metadata information for datasets previously determined to be in bed format.""" i = 0 if dataset.has_data(): - for i, line in enumerate( open(dataset.file_name) ): + for i, line in enumerate(open(dataset.file_name)): metadata_set = False line = line.rstrip('\r\n') if line and not line.startswith('#'): elems = line.split('\t') if len(elems) > 2: for startswith in data.col1_startswith: - if line.lower().startswith( startswith ): - if len( elems ) > 3: - if overwrite or not dataset.metadata.element_is_set( 'nameCol' ): + if line.lower().startswith(startswith): + if len(elems) > 3: + if overwrite or not dataset.metadata.element_is_set('nameCol'): dataset.metadata.nameCol = 4 if len(elems) < 6: - if overwrite or not dataset.metadata.element_is_set( 'strandCol' ): + if overwrite or not dataset.metadata.element_is_set('strandCol'): dataset.metadata.strandCol = 0 else: - if overwrite or not dataset.metadata.element_is_set( 'strandCol' ): + if overwrite or not dataset.metadata.element_is_set('strandCol'): dataset.metadata.strandCol = 6 metadata_set = True break if metadata_set: break - Tabular.set_meta( self, dataset, overwrite=overwrite, skip=i ) + Tabular.set_meta(self, dataset, overwrite=overwrite, skip=i) - def as_ucsc_display_file( self, dataset, **kwd ): + def as_ucsc_display_file(self, dataset, **kwd): """Returns file contents with only the bed data. If bed 6+, treat as interval.""" for line in open(dataset.file_name): line = line.strip() @@ -465,7 +468,7 @@ def as_ucsc_display_file( self, dataset, **kwd ): except: return "This item contains no content" - def sniff( self, filename ): + def sniff(self, filename): """ Checks for 'bedness' @@ -489,10 +492,10 @@ def sniff( self, filename ): >>> Bed().sniff( fname ) True """ - headers = get_headers( filename, '\t', comment_designator='#' ) + if not get_headers(filename, '\t', comment_designator='#', count=1): + return False try: - if not headers: - return False + headers = iter_headers(filename, '\t', comment_designator='#') for hdr in headers: if hdr[0] == '': continue @@ -505,57 +508,57 @@ def sniff( self, filename ): break if valid_col1: try: - int( hdr[1] ) - int( hdr[2] ) + int(hdr[1]) + int(hdr[2]) except: return False - if len( hdr ) > 4: + if len(hdr) > 4: # hdr[3] is a string, 'name', which defines the name of the BED line - difficult to test for this. # hdr[4] is an int, 'score', a score between 0 and 1000. try: - if int( hdr[4] ) < 0 or int( hdr[4] ) > 1000: + if int(hdr[4]) < 0 or int(hdr[4]) > 1000: return False except: return False - if len( hdr ) > 5: + if len(hdr) > 5: # hdr[5] is strand if hdr[5] not in data.valid_strand: return False - if len( hdr ) > 6: + if len(hdr) > 6: # hdr[6] is thickStart, the starting position at which the feature is drawn thickly. try: - int( hdr[6] ) + int(hdr[6]) except: return False - if len( hdr ) > 7: + if len(hdr) > 7: # hdr[7] is thickEnd, the ending position at which the feature is drawn thickly try: - int( hdr[7] ) + int(hdr[7]) except: return False - if len( hdr ) > 8: + if len(hdr) > 8: # hdr[8] is itemRgb, an RGB value of the form R,G,B (e.g. 255,0,0). However, this could also be an int (e.g., 0) try: - int( hdr[8] ) + int(hdr[8]) except: try: hdr[8].split(',') except: return False - if len( hdr ) > 9: + if len(hdr) > 9: # hdr[9] is blockCount, the number of blocks (exons) in the BED line. try: - block_count = int( hdr[9] ) + block_count = int(hdr[9]) except: return False - if len( hdr ) > 10: + if len(hdr) > 10: # hdr[10] is blockSizes - A comma-separated list of the block sizes. # Sometimes the blosck_sizes and block_starts lists end in extra commas try: block_sizes = hdr[10].rstrip(',').split(',') except: return False - if len( hdr ) > 11: + if len(hdr) > 11: # hdr[11] is blockStarts - A comma-separated list of block starts. try: block_starts = hdr[11].rstrip(',').split(',') @@ -570,7 +573,7 @@ def sniff( self, filename ): return False -class BedStrict( Bed ): +class BedStrict(Bed): """Tab delimited data in strict BED format - no non-standard columns allowed""" edam_format = "format_3584" file_ext = "bedstrict" @@ -579,81 +582,81 @@ class BedStrict( Bed ): allow_datatype_change = False # Read only metadata elements - MetadataElement( name="chromCol", default=1, desc="Chrom column", readonly=True, param=metadata.MetadataParameter ) - MetadataElement( name="startCol", default=2, desc="Start column", readonly=True, param=metadata.MetadataParameter ) # TODO: start and end should be able to be set to these or the proper thick[start/end]? - MetadataElement( name="endCol", default=3, desc="End column", readonly=True, param=metadata.MetadataParameter ) - MetadataElement( name="strandCol", desc="Strand column (click box & select)", readonly=True, param=metadata.MetadataParameter, no_value=0, optional=True ) - MetadataElement( name="nameCol", desc="Name/Identifier column (click box & select)", readonly=True, param=metadata.MetadataParameter, no_value=0, optional=True ) - MetadataElement( name="columns", default=3, desc="Number of columns", readonly=True, visible=False ) - - def __init__( self, **kwd ): - Tabular.__init__( self, **kwd ) + MetadataElement(name="chromCol", default=1, desc="Chrom column", readonly=True, param=metadata.MetadataParameter) + MetadataElement(name="startCol", default=2, desc="Start column", readonly=True, param=metadata.MetadataParameter) # TODO: start and end should be able to be set to these or the proper thick[start/end]? + MetadataElement(name="endCol", default=3, desc="End column", readonly=True, param=metadata.MetadataParameter) + MetadataElement(name="strandCol", desc="Strand column (click box & select)", readonly=True, param=metadata.MetadataParameter, no_value=0, optional=True) + MetadataElement(name="nameCol", desc="Name/Identifier column (click box & select)", readonly=True, param=metadata.MetadataParameter, no_value=0, optional=True) + MetadataElement(name="columns", default=3, desc="Number of columns", readonly=True, visible=False) + + def __init__(self, **kwd): + Tabular.__init__(self, **kwd) self.clear_display_apps() # only new style display applications for this datatype - def set_meta( self, dataset, overwrite=True, **kwd ): - Tabular.set_meta( self, dataset, overwrite=overwrite, **kwd) # need column count first + def set_meta(self, dataset, overwrite=True, **kwd): + Tabular.set_meta(self, dataset, overwrite=overwrite, **kwd) # need column count first if dataset.metadata.columns >= 4: dataset.metadata.nameCol = 4 if dataset.metadata.columns >= 6: dataset.metadata.strandCol = 6 - def sniff( self, filename ): + def sniff(self, filename): return False # NOTE: This would require aggressively validating the entire file -class Bed6( BedStrict ): +class Bed6(BedStrict): """Tab delimited data in strict BED format - no non-standard columns allowed; column count forced to 6""" edam_format = "format_3585" file_ext = "bed6" -class Bed12( BedStrict ): +class Bed12(BedStrict): """Tab delimited data in strict BED format - no non-standard columns allowed; column count forced to 12""" edam_format = "format_3586" file_ext = "bed12" class _RemoteCallMixin: - def _get_remote_call_url( self, redirect_url, site_name, dataset, type, app, base_url ): + def _get_remote_call_url(self, redirect_url, site_name, dataset, type, app, base_url): """Retrieve the URL to call out to an external site and retrieve data. This routes our external URL through a local galaxy instance which makes the data available, followed by redirecting to the remote site with a link back to the available information. """ - internal_url = "%s" % url_for( controller='dataset', dataset_id=dataset.id, action='display_at', filename='%s_%s' % ( type, site_name ) ) - base_url = app.config.get( "display_at_callback", base_url ) - display_url = quote_plus( "%s%s/display_as?id=%i&display_app=%s&authz_method=display_at" % - ( base_url, url_for( controller='root' ), dataset.id, type ) ) - link = '%s?redirect_url=%s&display_url=%s' % ( internal_url, redirect_url, display_url ) + internal_url = "%s" % url_for(controller='dataset', dataset_id=dataset.id, action='display_at', filename='%s_%s' % (type, site_name)) + base_url = app.config.get("display_at_callback", base_url) + display_url = quote_plus("%s%s/display_as?id=%i&display_app=%s&authz_method=display_at" % + (base_url, url_for(controller='root'), dataset.id, type)) + link = '%s?redirect_url=%s&display_url=%s' % (internal_url, redirect_url, display_url) return link @dataproviders.decorators.has_dataproviders -class Gff( Tabular, _RemoteCallMixin ): +class Gff(Tabular, _RemoteCallMixin): """Tab delimited data in Gff format""" edam_data = "data_1255" edam_format = "format_2305" file_ext = "gff" valid_gff_frame = ['.', '0', '1', '2'] - column_names = [ 'Seqname', 'Source', 'Feature', 'Start', 'End', 'Score', 'Strand', 'Frame', 'Group' ] - data_sources = { "data": "interval_index", "index": "bigwig", "feature_search": "fli" } + column_names = ['Seqname', 'Source', 'Feature', 'Start', 'End', 'Score', 'Strand', 'Frame', 'Group'] + data_sources = {"data": "interval_index", "index": "bigwig", "feature_search": "fli"} track_type = Interval.track_type """Add metadata elements""" - MetadataElement( name="columns", default=9, desc="Number of columns", readonly=True, visible=False ) - MetadataElement( name="column_types", default=['str', 'str', 'str', 'int', 'int', 'int', 'str', 'str', 'str'], - param=metadata.ColumnTypesParameter, desc="Column types", readonly=True, visible=False ) + MetadataElement(name="columns", default=9, desc="Number of columns", readonly=True, visible=False) + MetadataElement(name="column_types", default=['str', 'str', 'str', 'int', 'int', 'int', 'str', 'str', 'str'], + param=metadata.ColumnTypesParameter, desc="Column types", readonly=True, visible=False) - MetadataElement( name="attributes", default=0, desc="Number of attributes", readonly=True, visible=False, no_value=0 ) - MetadataElement( name="attribute_types", default={}, desc="Attribute types", param=metadata.DictParameter, readonly=True, visible=False, no_value=[] ) + MetadataElement(name="attributes", default=0, desc="Number of attributes", readonly=True, visible=False, no_value=0) + MetadataElement(name="attribute_types", default={}, desc="Attribute types", param=metadata.DictParameter, readonly=True, visible=False, no_value=[]) - def __init__( self, **kwd ): + def __init__(self, **kwd): """Initialize datatype, by adding GBrowse display app""" Tabular.__init__(self, **kwd) - self.add_display_app( 'ucsc', 'display at UCSC', 'as_ucsc_display_file', 'ucsc_links' ) - self.add_display_app( 'gbrowse', 'display in Gbrowse', 'as_gbrowse_display_file', 'gbrowse_links' ) + self.add_display_app('ucsc', 'display at UCSC', 'as_ucsc_display_file', 'ucsc_links') + self.add_display_app('gbrowse', 'display in Gbrowse', 'as_gbrowse_display_file', 'gbrowse_links') - def set_attribute_metadata( self, dataset ): + def set_attribute_metadata(self, dataset): """ Sets metadata elements for dataset's attributes. """ @@ -662,27 +665,27 @@ def set_attribute_metadata( self, dataset ): # not found in the first N lines will not have metadata. num_lines = 200 attribute_types = {} - for i, line in enumerate( open( dataset.file_name ) ): - if line and not line.startswith( '#' ): - elems = line.split( '\t' ) - if len( elems ) == 9: + for i, line in enumerate(open(dataset.file_name)): + if line and not line.startswith('#'): + elems = line.split('\t') + if len(elems) == 9: try: # Loop through attributes to set types. - for name, value in parse_gff_attributes( elems[8] ).items(): + for name, value in parse_gff_attributes(elems[8]).items(): # Default type is string. value_type = "str" try: # Try int. - int( value ) + int(value) value_type = "int" except: try: # Try float. - float( value ) + float(value) value_type = "float" except: pass - attribute_types[ name ] = value_type + attribute_types[name] = value_type except: pass if i + 1 == num_lines: @@ -690,92 +693,92 @@ def set_attribute_metadata( self, dataset ): # Set attribute metadata and then set additional metadata. dataset.metadata.attribute_types = attribute_types - dataset.metadata.attributes = len( attribute_types ) + dataset.metadata.attributes = len(attribute_types) - def set_meta( self, dataset, overwrite=True, **kwd ): - self.set_attribute_metadata( dataset ) + def set_meta(self, dataset, overwrite=True, **kwd): + self.set_attribute_metadata(dataset) i = 0 - for i, line in enumerate( open( dataset.file_name ) ): + for i, line in enumerate(open(dataset.file_name)): line = line.rstrip('\r\n') - if line and not line.startswith( '#' ): - elems = line.split( '\t' ) + if line and not line.startswith('#'): + elems = line.split('\t') if len(elems) == 9: try: - int( elems[3] ) - int( elems[4] ) + int(elems[3]) + int(elems[4]) break except: pass - Tabular.set_meta( self, dataset, overwrite=overwrite, skip=i ) + Tabular.set_meta(self, dataset, overwrite=overwrite, skip=i) - def display_peek( self, dataset ): + def display_peek(self, dataset): """Returns formated html of peek""" - return self.make_html_table( dataset, column_names=self.column_names ) + return self.make_html_table(dataset, column_names=self.column_names) - def get_estimated_display_viewport( self, dataset ): + def get_estimated_display_viewport(self, dataset): """ Return a chrom, start, stop tuple for viewing a file. There are slight differences between gff 2 and gff 3 formats. This function should correctly handle both... """ viewport_feature_count = 100 # viewport should check at least 100 features; excludes comment lines - max_line_count = max( viewport_feature_count, 500 ) # maximum number of lines to check; includes comment lines - if self.displayable( dataset ): + max_line_count = max(viewport_feature_count, 500) # maximum number of lines to check; includes comment lines + if self.displayable(dataset): try: seqid = None start = sys.maxsize stop = 0 - fh = open( dataset.file_name ) + fh = open(dataset.file_name) while True: - line = fh.readline( VIEWPORT_READLINE_BUFFER_SIZE ) + line = fh.readline(VIEWPORT_READLINE_BUFFER_SIZE) if not line: break # EOF try: - if line.startswith( '##sequence-region' ): # ##sequence-region IV 6000000 6030000 - elems = line.rstrip( '\n\r' ).split() - if len( elems ) > 3: + if line.startswith('##sequence-region'): # ##sequence-region IV 6000000 6030000 + elems = line.rstrip('\n\r').split() + if len(elems) > 3: # line looks like: # sequence-region ctg123 1 1497228 seqid = elems[1] # IV - start = int( elems[2] ) # 6000000 - stop = int( elems[3] ) # 6030000 + start = int(elems[2]) # 6000000 + stop = int(elems[3]) # 6030000 break # use location declared in file - elif len( elems ) == 2 and elems[1].find( '..' ) > 0: + elif len(elems) == 2 and elems[1].find('..') > 0: # line looks like this: # sequence-region X:120000..140000 - elems = elems[1].split( ':' ) + elems = elems[1].split(':') seqid = elems[0] - start = int( elems[1].split( '..' )[0] ) - stop = int( elems[1].split( '..' )[1] ) + start = int(elems[1].split('..')[0]) + stop = int(elems[1].split('..')[1]) break # use location declared in file else: - log.exception( "line (%s) uses an unsupported ##sequence-region definition." % str( line ) ) + log.exception("line (%s) uses an unsupported ##sequence-region definition." % str(line)) # break #no break, if bad definition, we try another method elif line.startswith("browser position"): # Allow UCSC style browser and track info in the GFF file pos_info = line.split()[-1] seqid, startend = pos_info.split(":") - start, stop = map( int, startend.split("-") ) + start, stop = map(int, startend.split("-")) break # use location declared in file - elif True not in map( line.startswith, ( '#', 'track', 'browser' ) ): # line.startswith() does not accept iterator in python2.4 + elif True not in map(line.startswith, ('#', 'track', 'browser')): # line.startswith() does not accept iterator in python2.4 viewport_feature_count -= 1 - elems = line.rstrip( '\n\r' ).split( '\t' ) - if len( elems ) > 3: + elems = line.rstrip('\n\r').split('\t') + if len(elems) > 3: if not seqid: # We can only set the viewport for a single chromosome seqid = elems[0] if seqid == elems[0]: # Make sure we have not spanned chromosomes - start = min( start, int( elems[3] ) ) - stop = max( stop, int( elems[4] ) ) + start = min(start, int(elems[3])) + stop = max(stop, int(elems[4])) except: # most likely start/stop is not an int or not enough fields pass # make sure we are at the next new line readline_count = VIEWPORT_MAX_READS_PER_LINE - while line.rstrip( '\n\r' ) == line: - assert readline_count > 0, Exception( 'Viewport readline count exceeded for dataset %s.' % dataset.id ) - line = fh.readline( VIEWPORT_READLINE_BUFFER_SIZE ) + while line.rstrip('\n\r') == line: + assert readline_count > 0, Exception('Viewport readline count exceeded for dataset %s.' % dataset.id) + line = fh.readline(VIEWPORT_READLINE_BUFFER_SIZE) if not line: break # EOF readline_count -= 1 @@ -784,39 +787,39 @@ def get_estimated_display_viewport( self, dataset ): # exceeded viewport or total line count to check break if seqid is not None: - return ( seqid, str( start ), str( stop ) ) # Necessary to return strings? + return (seqid, str(start), str(stop)) # Necessary to return strings? except Exception as e: # unexpected error - log.exception( str( e ) ) - return ( None, None, None ) # could not determine viewport + log.exception(str(e)) + return (None, None, None) # could not determine viewport - def ucsc_links( self, dataset, type, app, base_url ): + def ucsc_links(self, dataset, type, app, base_url): ret_val = [] - seqid, start, stop = self.get_estimated_display_viewport( dataset ) + seqid, start, stop = self.get_estimated_display_viewport(dataset) if seqid is not None: - for site_name, site_url in app.datatypes_registry.get_legacy_sites_by_build('ucsc', dataset.dbkey ): + for site_name, site_url in app.datatypes_registry.get_legacy_sites_by_build('ucsc', dataset.dbkey): if site_name in app.datatypes_registry.get_display_sites('ucsc'): redirect_url = quote_plus( "%sdb=%s&position=%s:%s-%s&hgt.customText=%%s" % - ( site_url, dataset.dbkey, seqid, start, stop ) ) - link = self._get_remote_call_url( redirect_url, site_name, dataset, type, app, base_url ) - ret_val.append( ( site_name, link ) ) + (site_url, dataset.dbkey, seqid, start, stop)) + link = self._get_remote_call_url(redirect_url, site_name, dataset, type, app, base_url) + ret_val.append((site_name, link)) return ret_val - def gbrowse_links( self, dataset, type, app, base_url ): + def gbrowse_links(self, dataset, type, app, base_url): ret_val = [] - seqid, start, stop = self.get_estimated_display_viewport( dataset ) + seqid, start, stop = self.get_estimated_display_viewport(dataset) if seqid is not None: - for site_name, site_url in app.datatypes_registry.get_legacy_sites_by_build('gbrowse', dataset.dbkey ): + for site_name, site_url in app.datatypes_registry.get_legacy_sites_by_build('gbrowse', dataset.dbkey): if site_name in app.datatypes_registry.get_display_sites('gbrowse'): - if seqid.startswith( 'chr' ) and len( seqid ) > 3: + if seqid.startswith('chr') and len(seqid) > 3: seqid = seqid[3:] - redirect_url = quote_plus( "%s/?q=%s:%s..%s&eurl=%%s" % ( site_url, seqid, start, stop ) ) - link = self._get_remote_call_url( redirect_url, site_name, dataset, type, app, base_url ) - ret_val.append( ( site_name, link ) ) + redirect_url = quote_plus("%s/?q=%s:%s..%s&eurl=%%s" % (site_url, seqid, start, stop)) + link = self._get_remote_call_url(redirect_url, site_name, dataset, type, app, base_url) + ret_val.append((site_name, link)) return ret_val - def sniff( self, filename ): + def sniff(self, filename): """ Determines whether the file is in gff format @@ -832,24 +835,24 @@ def sniff( self, filename ): >>> Gff().sniff( fname ) True """ - headers = get_headers( filename, '\t' ) + if len(get_headers(filename, '\t', count=2)) < 2: + return False try: - if len(headers) < 2: - return False + headers = iter_headers(filename, '\t') for hdr in headers: - if hdr and hdr[0].startswith( '##gff-version' ) and hdr[0].find( '2' ) < 0: + if hdr and hdr[0].startswith('##gff-version') and hdr[0].find('2') < 0: return False - if hdr and hdr[0] and not hdr[0].startswith( '#' ): + if hdr and hdr[0] and not hdr[0].startswith('#'): if len(hdr) != 9: return False try: - int( hdr[3] ) - int( hdr[4] ) + int(hdr[3]) + int(hdr[4]) except: return False if hdr[5] != '.': try: - float( hdr[5] ) + float(hdr[5]) except: return False if hdr[6] not in data.valid_strand: @@ -862,65 +865,65 @@ def sniff( self, filename ): # ------------- Dataproviders # redefine bc super is Tabular - @dataproviders.decorators.dataprovider_factory( 'genomic-region', - dataproviders.dataset.GenomicRegionDataProvider.settings ) - def genomic_region_dataprovider( self, dataset, **settings ): - return dataproviders.dataset.GenomicRegionDataProvider( dataset, 0, 3, 4, **settings ) - - @dataproviders.decorators.dataprovider_factory( 'genomic-region-dict', - dataproviders.dataset.GenomicRegionDataProvider.settings ) - def genomic_region_dict_dataprovider( self, dataset, **settings ): - settings[ 'named_columns' ] = True - return self.genomic_region_dataprovider( dataset, **settings ) - - @dataproviders.decorators.dataprovider_factory( 'interval', - dataproviders.dataset.IntervalDataProvider.settings ) - def interval_dataprovider( self, dataset, **settings ): - return dataproviders.dataset.IntervalDataProvider( dataset, 0, 3, 4, 6, 2, **settings ) - - @dataproviders.decorators.dataprovider_factory( 'interval-dict', - dataproviders.dataset.IntervalDataProvider.settings ) - def interval_dict_dataprovider( self, dataset, **settings ): - settings[ 'named_columns' ] = True - return self.interval_dataprovider( dataset, **settings ) - - -class Gff3( Gff ): + @dataproviders.decorators.dataprovider_factory('genomic-region', + dataproviders.dataset.GenomicRegionDataProvider.settings) + def genomic_region_dataprovider(self, dataset, **settings): + return dataproviders.dataset.GenomicRegionDataProvider(dataset, 0, 3, 4, **settings) + + @dataproviders.decorators.dataprovider_factory('genomic-region-dict', + dataproviders.dataset.GenomicRegionDataProvider.settings) + def genomic_region_dict_dataprovider(self, dataset, **settings): + settings['named_columns'] = True + return self.genomic_region_dataprovider(dataset, **settings) + + @dataproviders.decorators.dataprovider_factory('interval', + dataproviders.dataset.IntervalDataProvider.settings) + def interval_dataprovider(self, dataset, **settings): + return dataproviders.dataset.IntervalDataProvider(dataset, 0, 3, 4, 6, 2, **settings) + + @dataproviders.decorators.dataprovider_factory('interval-dict', + dataproviders.dataset.IntervalDataProvider.settings) + def interval_dict_dataprovider(self, dataset, **settings): + settings['named_columns'] = True + return self.interval_dataprovider(dataset, **settings) + + +class Gff3(Gff): """Tab delimited data in Gff3 format""" edam_format = "format_1975" file_ext = "gff3" valid_gff3_strand = ['+', '-', '.', '?'] valid_gff3_phase = Gff.valid_gff_frame - column_names = [ 'Seqid', 'Source', 'Type', 'Start', 'End', 'Score', 'Strand', 'Phase', 'Attributes' ] + column_names = ['Seqid', 'Source', 'Type', 'Start', 'End', 'Score', 'Strand', 'Phase', 'Attributes'] track_type = Interval.track_type """Add metadata elements""" - MetadataElement( name="column_types", default=['str', 'str', 'str', 'int', 'int', 'float', 'str', 'int', 'list'], - param=metadata.ColumnTypesParameter, desc="Column types", readonly=True, visible=False ) + MetadataElement(name="column_types", default=['str', 'str', 'str', 'int', 'int', 'float', 'str', 'int', 'list'], + param=metadata.ColumnTypesParameter, desc="Column types", readonly=True, visible=False) def __init__(self, **kwd): """Initialize datatype, by adding GBrowse display app""" Gff.__init__(self, **kwd) - def set_meta( self, dataset, overwrite=True, **kwd ): - self.set_attribute_metadata( dataset ) + def set_meta(self, dataset, overwrite=True, **kwd): + self.set_attribute_metadata(dataset) i = 0 - for i, line in enumerate( open( dataset.file_name ) ): + for i, line in enumerate(open(dataset.file_name)): line = line.rstrip('\r\n') - if line and not line.startswith( '#' ): - elems = line.split( '\t' ) + if line and not line.startswith('#'): + elems = line.split('\t') valid_start = False valid_end = False - if len( elems ) == 9: + if len(elems) == 9: try: - start = int( elems[3] ) + start = int(elems[3]) valid_start = True except: if elems[3] == '.': valid_start = True try: - end = int( elems[4] ) + end = int(elems[4]) valid_end = True except: if elems[4] == '.': @@ -929,9 +932,9 @@ def set_meta( self, dataset, overwrite=True, **kwd ): phase = elems[7] if valid_start and valid_end and start < end and strand in self.valid_gff3_strand and phase in self.valid_gff3_phase: break - Tabular.set_meta( self, dataset, overwrite=overwrite, skip=i ) + Tabular.set_meta(self, dataset, overwrite=overwrite, skip=i) - def sniff( self, filename ): + def sniff(self, filename): """ Determines whether the file is in GFF version 3 format @@ -964,32 +967,32 @@ def sniff( self, filename ): >>> Gff3().sniff( fname ) True """ - headers = get_headers( filename, '\t' ) + if len(get_headers(filename, '\t', count=2)) < 2: + return False try: - if len(headers) < 2: - return False + headers = iter_headers(filename, '\t') for hdr in headers: - if hdr and hdr[0].startswith( '##gff-version' ) and hdr[0].find( '3' ) >= 0: + if hdr and hdr[0].startswith('##gff-version') and hdr[0].find('3') >= 0: return True - elif hdr and hdr[0].startswith( '##gff-version' ) and hdr[0].find( '3' ) < 0: + elif hdr and hdr[0].startswith('##gff-version') and hdr[0].find('3') < 0: return False # Header comments may have been stripped, so inspect the data - if hdr and hdr[0] and not hdr[0].startswith( '#' ): + if hdr and hdr[0] and not hdr[0].startswith('#'): if len(hdr) != 9: return False try: - int( hdr[3] ) + int(hdr[3]) except: if hdr[3] != '.': return False try: - int( hdr[4] ) + int(hdr[4]) except: if hdr[4] != '.': return False if hdr[5] != '.': try: - float( hdr[5] ) + float(hdr[5]) except: return False if hdr[6] not in self.valid_gff3_strand: @@ -1002,19 +1005,19 @@ def sniff( self, filename ): return False -class Gtf( Gff ): +class Gtf(Gff): """Tab delimited data in Gtf format""" edam_format = "format_2306" file_ext = "gtf" - column_names = [ 'Seqname', 'Source', 'Feature', 'Start', 'End', 'Score', 'Strand', 'Frame', 'Attributes' ] + column_names = ['Seqname', 'Source', 'Feature', 'Start', 'End', 'Score', 'Strand', 'Frame', 'Attributes'] track_type = Interval.track_type """Add metadata elements""" - MetadataElement( name="columns", default=9, desc="Number of columns", readonly=True, visible=False ) - MetadataElement( name="column_types", default=['str', 'str', 'str', 'int', 'int', 'float', 'str', 'int', 'list'], - param=metadata.ColumnTypesParameter, desc="Column types", readonly=True, visible=False ) + MetadataElement(name="columns", default=9, desc="Number of columns", readonly=True, visible=False) + MetadataElement(name="column_types", default=['str', 'str', 'str', 'int', 'int', 'float', 'str', 'int', 'list'], + param=metadata.ColumnTypesParameter, desc="Column types", readonly=True, visible=False) - def sniff( self, filename ): + def sniff(self, filename): """ Determines whether the file is in gtf format @@ -1039,24 +1042,24 @@ def sniff( self, filename ): >>> Gtf().sniff( fname ) True """ - headers = get_headers( filename, '\t' ) + if len(get_headers(filename, '\t', count=2)) < 2: + return False try: - if len(headers) < 2: - return False + headers = iter_headers(filename, '\t') for hdr in headers: - if hdr and hdr[0].startswith( '##gff-version' ) and hdr[0].find( '2' ) < 0: + if hdr and hdr[0].startswith('##gff-version') and hdr[0].find('2') < 0: return False - if hdr and hdr[0] and not hdr[0].startswith( '#' ): + if hdr and hdr[0] and not hdr[0].startswith('#'): if len(hdr) != 9: return False try: - int( hdr[3] ) - int( hdr[4] ) + int(hdr[3]) + int(hdr[4]) except: return False if hdr[5] != '.': try: - float( hdr[5] ) + float(hdr[5]) except: return False if hdr[6] not in data.valid_strand: @@ -1065,8 +1068,8 @@ def sniff( self, filename ): return False # Check attributes for gene_id, transcript_id - attributes = parse_gff_attributes( hdr[8] ) - if len( attributes ) >= 2: + attributes = parse_gff_attributes(hdr[8]) + if len(attributes) >= 2: if 'gene_id' not in attributes: return False if 'transcript_id' not in attributes: @@ -1079,54 +1082,54 @@ def sniff( self, filename ): @dataproviders.decorators.has_dataproviders -class Wiggle( Tabular, _RemoteCallMixin ): +class Wiggle(Tabular, _RemoteCallMixin): """Tab delimited data in wiggle format""" edam_format = "format_3005" file_ext = "wig" track_type = "LineTrack" - data_sources = { "data": "bigwig", "index": "bigwig" } + data_sources = {"data": "bigwig", "index": "bigwig"} - MetadataElement( name="columns", default=3, desc="Number of columns", readonly=True, visible=False ) + MetadataElement(name="columns", default=3, desc="Number of columns", readonly=True, visible=False) - def __init__( self, **kwd ): - Tabular.__init__( self, **kwd ) - self.add_display_app( 'ucsc', 'display at UCSC', 'as_ucsc_display_file', 'ucsc_links' ) - self.add_display_app( 'gbrowse', 'display in Gbrowse', 'as_gbrowse_display_file', 'gbrowse_links' ) + def __init__(self, **kwd): + Tabular.__init__(self, **kwd) + self.add_display_app('ucsc', 'display at UCSC', 'as_ucsc_display_file', 'ucsc_links') + self.add_display_app('gbrowse', 'display in Gbrowse', 'as_gbrowse_display_file', 'gbrowse_links') - def get_estimated_display_viewport( self, dataset ): + def get_estimated_display_viewport(self, dataset): """Return a chrom, start, stop tuple for viewing a file.""" viewport_feature_count = 100 # viewport should check at least 100 features; excludes comment lines - max_line_count = max( viewport_feature_count, 500 ) # maximum number of lines to check; includes comment lines - if self.displayable( dataset ): + max_line_count = max(viewport_feature_count, 500) # maximum number of lines to check; includes comment lines + if self.displayable(dataset): try: chrom = None start = sys.maxsize end = 0 span = 1 step = None - fh = open( dataset.file_name ) + fh = open(dataset.file_name) while True: - line = fh.readline( VIEWPORT_READLINE_BUFFER_SIZE ) + line = fh.readline(VIEWPORT_READLINE_BUFFER_SIZE) if not line: break # EOF try: - if line.startswith( "browser" ): - chr_info = line.rstrip( '\n\r' ).split()[-1] - chrom, coords = chr_info.split( ":" ) - start, end = map( int, coords.split( "-" ) ) + if line.startswith("browser"): + chr_info = line.rstrip('\n\r').split()[-1] + chrom, coords = chr_info.split(":") + start, end = map(int, coords.split("-")) break # use the browser line # variableStep chrom=chr20 - if line and ( line.lower().startswith( "variablestep" ) or line.lower().startswith( "fixedstep" ) ): + if line and (line.lower().startswith("variablestep") or line.lower().startswith("fixedstep")): if chrom is not None: break # different chrom or different section of the chrom - chrom = line.rstrip( '\n\r' ).split("chrom=")[1].split()[0] + chrom = line.rstrip('\n\r').split("chrom=")[1].split()[0] if 'span=' in line: - span = int( line.rstrip( '\n\r' ).split("span=")[1].split()[0] ) + span = int(line.rstrip('\n\r').split("span=")[1].split()[0]) if 'step=' in line: - step = int( line.rstrip( '\n\r' ).split("step=")[1].split()[0] ) - start = int( line.rstrip( '\n\r' ).split("start=")[1].split()[0] ) + step = int(line.rstrip('\n\r').split("step=")[1].split()[0]) + start = int(line.rstrip('\n\r').split("start=")[1].split()[0]) else: - fields = line.rstrip( '\n\r' ).split() + fields = line.rstrip('\n\r').split() if fields: if step is not None: if not end: @@ -1134,16 +1137,16 @@ def get_estimated_display_viewport( self, dataset ): else: end += step else: - start = min( int( fields[0] ), start ) - end = max( end, int( fields[0] ) + span ) + start = min(int(fields[0]), start) + end = max(end, int(fields[0]) + span) viewport_feature_count -= 1 except: pass # make sure we are at the next new line readline_count = VIEWPORT_MAX_READS_PER_LINE - while line.rstrip( '\n\r' ) == line: - assert readline_count > 0, Exception( 'Viewport readline count exceeded for dataset %s.' % dataset.id ) - line = fh.readline( VIEWPORT_READLINE_BUFFER_SIZE ) + while line.rstrip('\n\r') == line: + assert readline_count > 0, Exception('Viewport readline count exceeded for dataset %s.' % dataset.id) + line = fh.readline(VIEWPORT_READLINE_BUFFER_SIZE) if not line: break # EOF readline_count -= 1 @@ -1152,54 +1155,54 @@ def get_estimated_display_viewport( self, dataset ): # exceeded viewport or total line count to check break if chrom is not None: - return ( chrom, str( start ), str( end ) ) # Necessary to return strings? + return (chrom, str(start), str(end)) # Necessary to return strings? except Exception as e: # unexpected error - log.exception( str( e ) ) - return ( None, None, None ) # could not determine viewport + log.exception(str(e)) + return (None, None, None) # could not determine viewport - def gbrowse_links( self, dataset, type, app, base_url ): + def gbrowse_links(self, dataset, type, app, base_url): ret_val = [] - chrom, start, stop = self.get_estimated_display_viewport( dataset ) + chrom, start, stop = self.get_estimated_display_viewport(dataset) if chrom is not None: - for site_name, site_url in app.datatypes_registry.get_legacy_sites_by_build('gbrowse', dataset.dbkey ): + for site_name, site_url in app.datatypes_registry.get_legacy_sites_by_build('gbrowse', dataset.dbkey): if site_name in app.datatypes_registry.get_display_sites('gbrowse'): - if chrom.startswith( 'chr' ) and len( chrom ) > 3: + if chrom.startswith('chr') and len(chrom) > 3: chrom = chrom[3:] - redirect_url = quote_plus( "%s/?q=%s:%s..%s&eurl=%%s" % ( site_url, chrom, start, stop ) ) - link = self._get_remote_call_url( redirect_url, site_name, dataset, type, app, base_url ) - ret_val.append( ( site_name, link ) ) + redirect_url = quote_plus("%s/?q=%s:%s..%s&eurl=%%s" % (site_url, chrom, start, stop)) + link = self._get_remote_call_url(redirect_url, site_name, dataset, type, app, base_url) + ret_val.append((site_name, link)) return ret_val - def ucsc_links( self, dataset, type, app, base_url ): + def ucsc_links(self, dataset, type, app, base_url): ret_val = [] - chrom, start, stop = self.get_estimated_display_viewport( dataset ) + chrom, start, stop = self.get_estimated_display_viewport(dataset) if chrom is not None: - for site_name, site_url in app.datatypes_registry.get_legacy_sites_by_build('ucsc', dataset.dbkey ): + for site_name, site_url in app.datatypes_registry.get_legacy_sites_by_build('ucsc', dataset.dbkey): if site_name in app.datatypes_registry.get_display_sites('ucsc'): - redirect_url = quote_plus( "%sdb=%s&position=%s:%s-%s&hgt.customText=%%s" % ( site_url, dataset.dbkey, chrom, start, stop ) ) - link = self._get_remote_call_url( redirect_url, site_name, dataset, type, app, base_url ) - ret_val.append( ( site_name, link ) ) + redirect_url = quote_plus("%sdb=%s&position=%s:%s-%s&hgt.customText=%%s" % (site_url, dataset.dbkey, chrom, start, stop)) + link = self._get_remote_call_url(redirect_url, site_name, dataset, type, app, base_url) + ret_val.append((site_name, link)) return ret_val - def display_peek( self, dataset ): + def display_peek(self, dataset): """Returns formated html of peek""" - return self.make_html_table( dataset, skipchars=['track', '#'] ) + return self.make_html_table(dataset, skipchars=['track', '#']) - def set_meta( self, dataset, overwrite=True, **kwd ): + def set_meta(self, dataset, overwrite=True, **kwd): max_data_lines = None i = 0 - for i, line in enumerate( open( dataset.file_name ) ): + for i, line in enumerate(open(dataset.file_name)): line = line.rstrip('\r\n') - if line and not line.startswith( '#' ): - elems = line.split( '\t' ) + if line and not line.startswith('#'): + elems = line.split('\t') try: - float( elems[0] ) # "Wiggle track data values can be integer or real, positive or negative values" + float(elems[0]) # "Wiggle track data values can be integer or real, positive or negative values" break except: do_break = False for col_startswith in data.col1_startswith: - if elems[0].lower().startswith( col_startswith ): + if elems[0].lower().startswith(col_startswith): do_break = True break if do_break: @@ -1210,9 +1213,9 @@ def set_meta( self, dataset, overwrite=True, **kwd ): # but those cases are not a single table that would have consistant column definitions # optional metadata values set in Tabular class will be 'None' max_data_lines = 100 - Tabular.set_meta( self, dataset, overwrite=overwrite, skip=i, max_data_lines=max_data_lines ) + Tabular.set_meta(self, dataset, overwrite=overwrite, skip=i, max_data_lines=max_data_lines) - def sniff( self, filename ): + def sniff(self, filename): """ Determines wether the file is in wiggle format @@ -1235,8 +1238,8 @@ def sniff( self, filename ): >>> Wiggle().sniff( fname ) True """ - headers = get_headers( filename, None ) try: + headers = iter_headers(filename, None) for hdr in headers: if len(hdr) > 1 and hdr[0] == 'track' and hdr[1].startswith('type=wiggle'): return True @@ -1244,29 +1247,29 @@ def sniff( self, filename ): except: return False - def get_track_resolution( self, dataset, start, end): + def get_track_resolution(self, dataset, start, end): range = end - start # Determine appropriate resolution to plot ~1000 points - resolution = math.ceil( 10 ** math.ceil( math.log10( range / 1000 ) ) ) + resolution = math.ceil(10 ** math.ceil(math.log10(range / 1000))) # Restrict to valid range - resolution = min( resolution, 100000 ) - resolution = max( resolution, 1 ) + resolution = min(resolution, 100000) + resolution = max(resolution, 1) return resolution # ------------- Dataproviders - @dataproviders.decorators.dataprovider_factory( 'wiggle', dataproviders.dataset.WiggleDataProvider.settings ) - def wiggle_dataprovider( self, dataset, **settings ): - dataset_source = dataproviders.dataset.DatasetDataProvider( dataset ) - return dataproviders.dataset.WiggleDataProvider( dataset_source, **settings ) + @dataproviders.decorators.dataprovider_factory('wiggle', dataproviders.dataset.WiggleDataProvider.settings) + def wiggle_dataprovider(self, dataset, **settings): + dataset_source = dataproviders.dataset.DatasetDataProvider(dataset) + return dataproviders.dataset.WiggleDataProvider(dataset_source, **settings) - @dataproviders.decorators.dataprovider_factory( 'wiggle-dict', dataproviders.dataset.WiggleDataProvider.settings ) - def wiggle_dict_dataprovider( self, dataset, **settings ): - dataset_source = dataproviders.dataset.DatasetDataProvider( dataset ) - settings[ 'named_columns' ] = True - return dataproviders.dataset.WiggleDataProvider( dataset_source, **settings ) + @dataproviders.decorators.dataprovider_factory('wiggle-dict', dataproviders.dataset.WiggleDataProvider.settings) + def wiggle_dict_dataprovider(self, dataset, **settings): + dataset_source = dataproviders.dataset.DatasetDataProvider(dataset) + settings['named_columns'] = True + return dataproviders.dataset.WiggleDataProvider(dataset_source, **settings) -class CustomTrack ( Tabular ): +class CustomTrack (Tabular): """UCSC CustomTrack""" edam_format = "format_3588" file_ext = "customtrack" @@ -1274,16 +1277,16 @@ class CustomTrack ( Tabular ): def __init__(self, **kwd): """Initialize interval datatype, by adding UCSC display app""" Tabular.__init__(self, **kwd) - self.add_display_app( 'ucsc', 'display at UCSC', 'as_ucsc_display_file', 'ucsc_links' ) + self.add_display_app('ucsc', 'display at UCSC', 'as_ucsc_display_file', 'ucsc_links') - def set_meta( self, dataset, overwrite=True, **kwd ): - Tabular.set_meta( self, dataset, overwrite=overwrite, skip=1 ) + def set_meta(self, dataset, overwrite=True, **kwd): + Tabular.set_meta(self, dataset, overwrite=overwrite, skip=1) - def display_peek( self, dataset ): + def display_peek(self, dataset): """Returns formated html of peek""" - return self.make_html_table( dataset, skipchars=['track', '#'] ) + return self.make_html_table(dataset, skipchars=['track', '#']) - def get_estimated_display_viewport( self, dataset, chrom_col=None, start_col=None, end_col=None ): + def get_estimated_display_viewport(self, dataset, chrom_col=None, start_col=None, end_col=None): """Return a chrom, start, stop tuple for viewing a file.""" # FIXME: only BED and WIG custom tracks are currently supported # As per previously existing behavior, viewport will only be over the first intervals @@ -1291,44 +1294,44 @@ def get_estimated_display_viewport( self, dataset, chrom_col=None, start_col=Non variable_step_wig = False chrom = None span = 1 - if self.displayable( dataset ): + if self.displayable(dataset): try: - fh = open( dataset.file_name ) + fh = open(dataset.file_name) while True: - line = fh.readline( VIEWPORT_READLINE_BUFFER_SIZE ) + line = fh.readline(VIEWPORT_READLINE_BUFFER_SIZE) if not line: break # EOF - if not line.startswith( '#' ): + if not line.startswith('#'): try: if variable_step_wig: fields = line.rstrip().split() - if len( fields ) == 2: - start = int( fields[ 0 ] ) - return ( chrom, str( start ), str( start + span ) ) - elif line and ( line.lower().startswith( "variablestep" ) or line.lower().startswith( "fixedstep" ) ): - chrom = line.rstrip( '\n\r' ).split("chrom=")[1].split()[0] + if len(fields) == 2: + start = int(fields[0]) + return (chrom, str(start), str(start + span)) + elif line and (line.lower().startswith("variablestep") or line.lower().startswith("fixedstep")): + chrom = line.rstrip('\n\r').split("chrom=")[1].split()[0] if 'span=' in line: - span = int( line.rstrip( '\n\r' ).split("span=")[1].split()[0] ) + span = int(line.rstrip('\n\r').split("span=")[1].split()[0]) if 'start=' in line: - start = int( line.rstrip( '\n\r' ).split("start=")[1].split()[0] ) - return ( chrom, str( start ), str( start + span ) ) + start = int(line.rstrip('\n\r').split("start=")[1].split()[0]) + return (chrom, str(start), str(start + span)) else: variable_step_wig = True else: - fields = line.rstrip().split( '\t' ) - if len( fields ) >= 3: - chrom = fields[ 0 ] - start = int( fields[ 1 ] ) - end = int( fields[ 2 ] ) - return ( chrom, str( start ), str( end ) ) + fields = line.rstrip().split('\t') + if len(fields) >= 3: + chrom = fields[0] + start = int(fields[1]) + end = int(fields[2]) + return (chrom, str(start), str(end)) except Exception: # most likely a non-integer field has been encountered for start / stop continue # make sure we are at the next new line readline_count = VIEWPORT_MAX_READS_PER_LINE - while line.rstrip( '\n\r' ) == line: - assert readline_count > 0, Exception( 'Viewport readline count exceeded for dataset %s.' % dataset.id ) - line = fh.readline( VIEWPORT_READLINE_BUFFER_SIZE ) + while line.rstrip('\n\r') == line: + assert readline_count > 0, Exception('Viewport readline count exceeded for dataset %s.' % dataset.id) + line = fh.readline(VIEWPORT_READLINE_BUFFER_SIZE) if not line: break # EOF readline_count -= 1 @@ -1338,23 +1341,23 @@ def get_estimated_display_viewport( self, dataset, chrom_col=None, start_col=Non break except Exception as e: # unexpected error - log.exception( str( e ) ) - return ( None, None, None ) # could not determine viewport + log.exception(str(e)) + return (None, None, None) # could not determine viewport - def ucsc_links( self, dataset, type, app, base_url ): + def ucsc_links(self, dataset, type, app, base_url): ret_val = [] chrom, start, stop = self.get_estimated_display_viewport(dataset) if chrom is not None: for site_name, site_url in app.datatypes_registry.get_legacy_sites_by_build('ucsc', dataset.dbkey): if site_name in app.datatypes_registry.get_display_sites('ucsc'): - internal_url = "%s" % url_for( controller='dataset', dataset_id=dataset.id, action='display_at', filename='ucsc_' + site_name ) - display_url = quote_plus( "%s%s/display_as?id=%i&display_app=%s&authz_method=display_at" % (base_url, url_for( controller='root' ), dataset.id, type) ) - redirect_url = quote_plus( "%sdb=%s&position=%s:%s-%s&hgt.customText=%%s" % (site_url, dataset.dbkey, chrom, start, stop ) ) - link = '%s?redirect_url=%s&display_url=%s' % ( internal_url, redirect_url, display_url ) - ret_val.append( (site_name, link) ) + internal_url = "%s" % url_for(controller='dataset', dataset_id=dataset.id, action='display_at', filename='ucsc_' + site_name) + display_url = quote_plus("%s%s/display_as?id=%i&display_app=%s&authz_method=display_at" % (base_url, url_for(controller='root'), dataset.id, type)) + redirect_url = quote_plus("%sdb=%s&position=%s:%s-%s&hgt.customText=%%s" % (site_url, dataset.dbkey, chrom, start, stop)) + link = '%s?redirect_url=%s&display_url=%s' % (internal_url, redirect_url, display_url) + ret_val.append((site_name, link)) return ret_val - def sniff( self, filename ): + def sniff(self, filename): """ Determines whether the file is in customtrack format. @@ -1371,7 +1374,7 @@ def sniff( self, filename ): >>> CustomTrack().sniff( fname ) True """ - headers = get_headers( filename, None ) + headers = iter_headers(filename, None) first_line = True for hdr in headers: if first_line: @@ -1395,12 +1398,12 @@ def sniff( self, filename ): return False else: try: - if hdr[0] and not hdr[0].startswith( '#' ): - if len( hdr ) < 3: + if hdr[0] and not hdr[0].startswith('#'): + if len(hdr) < 3: return False try: - int( hdr[1] ) - int( hdr[2] ) + int(hdr[1]) + int(hdr[2]) except: return False except: @@ -1408,7 +1411,7 @@ def sniff( self, filename ): return True -class ENCODEPeak( Interval ): +class ENCODEPeak(Interval): ''' Human ENCODE peak format. There are both broad and narrow peak formats. Formats are very similar; narrow peak has an additional column, though. @@ -1423,41 +1426,41 @@ class ENCODEPeak( Interval ): ''' edam_format = "format_3612" file_ext = "encodepeak" - column_names = [ 'Chrom', 'Start', 'End', 'Name', 'Score', 'Strand', 'SignalValue', 'pValue', 'qValue', 'Peak' ] - data_sources = { "data": "tabix", "index": "bigwig" } + column_names = ['Chrom', 'Start', 'End', 'Name', 'Score', 'Strand', 'SignalValue', 'pValue', 'qValue', 'Peak'] + data_sources = {"data": "tabix", "index": "bigwig"} """Add metadata elements""" - MetadataElement( name="chromCol", default=1, desc="Chrom column", param=metadata.ColumnParameter ) - MetadataElement( name="startCol", default=2, desc="Start column", param=metadata.ColumnParameter ) - MetadataElement( name="endCol", default=3, desc="End column", param=metadata.ColumnParameter ) - MetadataElement( name="strandCol", desc="Strand column (click box & select)", param=metadata.ColumnParameter, optional=True, no_value=0 ) - MetadataElement( name="columns", default=3, desc="Number of columns", readonly=True, visible=False ) + MetadataElement(name="chromCol", default=1, desc="Chrom column", param=metadata.ColumnParameter) + MetadataElement(name="startCol", default=2, desc="Start column", param=metadata.ColumnParameter) + MetadataElement(name="endCol", default=3, desc="End column", param=metadata.ColumnParameter) + MetadataElement(name="strandCol", desc="Strand column (click box & select)", param=metadata.ColumnParameter, optional=True, no_value=0) + MetadataElement(name="columns", default=3, desc="Number of columns", readonly=True, visible=False) - def sniff( self, filename ): + def sniff(self, filename): return False -class ChromatinInteractions( Interval ): +class ChromatinInteractions(Interval): ''' Chromatin interactions obtained from 3C/5C/Hi-C experiments. ''' file_ext = "chrint" track_type = "DiagonalHeatmapTrack" - data_sources = { "data": "tabix", "index": "bigwig" } - column_names = [ 'Chrom1', 'Start1', 'End1', 'Chrom2', 'Start2', 'End2', 'Value' ] + data_sources = {"data": "tabix", "index": "bigwig"} + column_names = ['Chrom1', 'Start1', 'End1', 'Chrom2', 'Start2', 'End2', 'Value'] """Add metadata elements""" - MetadataElement( name="chrom1Col", default=1, desc="Chrom1 column", param=metadata.ColumnParameter ) - MetadataElement( name="start1Col", default=2, desc="Start1 column", param=metadata.ColumnParameter ) - MetadataElement( name="end1Col", default=3, desc="End1 column", param=metadata.ColumnParameter ) - MetadataElement( name="chrom2Col", default=4, desc="Chrom2 column", param=metadata.ColumnParameter ) - MetadataElement( name="start2Col", default=5, desc="Start2 column", param=metadata.ColumnParameter ) - MetadataElement( name="end2Col", default=6, desc="End2 column", param=metadata.ColumnParameter ) - MetadataElement( name="valueCol", default=7, desc="Value column", param=metadata.ColumnParameter ) + MetadataElement(name="chrom1Col", default=1, desc="Chrom1 column", param=metadata.ColumnParameter) + MetadataElement(name="start1Col", default=2, desc="Start1 column", param=metadata.ColumnParameter) + MetadataElement(name="end1Col", default=3, desc="End1 column", param=metadata.ColumnParameter) + MetadataElement(name="chrom2Col", default=4, desc="Chrom2 column", param=metadata.ColumnParameter) + MetadataElement(name="start2Col", default=5, desc="Start2 column", param=metadata.ColumnParameter) + MetadataElement(name="end2Col", default=6, desc="End2 column", param=metadata.ColumnParameter) + MetadataElement(name="valueCol", default=7, desc="Value column", param=metadata.ColumnParameter) - MetadataElement( name="columns", default=7, desc="Number of columns", readonly=True, visible=False ) + MetadataElement(name="columns", default=7, desc="Number of columns", readonly=True, visible=False) - def sniff( self, filename ): + def sniff(self, filename): return False diff --git a/lib/galaxy/datatypes/molecules.py b/lib/galaxy/datatypes/molecules.py index 703e143e6c8c..bc65b2a7c07f 100644 --- a/lib/galaxy/datatypes/molecules.py +++ b/lib/galaxy/datatypes/molecules.py @@ -10,7 +10,10 @@ from galaxy.datatypes.binary import Binary from galaxy.datatypes.data import get_file_peek from galaxy.datatypes.metadata import MetadataElement -from galaxy.datatypes.sniff import get_headers +from galaxy.datatypes.sniff import ( + get_headers, + iter_headers +) from galaxy.datatypes.tabular import Tabular from galaxy.datatypes.xml import GenericXml @@ -351,7 +354,7 @@ def merge(split_files, output_file): class OBFS(Binary): """OpenBabel Fastsearch format (fs).""" - file_ext = 'fs' + file_ext = 'obfs' composite_type = 'basic' allow_datatype_change = False @@ -461,7 +464,7 @@ def sniff(self, filename): >>> PDB().sniff(fname) False """ - headers = get_headers(filename, sep=' ', count=300) + headers = iter_headers(filename, sep=' ', count=300) h = t = c = s = k = e = False for line in headers: section_name = line[0].strip() @@ -514,7 +517,7 @@ def sniff(self, filename): >>> PDBQT().sniff(fname) False """ - headers = get_headers(filename, sep=' ', count=300) + headers = iter_headers(filename, sep=' ', count=300) h = t = c = s = k = False for line in headers: section_name = line[0].strip() @@ -607,7 +610,7 @@ def sniff(self, filename): >>> InChI().sniff(fname) False """ - inchi_lines = get_headers(filename, sep=' ', count=10) + inchi_lines = iter_headers(filename, sep=' ', count=10) for inchi in inchi_lines: if not inchi[0].startswith('InChI='): return False @@ -751,7 +754,7 @@ def _read_cml_records(filename): if line.lstrip().startswith('') or \ line.lstrip().startswith('>> Sabund().sniff( fname ) False """ - headers = get_headers(filename, sep='\t') + headers = iter_headers(filename, sep='\t') count = 0 for line in headers: if not line[0].startswith('@'): @@ -151,7 +155,7 @@ def set_meta(self, dataset, overwrite=True, skip=1, **kwd): comment_lines = 0 ncols = 0 - headers = get_headers(dataset.file_name, sep='\t', count=-1) + headers = iter_headers(dataset.file_name, sep='\t', count=-1) for line in headers: if line[0] == 'label' and line[1] == 'Group': skip = 1 @@ -187,7 +191,7 @@ def sniff(self, filename, vals_are_int=False): >>> GroupAbund().sniff( fname ) False """ - headers = get_headers(filename, sep='\t') + headers = iter_headers(filename, sep='\t') count = 0 for line in headers: if not line[0].startswith('@'): @@ -234,7 +238,7 @@ def sniff(self, filename): >>> SecondaryStructureMap().sniff( fname ) False """ - headers = get_headers(filename, sep='\t') + headers = iter_headers(filename, sep='\t') line_num = 0 rowidxmap = {} for line in headers: @@ -302,7 +306,7 @@ def init_meta(self, dataset, copy_from=None): def set_meta(self, dataset, overwrite=True, skip=0, **kwd): super(DistanceMatrix, self).set_meta(dataset, overwrite=overwrite, skip=skip, **kwd) - headers = get_headers(dataset.file_name, sep='\t') + headers = iter_headers(dataset.file_name, sep='\t') for line in headers: if not line[0].startswith('@'): try: @@ -344,7 +348,7 @@ def sniff(self, filename): False """ numlines = 300 - headers = get_headers(filename, sep='\t', count=numlines) + headers = iter_headers(filename, sep='\t', count=numlines) line_num = 0 for line in headers: if not line[0].startswith('@'): @@ -405,7 +409,7 @@ def sniff(self, filename): False """ numlines = 300 - headers = get_headers(filename, sep='\t', count=numlines) + headers = iter_headers(filename, sep='\t', count=numlines) line_num = 0 for line in headers: if not line[0].startswith('@'): @@ -461,7 +465,7 @@ def sniff(self, filename): >>> PairwiseDistanceMatrix().sniff( fname ) False """ - headers = get_headers(filename, sep='\t') + headers = iter_headers(filename, sep='\t') count = 0 for line in headers: if not line[0].startswith('@'): @@ -525,7 +529,7 @@ def set_meta(self, dataset, overwrite=True, skip=None, max_data_lines=None, **kw super(Group, self).set_meta(dataset, overwrite, skip, max_data_lines) group_names = set() - headers = get_headers(dataset.file_name, sep='\t', count=-1) + headers = iter_headers(dataset.file_name, sep='\t', count=-1) for line in headers: if len(line) > 1: group_names.add(line[1]) @@ -558,7 +562,7 @@ def sniff(self, filename): >>> Oligos().sniff( fname ) False """ - headers = get_headers(filename, sep='\t') + headers = iter_headers(filename, sep='\t') count = 0 for line in headers: if not line[0].startswith('@') and not line[0].startswith('#'): @@ -602,7 +606,7 @@ def sniff(self, filename): >>> Frequency().sniff( fname ) False """ - headers = get_headers(filename, sep='\t') + headers = iter_headers(filename, sep='\t') count = 0 for line in headers: if not line[0].startswith('@'): @@ -653,7 +657,7 @@ def sniff(self, filename): >>> Quantile().sniff( fname ) False """ - headers = get_headers(filename, sep='\t') + headers = iter_headers(filename, sep='\t') count = 0 for line in headers: if not line[0].startswith('@') and not line[0].startswith('#'): @@ -691,7 +695,7 @@ def sniff(self, filename): >>> LaneMask().sniff( fname ) False """ - headers = get_headers(filename, sep='\t') + headers = get_headers(filename, sep='\t', count=2) if len(headers) != 1 or len(headers[0]) != 1: return False @@ -730,7 +734,7 @@ def set_meta(self, dataset, overwrite=True, skip=1, max_data_lines=None, **kwd): headers = get_headers(dataset.file_name, sep='\t', count=1) colnames = headers[0] - dataset.metadata.column_types = ['str'] + (['int'] * ( len(headers[0]) - 1)) + dataset.metadata.column_types = ['str'] + (['int'] * (len(headers[0]) - 1)) if len(colnames) > 1: dataset.metadata.columns = len(colnames) if len(colnames) > 2: @@ -774,7 +778,7 @@ def sniff(self, filename): >>> RefTaxonomy().sniff( fname ) False """ - headers = get_headers(filename, sep='\t', count=300) + headers = iter_headers(filename, sep='\t', count=300) count = 0 pat_prog = re.compile('^([^ \t\n\r\x0c\x0b;]+([(]\\d+[)])?(;[^ \t\n\r\x0c\x0b;]+([(]\\d+[)])?)*(;)?)$') found_semicolons = False @@ -849,7 +853,7 @@ def sniff(self, filename): >>> Axes().sniff( fname ) False """ - headers = get_headers(filename, sep='\t') + headers = iter_headers(filename, sep='\t') count = 0 col_cnt = None all_integers = True @@ -900,6 +904,7 @@ class SffFlow(Tabular): GQY1XT001CQIRF 84 1.02 0.06 0.98 0.06 0.09 1.05 0.07 ... GQY1XT001CF5YW 88 1.02 0.02 1.01 0.04 0.06 1.02 0.03 ... """ + def __init__(self, **kwd): super(SffFlow, self).__init__(**kwd) diff --git a/lib/galaxy/datatypes/msa.py b/lib/galaxy/datatypes/msa.py index 7b31ac86adff..0c95fd6d93c0 100644 --- a/lib/galaxy/datatypes/msa.py +++ b/lib/galaxy/datatypes/msa.py @@ -11,13 +11,13 @@ log = logging.getLogger(__name__) -class Hmmer( Text ): +class Hmmer(Text): edam_data = "data_1364" edam_format = "format_1370" def set_peek(self, dataset, is_multi_byte=False): if not dataset.dataset.purged: - dataset.peek = get_file_peek( dataset.file_name, is_multi_byte=is_multi_byte ) + dataset.peek = get_file_peek(dataset.file_name, is_multi_byte=is_multi_byte) dataset.blurb = "HMMER Database" else: dataset.peek = 'file does not exist' @@ -27,14 +27,14 @@ def display_peek(self, dataset): try: return dataset.peek except: - return "HMMER database (%s)" % ( nice_size( dataset.get_size() ) ) + return "HMMER database (%s)" % (nice_size(dataset.get_size())) @abc.abstractmethod def sniff(self, filename): raise NotImplementedError -class Hmmer2( Hmmer ): +class Hmmer2(Hmmer): edam_format = "format_3328" file_ext = "hmm2" @@ -46,7 +46,7 @@ def sniff(self, filename): return False -class Hmmer3( Hmmer ): +class Hmmer3(Hmmer): edam_format = "format_3329" file_ext = "hmm3" @@ -58,13 +58,13 @@ def sniff(self, filename): return False -class HmmerPress( Binary ): +class HmmerPress(Binary): """Class for hmmpress database files.""" file_ext = 'hmmpress' allow_datatype_change = False composite_type = 'basic' - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): """Set the peek and blurb text.""" if not dataset.dataset.purged: dataset.peek = "HMMER Binary database" @@ -73,7 +73,7 @@ def set_peek( self, dataset, is_multi_byte=False ): dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disk' - def display_peek( self, dataset ): + def display_peek(self, dataset): """Create HTML content, used for displaying peek.""" try: return dataset.peek @@ -95,39 +95,39 @@ def __init__(self, **kwd): Binary.register_unsniffable_binary_ext("hmmpress") -class Stockholm_1_0( Text ): +class Stockholm_1_0(Text): edam_data = "data_0863" edam_format = "format_1961" file_ext = "stockholm" - MetadataElement( name="number_of_models", default=0, desc="Number of multiple alignments", readonly=True, visible=True, optional=True, no_value=0 ) + MetadataElement(name="number_of_models", default=0, desc="Number of multiple alignments", readonly=True, visible=True, optional=True, no_value=0) - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): if not dataset.dataset.purged: - dataset.peek = get_file_peek( dataset.file_name, is_multi_byte=is_multi_byte ) + dataset.peek = get_file_peek(dataset.file_name, is_multi_byte=is_multi_byte) if (dataset.metadata.number_of_models == 1): dataset.blurb = "1 alignment" else: dataset.blurb = "%s alignments" % dataset.metadata.number_of_models - dataset.peek = get_file_peek( dataset.file_name, is_multi_byte=is_multi_byte ) + dataset.peek = get_file_peek(dataset.file_name, is_multi_byte=is_multi_byte) else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disc' - def sniff( self, filename ): + def sniff(self, filename): if generic_util.count_special_lines('^#[[:space:]+]STOCKHOLM[[:space:]+]1.0', filename) > 0: return True else: return False - def set_meta( self, dataset, **kwd ): + def set_meta(self, dataset, **kwd): """ Set the number of models in dataset. """ dataset.metadata.number_of_models = generic_util.count_special_lines('^#[[:space:]+]STOCKHOLM[[:space:]+]1.0', dataset.file_name) - def split( cls, input_datasets, subdir_generator_function, split_params): + def split(cls, input_datasets, subdir_generator_function, split_params): """ Split the input files by model records. @@ -147,60 +147,60 @@ def split( cls, input_datasets, subdir_generator_function, split_params): else: raise Exception('Unsupported split mode %s' % split_params['split_mode']) - def _read_stockholm_records( filename ): + def _read_stockholm_records(filename): lines = [] with open(filename) as handle: for line in handle: - lines.append( line ) + lines.append(line) if line.strip() == '//': yield lines lines = [] - def _write_part_stockholm_file( accumulated_lines ): + def _write_part_stockholm_file(accumulated_lines): part_dir = subdir_generator_function() - part_path = os.path.join( part_dir, os.path.basename( input_files[0] ) ) - part_file = open( part_path, 'w' ) - part_file.writelines( accumulated_lines ) + part_path = os.path.join(part_dir, os.path.basename(input_files[0])) + part_file = open(part_path, 'w') + part_file.writelines(accumulated_lines) part_file.close() try: - stockholm_records = _read_stockholm_records( input_files[0] ) + stockholm_records = _read_stockholm_records(input_files[0]) stockholm_lines_accumulated = [] - for counter, stockholm_record in enumerate( stockholm_records, start=1): - stockholm_lines_accumulated.extend( stockholm_record ) + for counter, stockholm_record in enumerate(stockholm_records, start=1): + stockholm_lines_accumulated.extend(stockholm_record) if counter % chunk_size == 0: - _write_part_stockholm_file( stockholm_lines_accumulated ) + _write_part_stockholm_file(stockholm_lines_accumulated) stockholm_lines_accumulated = [] if stockholm_lines_accumulated: - _write_part_stockholm_file( stockholm_lines_accumulated ) + _write_part_stockholm_file(stockholm_lines_accumulated) except Exception as e: log.error('Unable to split files: %s' % str(e)) raise split = classmethod(split) -class MauveXmfa( Text ): +class MauveXmfa(Text): file_ext = "xmfa" - MetadataElement( name="number_of_models", default=0, desc="Number of alignmened sequences", readonly=True, visible=True, optional=True, no_value=0 ) + MetadataElement(name="number_of_models", default=0, desc="Number of alignmened sequences", readonly=True, visible=True, optional=True, no_value=0) - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): if not dataset.dataset.purged: - dataset.peek = get_file_peek( dataset.file_name, is_multi_byte=is_multi_byte ) + dataset.peek = get_file_peek(dataset.file_name, is_multi_byte=is_multi_byte) if (dataset.metadata.number_of_models == 1): dataset.blurb = "1 alignment" else: dataset.blurb = "%s alignments" % dataset.metadata.number_of_models - dataset.peek = get_file_peek( dataset.file_name, is_multi_byte=is_multi_byte ) + dataset.peek = get_file_peek(dataset.file_name, is_multi_byte=is_multi_byte) else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disc' - def sniff( self, filename ): + def sniff(self, filename): with open(filename, 'r') as handle: return handle.read(21) == '#FormatVersion Mauve1' return False - def set_meta( self, dataset, **kwd ): + def set_meta(self, dataset, **kwd): dataset.metadata.number_of_models = generic_util.count_special_lines('^#Sequence([[:digit:]]+)Entry', dataset.file_name) diff --git a/lib/galaxy/datatypes/neo4j.py b/lib/galaxy/datatypes/neo4j.py index 3c7d0644eccb..8c85a5e1c8d8 100644 --- a/lib/galaxy/datatypes/neo4j.py +++ b/lib/galaxy/datatypes/neo4j.py @@ -20,6 +20,7 @@ class Neo4j(Html): derived from html - composite datatype elements stored in extra files path """ + def generate_primary_file(self, dataset=None): """ This is called only at upload to write the html file diff --git a/lib/galaxy/datatypes/ngsindex.py b/lib/galaxy/datatypes/ngsindex.py index 0b65e7ab65ee..77b5c3348d1e 100644 --- a/lib/galaxy/datatypes/ngsindex.py +++ b/lib/galaxy/datatypes/ngsindex.py @@ -10,19 +10,19 @@ log = logging.getLogger(__name__) -class BowtieIndex( Html ): +class BowtieIndex(Html): """ base class for BowtieIndex is subclassed by BowtieColorIndex and BowtieBaseIndex """ - MetadataElement( name="base_name", desc="base name for this index set", default='galaxy_generated_bowtie_index', set_in_upload=True, readonly=True ) - MetadataElement( name="sequence_space", desc="sequence_space for this index set", default='unknown', set_in_upload=True, readonly=True ) + MetadataElement(name="base_name", desc="base name for this index set", default='galaxy_generated_bowtie_index', set_in_upload=True, readonly=True) + MetadataElement(name="sequence_space", desc="sequence_space for this index set", default='unknown', set_in_upload=True, readonly=True) is_binary = True composite_type = 'auto_primary_file' allow_datatype_change = False - def generate_primary_file( self, dataset=None ): + def generate_primary_file(self, dataset=None): """ This is called only at upload to write the html file cannot rename the datasets here - they come with the default unfortunately @@ -38,40 +38,40 @@ def regenerate_primary_file(self, dataset): rval = ['Files for Composite Dataset %s

    Comprises the following files:

      ' % (bn)] for i, fname in enumerate(flist): sfname = os.path.split(fname)[-1] - rval.append( '
    • %s' % ( sfname, sfname ) ) - rval.append( '
    ' ) + rval.append('
  • %s' % (sfname, sfname)) + rval.append('') with open(dataset.file_name, 'w') as f: - f.write("\n".join( rval )) + f.write("\n".join(rval)) f.write('\n') - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): if not dataset.dataset.purged: - dataset.peek = "Bowtie index file (%s)" % ( dataset.metadata.sequence_space ) - dataset.blurb = "%s space" % ( dataset.metadata.sequence_space ) + dataset.peek = "Bowtie index file (%s)" % (dataset.metadata.sequence_space) + dataset.blurb = "%s space" % (dataset.metadata.sequence_space) else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disk' - def display_peek( self, dataset ): + def display_peek(self, dataset): try: return dataset.peek except: return "Bowtie index file" -class BowtieColorIndex( BowtieIndex ): +class BowtieColorIndex(BowtieIndex): """ Bowtie color space index """ - MetadataElement( name="sequence_space", desc="sequence_space for this index set", default='color', set_in_upload=True, readonly=True ) + MetadataElement(name="sequence_space", desc="sequence_space for this index set", default='color', set_in_upload=True, readonly=True) file_ext = 'bowtie_color_index' -class BowtieBaseIndex( BowtieIndex ): +class BowtieBaseIndex(BowtieIndex): """ Bowtie base space index """ - MetadataElement( name="sequence_space", desc="sequence_space for this index set", default='base', set_in_upload=True, readonly=True ) + MetadataElement(name="sequence_space", desc="sequence_space for this index set", default='base', set_in_upload=True, readonly=True) file_ext = 'bowtie_base_index' diff --git a/lib/galaxy/datatypes/proteomics.py b/lib/galaxy/datatypes/proteomics.py index de6229b81f93..d803d1de0c2a 100644 --- a/lib/galaxy/datatypes/proteomics.py +++ b/lib/galaxy/datatypes/proteomics.py @@ -52,7 +52,7 @@ def generate_primary_file(self, dataset=None): return "\n".join(rval) -Binary.register_sniffable_binary_format("wiff", "wiff", Wiff ) +Binary.register_sniffable_binary_format("wiff", "wiff", Wiff) class PepXmlReport(Tabular): @@ -303,7 +303,7 @@ def display_peek(self, dataset): return "Thermo Finnigan RAW file (%s)" % (nice_size(dataset.get_size())) -Binary.register_sniffable_binary_format("thermo.raw", "raw", ThermoRAW ) +Binary.register_sniffable_binary_format("thermo.raw", "raw", ThermoRAW) class Msp(Text): @@ -327,14 +327,14 @@ def sniff(self, filename): return lines[0].startswith("Name:") and lines[1].startswith("MW:") -class SPLibNoIndex( Text ): +class SPLibNoIndex(Text): """SPlib without index file """ file_ext = "splib_noindex" - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): """Set the peek and blurb text""" if not dataset.dataset.purged: - dataset.peek = data.get_file_peek( dataset.file_name, is_multi_byte=is_multi_byte ) + dataset.peek = data.get_file_peek(dataset.file_name, is_multi_byte=is_multi_byte) dataset.blurb = 'Spectral Library without index files' else: dataset.peek = 'file does not exist' @@ -425,3 +425,42 @@ class XHunterAslFormat(Binary): class Sf3(Binary): """Class describing a Scaffold SF3 files""" file_ext = "sf3" + + +class ImzML(Binary): + """ + Class for imzML files. + http://www.imzml.org + """ + edam_format = "format_3682" + file_ext = 'imzml' + allow_datatype_change = False + composite_type = 'auto_primary_file' + + def __init__(self, **kwd): + Binary.__init__(self, **kwd) + + """The metadata""" + self.add_composite_file( + 'imzml', + description='The imzML metadata component.', + is_binary=False) + + """The mass spectral data""" + self.add_composite_file( + 'ibd', + description='The mass spectral data component.', + is_binary=True) + + def generate_primary_file(self, dataset=None): + rval = ['imzML Composite Dataset

    '] + rval.append('

    This composite dataset is composed of the following files:

      ') + for composite_name, composite_file in self.get_composite_files(dataset=dataset).iteritems(): + fn = composite_name + opt_text = '' + if composite_file.get('description'): + rval.append('
    • %s (%s)%s
    • ' % (fn, fn, composite_file.get('description'), opt_text)) + else: + rval.append('
    • %s%s
    • ' % (fn, fn, opt_text)) + rval.append('
    ') + return "\n".join(rval) diff --git a/lib/galaxy/datatypes/qualityscore.py b/lib/galaxy/datatypes/qualityscore.py index 9fda147c4de9..770af6def39c 100644 --- a/lib/galaxy/datatypes/qualityscore.py +++ b/lib/galaxy/datatypes/qualityscore.py @@ -8,7 +8,7 @@ log = logging.getLogger(__name__) -class QualityScore ( data.Text ): +class QualityScore (data.Text): """ until we know more about quality score formats """ @@ -17,14 +17,14 @@ class QualityScore ( data.Text ): file_ext = "qual" -class QualityScoreSOLiD ( QualityScore ): +class QualityScoreSOLiD (QualityScore): """ until we know more about quality score formats """ edam_format = "format_3610" file_ext = "qualsolid" - def sniff( self, filename ): + def sniff(self, filename): """ >>> from galaxy.datatypes.sniff import get_test_fname >>> fname = get_test_fname( 'sequence.fasta' ) @@ -35,7 +35,7 @@ def sniff( self, filename ): True """ try: - fh = open( filename ) + fh = open(filename) readlen = None goodblock = 0 while True: @@ -46,13 +46,13 @@ def sniff( self, filename ): else: break # EOF line = line.strip() - if line and not line.startswith( '#' ): # first non-empty non-comment line - if line.startswith( '>' ): + if line and not line.startswith('#'): # first non-empty non-comment line + if line.startswith('>'): line = fh.readline().strip() - if line == '' or line.startswith( '>' ): + if line == '' or line.startswith('>'): break try: - [ int( x ) for x in line.split() ] + [int(x) for x in line.split()] if not(readlen): readlen = len(line.split()) assert len(line.split()) == readlen # SOLiD reads should be of the same length @@ -68,21 +68,21 @@ def sniff( self, filename ): pass return False - def set_meta( self, dataset, **kwd ): + def set_meta(self, dataset, **kwd): if self.max_optional_metadata_filesize >= 0 and dataset.get_size() > self.max_optional_metadata_filesize: dataset.metadata.data_lines = None return - return QualityScore.set_meta( self, dataset, **kwd ) + return QualityScore.set_meta(self, dataset, **kwd) -class QualityScore454 ( QualityScore ): +class QualityScore454 (QualityScore): """ until we know more about quality score formats """ edam_format = "format_3611" file_ext = "qual454" - def sniff( self, filename ): + def sniff(self, filename): """ >>> from galaxy.datatypes.sniff import get_test_fname >>> fname = get_test_fname( 'sequence.fasta' ) @@ -93,19 +93,19 @@ def sniff( self, filename ): True """ try: - fh = open( filename ) + fh = open(filename) while True: line = fh.readline() if not line: break # EOF line = line.strip() - if line and not line.startswith( '#' ): # first non-empty non-comment line - if line.startswith( '>' ): + if line and not line.startswith('#'): # first non-empty non-comment line + if line.startswith('>'): line = fh.readline().strip() - if line == '' or line.startswith( '>' ): + if line == '' or line.startswith('>'): break try: - [ int( x ) for x in line.split() ] + [int(x) for x in line.split()] except: break return True @@ -117,7 +117,7 @@ def sniff( self, filename ): return False -class QualityScoreSolexa ( QualityScore ): +class QualityScoreSolexa (QualityScore): """ until we know more about quality score formats """ @@ -125,7 +125,7 @@ class QualityScoreSolexa ( QualityScore ): file_ext = "qualsolexa" -class QualityScoreIllumina ( QualityScore ): +class QualityScoreIllumina (QualityScore): """ until we know more about quality score formats """ diff --git a/lib/galaxy/datatypes/registry.py b/lib/galaxy/datatypes/registry.py index 4be4249130e1..b4d352cb359c 100644 --- a/lib/galaxy/datatypes/registry.py +++ b/lib/galaxy/datatypes/registry.py @@ -30,15 +30,15 @@ from .display_applications.application import DisplayApplication -class ConfigurationError( Exception ): +class ConfigurationError(Exception): pass -class Registry( object ): +class Registry(object): - def __init__( self, config=None ): + def __init__(self, config=None): self.log = logging.getLogger(__name__) - self.log.addHandler( logging.NullHandler() ) + self.log.addHandler(logging.NullHandler()) self.config = config self.datatypes_by_extension = {} self.mimetypes_by_extension = {} @@ -78,7 +78,7 @@ def __init__( self, config=None ): self.display_sites = {} self.legacy_build_sites = {} - def load_datatypes( self, root_dir=None, config=None, deactivate=False, override=True ): + def load_datatypes(self, root_dir=None, config=None, deactivate=False, override=True): """ Parse a datatypes XML file located at root_dir/config (if processing the Galaxy distributed config) or contained within an installed Tool Shed repository. If deactivate is True, an installed Tool Shed repository that includes custom datatypes @@ -87,9 +87,9 @@ def load_datatypes( self, root_dir=None, config=None, deactivate=False, override registry has been initialized at server startup, its contents cannot be overridden by newly introduced conflicting data types. """ - def __import_module( full_path, datatype_module, datatype_class_name ): - open_file_obj, file_name, description = imp.find_module( datatype_module, [ full_path ] ) - imported_module = imp.load_module( datatype_class_name, open_file_obj, file_name, description ) + def __import_module(full_path, datatype_module, datatype_class_name): + open_file_obj, file_name, description = imp.find_module(datatype_module, [full_path]) + imported_module = imp.load_module(datatype_class_name, open_file_obj, file_name, description) return imported_module if root_dir and config: @@ -103,7 +103,7 @@ def __import_module( full_path, datatype_module, datatype_class_name ): handling_proprietary_datatypes = False if not isinstance(config, Element): # Parse datatypes_conf.xml - tree = galaxy.util.parse_xml( config ) + tree = galaxy.util.parse_xml(config) root = tree.getroot() # Load datatypes and converters from config if deactivate: @@ -112,37 +112,37 @@ def __import_module( full_path, datatype_module, datatype_class_name ): self.log.debug('Loading datatypes from %s' % config) else: root = config - registration = root.find( 'registration' ) + registration = root.find('registration') # Set default paths defined in local datatypes_conf.xml. if not self.converters_path: - self.converters_path_attr = registration.get( 'converters_path', 'lib/galaxy/datatypes/converters' ) - self.converters_path = os.path.join( root_dir, self.converters_path_attr ) - if not os.path.isdir( self.converters_path ): - raise ConfigurationError( "Directory does not exist: %s" % self.converters_path ) + self.converters_path_attr = registration.get('converters_path', 'lib/galaxy/datatypes/converters') + self.converters_path = os.path.join(root_dir, self.converters_path_attr) + if not os.path.isdir(self.converters_path): + raise ConfigurationError("Directory does not exist: %s" % self.converters_path) if not self.display_applications_path: - self.display_path_attr = registration.get( 'display_path', 'display_applications' ) - self.display_applications_path = os.path.join( root_dir, self.display_path_attr ) + self.display_path_attr = registration.get('display_path', 'display_applications') + self.display_applications_path = os.path.join(root_dir, self.display_path_attr) # Proprietary datatype's tag may have special attributes, proprietary_converter_path and proprietary_display_path. - proprietary_converter_path = registration.get( 'proprietary_converter_path', None ) - proprietary_display_path = registration.get( 'proprietary_display_path', None ) + proprietary_converter_path = registration.get('proprietary_converter_path', None) + proprietary_display_path = registration.get('proprietary_display_path', None) if proprietary_converter_path is not None or proprietary_display_path is not None and not handling_proprietary_datatypes: handling_proprietary_datatypes = True - for elem in registration.findall( 'datatype' ): + for elem in registration.findall('datatype'): # Keep a status of the process steps to enable stopping the process of handling the datatype if necessary. ok = True - extension = self.get_extension( elem ) - dtype = elem.get( 'type', None ) - type_extension = elem.get( 'type_extension', None ) - mimetype = elem.get( 'mimetype', None ) - display_in_upload = galaxy.util.string_as_bool( elem.get( 'display_in_upload', False ) ) + extension = self.get_extension(elem) + dtype = elem.get('type', None) + type_extension = elem.get('type_extension', None) + mimetype = elem.get('mimetype', None) + display_in_upload = galaxy.util.string_as_bool(elem.get('display_in_upload', False)) # If make_subclass is True, it does not necessarily imply that we are subclassing a datatype that is contained # in the distribution. - make_subclass = galaxy.util.string_as_bool( elem.get( 'subclass', False ) ) - edam_format = elem.get( 'edam_format', None ) + make_subclass = galaxy.util.string_as_bool(elem.get('subclass', False)) + edam_format = elem.get('edam_format', None) if edam_format and not make_subclass: self.log.warning("Cannot specify edam_format without setting subclass to True, skipping datatype.") continue - edam_data = elem.get( 'edam_data', None ) + edam_data = elem.get('edam_data', None) if edam_data and not make_subclass: self.log.warning("Cannot specify edam_data without setting subclass to True, skipping datatype.") continue @@ -150,35 +150,35 @@ def __import_module( full_path, datatype_module, datatype_class_name ): # (proprietary_path and proprietary_datatype_module) if they depend on proprietary datatypes classes. # The value of proprietary_path is the path to the cloned location of the tool shed repository's contained # datatypes_conf.xml file. - proprietary_path = elem.get( 'proprietary_path', None ) - proprietary_datatype_module = elem.get( 'proprietary_datatype_module', None ) + proprietary_path = elem.get('proprietary_path', None) + proprietary_datatype_module = elem.get('proprietary_datatype_module', None) if proprietary_path is not None or proprietary_datatype_module is not None and not handling_proprietary_datatypes: handling_proprietary_datatypes = True if deactivate: # We are deactivating or uninstalling an installed tool shed repository, so eliminate the datatype # elem from the in-memory list of datatype elems. for in_memory_elem in self.datatype_elems: - in_memory_extension = in_memory_elem.get( 'extension', None ) + in_memory_extension = in_memory_elem.get('extension', None) if in_memory_extension == extension: - in_memory_dtype = elem.get( 'type', None ) - in_memory_type_extension = elem.get( 'type_extension', None ) - in_memory_mimetype = elem.get( 'mimetype', None ) - in_memory_display_in_upload = galaxy.util.string_as_bool( elem.get( 'display_in_upload', False ) ) - in_memory_make_subclass = galaxy.util.string_as_bool( elem.get( 'subclass', False ) ) + in_memory_dtype = elem.get('type', None) + in_memory_type_extension = elem.get('type_extension', None) + in_memory_mimetype = elem.get('mimetype', None) + in_memory_display_in_upload = galaxy.util.string_as_bool(elem.get('display_in_upload', False)) + in_memory_make_subclass = galaxy.util.string_as_bool(elem.get('subclass', False)) if in_memory_dtype == dtype and \ in_memory_type_extension == type_extension and \ in_memory_mimetype == mimetype and \ in_memory_display_in_upload == display_in_upload and \ in_memory_make_subclass == make_subclass: - self.datatype_elems.remove( in_memory_elem ) + self.datatype_elems.remove(in_memory_elem) if extension is not None and extension in self.datatypes_by_extension: # We are deactivating or uninstalling an installed tool shed repository, so eliminate the datatype # from the registry. TODO: Handle deactivating datatype converters, etc before removing from # self.datatypes_by_extension. - del self.datatypes_by_extension[ extension ] + del self.datatypes_by_extension[extension] if extension in self.upload_file_formats: - self.upload_file_formats.remove( extension ) - self.log.debug( "Removed datatype with extension '%s' from the registry." % extension ) + self.upload_file_formats.remove(extension) + self.log.debug("Removed datatype with extension '%s' from the registry." % extension) else: # We are loading new datatype, so we'll make sure it is correctly defined before proceeding. can_process_datatype = False @@ -189,11 +189,11 @@ def __import_module( full_path, datatype_module, datatype_class_name ): if can_process_datatype: if dtype is not None: try: - fields = dtype.split( ':' ) - datatype_module = fields[ 0 ] - datatype_class_name = fields[ 1 ] + fields = dtype.split(':') + datatype_module = fields[0] + datatype_class_name = fields[1] except Exception as e: - self.log.exception( 'Error parsing datatype definition for dtype %s', str( dtype ) ) + self.log.exception('Error parsing datatype definition for dtype %s', str(dtype)) ok = False if ok: datatype_class = None @@ -201,35 +201,35 @@ def __import_module( full_path, datatype_module, datatype_class_name ): # TODO: previously comments suggested this needs to be locked because it modifys # the sys.path, probably true but the previous lock wasn't doing that. try: - imported_module = __import_module( proprietary_path, - proprietary_datatype_module, - datatype_class_name ) + imported_module = __import_module(proprietary_path, + proprietary_datatype_module, + datatype_class_name) if imported_module not in self.imported_modules: - self.imported_modules.append( imported_module ) - if hasattr( imported_module, datatype_class_name ): - datatype_class = getattr( imported_module, datatype_class_name ) + self.imported_modules.append(imported_module) + if hasattr(imported_module, datatype_class_name): + datatype_class = getattr(imported_module, datatype_class_name) except Exception as e: - full_path = os.path.join( proprietary_path, proprietary_datatype_module ) - self.log.debug( "Exception importing proprietary code file %s: %s" % ( str( full_path ), str( e ) ) ) + full_path = os.path.join(proprietary_path, proprietary_datatype_module) + self.log.debug("Exception importing proprietary code file %s: %s" % (str(full_path), str(e))) # Either the above exception was thrown because the proprietary_datatype_module is not derived from a class # in the repository, or we are loading Galaxy's datatypes. In either case we'll look in the registry. if datatype_class is None: try: # The datatype class name must be contained in one of the datatype modules in the Galaxy distribution. - fields = datatype_module.split( '.' )[1:] - module = __import__( datatype_module ) + fields = datatype_module.split('.')[1:] + module = __import__(datatype_module) for mod in fields: - module = getattr( module, mod ) - datatype_class = getattr( module, datatype_class_name ) - self.log.debug( 'Retrieved datatype module %s:%s from the datatype registry.' % ( str( datatype_module ), datatype_class_name ) ) + module = getattr(module, mod) + datatype_class = getattr(module, datatype_class_name) + self.log.debug('Retrieved datatype module %s:%s from the datatype registry.' % (str(datatype_module), datatype_class_name)) except Exception as e: - self.log.exception( 'Error importing datatype module %s', str( datatype_module ) ) + self.log.exception('Error importing datatype module %s', str(datatype_module)) ok = False elif type_extension is not None: try: - datatype_class = self.datatypes_by_extension[ type_extension ].__class__ + datatype_class = self.datatypes_by_extension[type_extension].__class__ except Exception as e: - self.log.exception( 'Error determining datatype_class for type_extension %s', str( type_extension ) ) + self.log.exception('Error determining datatype_class for type_extension %s', str(type_extension)) ok = False if ok: if not deactivate: @@ -239,57 +239,57 @@ def __import_module( full_path, datatype_module, datatype_class_name ): if extension in self.datatypes_by_extension: # Because of the way that the value of can_process_datatype was set above, we know that the value of # override is True. - self.log.debug( "Overriding conflicting datatype with extension '%s', using datatype from %s." % - ( str( extension ), str( config ) ) ) + self.log.debug("Overriding conflicting datatype with extension '%s', using datatype from %s." % + (str(extension), str(config))) if make_subclass: - datatype_class = type( datatype_class_name, ( datatype_class, ), {} ) + datatype_class = type(datatype_class_name, (datatype_class, ), {}) if edam_format: datatype_class.edam_format = edam_format if edam_data: datatype_class.edam_data = edam_data - self.datatypes_by_extension[ extension ] = datatype_class() + self.datatypes_by_extension[extension] = datatype_class() if mimetype is None: # Use default mimetype per datatype specification. - mimetype = self.datatypes_by_extension[ extension ].get_mime() - self.mimetypes_by_extension[ extension ] = mimetype + mimetype = self.datatypes_by_extension[extension].get_mime() + self.mimetypes_by_extension[extension] = mimetype if datatype_class.track_type: - self.available_tracks.append( extension ) + self.available_tracks.append(extension) if display_in_upload and extension not in self.upload_file_formats: - self.upload_file_formats.append( extension ) + self.upload_file_formats.append(extension) # Max file size cut off for setting optional metadata. - self.datatypes_by_extension[ extension ].max_optional_metadata_filesize = elem.get( 'max_optional_metadata_filesize', None ) - for converter in elem.findall( 'converter' ): + self.datatypes_by_extension[extension].max_optional_metadata_filesize = elem.get('max_optional_metadata_filesize', None) + for converter in elem.findall('converter'): # Build the list of datatype converters which will later be loaded into the calling app's toolbox. - converter_config = converter.get( 'file', None ) - target_datatype = converter.get( 'target_datatype', None ) - depends_on = converter.get( 'depends_on', None ) + converter_config = converter.get('file', None) + target_datatype = converter.get('target_datatype', None) + depends_on = converter.get('depends_on', None) if depends_on is not None and target_datatype is not None: if extension not in self.converter_deps: - self.converter_deps[ extension ] = {} - self.converter_deps[ extension ][ target_datatype ] = depends_on.split( ',' ) + self.converter_deps[extension] = {} + self.converter_deps[extension][target_datatype] = depends_on.split(',') if converter_config and target_datatype: if proprietary_converter_path: - self.proprietary_converters.append( ( converter_config, extension, target_datatype ) ) + self.proprietary_converters.append((converter_config, extension, target_datatype)) else: - self.converters.append( ( converter_config, extension, target_datatype ) ) + self.converters.append((converter_config, extension, target_datatype)) # Add composite files. - for composite_file in elem.findall( 'composite_file' ): - name = composite_file.get( 'name', None ) + for composite_file in elem.findall('composite_file'): + name = composite_file.get('name', None) if name is None: - self.log.warning( "You must provide a name for your composite_file (%s)." % composite_file ) - optional = composite_file.get( 'optional', False ) - mimetype = composite_file.get( 'mimetype', None ) - self.datatypes_by_extension[ extension ].add_composite_file( name, optional=optional, mimetype=mimetype ) - for display_app in elem.findall( 'display' ): + self.log.warning("You must provide a name for your composite_file (%s)." % composite_file) + optional = composite_file.get('optional', False) + mimetype = composite_file.get('mimetype', None) + self.datatypes_by_extension[extension].add_composite_file(name, optional=optional, mimetype=mimetype) + for display_app in elem.findall('display'): if proprietary_display_path: if elem not in self.proprietary_display_app_containers: - self.proprietary_display_app_containers.append( elem ) + self.proprietary_display_app_containers.append(elem) else: if elem not in self.display_app_containers: - self.display_app_containers.append( elem ) + self.display_app_containers.append(elem) # Processing the new datatype elem is now complete, so make sure the element defining it is retained by appending # the new datatype to the in-memory list of datatype elems to enable persistence. - self.datatype_elems.append( elem ) + self.datatype_elems.append(elem) else: if extension is not None: if dtype is not None or type_extension is not None: @@ -297,16 +297,16 @@ def __import_module( full_path, datatype_module, datatype_class_name ): if not override: # Do not load the datatype since it conflicts with an existing datatype which we are not supposed # to override. - self.log.debug( "Ignoring conflicting datatype with extension '%s' from %s." % ( extension, config ) ) + self.log.debug("Ignoring conflicting datatype with extension '%s' from %s." % (extension, config)) # Load datatype sniffers from the config - we'll do this even if one or more datatypes were not properly processed in the config # since sniffers are not tightly coupled with datatypes. - self.load_datatype_sniffers( root, - deactivate=deactivate, - handling_proprietary_datatypes=handling_proprietary_datatypes, - override=override ) + self.load_datatype_sniffers(root, + deactivate=deactivate, + handling_proprietary_datatypes=handling_proprietary_datatypes, + override=override) self.upload_file_formats.sort() # Load build sites - self._load_build_sites( root ) + self._load_build_sites(root) self.set_default_values() def append_to_sniff_order(): @@ -314,69 +314,69 @@ def append_to_sniff_order(): for ext, datatype in self.datatypes_by_extension.items(): included = False for atype in self.sniff_order: - if isinstance( atype, datatype.__class__ ): + if isinstance(atype, datatype.__class__): included = True break if not included: - self.sniff_order.append( datatype ) + self.sniff_order.append(datatype) append_to_sniff_order() - def _load_build_sites( self, root ): + def _load_build_sites(self, root): - def load_build_site( build_site_config ): + def load_build_site(build_site_config): # Take in either an XML element or simple dictionary from YAML and add build site for this. - if not (build_site_config.get( 'type' ) and build_site_config.get( 'file' )): - self.log.exception( "Site is missing required 'type' and 'file' attributes" ) + if not (build_site_config.get('type') and build_site_config.get('file')): + self.log.exception("Site is missing required 'type' and 'file' attributes") return - site_type = build_site_config.get( 'type' ) - path = build_site_config.get( 'file' ) - if not os.path.exists( path ): + site_type = build_site_config.get('type') + path = build_site_config.get('file') + if not os.path.exists(path): sample_path = "%s.sample" % path - if os.path.exists( sample_path ): - self.log.debug( "Build site file [%s] not found using sample [%s]." % ( path, sample_path ) ) + if os.path.exists(sample_path): + self.log.debug("Build site file [%s] not found using sample [%s]." % (path, sample_path)) path = sample_path self.build_sites[site_type] = path if site_type in ('ucsc', 'gbrowse'): - self.legacy_build_sites[site_type] = galaxy.util.read_build_sites( path ) - if build_site_config.get( 'display', None ): - display = build_site_config.get( 'display' ) - if not isinstance( display, list ): - display = [ x.strip() for x in display.lower().split( ',' ) ] + self.legacy_build_sites[site_type] = galaxy.util.read_build_sites(path) + if build_site_config.get('display', None): + display = build_site_config.get('display') + if not isinstance(display, list): + display = [x.strip() for x in display.lower().split(',')] self.display_sites[site_type] = display - self.log.debug( "Loaded build site '%s': %s with display sites: %s", site_type, path, display ) + self.log.debug("Loaded build site '%s': %s with display sites: %s", site_type, path, display) else: - self.log.debug( "Loaded build site '%s': %s", site_type, path ) + self.log.debug("Loaded build site '%s': %s", site_type, path) - if root.find( 'build_sites' ) is not None: - for elem in root.find( 'build_sites' ).findall( 'site' ): - load_build_site( elem ) + if root.find('build_sites') is not None: + for elem in root.find('build_sites').findall('site'): + load_build_site(elem) else: - build_sites_config_file = getattr( self.config, "build_sites_config_file", None ) - if build_sites_config_file and os.path.exists( build_sites_config_file ): - with open( build_sites_config_file, "r" ) as f: - build_sites_config = yaml.load( f ) - if not isinstance( build_sites_config, list ): - self.log.exception( "Build sites configuration YAML file does not declare list of sites." ) + build_sites_config_file = getattr(self.config, "build_sites_config_file", None) + if build_sites_config_file and os.path.exists(build_sites_config_file): + with open(build_sites_config_file, "r") as f: + build_sites_config = yaml.load(f) + if not isinstance(build_sites_config, list): + self.log.exception("Build sites configuration YAML file does not declare list of sites.") return for build_site_config in build_sites_config: - load_build_site( build_site_config ) + load_build_site(build_site_config) else: self.log.debug("No build sites source located.") - def get_legacy_sites_by_build( self, site_type, build ): + def get_legacy_sites_by_build(self, site_type, build): sites = [] for site in self.legacy_build_sites.get(site_type, []): if build in site['builds']: sites.append((site['name'], site['url'])) return sites - def get_display_sites( self, site_type ): - return self.display_sites.get( site_type, [] ) + def get_display_sites(self, site_type): + return self.display_sites.get(site_type, []) - def load_datatype_sniffers( self, root, deactivate=False, handling_proprietary_datatypes=False, override=False ): + def load_datatype_sniffers(self, root, deactivate=False, handling_proprietary_datatypes=False, override=False): """ Process the sniffers element from a parsed a datatypes XML file located at root_dir/config (if processing the Galaxy distributed config) or contained within an installed Tool Shed repository. If deactivate is True, an installed Tool @@ -385,85 +385,85 @@ def load_datatype_sniffers( self, root, deactivate=False, handling_proprietary_d Since installation is occurring after the datatypes registry has been initialized at server startup, its contents cannot be overridden by newly introduced conflicting sniffers. """ - sniffer_elem_classes = [ e.attrib[ 'type' ] for e in self.sniffer_elems ] - sniffers = root.find( 'sniffers' ) + sniffer_elem_classes = [e.attrib['type'] for e in self.sniffer_elems] + sniffers = root.find('sniffers') if sniffers is not None: - for elem in sniffers.findall( 'sniffer' ): + for elem in sniffers.findall('sniffer'): # Keep a status of the process steps to enable stopping the process of handling the sniffer if necessary. ok = True - dtype = elem.get( 'type', None ) + dtype = elem.get('type', None) if dtype is not None: try: - fields = dtype.split( ":" ) - datatype_module = fields[ 0 ] - datatype_class_name = fields[ 1 ] + fields = dtype.split(":") + datatype_module = fields[0] + datatype_class_name = fields[1] module = None except Exception as e: - self.log.exception( 'Error determining datatype class or module for dtype %s', str( dtype ) ) + self.log.exception('Error determining datatype class or module for dtype %s', str(dtype)) ok = False if ok: if handling_proprietary_datatypes: # See if one of the imported modules contains the datatype class name. for imported_module in self.imported_modules: - if hasattr( imported_module, datatype_class_name ): + if hasattr(imported_module, datatype_class_name): module = imported_module break if module is None: try: # The datatype class name must be contained in one of the datatype modules in the Galaxy distribution. - module = __import__( datatype_module ) - for comp in datatype_module.split( '.' )[ 1: ]: - module = getattr( module, comp ) + module = __import__(datatype_module) + for comp in datatype_module.split('.')[1:]: + module = getattr(module, comp) except Exception as e: - self.log.exception( "Error importing datatype class for '%s'", str( dtype ) ) + self.log.exception("Error importing datatype class for '%s'", str(dtype)) ok = False if ok: try: - aclass = getattr( module, datatype_class_name )() + aclass = getattr(module, datatype_class_name)() except Exception as e: - self.log.exception( 'Error calling method %s from class %s', str( datatype_class_name ), str( module ) ) + self.log.exception('Error calling method %s from class %s', str(datatype_class_name), str(module)) ok = False if ok: if deactivate: # We are deactivating or uninstalling an installed Tool Shed repository, so eliminate the appropriate sniffers. - sniffer_class = elem.get( 'type', None ) + sniffer_class = elem.get('type', None) if sniffer_class is not None: - for index, s_e_c in enumerate( sniffer_elem_classes ): + for index, s_e_c in enumerate(sniffer_elem_classes): if sniffer_class == s_e_c: - del self.sniffer_elems[ index ] - sniffer_elem_classes = [ elem.attrib[ 'type' ] for elem in self.sniffer_elems ] - self.log.debug( "Removed sniffer element for datatype '%s'" % str( dtype ) ) + del self.sniffer_elems[index] + sniffer_elem_classes = [elem.attrib['type'] for elem in self.sniffer_elems] + self.log.debug("Removed sniffer element for datatype '%s'" % str(dtype)) break for sniffer_class in self.sniff_order: if sniffer_class.__class__ == aclass.__class__: - self.sniff_order.remove( sniffer_class ) - self.log.debug( "Removed sniffer class for datatype '%s' from sniff order" % str( dtype ) ) + self.sniff_order.remove(sniffer_class) + self.log.debug("Removed sniffer class for datatype '%s' from sniff order" % str(dtype)) break else: # We are loading new sniffer, so see if we have a conflicting sniffer already loaded. conflict = False - for conflict_loc, sniffer_class in enumerate( self.sniff_order ): + for conflict_loc, sniffer_class in enumerate(self.sniff_order): if sniffer_class.__class__ == aclass.__class__: # We have a conflicting sniffer, so replace the one previously loaded. conflict = True if override: - del self.sniff_order[ conflict_loc ] - self.log.debug( "Removed conflicting sniffer for datatype '%s'" % dtype ) + del self.sniff_order[conflict_loc] + self.log.debug("Removed conflicting sniffer for datatype '%s'" % dtype) break if conflict: if override: - self.sniff_order.append( aclass ) - self.log.debug( "Loaded sniffer for datatype '%s'" % dtype ) + self.sniff_order.append(aclass) + self.log.debug("Loaded sniffer for datatype '%s'" % dtype) else: - self.sniff_order.append( aclass ) - self.log.debug( "Loaded sniffer for datatype '%s'" % dtype ) + self.sniff_order.append(aclass) + self.log.debug("Loaded sniffer for datatype '%s'" % dtype) # Processing the new sniffer elem is now complete, so make sure the element defining it is loaded if necessary. - sniffer_class = elem.get( 'type', None ) + sniffer_class = elem.get('type', None) if sniffer_class is not None: if sniffer_class not in sniffer_elem_classes: - self.sniffer_elems.append( elem ) + self.sniffer_elems.append(elem) - def get_datatype_class_by_name( self, name ): + def get_datatype_class_by_name(self, name): """ Return the datatype class where the datatype's `type` attribute (as defined in the datatype_conf.xml file) contains `name`. @@ -476,43 +476,43 @@ def get_datatype_class_by_name( self, name ): # TODO: too inefficient - would be better to generate this once as a map and store in this object for ext, datatype_obj in self.datatypes_by_extension.items(): datatype_obj_class = datatype_obj.__class__ - datatype_obj_class_str = str( datatype_obj_class ) + datatype_obj_class_str = str(datatype_obj_class) if name in datatype_obj_class_str: return datatype_obj_class return None - def get_available_tracks( self ): + def get_available_tracks(self): return self.available_tracks - def get_mimetype_by_extension( self, ext, default='application/octet-stream' ): + def get_mimetype_by_extension(self, ext, default='application/octet-stream'): """Returns a mimetype based on an extension""" try: - mimetype = self.mimetypes_by_extension[ ext ] + mimetype = self.mimetypes_by_extension[ext] except KeyError: # datatype was never declared mimetype = default - self.log.warning( 'unknown mimetype in data factory %s' % str( ext ) ) + self.log.warning('unknown mimetype in data factory %s' % str(ext)) return mimetype - def get_datatype_by_extension( self, ext ): + def get_datatype_by_extension(self, ext): """Returns a datatype object based on an extension""" try: - builder = self.datatypes_by_extension[ ext ] + builder = self.datatypes_by_extension[ext] except KeyError: builder = None return builder - def change_datatype( self, data, ext ): + def change_datatype(self, data, ext): data.extension = ext # call init_meta and copy metadata from itself. The datatype # being converted *to* will handle any metadata copying and # initialization. if data.has_data(): data.set_size() - data.init_meta( copy_from=data ) + data.init_meta(copy_from=data) return data - def load_datatype_converters( self, toolbox, installed_repository_dict=None, deactivate=False, use_cached=False ): + def load_datatype_converters(self, toolbox, installed_repository_dict=None, deactivate=False, use_cached=False): """ If deactivate is False, add datatype converters from self.converters or self.proprietary_converters to the calling app's toolbox. If deactivate is True, eliminates relevant converters from the calling @@ -525,51 +525,51 @@ def load_datatype_converters( self, toolbox, installed_repository_dict=None, dea # Load converters defined by local datatypes_conf.xml. converters = self.converters for elem in converters: - tool_config = elem[ 0 ] - source_datatype = elem[ 1 ] - target_datatype = elem[ 2 ] + tool_config = elem[0] + source_datatype = elem[1] + target_datatype = elem[2] if installed_repository_dict: - converter_path = installed_repository_dict[ 'converter_path' ] + converter_path = installed_repository_dict['converter_path'] else: converter_path = self.converters_path try: - config_path = os.path.join( converter_path, tool_config ) - converter = toolbox.load_tool( config_path, use_cached=use_cached ) + config_path = os.path.join(converter_path, tool_config) + converter = toolbox.load_tool(config_path, use_cached=use_cached) if installed_repository_dict: # If the converter is included in an installed tool shed repository, set the tool # shed related tool attributes. - converter.tool_shed = installed_repository_dict[ 'tool_shed' ] - converter.repository_name = installed_repository_dict[ 'repository_name' ] - converter.repository_owner = installed_repository_dict[ 'repository_owner' ] - converter.installed_changeset_revision = installed_repository_dict[ 'installed_changeset_revision' ] + converter.tool_shed = installed_repository_dict['tool_shed'] + converter.repository_name = installed_repository_dict['repository_name'] + converter.repository_owner = installed_repository_dict['repository_owner'] + converter.installed_changeset_revision = installed_repository_dict['installed_changeset_revision'] converter.old_id = converter.id # The converter should be included in the list of tools defined in tool_dicts. - tool_dicts = installed_repository_dict[ 'tool_dicts' ] + tool_dicts = installed_repository_dict['tool_dicts'] for tool_dict in tool_dicts: - if tool_dict[ 'id' ] == converter.id: - converter.guid = tool_dict[ 'guid' ] - converter.id = tool_dict[ 'guid' ] + if tool_dict['id'] == converter.id: + converter.guid = tool_dict['guid'] + converter.id = tool_dict['guid'] break if deactivate: - toolbox.remove_tool_by_id( converter.id, remove_from_panel=False ) + toolbox.remove_tool_by_id(converter.id, remove_from_panel=False) if source_datatype in self.datatype_converters: - if target_datatype in self.datatype_converters[ source_datatype ]: - del self.datatype_converters[ source_datatype ][ target_datatype ] - self.log.debug( "Deactivated converter: %s", converter.id ) + if target_datatype in self.datatype_converters[source_datatype]: + del self.datatype_converters[source_datatype][target_datatype] + self.log.debug("Deactivated converter: %s", converter.id) else: - toolbox.register_tool( converter ) + toolbox.register_tool(converter) if source_datatype not in self.datatype_converters: - self.datatype_converters[ source_datatype ] = odict() - self.datatype_converters[ source_datatype ][ target_datatype ] = converter + self.datatype_converters[source_datatype] = odict() + self.datatype_converters[source_datatype][target_datatype] = converter if not hasattr(toolbox.app, 'tool_cache') or converter.id in toolbox.app.tool_cache._new_tool_ids: - self.log.debug( "Loaded converter: %s", converter.id ) + self.log.debug("Loaded converter: %s", converter.id) except Exception: if deactivate: - self.log.exception( "Error deactivating converter from (%s)" % converter_path ) + self.log.exception("Error deactivating converter from (%s)" % converter_path) else: - self.log.exception( "Error loading converter (%s)" % converter_path ) + self.log.exception("Error loading converter (%s)" % converter_path) - def load_display_applications( self, app, installed_repository_dict=None, deactivate=False ): + def load_display_applications(self, app, installed_repository_dict=None, deactivate=False): """ If deactivate is False, add display applications from self.display_app_containers or self.proprietary_display_app_containers to appropriate datatypes. If deactivate is @@ -582,99 +582,99 @@ def load_display_applications( self, app, installed_repository_dict=None, deacti # Load display applications defined by local datatypes_conf.xml. datatype_elems = self.display_app_containers for elem in datatype_elems: - extension = self.get_extension( elem ) - for display_app in elem.findall( 'display' ): - display_file = display_app.get( 'file', None ) + extension = self.get_extension(elem) + for display_app in elem.findall('display'): + display_file = display_app.get('file', None) if installed_repository_dict: - display_path = installed_repository_dict[ 'display_path' ] - display_file_head, display_file_tail = os.path.split( display_file ) - config_path = os.path.join( display_path, display_file_tail ) + display_path = installed_repository_dict['display_path'] + display_file_head, display_file_tail = os.path.split(display_file) + config_path = os.path.join(display_path, display_file_tail) else: - config_path = os.path.join( self.display_applications_path, display_file ) + config_path = os.path.join(self.display_applications_path, display_file) try: - inherit = galaxy.util.string_as_bool( display_app.get( 'inherit', 'False' ) ) - display_app = DisplayApplication.from_file( config_path, app ) + inherit = galaxy.util.string_as_bool(display_app.get('inherit', 'False')) + display_app = DisplayApplication.from_file(config_path, app) if display_app: if display_app.id in self.display_applications: if deactivate: - del self.display_applications[ display_app.id ] + del self.display_applications[display_app.id] else: # If we already loaded this display application, we'll use the first one loaded. - display_app = self.display_applications[ display_app.id ] + display_app = self.display_applications[display_app.id] elif installed_repository_dict: # If the display application is included in an installed tool shed repository, # set the tool shed related tool attributes. - display_app.tool_shed = installed_repository_dict[ 'tool_shed' ] - display_app.repository_name = installed_repository_dict[ 'repository_name' ] - display_app.repository_owner = installed_repository_dict[ 'repository_owner' ] - display_app.installed_changeset_revision = installed_repository_dict[ 'installed_changeset_revision' ] + display_app.tool_shed = installed_repository_dict['tool_shed'] + display_app.repository_name = installed_repository_dict['repository_name'] + display_app.repository_owner = installed_repository_dict['repository_owner'] + display_app.installed_changeset_revision = installed_repository_dict['installed_changeset_revision'] display_app.old_id = display_app.id # The display application should be included in the list of tools defined in tool_dicts. - tool_dicts = installed_repository_dict[ 'tool_dicts' ] + tool_dicts = installed_repository_dict['tool_dicts'] for tool_dict in tool_dicts: - if tool_dict[ 'id' ] == display_app.id: - display_app.guid = tool_dict[ 'guid' ] - display_app.id = tool_dict[ 'guid' ] + if tool_dict['id'] == display_app.id: + display_app.guid = tool_dict['guid'] + display_app.id = tool_dict['guid'] break if deactivate: if display_app.id in self.display_applications: - del self.display_applications[ display_app.id ] + del self.display_applications[display_app.id] if extension in self.datatypes_by_extension: - if display_app.id in self.datatypes_by_extension[ extension ].display_applications: - del self.datatypes_by_extension[ extension ].display_applications[ display_app.id ] - if inherit and ( self.datatypes_by_extension[ extension ], display_app ) in self.inherit_display_application_by_class: - self.inherit_display_application_by_class.remove( ( self.datatypes_by_extension[ extension ], display_app ) ) - self.log.debug( "Deactivated display application '%s' for datatype '%s'." % ( display_app.id, extension ) ) + if display_app.id in self.datatypes_by_extension[extension].display_applications: + del self.datatypes_by_extension[extension].display_applications[display_app.id] + if inherit and (self.datatypes_by_extension[extension], display_app) in self.inherit_display_application_by_class: + self.inherit_display_application_by_class.remove((self.datatypes_by_extension[extension], display_app)) + self.log.debug("Deactivated display application '%s' for datatype '%s'." % (display_app.id, extension)) else: - self.display_applications[ display_app.id ] = display_app - self.datatypes_by_extension[ extension ].add_display_application( display_app ) - if inherit and ( self.datatypes_by_extension[ extension ], display_app ) not in self.inherit_display_application_by_class: - self.inherit_display_application_by_class.append( ( self.datatypes_by_extension[ extension ], display_app ) ) - self.log.debug( "Loaded display application '%s' for datatype '%s', inherit=%s." % ( display_app.id, extension, inherit ) ) + self.display_applications[display_app.id] = display_app + self.datatypes_by_extension[extension].add_display_application(display_app) + if inherit and (self.datatypes_by_extension[extension], display_app) not in self.inherit_display_application_by_class: + self.inherit_display_application_by_class.append((self.datatypes_by_extension[extension], display_app)) + self.log.debug("Loaded display application '%s' for datatype '%s', inherit=%s." % (display_app.id, extension, inherit)) except Exception: if deactivate: - self.log.exception( "Error deactivating display application (%s)" % config_path ) + self.log.exception("Error deactivating display application (%s)" % config_path) else: - self.log.exception( "Error loading display application (%s)" % config_path ) + self.log.exception("Error loading display application (%s)" % config_path) # Handle display_application subclass inheritance. for extension, d_type1 in self.datatypes_by_extension.items(): for d_type2, display_app in self.inherit_display_application_by_class: - current_app = d_type1.get_display_application( display_app.id, None ) - if current_app is None and isinstance( d_type1, type( d_type2 ) ): - self.log.debug( "Adding inherited display application '%s' to datatype '%s'" % ( display_app.id, extension ) ) - d_type1.add_display_application( display_app ) + current_app = d_type1.get_display_application(display_app.id, None) + if current_app is None and isinstance(d_type1, type(d_type2)): + self.log.debug("Adding inherited display application '%s' to datatype '%s'" % (display_app.id, extension)) + d_type1.add_display_application(display_app) - def reload_display_applications( self, display_application_ids=None ): + def reload_display_applications(self, display_application_ids=None): """ Reloads display applications: by id, or all if no ids provided Returns tuple( [reloaded_ids], [failed_ids] ) """ if not display_application_ids: display_application_ids = self.display_applications.keys() - elif not isinstance( display_application_ids, list ): - display_application_ids = [ display_application_ids ] + elif not isinstance(display_application_ids, list): + display_application_ids = [display_application_ids] reloaded = [] failed = [] for display_application_id in display_application_ids: try: - self.display_applications[ display_application_id ].reload() - reloaded.append( display_application_id ) + self.display_applications[display_application_id].reload() + reloaded.append(display_application_id) except Exception as e: - self.log.debug( 'Requested to reload display application "%s", but failed: %s.', display_application_id, e ) - failed.append( display_application_id ) - return ( reloaded, failed ) + self.log.debug('Requested to reload display application "%s", but failed: %s.', display_application_id, e) + failed.append(display_application_id) + return (reloaded, failed) - def load_external_metadata_tool( self, toolbox ): + def load_external_metadata_tool(self, toolbox): """Adds a tool which is used to set external metadata""" # We need to be able to add a job to the queue to set metadata. The queue will currently only accept jobs with an associated # tool. We'll load a special tool to be used for Auto-Detecting metadata; this is less than ideal, but effective # Properly building a tool without relying on parsing an XML file is near difficult...so we bundle with Galaxy. self.to_xml_file() - set_meta_tool = toolbox.load_hidden_lib_tool( "galaxy/datatypes/set_metadata_tool.xml" ) + set_meta_tool = toolbox.load_hidden_lib_tool("galaxy/datatypes/set_metadata_tool.xml") self.set_external_metadata_tool = set_meta_tool - self.log.debug( "Loaded external metadata tool: %s", self.set_external_metadata_tool.id ) + self.log.debug("Loaded external metadata tool: %s", self.set_external_metadata_tool.id) - def set_default_values( self ): + def set_default_values(self): # Default values. if not self.datatypes_by_extension: self.datatypes_by_extension = { @@ -756,11 +756,11 @@ def set_default_values( self ): } # super supertype fix for input steps in workflows. if 'data' not in self.datatypes_by_extension: - self.datatypes_by_extension[ 'data' ] = data.Data() - self.mimetypes_by_extension[ 'data' ] = 'application/octet-stream' + self.datatypes_by_extension['data'] = data.Data() + self.mimetypes_by_extension['data'] = 'application/octet-stream' # Default values - the order in which we attempt to determine data types is critical # because some formats are much more flexibly defined than others. - if len( self.sniff_order ) < 1: + if len(self.sniff_order) < 1: self.sniff_order = [ binary.Bam(), binary.Sff(), @@ -793,47 +793,47 @@ def set_default_values( self ): tabular.CSV() ] - def get_converters_by_datatype( self, ext ): + def get_converters_by_datatype(self, ext): """Returns available converters by source type""" converters = odict() - source_datatype = type( self.get_datatype_by_extension( ext ) ) + source_datatype = type(self.get_datatype_by_extension(ext)) for ext2, converters_dict in self.datatype_converters.items(): - converter_datatype = type( self.get_datatype_by_extension( ext2 ) ) - if issubclass( source_datatype, converter_datatype ): - converters.update( converters_dict ) + converter_datatype = type(self.get_datatype_by_extension(ext2)) + if issubclass(source_datatype, converter_datatype): + converters.update(converters_dict) # Ensure ext-level converters are present if ext in self.datatype_converters.keys(): - converters.update( self.datatype_converters[ ext ] ) + converters.update(self.datatype_converters[ext]) return converters - def get_converter_by_target_type( self, source_ext, target_ext ): + def get_converter_by_target_type(self, source_ext, target_ext): """Returns a converter based on source and target datatypes""" - converters = self.get_converters_by_datatype( source_ext ) + converters = self.get_converters_by_datatype(source_ext) if target_ext in converters.keys(): - return converters[ target_ext ] + return converters[target_ext] return None - def find_conversion_destination_for_dataset_by_extensions( self, dataset, accepted_formats, converter_safe=True ): + def find_conversion_destination_for_dataset_by_extensions(self, dataset, accepted_formats, converter_safe=True): """Returns ( target_ext, existing converted dataset )""" - for convert_ext in self.get_converters_by_datatype( dataset.ext ): - convert_ext_datatype = self.get_datatype_by_extension( convert_ext ) + for convert_ext in self.get_converters_by_datatype(dataset.ext): + convert_ext_datatype = self.get_datatype_by_extension(convert_ext) if convert_ext_datatype is None: self.log.warning("Datatype class not found for extension '%s', which is used as target for conversion from datatype '%s'" % (convert_ext, dataset.ext)) - elif convert_ext_datatype.matches_any( accepted_formats ): - converted_dataset = dataset.get_converted_files_by_type( convert_ext ) + elif convert_ext_datatype.matches_any(accepted_formats): + converted_dataset = dataset.get_converted_files_by_type(convert_ext) if converted_dataset: ret_data = converted_dataset elif not converter_safe: continue else: ret_data = None - return ( convert_ext, ret_data ) - return ( None, None ) + return (convert_ext, ret_data) + return (None, None) - def get_composite_extensions( self ): - return [ ext for ( ext, d_type ) in self.datatypes_by_extension.items() if d_type.composite_type is not None ] + def get_composite_extensions(self): + return [ext for (ext, d_type) in self.datatypes_by_extension.items() if d_type.composite_type is not None] - def get_upload_metadata_params( self, context, group, tool ): + def get_upload_metadata_params(self, context, group, tool): """Returns dict of case value:inputs for metadata conditional for upload tool""" rval = {} for ext, d_type in self.datatypes_by_extension.items(): @@ -843,42 +843,42 @@ def get_upload_metadata_params( self, context, group, tool ): help_txt = meta_spec.desc if not help_txt or help_txt == meta_name: help_txt = "" - inputs.append( '' % ( meta_name, meta_name, meta_spec.default, help_txt ) ) - rval[ ext ] = "\n".join( inputs ) + inputs.append('' % (meta_name, meta_name, meta_spec.default, help_txt)) + rval[ext] = "\n".join(inputs) if 'auto' not in rval and 'txt' in rval: # need to manually add 'auto' datatype - rval[ 'auto' ] = rval[ 'txt' ] + rval['auto'] = rval['txt'] return rval @property - def edam_formats( self ): + def edam_formats(self): """ """ mapping = dict((k, v.edam_format) for k, v in self.datatypes_by_extension.items()) return mapping @property - def edam_data( self ): + def edam_data(self): """ """ mapping = dict((k, v.edam_data) for k, v in self.datatypes_by_extension.items()) return mapping @property - def integrated_datatypes_configs( self ): - if self.xml_filename and os.path.isfile( self.xml_filename ): + def integrated_datatypes_configs(self): + if self.xml_filename and os.path.isfile(self.xml_filename): return self.xml_filename return self.xml_filename - def to_xml_file( self ): + def to_xml_file(self): if self.xml_filename is not None: # If persisted previously, attempt to remove the temporary file in which we were written. try: - os.unlink( self.xml_filename ) + os.unlink(self.xml_filename) except: pass self.xml_filename = None fd, filename = tempfile.mkstemp() - self.xml_filename = os.path.abspath( filename ) + self.xml_filename = os.path.abspath(filename) if self.converters_path_attr: converters_path_str = ' converters_path="%s"' % self.converters_path_attr else: @@ -887,21 +887,21 @@ def to_xml_file( self ): display_path_str = ' display_path="%s"' % self.display_path_attr else: display_path_str = '' - os.write( fd, '\n' ) - os.write( fd, '\n' ) - os.write( fd, '\n' % ( converters_path_str, display_path_str ) ) + os.write(fd, '\n') + os.write(fd, '\n') + os.write(fd, '\n' % (converters_path_str, display_path_str)) for elem in self.datatype_elems: - os.write( fd, '%s' % galaxy.util.xml_to_string( elem ) ) - os.write( fd, '\n' ) - os.write( fd, '\n' ) + os.write(fd, '%s' % galaxy.util.xml_to_string(elem)) + os.write(fd, '\n') + os.write(fd, '\n') for elem in self.sniffer_elems: - os.write( fd, '%s' % galaxy.util.xml_to_string( elem ) ) - os.write( fd, '\n' ) - os.write( fd, '\n' ) - os.close( fd ) - os.chmod( self.xml_filename, 0o644 ) + os.write(fd, '%s' % galaxy.util.xml_to_string(elem)) + os.write(fd, '\n') + os.write(fd, '\n') + os.close(fd) + os.chmod(self.xml_filename, 0o644) - def get_extension( self, elem ): + def get_extension(self, elem): """ Function which returns the extension lowercased :param elem: @@ -910,7 +910,7 @@ def get_extension( self, elem ): extension = elem.get('extension', None) # If extension is not None and is uppercase or mixed case, we need to lowercase it if extension is not None and not extension.islower(): - self.log.debug( "%s is not lower case, that could cause troubles in the future. \ - Please change it to lower case" % extension ) + self.log.debug("%s is not lower case, that could cause troubles in the future. \ + Please change it to lower case" % extension) extension = extension.lower() return extension diff --git a/lib/galaxy/datatypes/sequence.py b/lib/galaxy/datatypes/sequence.py index b3c4f65f2ba1..1267ff3da4a7 100644 --- a/lib/galaxy/datatypes/sequence.py +++ b/lib/galaxy/datatypes/sequence.py @@ -9,6 +9,7 @@ import string import sys from cgi import escape +from itertools import islice import bx.align.maf @@ -16,7 +17,10 @@ from galaxy.datatypes import metadata from galaxy.datatypes.binary import Binary from galaxy.datatypes.metadata import MetadataElement -from galaxy.datatypes.sniff import get_headers +from galaxy.datatypes.sniff import ( + get_headers, + iter_headers +) from galaxy.util import ( compression_utils, nice_size @@ -25,6 +29,7 @@ is_bz2, is_gzip ) + from galaxy.util.image_util import check_image_type from . import data @@ -37,7 +42,7 @@ SNIFF_COMPRESSED_FASTQS = os.environ.get("GALAXY_ENABLE_BETA_COMPRESSED_FASTQ_SNIFFING", "0") == "1" -class SequenceSplitLocations( data.Text ): +class SequenceSplitLocations(data.Text): """ Class storing information about a sequence file composed of multiple gzip files concatenated as one OR an uncompressed file. In the GZIP case, each sub-file's location is stored in start and end. @@ -52,12 +57,12 @@ class SequenceSplitLocations( data.Text ): """ file_ext = "fqtoc" - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): if not dataset.dataset.purged: try: parsed_data = json.load(open(dataset.file_name)) # dataset.peek = json.dumps(data, sort_keys=True, indent=4) - dataset.peek = data.get_file_peek( dataset.file_name, is_multi_byte=is_multi_byte ) + dataset.peek = data.get_file_peek(dataset.file_name, is_multi_byte=is_multi_byte) dataset.blurb = '%d sections' % len(parsed_data['sections']) except Exception: dataset.peek = 'Not FQTOC file' @@ -66,7 +71,7 @@ def set_peek( self, dataset, is_multi_byte=False ): dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disk' - def sniff( self, filename ): + def sniff(self, filename): if os.path.getsize(filename) < 50000: try: data = json.load(open(filename)) @@ -80,25 +85,25 @@ def sniff( self, filename ): return False -class Sequence( data.Text ): +class Sequence(data.Text): """Class describing a sequence""" edam_data = "data_2044" """Add metadata elements""" - MetadataElement( name="sequences", default=0, desc="Number of sequences", readonly=True, visible=False, optional=True, no_value=0 ) + MetadataElement(name="sequences", default=0, desc="Number of sequences", readonly=True, visible=False, optional=True, no_value=0) - def set_meta( self, dataset, **kwd ): + def set_meta(self, dataset, **kwd): """ Set the number of sequences and the number of data lines in dataset. """ data_lines = 0 sequences = 0 - for line in open( dataset.file_name ): + for line in open(dataset.file_name): line = line.strip() - if line and line.startswith( '#' ): + if line and line.startswith('#'): # We don't count comment lines for sequence data types continue - if line and line.startswith( '>' ): + if line and line.startswith('>'): sequences += 1 data_lines += 1 else: @@ -106,13 +111,13 @@ def set_meta( self, dataset, **kwd ): dataset.metadata.data_lines = data_lines dataset.metadata.sequences = sequences - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): if not dataset.dataset.purged: - dataset.peek = data.get_file_peek( dataset.file_name, is_multi_byte=is_multi_byte ) + dataset.peek = data.get_file_peek(dataset.file_name, is_multi_byte=is_multi_byte) if dataset.metadata.sequences: - dataset.blurb = "%s sequences" % util.commaify( str( dataset.metadata.sequences ) ) + dataset.blurb = "%s sequences" % util.commaify(str(dataset.metadata.sequences)) else: - dataset.blurb = nice_size( dataset.get_size() ) + dataset.blurb = nice_size(dataset.get_size()) else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disk' @@ -137,7 +142,7 @@ def get_sequences_per_file(total_sequences, split_params): return sequences_per_file get_sequences_per_file = staticmethod(get_sequences_per_file) - def do_slow_split( cls, input_datasets, subdir_generator_function, split_params): + def do_slow_split(cls, input_datasets, subdir_generator_function, split_params): # count the sequences so we can split # TODO: if metadata is present, take the number of lines / 4 if input_datasets[0].metadata is not None and input_datasets[0].metadata.sequences is not None: @@ -153,7 +158,7 @@ def do_slow_split( cls, input_datasets, subdir_generator_function, split_params) return cls.write_split_files(input_datasets, None, subdir_generator_function, sequences_per_file) do_slow_split = classmethod(do_slow_split) - def do_fast_split( cls, input_datasets, toc_file_datasets, subdir_generator_function, split_params): + def do_fast_split(cls, input_datasets, toc_file_datasets, subdir_generator_function, split_params): data = json.load(open(toc_file_datasets[0].file_name)) sections = data['sections'] total_sequences = long(0) @@ -197,7 +202,7 @@ def get_subdir(idx): return directories write_split_files = classmethod(write_split_files) - def split( cls, input_datasets, subdir_generator_function, split_params): + def split(cls, input_datasets, subdir_generator_function, split_params): """Split a generic sequence file (not sensible or possible, see subclasses).""" if split_params is None: return None @@ -293,26 +298,26 @@ def get_split_commands_sequential(is_compressed, input_name, output_name, start_ get_split_commands_sequential = staticmethod(get_split_commands_sequential) -class Alignment( data.Text ): +class Alignment(data.Text): """Class describing an alignment""" edam_data = "data_0863" """Add metadata elements""" - MetadataElement( name="species", desc="Species", default=[], param=metadata.SelectParameter, multiple=True, readonly=True, no_value=None ) + MetadataElement(name="species", desc="Species", default=[], param=metadata.SelectParameter, multiple=True, readonly=True, no_value=None) - def split( cls, input_datasets, subdir_generator_function, split_params): + def split(cls, input_datasets, subdir_generator_function, split_params): """Split a generic alignment file (not sensible or possible, see subclasses).""" if split_params is None: return None raise NotImplementedError("Can't split generic alignment files") -class Fasta( Sequence ): +class Fasta(Sequence): """Class representing a FASTA sequence""" edam_format = "format_1929" file_ext = "fasta" - def sniff( self, filename ): + def sniff(self, filename): """ Determines whether the file is in fasta format @@ -348,17 +353,17 @@ def sniff( self, filename ): """ try: - fh = open( filename ) + fh = open(filename) while True: line = fh.readline() if not line: break # EOF line = line.strip() if line: # first non-empty line - if line.startswith( '>' ): + if line.startswith('>'): # The next line.strip() must not be '', nor startwith '>' line = fh.readline().strip() - if line == '' or line.startswith( '>' ): + if line == '' or line.startswith('>'): break # If there is a third line, and it isn't a header line, it may not contain chars like '()[].' otherwise it's most likely a DotBracket file @@ -498,12 +503,12 @@ def _count_split(cls, input_file, chunk_size, subdir_generator_function): _count_split = classmethod(_count_split) -class csFasta( Sequence ): +class csFasta(Sequence): """ Class representing the SOLID Color-Space sequence ( csfasta ) """ edam_format = "format_3589" file_ext = "csfasta" - def sniff( self, filename ): + def sniff(self, filename): """ Color-space sequence: >2_15_85_F3 @@ -518,20 +523,20 @@ def sniff( self, filename ): True """ try: - fh = open( filename ) + fh = open(filename) while True: line = fh.readline() if not line: break # EOF line = line.strip() - if line and not line.startswith( '#' ): # first non-empty non-comment line - if line.startswith( '>' ): + if line and not line.startswith('#'): # first non-empty non-comment line + if line.startswith('>'): line = fh.readline().strip() - if line == '' or line.startswith( '>' ): + if line == '' or line.startswith('>'): break elif line[0] not in string.ascii_uppercase: return False - elif len( line ) > 1 and not re.search( '^[\d.]+$', line[1:] ): + elif len(line) > 1 and not re.search('^[\d.]+$', line[1:]): return False return True else: @@ -541,20 +546,20 @@ def sniff( self, filename ): pass return False - def set_meta( self, dataset, **kwd ): + def set_meta(self, dataset, **kwd): if self.max_optional_metadata_filesize >= 0 and dataset.get_size() > self.max_optional_metadata_filesize: dataset.metadata.data_lines = None dataset.metadata.sequences = None return - return Sequence.set_meta( self, dataset, **kwd ) + return Sequence.set_meta(self, dataset, **kwd) -class BaseFastq ( Sequence ): +class BaseFastq (Sequence): """Base class for FastQ sequences""" edam_format = "format_1930" file_ext = "fastq" - def set_meta( self, dataset, **kwd ): + def set_meta(self, dataset, **kwd): """ Set the number of sequences and the number of data lines in dataset. @@ -570,12 +575,12 @@ def set_meta( self, dataset, **kwd ): with compression_utils.get_fileobj(dataset.file_name) as in_file: for line in in_file: line = line.strip() - if line and line.startswith( '#' ) and not data_lines: + if line and line.startswith('#') and not data_lines: # We don't count comment lines for sequence data types continue seq_counter += 1 data_lines += 1 - if line and line.startswith( '@' ): + if line and line.startswith('@'): if seq_counter >= 4: # count previous block # blocks should be 4 lines long @@ -587,7 +592,7 @@ def set_meta( self, dataset, **kwd ): dataset.metadata.data_lines = data_lines dataset.metadata.sequences = sequences - def sniff( self, filename ): + def sniff(self, filename): """ Determines whether the file is in generic fastq format For details, see http://maq.sourceforge.net/fastq.shtml @@ -611,19 +616,20 @@ def sniff( self, filename ): compressed = is_gzip(filename) or is_bz2(filename) if compressed and not isinstance(self, Binary): return False - headers = get_headers( filename, None, count=1000 ) + headers = iter_headers(filename, None, count=1000) # If this is a FastqSanger-derived class, then check to see if the base qualities match if isinstance(self, FastqSanger) or isinstance(self, FastqSangerGz) or isinstance(self, FastqSangerBz2): if not self.sangerQualities(headers): return False - bases_regexp = re.compile( "^[NGTAC]*" ) + bases_regexp = re.compile("^[NGTAC]*") # check that first block looks like a fastq block try: - if len( headers ) >= 4 and headers[0][0] and headers[0][0][0] == "@" and headers[2][0] and headers[2][0][0] == "+" and headers[1][0]: + headers = get_headers(filename, None, count=4) + if len(headers) == 4 and headers[0][0] and headers[0][0][0] == "@" and headers[2][0] and headers[2][0][0] == "+" and headers[1][0]: # Check the sequence line, make sure it contains only G/C/A/T/N - if not bases_regexp.match( headers[1][0] ): + if not bases_regexp.match(headers[1][0]): return False return True return False @@ -634,17 +640,17 @@ def display_data(self, trans, dataset, preview=False, filename=None, to_ext=None if preview: with compression_utils.get_fileobj(dataset.file_name) as fh: max_peek_size = 1000000 # 1 MB - if os.stat( dataset.file_name ).st_size < max_peek_size: + if os.stat(dataset.file_name).st_size < max_peek_size: mime = "text/plain" - self._clean_and_set_mime_type( trans, mime ) + self._clean_and_set_mime_type(trans, mime) return fh.read() - return trans.stream_template_mako( "/dataset/large_file.mako", - truncated_data=fh.read(max_peek_size), - data=dataset) + return trans.stream_template_mako("/dataset/large_file.mako", + truncated_data=fh.read(max_peek_size), + data=dataset) else: return Sequence.display_data(self, trans, dataset, preview, filename, to_ext, **kwd) - def split( cls, input_datasets, subdir_generator_function, split_params): + def split(cls, input_datasets, subdir_generator_function, split_params): """ FASTQ files are split on cluster boundaries, in increments of 4 lines """ @@ -693,63 +699,63 @@ def process_split_file(data): process_split_file = staticmethod(process_split_file) @staticmethod - def sangerQualities( lines ): + def sangerQualities(lines): """Presuming lines are lines from a fastq file, return True if the qualities are compatible with sanger encoding""" - for line in lines[3::4]: + for line in islice(lines, 3, None, 4): if not all(_ >= '!' and _ <= 'M' for _ in line[0]): return False return True -class Fastq( BaseFastq ): +class Fastq(BaseFastq): """Class representing a generic FASTQ sequence""" edam_format = "format_1930" file_ext = "fastq" -class FastqSanger( Fastq ): +class FastqSanger(Fastq): """Class representing a FASTQ sequence ( the Sanger variant )""" edam_format = "format_1932" file_ext = "fastqsanger" -class FastqSolexa( Fastq ): +class FastqSolexa(Fastq): """Class representing a FASTQ sequence ( the Solexa variant )""" edam_format = "format_1933" file_ext = "fastqsolexa" -class FastqIllumina( Fastq ): +class FastqIllumina(Fastq): """Class representing a FASTQ sequence ( the Illumina 1.3+ variant )""" edam_format = "format_1931" file_ext = "fastqillumina" -class FastqCSSanger( Fastq ): +class FastqCSSanger(Fastq): """Class representing a Color Space FASTQ sequence ( e.g a SOLiD variant )""" file_ext = "fastqcssanger" -class FastqGz ( BaseFastq, Binary ): +class FastqGz (BaseFastq, Binary): """Class representing a generic compressed FASTQ sequence""" edam_format = "format_1930" file_ext = "fastq.gz" compressed = True - def sniff( self, filename ): + def sniff(self, filename): """Determines whether the file is in gzip-compressed FASTQ format""" if not is_gzip(filename): return False - return BaseFastq.sniff( self, filename ) + return BaseFastq.sniff(self, filename) -class FastqSangerGz( FastqGz ): +class FastqSangerGz(FastqGz): """Class representing a compressed FASTQ sequence ( the Sanger variant )""" edam_format = "format_1932" file_ext = "fastqsanger.gz" -class FastqSolexaGz( FastqGz ): +class FastqSolexaGz(FastqGz): """Class representing a compressed FASTQ sequence ( the Solexa variant )""" edam_format = "format_1933" file_ext = "fastqsolexa.gz" @@ -760,31 +766,31 @@ class FastqSolexaGz( FastqGz ): Binary.register_sniffable_binary_format("fastq.gz", "fastq.gz", FastqGz) -class FastqIlluminaGz( FastqGz ): +class FastqIlluminaGz(FastqGz): """Class representing a compressed FASTQ sequence ( the Illumina 1.3+ variant )""" edam_format = "format_1931" file_ext = "fastqillumina.gz" -class FastqCSSangerGz( FastqGz ): +class FastqCSSangerGz(FastqGz): """Class representing a Color Space compressed FASTQ sequence ( e.g a SOLiD variant )""" file_ext = "fastqcssanger.gz" -class FastqBz2 ( BaseFastq, Binary ): +class FastqBz2 (BaseFastq, Binary): """Class representing a generic compressed FASTQ sequence""" edam_format = "format_1930" file_ext = "fastq.bz2" compressed = True - def sniff( self, filename ): + def sniff(self, filename): """Determine whether the file is in bzip2-compressed FASTQ format""" if not is_bz2(filename): return False - return BaseFastq.sniff( self, filename ) + return BaseFastq.sniff(self, filename) -class FastqSangerBz2( FastqBz2 ): +class FastqSangerBz2(FastqBz2): """Class representing a compressed FASTQ sequence ( the Sanger variant )""" edam_format = "format_1932" file_ext = "fastqsanger.bz2" @@ -795,44 +801,44 @@ class FastqSangerBz2( FastqBz2 ): Binary.register_sniffable_binary_format("fastq.bz2", "fastq.bz2", FastqBz2) -class FastqSolexaBz2( FastqBz2 ): +class FastqSolexaBz2(FastqBz2): """Class representing a compressed FASTQ sequence ( the Solexa variant )""" edam_format = "format_1933" file_ext = "fastqsolexa.bz2" -class FastqIlluminaBz2( FastqBz2 ): +class FastqIlluminaBz2(FastqBz2): """Class representing a compressed FASTQ sequence ( the Illumina 1.3+ variant )""" edam_format = "format_1931" file_ext = "fastqillumina.bz2" -class FastqCSSangerBz2( FastqBz2 ): +class FastqCSSangerBz2(FastqBz2): """Class representing a Color Space compressed FASTQ sequence ( e.g a SOLiD variant )""" file_ext = "fastqcssanger.bz2" -class Maf( Alignment ): +class Maf(Alignment): """Class describing a Maf alignment""" edam_format = "format_3008" file_ext = "maf" # Readonly and optional, users can't unset it, but if it is not set, we are generally ok; if required use a metadata validator in the tool definition - MetadataElement( name="blocks", default=0, desc="Number of blocks", readonly=True, optional=True, visible=False, no_value=0 ) - MetadataElement( name="species_chromosomes", desc="Species Chromosomes", param=metadata.FileParameter, readonly=True, no_value=None, visible=False, optional=True ) - MetadataElement( name="maf_index", desc="MAF Index File", param=metadata.FileParameter, readonly=True, no_value=None, visible=False, optional=True ) + MetadataElement(name="blocks", default=0, desc="Number of blocks", readonly=True, optional=True, visible=False, no_value=0) + MetadataElement(name="species_chromosomes", desc="Species Chromosomes", param=metadata.FileParameter, readonly=True, no_value=None, visible=False, optional=True) + MetadataElement(name="maf_index", desc="MAF Index File", param=metadata.FileParameter, readonly=True, no_value=None, visible=False, optional=True) - def init_meta( self, dataset, copy_from=None ): - Alignment.init_meta( self, dataset, copy_from=copy_from ) + def init_meta(self, dataset, copy_from=None): + Alignment.init_meta(self, dataset, copy_from=copy_from) - def set_meta( self, dataset, overwrite=True, **kwd ): + def set_meta(self, dataset, overwrite=True, **kwd): """ Parses and sets species, chromosomes, index from MAF file. """ # these metadata values are not accessable by users, always overwrite # Imported here to avoid circular dependency from galaxy.tools.util.maf_utilities import build_maf_index_species_chromosomes - indexes, species, species_chromosomes, blocks = build_maf_index_species_chromosomes( dataset.file_name ) + indexes, species, species_chromosomes, blocks = build_maf_index_species_chromosomes(dataset.file_name) if indexes is None: return # this is not a MAF file dataset.metadata.species = species @@ -841,25 +847,25 @@ def set_meta( self, dataset, overwrite=True, **kwd ): # write species chromosomes to a file chrom_file = dataset.metadata.species_chromosomes if not chrom_file: - chrom_file = dataset.metadata.spec['species_chromosomes'].param.new_file( dataset=dataset ) - chrom_out = open( chrom_file.file_name, 'wb' ) + chrom_file = dataset.metadata.spec['species_chromosomes'].param.new_file(dataset=dataset) + chrom_out = open(chrom_file.file_name, 'wb') for spec, chroms in species_chromosomes.items(): - chrom_out.write( "%s\t%s\n" % ( spec, "\t".join( chroms ) ) ) + chrom_out.write("%s\t%s\n" % (spec, "\t".join(chroms))) chrom_out.close() dataset.metadata.species_chromosomes = chrom_file index_file = dataset.metadata.maf_index if not index_file: - index_file = dataset.metadata.spec['maf_index'].param.new_file( dataset=dataset ) - indexes.write( open( index_file.file_name, 'wb' ) ) + index_file = dataset.metadata.spec['maf_index'].param.new_file(dataset=dataset) + indexes.write(open(index_file.file_name, 'wb')) dataset.metadata.maf_index = index_file - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): if not dataset.dataset.purged: # The file must exist on disk for the get_file_peek() method - dataset.peek = data.get_file_peek( dataset.file_name, is_multi_byte=is_multi_byte ) + dataset.peek = data.get_file_peek(dataset.file_name, is_multi_byte=is_multi_byte) if dataset.metadata.blocks: - dataset.blurb = "%s blocks" % util.commaify( str( dataset.metadata.blocks ) ) + dataset.blurb = "%s blocks" % util.commaify(str(dataset.metadata.blocks)) else: # Number of blocks is not known ( this should not happen ), and auto-detect is # needed to set metadata @@ -868,18 +874,18 @@ def set_peek( self, dataset, is_multi_byte=False ): dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disk' - def display_peek( self, dataset ): + def display_peek(self, dataset): """Returns formated html of peek""" - return self.make_html_table( dataset ) + return self.make_html_table(dataset) - def make_html_table( self, dataset, skipchars=[] ): + def make_html_table(self, dataset, skipchars=[]): """Create HTML table, used for displaying peek""" out = [''] try: out.append('' ) + out.append('%s ' % species) + out.append('') if not dataset.peek: dataset.set_peek() data = dataset.peek @@ -888,14 +894,14 @@ def make_html_table( self, dataset, skipchars=[] ): line = line.strip() if not line: continue - out.append( '' % escape( line ) ) - out.append( '
    Species: ') for species in dataset.metadata.species: - out.append( '%s ' % species ) - out.append( '
    %s
    ' ) - out = "".join( out ) + out.append('%s' % escape(line)) + out.append('') + out = "".join(out) except Exception as exc: out = "Can't create peek %s" % exc return out - def sniff( self, filename ): + def sniff(self, filename): """ Determines wether the file is in maf format @@ -918,7 +924,7 @@ def sniff( self, filename ): >>> Maf().sniff( fname ) False """ - headers = get_headers( filename, None ) + headers = get_headers(filename, None) try: if len(headers) > 1 and headers[0][0] and headers[0][0] == "##maf": return True @@ -928,33 +934,33 @@ def sniff( self, filename ): return False -class MafCustomTrack( data.Text ): +class MafCustomTrack(data.Text): file_ext = "mafcustomtrack" - MetadataElement( name="vp_chromosome", default='chr1', desc="Viewport Chromosome", readonly=True, optional=True, visible=False, no_value='' ) - MetadataElement( name="vp_start", default='1', desc="Viewport Start", readonly=True, optional=True, visible=False, no_value='' ) - MetadataElement( name="vp_end", default='100', desc="Viewport End", readonly=True, optional=True, visible=False, no_value='' ) + MetadataElement(name="vp_chromosome", default='chr1', desc="Viewport Chromosome", readonly=True, optional=True, visible=False, no_value='') + MetadataElement(name="vp_start", default='1', desc="Viewport Start", readonly=True, optional=True, visible=False, no_value='') + MetadataElement(name="vp_end", default='100', desc="Viewport End", readonly=True, optional=True, visible=False, no_value='') - def set_meta( self, dataset, overwrite=True, **kwd ): + def set_meta(self, dataset, overwrite=True, **kwd): """ Parses and sets viewport metadata from MAF file. """ max_block_check = 10 chrom = None - forward_strand_start = float( 'inf' ) + forward_strand_start = float('inf') forward_strand_end = 0 try: - maf_file = open( dataset.file_name ) + maf_file = open(dataset.file_name) maf_file.readline() # move past track line - for i, block in enumerate( bx.align.maf.Reader( maf_file ) ): - ref_comp = block.get_component_by_src_start( dataset.metadata.dbkey ) + for i, block in enumerate(bx.align.maf.Reader(maf_file)): + ref_comp = block.get_component_by_src_start(dataset.metadata.dbkey) if ref_comp: - ref_chrom = bx.align.maf.src_split( ref_comp.src )[-1] + ref_chrom = bx.align.maf.src_split(ref_comp.src)[-1] if chrom is None: chrom = ref_chrom if chrom == ref_chrom: - forward_strand_start = min( forward_strand_start, ref_comp.forward_strand_start ) - forward_strand_end = max( forward_strand_end, ref_comp.forward_strand_end ) + forward_strand_start = min(forward_strand_start, ref_comp.forward_strand_start) + forward_strand_end = max(forward_strand_end, ref_comp.forward_strand_end) if i > max_block_check: break @@ -966,7 +972,7 @@ def set_meta( self, dataset, overwrite=True, **kwd ): pass -class Axt( data.Text ): +class Axt(data.Text): """Class describing an axt alignment""" # gvk- 11/19/09 - This is really an alignment, but we no longer have tools that use this data type, and it is # here simply for backward compatibility ( although it is still in the datatypes registry ). Subclassing @@ -976,7 +982,7 @@ class Axt( data.Text ): edam_format = "format_3013" file_ext = "axt" - def sniff( self, filename ): + def sniff(self, filename): """ Determines whether the file is in axt format @@ -1002,7 +1008,7 @@ def sniff( self, filename ): >>> Axt().sniff( fname ) False """ - headers = get_headers( filename, None ) + headers = get_headers(filename, None) if len(headers) < 4: return False for hdr in headers: @@ -1012,7 +1018,7 @@ def sniff( self, filename ): if len(hdr) != 9: return False try: - map( int, [hdr[0], hdr[2], hdr[3], hdr[5], hdr[6], hdr[8]] ) + map(int, [hdr[0], hdr[2], hdr[3], hdr[5], hdr[6], hdr[8]]) except: return False if hdr[7] not in data.valid_strand: @@ -1021,7 +1027,7 @@ def sniff( self, filename ): return True -class Lav( data.Text ): +class Lav(data.Text): """Class describing a LAV alignment""" # gvk- 11/19/09 - This is really an alignment, but we no longer have tools that use this data type, and it is # here simply for backward compatibility ( although it is still in the datatypes registry ). Subclassing @@ -1031,7 +1037,7 @@ class Lav( data.Text ): edam_format = "format_3014" file_ext = "lav" - def sniff( self, filename ): + def sniff(self, filename): """ Determines whether the file is in lav format @@ -1048,7 +1054,7 @@ def sniff( self, filename ): >>> Lav().sniff( fname ) False """ - headers = get_headers( filename, None ) + headers = get_headers(filename, None) try: if len(headers) > 1 and headers[0][0] and headers[0][0].startswith('#:lav'): return True @@ -1058,25 +1064,25 @@ def sniff( self, filename ): return False -class RNADotPlotMatrix( data.Data ): +class RNADotPlotMatrix(data.Data): edam_format = "format_3466" file_ext = "rna_eps" - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): if not dataset.dataset.purged: dataset.peek = 'RNA Dot Plot format (Postscript derivative)' - dataset.blurb = nice_size( dataset.get_size() ) + dataset.blurb = nice_size(dataset.get_size()) else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disk' def sniff(self, filename): """Determine if the file is in RNA dot plot format.""" - if check_image_type( filename, ['EPS'] ): + if check_image_type(filename, ['EPS']): seq = False coor = False pairs = False - with open( filename ) as handle: + with open(filename) as handle: for line in handle: line = line.strip() if line: @@ -1091,15 +1097,15 @@ def sniff(self, filename): return False -class DotBracket ( Sequence ): +class DotBracket (Sequence): edam_data = "data_0880" edam_format = "format_1457" file_ext = "dbn" - sequence_regexp = re.compile( "^[ACGTURYKMSWBDHVN]+$", re.I) - structure_regexp = re.compile( "^[\(\)\.\[\]{}]+$" ) + sequence_regexp = re.compile("^[ACGTURYKMSWBDHVN]+$", re.I) + structure_regexp = re.compile("^[\(\)\.\[\]{}]+$") - def set_meta( self, dataset, **kwd ): + def set_meta(self, dataset, **kwd): """ Set the number of sequences and the number of data lines in dataset. @@ -1113,11 +1119,11 @@ def set_meta( self, dataset, **kwd ): data_lines = 0 sequences = 0 - for line in open( dataset.file_name ): + for line in open(dataset.file_name): line = line.strip() data_lines += 1 - if line and line.startswith( '>' ): + if line and line.startswith('>'): sequences += 1 dataset.metadata.data_lines = data_lines @@ -1155,7 +1161,7 @@ def sniff(self, filename): state = 0 - with open( filename, "r" ) as handle: + with open(filename, "r") as handle: for line in handle: line = line.strip() @@ -1203,3 +1209,67 @@ def sniff(self, filename): pass return False + + +class MemePsp(Sequence): + """Class representing MEME Position Specific Priors""" + file_ext = "memepsp" + + def sniff(self, filename): + """ + The format of an entry in a PSP file is: + + >ID WIDTH + PRIORS + + For complete details see http://meme-suite.org/doc/psp-format.html + + >>> from galaxy.datatypes.sniff import get_test_fname + >>> fname = get_test_fname('1.memepsp') + >>> MemePsp().sniff(fname) + True + >>> fname = get_test_fname('sequence.fasta') + >>> MemePsp().sniff(fname) + False + """ + def floats_verified(l): + for item in l.split(): + try: + float(item) + except: + return False + return True + try: + num_lines = 0 + with open(filename) as fh: + line = fh.readline() + if not line: + # EOF. + return False + num_lines += 1 + if num_lines > 100: + return True + line = line.strip() + if line: + if line.startswith('>'): + # The line must not be blank, nor start with '>' + line = fh.readline().strip() + if line == '' or line.startswith('>'): + return False + # All items within the line must be floats. + if not floats_verified(line): + return False + # If there is a second line within the ID section, + # all items within the line must be floats. + line = fh.readline().strip() + if line: + if not floats_verified(line): + return False + else: + # We found a non-empty line, + # but it's not a psp id width. + return False + except: + return False + # We've reached EOF in less than 100 lines. + return True diff --git a/lib/galaxy/datatypes/sniff.py b/lib/galaxy/datatypes/sniff.py index 4e1aa31b223d..10222f6a7d99 100644 --- a/lib/galaxy/datatypes/sniff.py +++ b/lib/galaxy/datatypes/sniff.py @@ -40,7 +40,7 @@ def get_test_fname(fname): return full_path -def stream_to_open_named_file( stream, fd, filename, source_encoding=None, source_error='strict', target_encoding=None, target_error='strict' ): +def stream_to_open_named_file(stream, fd, filename, source_encoding=None, source_error='strict', target_encoding=None, target_error='strict'): """Writes a stream to the provided file descriptor, returns the file's name and bool( is_multi_byte ). Closes file descriptor""" # signature and behavor is somewhat odd, due to backwards compatibility, but this can/should be done better CHUNK_SIZE = 1048576 @@ -55,62 +55,62 @@ def stream_to_open_named_file( stream, fd, filename, source_encoding=None, sourc if not source_encoding: source_encoding = util.DEFAULT_ENCODING # sys.getdefaultencoding() would mimic old behavior (defaults to ascii) while True: - chunk = stream.read( CHUNK_SIZE ) + chunk = stream.read(CHUNK_SIZE) if not chunk: break if not data_checked: # See if we're uploading a compressed file - if zipfile.is_zipfile( filename ): + if zipfile.is_zipfile(filename): is_compressed = True else: try: - if text_type( chunk[:2] ) == text_type( util.gzip_magic ): + if text_type(chunk[:2]) == text_type(util.gzip_magic): is_compressed = True except: pass if not is_compressed: # See if we have a multi-byte character file chars = chunk[:100] - is_multi_byte = multi_byte.is_multi_byte( chars ) + is_multi_byte = multi_byte.is_multi_byte(chars) if not is_multi_byte: - is_binary = util.is_binary( chunk ) + is_binary = util.is_binary(chunk) data_checked = True if not is_compressed and not is_binary: - if not isinstance( chunk, text_type ): - chunk = chunk.decode( source_encoding, source_error ) - os.write( fd, chunk.encode( target_encoding, target_error ) ) + if not isinstance(chunk, text_type): + chunk = chunk.decode(source_encoding, source_error) + os.write(fd, chunk.encode(target_encoding, target_error)) else: # Compressed files must be encoded after they are uncompressed in the upload utility, # while binary files should not be encoded at all. - os.write( fd, chunk ) - os.close( fd ) + os.write(fd, chunk) + os.close(fd) return filename, is_multi_byte -def stream_to_file( stream, suffix='', prefix='', dir=None, text=False, **kwd ): +def stream_to_file(stream, suffix='', prefix='', dir=None, text=False, **kwd): """Writes a stream to a temporary file, returns the temporary file's name""" - fd, temp_name = tempfile.mkstemp( suffix=suffix, prefix=prefix, dir=dir, text=text ) - return stream_to_open_named_file( stream, fd, temp_name, **kwd ) + fd, temp_name = tempfile.mkstemp(suffix=suffix, prefix=prefix, dir=dir, text=text) + return stream_to_open_named_file(stream, fd, temp_name, **kwd) -def check_newlines( fname, bytes_to_read=52428800 ): +def check_newlines(fname, bytes_to_read=52428800): """ Determines if there are any non-POSIX newlines in the first number_of_bytes (by default, 50MB) of the file. """ CHUNK_SIZE = 2 ** 20 - f = open( fname, 'r' ) - for chunk in f.read( CHUNK_SIZE ): + f = open(fname, 'r') + for chunk in f.read(CHUNK_SIZE): if f.tell() > bytes_to_read: break - if chunk.count( '\r' ): + if chunk.count('\r'): f.close() return True f.close() return False -def convert_newlines( fname, in_place=True, tmp_dir=None, tmp_prefix=None ): +def convert_newlines(fname, in_place=True, tmp_dir=None, tmp_prefix=None): """ Converts in place a file from universal line endings to Posix line endings. @@ -122,25 +122,25 @@ def convert_newlines( fname, in_place=True, tmp_dir=None, tmp_prefix=None ): >>> open(fname).read() '1 2\\n3 4\\n' """ - fd, temp_name = tempfile.mkstemp( prefix=tmp_prefix, dir=tmp_dir ) - fp = os.fdopen( fd, "wt" ) + fd, temp_name = tempfile.mkstemp(prefix=tmp_prefix, dir=tmp_dir) + fp = os.fdopen(fd, "wt") i = None - for i, line in enumerate( open( fname, "U" ) ): - fp.write( "%s\n" % line.rstrip( "\r\n" ) ) + for i, line in enumerate(open(fname, "U")): + fp.write("%s\n" % line.rstrip("\r\n")) fp.close() if i is None: i = 0 else: i += 1 if in_place: - shutil.move( temp_name, fname ) + shutil.move(temp_name, fname) # Return number of lines in file. - return ( i, None ) + return (i, None) else: - return ( i, temp_name ) + return (i, temp_name) -def sep2tabs( fname, in_place=True, patt="\\s+" ): +def sep2tabs(fname, in_place=True, patt="\\s+"): """ Transforms in place a 'sep' separated file to a tab separated one @@ -151,28 +151,28 @@ def sep2tabs( fname, in_place=True, patt="\\s+" ): >>> open(fname).read() '1\\t2\\n3\\t4\\n' """ - regexp = re.compile( patt ) + regexp = re.compile(patt) fd, temp_name = tempfile.mkstemp() - fp = os.fdopen( fd, "wt" ) + fp = os.fdopen(fd, "wt") i = None - for i, line in enumerate( open( fname ) ): - line = line.rstrip( '\r\n' ) - elems = regexp.split( line ) - fp.write( "%s\n" % '\t'.join( elems ) ) + for i, line in enumerate(open(fname)): + line = line.rstrip('\r\n') + elems = regexp.split(line) + fp.write("%s\n" % '\t'.join(elems)) fp.close() if i is None: i = 0 else: i += 1 if in_place: - shutil.move( temp_name, fname ) + shutil.move(temp_name, fname) # Return number of lines in file. - return ( i, None ) + return (i, None) else: - return ( i, temp_name ) + return (i, temp_name) -def convert_newlines_sep2tabs( fname, in_place=True, patt="\\s+", tmp_dir=None, tmp_prefix=None ): +def convert_newlines_sep2tabs(fname, in_place=True, patt="\\s+", tmp_dir=None, tmp_prefix=None): """ Combines above methods: convert_newlines() and sep2tabs() so that files do not need to be read twice @@ -184,55 +184,57 @@ def convert_newlines_sep2tabs( fname, in_place=True, patt="\\s+", tmp_dir=None, >>> open(fname).read() '1\\t2\\n3\\t4\\n' """ - regexp = re.compile( patt ) - fd, temp_name = tempfile.mkstemp( prefix=tmp_prefix, dir=tmp_dir ) - fp = os.fdopen( fd, "wt" ) - for i, line in enumerate( open( fname, "U" ) ): - line = line.rstrip( '\r\n' ) - elems = regexp.split( line ) - fp.write( "%s\n" % '\t'.join( elems ) ) + regexp = re.compile(patt) + fd, temp_name = tempfile.mkstemp(prefix=tmp_prefix, dir=tmp_dir) + fp = os.fdopen(fd, "wt") + for i, line in enumerate(open(fname, "U")): + line = line.rstrip('\r\n') + elems = regexp.split(line) + fp.write("%s\n" % '\t'.join(elems)) fp.close() if in_place: - shutil.move( temp_name, fname ) + shutil.move(temp_name, fname) # Return number of lines in file. - return ( i + 1, None ) + return (i + 1, None) else: - return ( i + 1, temp_name ) + return (i + 1, temp_name) -def get_headers( fname, sep, count=60, is_multi_byte=False, comment_designator=None ): - """ - Returns a list with the first 'count' lines split by 'sep', ignoring lines - starting with 'comment_designator' - - >>> fname = get_test_fname('complete.bed') - >>> get_headers(fname,'\\t') - [['chr7', '127475281', '127491632', 'NM_000230', '0', '+', '127486022', '127488767', '0', '3', '29,172,3225,', '0,10713,13126,'], ['chr7', '127486011', '127488900', 'D49487', '0', '+', '127486022', '127488767', '0', '2', '155,490,', '0,2399']] - >>> fname = get_test_fname('test.gff') - >>> get_headers(fname, '\\t', count=5, comment_designator='#') - [[''], ['chr7', 'bed2gff', 'AR', '26731313', '26731437', '.', '+', '.', 'score'], ['chr7', 'bed2gff', 'AR', '26731491', '26731536', '.', '+', '.', 'score'], ['chr7', 'bed2gff', 'AR', '26731541', '26731649', '.', '+', '.', 'score'], ['chr7', 'bed2gff', 'AR', '26731659', '26731841', '.', '+', '.', 'score']] - """ - headers = [] +def iter_headers(fname, sep, count=60, is_multi_byte=False, comment_designator=None): with compression_utils.get_fileobj(fname) as in_file: idx = 0 for line in in_file: line = line.rstrip('\n\r') if is_multi_byte: # TODO: fix this - sep is never found in line - line = unicodify( line, 'utf-8' ) - sep = sep.encode( 'utf-8' ) + line = unicodify(line, 'utf-8') + sep = sep.encode('utf-8') if comment_designator is not None and comment_designator != '': - comment_designator = comment_designator.encode( 'utf-8' ) - if comment_designator is not None and comment_designator != '' and line.startswith( comment_designator ): + comment_designator = comment_designator.encode('utf-8') + if comment_designator is not None and comment_designator != '' and line.startswith(comment_designator): continue - headers.append( line.split(sep) ) + yield line.split(sep) idx += 1 if idx == count: break - return headers -def is_column_based( fname, sep='\t', skip=0, is_multi_byte=False ): +def get_headers(fname, sep, count=60, is_multi_byte=False, comment_designator=None): + """ + Returns a list with the first 'count' lines split by 'sep', ignoring lines + starting with 'comment_designator' + + >>> fname = get_test_fname('complete.bed') + >>> get_headers(fname,'\\t') + [['chr7', '127475281', '127491632', 'NM_000230', '0', '+', '127486022', '127488767', '0', '3', '29,172,3225,', '0,10713,13126,'], ['chr7', '127486011', '127488900', 'D49487', '0', '+', '127486022', '127488767', '0', '2', '155,490,', '0,2399']] + >>> fname = get_test_fname('test.gff') + >>> get_headers(fname, '\\t', count=5, comment_designator='#') + [[''], ['chr7', 'bed2gff', 'AR', '26731313', '26731437', '.', '+', '.', 'score'], ['chr7', 'bed2gff', 'AR', '26731491', '26731536', '.', '+', '.', 'score'], ['chr7', 'bed2gff', 'AR', '26731541', '26731649', '.', '+', '.', 'score'], ['chr7', 'bed2gff', 'AR', '26731659', '26731841', '.', '+', '.', 'score']] + """ + return list(iter_headers(fname=fname, sep=sep, count=count, is_multi_byte=is_multi_byte, comment_designator=comment_designator)) + + +def is_column_based(fname, sep='\t', skip=0, is_multi_byte=False): """ Checks whether the file is column based with respect to a separator (defaults to tab separator). @@ -260,7 +262,7 @@ def is_column_based( fname, sep='\t', skip=0, is_multi_byte=False ): >>> is_column_based(fname) True """ - headers = get_headers( fname, sep, is_multi_byte=is_multi_byte ) + headers = get_headers(fname, sep, is_multi_byte=is_multi_byte) count = 0 if not headers: return False @@ -278,7 +280,7 @@ def is_column_based( fname, sep='\t', skip=0, is_multi_byte=False ): return True -def guess_ext( fname, sniff_order, is_multi_byte=False ): +def guess_ext(fname, sniff_order, is_multi_byte=False): """ Returns an extension that can be used in the datatype factory to generate a data for the 'fname' file @@ -381,6 +383,9 @@ def guess_ext( fname, sniff_order, is_multi_byte=False ): >>> fname = get_test_fname('diamond_db.dmnd') >>> guess_ext(fname, sniff_order) 'dmnd' + >>> fname = get_test_fname('1.xls') + >>> guess_ext(fname, sniff_order) + 'excel.xls' """ file_ext = None for datatype in sniff_order: @@ -393,7 +398,7 @@ def guess_ext( fname, sniff_order, is_multi_byte=False ): successfully discovered. """ try: - if datatype.sniff( fname ): + if datatype.sniff(fname): file_ext = datatype.file_ext break except: @@ -402,12 +407,12 @@ def guess_ext( fname, sniff_order, is_multi_byte=False ): # to tsv but it doesn't have a sniffer - is TSV was sniffed just check # if it is an okay tabular and use that instead. if file_ext == 'tsv': - if is_column_based( fname, '\t', 1, is_multi_byte=is_multi_byte ): + if is_column_based(fname, '\t', 1, is_multi_byte=is_multi_byte): file_ext = 'tabular' if file_ext is not None: return file_ext - headers = get_headers( fname, None ) + headers = get_headers(fname, None) is_binary = False if is_multi_byte: is_binary = False @@ -416,38 +421,38 @@ def guess_ext( fname, sniff_order, is_multi_byte=False ): for char in hdr: # old behavior had 'char' possibly having length > 1, # need to determine when/if this occurs - is_binary = util.is_binary( char ) + is_binary = util.is_binary(char) if is_binary: break if is_binary: break if is_binary: return 'data' # default binary data type file extension - if is_column_based( fname, '\t', 1, is_multi_byte=is_multi_byte ): + if is_column_based(fname, '\t', 1, is_multi_byte=is_multi_byte): return 'tabular' # default tabular data type file extension return 'txt' # default text data type file extension -def handle_compressed_file( filename, datatypes_registry, ext='auto' ): +def handle_compressed_file(filename, datatypes_registry, ext='auto'): CHUNK_SIZE = 2 ** 20 # 1Mb is_compressed = False compressed_type = None keep_compressed = False is_valid = False for compressed_type, check_compressed_function in COMPRESSION_CHECK_FUNCTIONS: - is_compressed = check_compressed_function( filename ) + is_compressed = check_compressed_function(filename) if is_compressed: break # found compression type if is_compressed: if ext in AUTO_DETECT_EXTENSIONS: - check_exts = COMPRESSION_DATATYPES[ compressed_type ] + check_exts = COMPRESSION_DATATYPES[compressed_type] elif ext in COMPRESSED_EXTENSIONS: - check_exts = [ ext ] + check_exts = [ext] else: check_exts = [] for compressed_ext in check_exts: - compressed_datatype = datatypes_registry.get_datatype_by_extension( compressed_ext ) - if compressed_datatype.sniff( filename ): + compressed_datatype = datatypes_registry.get_datatype_by_extension(compressed_ext) + if compressed_datatype.sniff(filename): ext = compressed_ext keep_compressed = True is_valid = True @@ -458,52 +463,52 @@ def handle_compressed_file( filename, datatypes_registry, ext='auto' ): elif not keep_compressed: is_valid = True fd, uncompressed = tempfile.mkstemp() - compressed_file = DECOMPRESSION_FUNCTIONS[ compressed_type ]( filename ) + compressed_file = DECOMPRESSION_FUNCTIONS[compressed_type](filename) while True: try: - chunk = compressed_file.read( CHUNK_SIZE ) + chunk = compressed_file.read(CHUNK_SIZE) except IOError as e: - os.close( fd ) - os.remove( uncompressed ) + os.close(fd) + os.remove(uncompressed) compressed_file.close() - raise IOError( 'Problem uncompressing %s data, please try retrieving the data uncompressed: %s' % ( compressed_type, e )) + raise IOError('Problem uncompressing %s data, please try retrieving the data uncompressed: %s' % (compressed_type, e)) if not chunk: break - os.write( fd, chunk ) - os.close( fd ) + os.write(fd, chunk) + os.close(fd) compressed_file.close() # Replace the compressed file with the uncompressed file - shutil.move( uncompressed, filename ) + shutil.move(uncompressed, filename) return is_valid, ext -def handle_uploaded_dataset_file( filename, datatypes_registry, ext='auto', is_multi_byte=False ): - is_valid, ext = handle_compressed_file( filename, datatypes_registry, ext=ext ) +def handle_uploaded_dataset_file(filename, datatypes_registry, ext='auto', is_multi_byte=False): + is_valid, ext = handle_compressed_file(filename, datatypes_registry, ext=ext) if not is_valid: - raise InappropriateDatasetContentError( 'The compressed uploaded file contains inappropriate content.' ) + raise InappropriateDatasetContentError('The compressed uploaded file contains inappropriate content.') if ext in AUTO_DETECT_EXTENSIONS: - ext = guess_ext( filename, sniff_order=datatypes_registry.sniff_order, is_multi_byte=is_multi_byte ) + ext = guess_ext(filename, sniff_order=datatypes_registry.sniff_order, is_multi_byte=is_multi_byte) - if check_binary( filename ): - if not Binary.is_ext_unsniffable(ext) and not datatypes_registry.get_datatype_by_extension( ext ).sniff( filename ): - raise InappropriateDatasetContentError( 'The binary uploaded file contains inappropriate content.' ) - elif check_html( filename ): - raise InappropriateDatasetContentError( 'The uploaded file contains inappropriate HTML content.' ) + if check_binary(filename): + if not Binary.is_ext_unsniffable(ext) and not datatypes_registry.get_datatype_by_extension(ext).sniff(filename): + raise InappropriateDatasetContentError('The binary uploaded file contains inappropriate content.') + elif check_html(filename): + raise InappropriateDatasetContentError('The uploaded file contains inappropriate HTML content.') return ext -AUTO_DETECT_EXTENSIONS = [ 'auto' ] # should 'data' also cause auto detect? -DECOMPRESSION_FUNCTIONS = dict( gzip=gzip.GzipFile, bz2=bz2.BZ2File ) -COMPRESSION_CHECK_FUNCTIONS = [ ( 'gzip', is_gzip ), ('bz2', is_bz2) ] -COMPRESSION_DATATYPES = dict( gzip=[ 'bam', 'fastq.gz', 'fastqsanger.gz', 'fastqillumina.gz', 'fastqsolexa.gz', 'fastqcssanger.gz'], bz2=['fastq.bz2', 'fastqsanger.bz2', 'fastqillumina.bz2', 'fastqsolexa.bz2', 'fastqcssanger.bz2' ] ) +AUTO_DETECT_EXTENSIONS = ['auto'] # should 'data' also cause auto detect? +DECOMPRESSION_FUNCTIONS = dict(gzip=gzip.GzipFile, bz2=bz2.BZ2File) +COMPRESSION_CHECK_FUNCTIONS = [('gzip', is_gzip), ('bz2', is_bz2)] +COMPRESSION_DATATYPES = dict(gzip=['bam', 'fastq.gz', 'fastqsanger.gz', 'fastqillumina.gz', 'fastqsolexa.gz', 'fastqcssanger.gz'], bz2=['fastq.bz2', 'fastqsanger.bz2', 'fastqillumina.bz2', 'fastqsolexa.bz2', 'fastqcssanger.bz2']) COMPRESSED_EXTENSIONS = [] for exts in COMPRESSION_DATATYPES.values(): - COMPRESSED_EXTENSIONS.extend( exts ) + COMPRESSED_EXTENSIONS.extend(exts) -class InappropriateDatasetContentError( Exception ): +class InappropriateDatasetContentError(Exception): pass diff --git a/lib/galaxy/datatypes/tabular.py b/lib/galaxy/datatypes/tabular.py index fbcba2b43a94..ca2547c7900f 100644 --- a/lib/galaxy/datatypes/tabular.py +++ b/lib/galaxy/datatypes/tabular.py @@ -8,6 +8,7 @@ import logging import os import re +import shutil import subprocess import sys import tempfile @@ -15,9 +16,12 @@ from json import dumps from galaxy import util -from galaxy.datatypes import data, metadata +from galaxy.datatypes import binary, data, metadata from galaxy.datatypes.metadata import MetadataElement -from galaxy.datatypes.sniff import get_headers +from galaxy.datatypes.sniff import ( + get_headers, + iter_headers +) from galaxy.util import compression_utils from . import dataproviders @@ -29,30 +33,30 @@ @dataproviders.decorators.has_dataproviders -class TabularData( data.Text ): +class TabularData(data.Text): """Generic tabular data""" edam_format = "format_3475" # All tabular data is chunkable. CHUNKABLE = True """Add metadata elements""" - MetadataElement( name="comment_lines", default=0, desc="Number of comment lines", readonly=False, optional=True, no_value=0 ) - MetadataElement( name="data_lines", default=0, desc="Number of data lines", readonly=True, visible=False, optional=True, no_value=0 ) - MetadataElement( name="columns", default=0, desc="Number of columns", readonly=True, visible=False, no_value=0 ) - MetadataElement( name="column_types", default=[], desc="Column types", param=metadata.ColumnTypesParameter, readonly=True, visible=False, no_value=[] ) - MetadataElement( name="column_names", default=[], desc="Column names", readonly=True, visible=False, optional=True, no_value=[] ) - MetadataElement( name="delimiter", default='\t', desc="Data delimiter", readonly=True, visible=False, optional=True, no_value=[] ) + MetadataElement(name="comment_lines", default=0, desc="Number of comment lines", readonly=False, optional=True, no_value=0) + MetadataElement(name="data_lines", default=0, desc="Number of data lines", readonly=True, visible=False, optional=True, no_value=0) + MetadataElement(name="columns", default=0, desc="Number of columns", readonly=True, visible=False, no_value=0) + MetadataElement(name="column_types", default=[], desc="Column types", param=metadata.ColumnTypesParameter, readonly=True, visible=False, no_value=[]) + MetadataElement(name="column_names", default=[], desc="Column names", readonly=True, visible=False, optional=True, no_value=[]) + MetadataElement(name="delimiter", default='\t', desc="Data delimiter", readonly=True, visible=False, optional=True, no_value=[]) @abc.abstractmethod - def set_meta( self, dataset, **kwd ): + def set_meta(self, dataset, **kwd): raise NotImplementedError - def set_peek( self, dataset, line_count=None, is_multi_byte=False, WIDTH=256, skipchars=None ): - super(TabularData, self).set_peek( dataset, line_count=line_count, is_multi_byte=is_multi_byte, WIDTH=WIDTH, skipchars=skipchars, line_wrap=False ) + def set_peek(self, dataset, line_count=None, is_multi_byte=False, WIDTH=256, skipchars=None): + super(TabularData, self).set_peek(dataset, line_count=line_count, is_multi_byte=is_multi_byte, WIDTH=WIDTH, skipchars=skipchars, line_wrap=False) if dataset.metadata.comment_lines: - dataset.blurb = "%s, %s comments" % ( dataset.blurb, util.commaify( str( dataset.metadata.comment_lines ) ) ) + dataset.blurb = "%s, %s comments" % (dataset.blurb, util.commaify(str(dataset.metadata.comment_lines))) - def displayable( self, dataset ): + def displayable(self, dataset): try: return dataset.has_data() \ and dataset.state == dataset.states.OK \ @@ -62,7 +66,7 @@ def displayable( self, dataset ): return False def get_chunk(self, trans, dataset, offset=0, ck_size=None): - with open(dataset.file_name) as f: + with compression_utils.get_fileobj(dataset.file_name) as f: f.seek(offset) ck_data = f.read(ck_size or trans.app.config.display_chunk_size) if ck_data and ck_data[-1] != '\n': @@ -71,11 +75,11 @@ def get_chunk(self, trans, dataset, offset=0, ck_size=None): ck_data += cursor cursor = f.read(1) last_read = f.tell() - return dumps( { 'ck_data': util.unicodify( ck_data ), - 'offset': last_read } ) + return dumps({'ck_data': util.unicodify(ck_data), + 'offset': last_read}) def display_data(self, trans, dataset, preview=False, filename=None, to_ext=None, offset=None, ck_size=None, **kwd): - preview = util.string_as_bool( preview ) + preview = util.string_as_bool(preview) if offset is not None: return self.get_chunk(trans, dataset, offset, ck_size) elif to_ext or not preview: @@ -86,14 +90,14 @@ def display_data(self, trans, dataset, preview=False, filename=None, to_ext=None # We should add a new datatype 'matrix', with its own draw method, suitable for this kind of data. # For now, default to the old behavior, ugly as it is. Remove this after adding 'matrix'. max_peek_size = 1000000 # 1 MB - if os.stat( dataset.file_name ).st_size < max_peek_size: - self._clean_and_set_mime_type( trans, dataset.get_mime() ) - return open( dataset.file_name ) + if os.stat(dataset.file_name).st_size < max_peek_size: + self._clean_and_set_mime_type(trans, dataset.get_mime()) + return open(dataset.file_name) else: - trans.response.set_content_type( "text/html" ) - return trans.stream_template_mako( "/dataset/large_file.mako", - truncated_data=open( dataset.file_name ).read(max_peek_size), - data=dataset) + trans.response.set_content_type("text/html") + return trans.stream_template_mako("/dataset/large_file.mako", + truncated_data=open(dataset.file_name).read(max_peek_size), + data=dataset) else: column_names = 'null' if dataset.metadata.column_names: @@ -106,26 +110,26 @@ def display_data(self, trans, dataset, preview=False, filename=None, to_ext=None column_number = dataset.metadata.columns if column_number is None: column_number = 'null' - return trans.fill_template( "/dataset/tabular_chunked.mako", - dataset=dataset, - chunk=self.get_chunk(trans, dataset, 0), - column_number=column_number, - column_names=column_names, - column_types=column_types ) - - def make_html_table( self, dataset, **kwargs ): + return trans.fill_template("/dataset/tabular_chunked.mako", + dataset=dataset, + chunk=self.get_chunk(trans, dataset, 0), + column_number=column_number, + column_names=column_names, + column_types=column_types) + + def make_html_table(self, dataset, **kwargs): """Create HTML table, used for displaying peek""" out = [''] try: - out.append( self.make_html_peek_header( dataset, **kwargs ) ) - out.append( self.make_html_peek_rows( dataset, **kwargs ) ) - out.append( '
    ' ) - out = "".join( out ) + out.append(self.make_html_peek_header(dataset, **kwargs)) + out.append(self.make_html_peek_rows(dataset, **kwargs)) + out.append('') + out = "".join(out) except Exception as exc: - out = "Can't create peek %s" % str( exc ) + out = "Can't create peek %s" % str(exc) return out - def make_html_peek_header( self, dataset, skipchars=None, column_names=None, column_number_format='%s', column_parameter_alias=None, **kwargs ): + def make_html_peek_header(self, dataset, skipchars=None, column_names=None, column_number_format='%s', column_parameter_alias=None, **kwargs): if skipchars is None: skipchars = [] if column_names is None: @@ -143,35 +147,35 @@ def make_html_peek_header( self, dataset, skipchars=None, column_names=None, col column_headers = [None] * columns # fill in empty headers with data from column_names - for i in range( min( columns, len( column_names ) ) ): + for i in range(min(columns, len(column_names))): if column_headers[i] is None and column_names[i] is not None: column_headers[i] = column_names[i] # fill in empty headers from ColumnParameters set in the metadata for name, spec in dataset.metadata.spec.items(): - if isinstance( spec.param, metadata.ColumnParameter ): + if isinstance(spec.param, metadata.ColumnParameter): try: - i = int( getattr( dataset.metadata, name ) ) - 1 + i = int(getattr(dataset.metadata, name)) - 1 except: i = -1 if 0 <= i < columns and column_headers[i] is None: column_headers[i] = column_parameter_alias.get(name, name) - out.append( '' ) - for i, header in enumerate( column_headers ): - out.append( '' ) + out.append('') + for i, header in enumerate(column_headers): + out.append('') if header is None: - out.append( column_number_format % str( i + 1 ) ) + out.append(column_number_format % str(i + 1)) else: - out.append( '%s.%s' % ( str( i + 1 ), escape( header ) ) ) - out.append( '' ) - out.append( '' ) + out.append('%s.%s' % (str(i + 1), escape(header))) + out.append('') + out.append('') except Exception as exc: - log.exception( 'make_html_peek_header failed on HDA %s', dataset.id ) - raise Exception( "Can't create peek header %s" % str( exc ) ) - return "".join( out ) + log.exception('make_html_peek_header failed on HDA %s', dataset.id) + raise Exception("Can't create peek header %s" % str(exc)) + return "".join(out) - def make_html_peek_rows( self, dataset, skipchars=None, **kwargs ): + def make_html_peek_rows(self, dataset, skipchars=None, **kwargs): if skipchars is None: skipchars = [] out = [] @@ -182,64 +186,64 @@ def make_html_peek_rows( self, dataset, skipchars=None, **kwargs ): if columns is None: columns = dataset.metadata.spec.columns.no_value for line in dataset.peek.splitlines(): - if line.startswith( tuple( skipchars ) ): - out.append( '%s' % escape( line ) ) + if line.startswith(tuple(skipchars)): + out.append('%s' % escape(line)) elif line: - elems = line.split( dataset.metadata.delimiter ) + elems = line.split(dataset.metadata.delimiter) # pad shortened elems, since lines could have been truncated by width - if len( elems ) < columns: - elems.extend( [''] * ( columns - len( elems ) ) ) + if len(elems) < columns: + elems.extend([''] * (columns - len(elems))) # we may have an invalid comment line or invalid data - if len( elems ) != columns: - out.append( '%s' % escape( line ) ) + if len(elems) != columns: + out.append('%s' % escape(line)) else: - out.append( '' ) + out.append('') for elem in elems: - out.append( '%s' % escape( elem ) ) - out.append( '' ) + out.append('%s' % escape(elem)) + out.append('') except Exception as exc: - log.exception( 'make_html_peek_rows failed on HDA %s', dataset.id ) - raise Exception( "Can't create peek rows %s" % str( exc ) ) - return "".join( out ) + log.exception('make_html_peek_rows failed on HDA %s', dataset.id) + raise Exception("Can't create peek rows %s" % str(exc)) + return "".join(out) - def display_peek( self, dataset ): + def display_peek(self, dataset): """Returns formatted html of peek""" - return self.make_html_table( dataset ) + return self.make_html_table(dataset) # ------------- Dataproviders - @dataproviders.decorators.dataprovider_factory( 'column', dataproviders.column.ColumnarDataProvider.settings ) - def column_dataprovider( self, dataset, **settings ): + @dataproviders.decorators.dataprovider_factory('column', dataproviders.column.ColumnarDataProvider.settings) + def column_dataprovider(self, dataset, **settings): """Uses column settings that are passed in""" - dataset_source = dataproviders.dataset.DatasetDataProvider( dataset ) + dataset_source = dataproviders.dataset.DatasetDataProvider(dataset) delimiter = dataset.metadata.delimiter - return dataproviders.column.ColumnarDataProvider( dataset_source, deliminator=delimiter, **settings ) + return dataproviders.column.ColumnarDataProvider(dataset_source, deliminator=delimiter, **settings) - @dataproviders.decorators.dataprovider_factory( 'dataset-column', - dataproviders.column.ColumnarDataProvider.settings ) - def dataset_column_dataprovider( self, dataset, **settings ): + @dataproviders.decorators.dataprovider_factory('dataset-column', + dataproviders.column.ColumnarDataProvider.settings) + def dataset_column_dataprovider(self, dataset, **settings): """Attempts to get column settings from dataset.metadata""" delimiter = dataset.metadata.delimiter - return dataproviders.dataset.DatasetColumnarDataProvider( dataset, deliminator=delimiter, **settings ) + return dataproviders.dataset.DatasetColumnarDataProvider(dataset, deliminator=delimiter, **settings) - @dataproviders.decorators.dataprovider_factory( 'dict', dataproviders.column.DictDataProvider.settings ) - def dict_dataprovider( self, dataset, **settings ): + @dataproviders.decorators.dataprovider_factory('dict', dataproviders.column.DictDataProvider.settings) + def dict_dataprovider(self, dataset, **settings): """Uses column settings that are passed in""" - dataset_source = dataproviders.dataset.DatasetDataProvider( dataset ) + dataset_source = dataproviders.dataset.DatasetDataProvider(dataset) delimiter = dataset.metadata.delimiter - return dataproviders.column.DictDataProvider( dataset_source, deliminator=delimiter, **settings ) + return dataproviders.column.DictDataProvider(dataset_source, deliminator=delimiter, **settings) - @dataproviders.decorators.dataprovider_factory( 'dataset-dict', dataproviders.column.DictDataProvider.settings ) - def dataset_dict_dataprovider( self, dataset, **settings ): + @dataproviders.decorators.dataprovider_factory('dataset-dict', dataproviders.column.DictDataProvider.settings) + def dataset_dict_dataprovider(self, dataset, **settings): """Attempts to get column settings from dataset.metadata""" delimiter = dataset.metadata.delimiter - return dataproviders.dataset.DatasetDictDataProvider( dataset, deliminator=delimiter, **settings ) + return dataproviders.dataset.DatasetDictDataProvider(dataset, deliminator=delimiter, **settings) @dataproviders.decorators.has_dataproviders -class Tabular( TabularData ): +class Tabular(TabularData): """Tab delimited data""" - def set_meta( self, dataset, overwrite=True, skip=None, max_data_lines=100000, max_guess_type_data_lines=None, **kwd ): + def set_meta(self, dataset, overwrite=True, skip=None, max_data_lines=100000, max_guess_type_data_lines=None, **kwd): """ Tries to determine the number of columns as well as those columns that contain numerical values in the dataset. A skip parameter is used @@ -268,12 +272,12 @@ def set_meta( self, dataset, overwrite=True, skip=None, max_data_lines=100000, m requested_skip = skip if skip is None: skip = 0 - column_type_set_order = [ 'int', 'float', 'list', 'str' ] # Order to set column types in + column_type_set_order = ['int', 'float', 'list', 'str'] # Order to set column types in default_column_type = column_type_set_order[-1] # Default column type is lowest in list - column_type_compare_order = list( column_type_set_order ) # Order to compare column types + column_type_compare_order = list(column_type_set_order) # Order to compare column types column_type_compare_order.reverse() - def type_overrules_type( column_type1, column_type2 ): + def type_overrules_type(column_type1, column_type2): if column_type1 is None or column_type1 == column_type2: return False if column_type2 is None: @@ -284,39 +288,39 @@ def type_overrules_type( column_type1, column_type2 ): if column_type2 == column_type: return False # neither column type was found in our ordered list, this cannot happen - raise ValueError( "Tried to compare unknown column types: %s and %s" % ( column_type1, column_type2 ) ) + raise ValueError("Tried to compare unknown column types: %s and %s" % (column_type1, column_type2)) - def is_int( column_text ): + def is_int(column_text): try: - int( column_text ) + int(column_text) return True except: return False - def is_float( column_text ): + def is_float(column_text): try: - float( column_text ) + float(column_text) return True except: if column_text.strip().lower() == 'na': return True # na is special cased to be a float return False - def is_list( column_text ): + def is_list(column_text): return "," in column_text - def is_str( column_text ): + def is_str(column_text): # anything, except an empty string, is True if column_text == "": return False return True is_column_type = {} # Dict to store column type string to checking function for column_type in column_type_set_order: - is_column_type[column_type] = locals()[ "is_%s" % ( column_type ) ] + is_column_type[column_type] = locals()["is_%s" % (column_type)] - def guess_column_type( column_text ): + def guess_column_type(column_text): for column_type in column_type_set_order: - if is_column_type[column_type]( column_text ): + if is_column_type[column_type](column_text): return column_type return None data_lines = 0 @@ -325,59 +329,58 @@ def guess_column_type( column_text ): first_line_column_types = [default_column_type] # default value is one column of type str if dataset.has_data(): # NOTE: if skip > num_check_lines, we won't detect any metadata, and will use default - dataset_fh = open( dataset.file_name ) - i = 0 - while True: - line = dataset_fh.readline() - if not line: - break - line = line.rstrip( '\r\n' ) - if i < skip or not line or line.startswith( '#' ): - # We'll call blank lines comments - comment_lines += 1 - else: - data_lines += 1 - if max_guess_type_data_lines is None or data_lines <= max_guess_type_data_lines: - fields = line.split( '\t' ) - for field_count, field in enumerate( fields ): - if field_count >= len( column_types ): # found a previously unknown column, we append None - column_types.append( None ) - column_type = guess_column_type( field ) - if type_overrules_type( column_type, column_types[field_count] ): - column_types[field_count] = column_type - if i == 0 and requested_skip is None: - # This is our first line, people seem to like to upload files that have a header line, but do not - # start with '#' (i.e. all column types would then most likely be detected as str). We will assume - # that the first line is always a header (this was previous behavior - it was always skipped). When - # the requested skip is None, we only use the data from the first line if we have no other data for - # a column. This is far from perfect, as - # 1,2,3 1.1 2.2 qwerty - # 0 0 1,2,3 - # will be detected as - # "column_types": ["int", "int", "float", "list"] - # instead of - # "column_types": ["list", "float", "float", "str"] *** would seem to be the 'Truth' by manual - # observation that the first line should be included as data. The old method would have detected as - # "column_types": ["int", "int", "str", "list"] - first_line_column_types = column_types - column_types = [ None for col in first_line_column_types ] - if max_data_lines is not None and data_lines >= max_data_lines: - if dataset_fh.tell() != dataset.get_size(): - data_lines = None # Clear optional data_lines metadata value - comment_lines = None # Clear optional comment_lines metadata value; additional comment lines could appear below this point - break - i += 1 - dataset_fh.close() + with compression_utils.get_fileobj(dataset.file_name) as dataset_fh: + i = 0 + while True: + line = dataset_fh.readline() + if not line: + break + line = line.rstrip('\r\n') + if i < skip or not line or line.startswith('#'): + # We'll call blank lines comments + comment_lines += 1 + else: + data_lines += 1 + if max_guess_type_data_lines is None or data_lines <= max_guess_type_data_lines: + fields = line.split('\t') + for field_count, field in enumerate(fields): + if field_count >= len(column_types): # found a previously unknown column, we append None + column_types.append(None) + column_type = guess_column_type(field) + if type_overrules_type(column_type, column_types[field_count]): + column_types[field_count] = column_type + if i == 0 and requested_skip is None: + # This is our first line, people seem to like to upload files that have a header line, but do not + # start with '#' (i.e. all column types would then most likely be detected as str). We will assume + # that the first line is always a header (this was previous behavior - it was always skipped). When + # the requested skip is None, we only use the data from the first line if we have no other data for + # a column. This is far from perfect, as + # 1,2,3 1.1 2.2 qwerty + # 0 0 1,2,3 + # will be detected as + # "column_types": ["int", "int", "float", "list"] + # instead of + # "column_types": ["list", "float", "float", "str"] *** would seem to be the 'Truth' by manual + # observation that the first line should be included as data. The old method would have detected as + # "column_types": ["int", "int", "str", "list"] + first_line_column_types = column_types + column_types = [None for col in first_line_column_types] + if max_data_lines is not None and data_lines >= max_data_lines: + if dataset_fh.tell() != dataset.get_size(): + data_lines = None # Clear optional data_lines metadata value + comment_lines = None # Clear optional comment_lines metadata value; additional comment lines could appear below this point + break + i += 1 # we error on the larger number of columns # first we pad our column_types by using data from first line - if len( first_line_column_types ) > len( column_types ): - for column_type in first_line_column_types[len( column_types ):]: - column_types.append( column_type ) + if len(first_line_column_types) > len(column_types): + for column_type in first_line_column_types[len(column_types):]: + column_types.append(column_type) # Now we fill any unknown (None) column_types with data from first line - for i in range( len( column_types ) ): + for i in range(len(column_types)): if column_types[i] is None: - if len( first_line_column_types ) <= i or first_line_column_types[i] is None: + if len(first_line_column_types) <= i or first_line_column_types[i] is None: column_types[i] = default_column_type else: column_types[i] = first_line_column_types[i] @@ -385,51 +388,51 @@ def guess_column_type( column_text ): dataset.metadata.data_lines = data_lines dataset.metadata.comment_lines = comment_lines dataset.metadata.column_types = column_types - dataset.metadata.columns = len( column_types ) + dataset.metadata.columns = len(column_types) dataset.metadata.delimiter = '\t' - def as_gbrowse_display_file( self, dataset, **kwd ): - return open( dataset.file_name ) + def as_gbrowse_display_file(self, dataset, **kwd): + return open(dataset.file_name) - def as_ucsc_display_file( self, dataset, **kwd ): - return open( dataset.file_name ) + def as_ucsc_display_file(self, dataset, **kwd): + return open(dataset.file_name) -class Taxonomy( Tabular ): +class Taxonomy(Tabular): def __init__(self, **kwd): """Initialize taxonomy datatype""" - super(Taxonomy, self).__init__( **kwd ) + super(Taxonomy, self).__init__(**kwd) self.column_names = ['Name', 'TaxId', 'Root', 'Superkingdom', 'Kingdom', 'Subkingdom', 'Superphylum', 'Phylum', 'Subphylum', 'Superclass', 'Class', 'Subclass', 'Superorder', 'Order', 'Suborder', 'Superfamily', 'Family', 'Subfamily', 'Tribe', 'Subtribe', 'Genus', 'Subgenus', 'Species', 'Subspecies' ] - def display_peek( self, dataset ): + def display_peek(self, dataset): """Returns formated html of peek""" - return self.make_html_table( dataset, column_names=self.column_names ) + return self.make_html_table(dataset, column_names=self.column_names) @dataproviders.decorators.has_dataproviders -class Sam( Tabular ): +class Sam(Tabular): edam_format = "format_2573" edam_data = "data_0863" file_ext = 'sam' track_type = "ReadTrack" - data_sources = { "data": "bam", "index": "bigwig" } + data_sources = {"data": "bam", "index": "bigwig"} def __init__(self, **kwd): """Initialize taxonomy datatype""" - super( Sam, self ).__init__( **kwd ) + super(Sam, self).__init__(**kwd) self.column_names = ['QNAME', 'FLAG', 'RNAME', 'POS', 'MAPQ', 'CIGAR', 'MRNM', 'MPOS', 'ISIZE', 'SEQ', 'QUAL', 'OPT' ] - def display_peek( self, dataset ): + def display_peek(self, dataset): """Returns formated html of peek""" - return self.make_html_table( dataset, column_names=self.column_names ) + return self.make_html_table(dataset, column_names=self.column_names) - def sniff( self, filename ): + def sniff(self, filename): """ Determines whether the file is in SAM format @@ -459,7 +462,7 @@ def sniff( self, filename ): True """ try: - fh = open( filename ) + fh = open(filename) count = 0 while True: line = fh.readline() @@ -489,9 +492,9 @@ def sniff( self, filename ): pass return False - def set_meta( self, dataset, overwrite=True, skip=None, max_data_lines=5, **kwd ): + def set_meta(self, dataset, overwrite=True, skip=None, max_data_lines=5, **kwd): if dataset.has_data(): - dataset_fh = open( dataset.file_name ) + dataset_fh = open(dataset.file_name) comment_lines = 0 if self.max_optional_metadata_filesize >= 0 and dataset.get_size() > self.max_optional_metadata_filesize: # If the dataset is larger than optional_metadata, just count comment lines. @@ -513,17 +516,17 @@ def set_meta( self, dataset, overwrite=True, skip=None, max_data_lines=5, **kwd dataset.metadata.columns = 12 dataset.metadata.column_types = ['str', 'int', 'str', 'int', 'int', 'str', 'str', 'int', 'int', 'str', 'str', 'str'] - def merge( split_files, output_file): + def merge(split_files, output_file): """ Multiple SAM files may each have headers. Since the headers should all be the same, remove the headers from files 1-n, keeping them in the first file only """ - cmd = 'mv %s %s' % ( split_files[0], output_file ) + cmd = 'mv %s %s' % (split_files[0], output_file) result = os.system(cmd) if result != 0: raise Exception('Result %s from %s' % (result, cmd)) if len(split_files) > 1: - cmd = 'egrep -v -h "^@" %s >> %s' % ( ' '.join(split_files[1:]), output_file ) + cmd = 'egrep -v -h "^@" %s >> %s' % (' '.join(split_files[1:]), output_file) result = os.system(cmd) if result != 0: raise Exception('Result %s from %s' % (result, cmd)) @@ -532,61 +535,61 @@ def merge( split_files, output_file): # Dataproviders # sam does not use '#' to indicate comments/headers - we need to strip out those headers from the std. providers # TODO:?? seems like there should be an easier way to do this - metadata.comment_char? - @dataproviders.decorators.dataprovider_factory( 'line', dataproviders.line.FilteredLineDataProvider.settings ) - def line_dataprovider( self, dataset, **settings ): - settings[ 'comment_char' ] = '@' - return super( Sam, self ).line_dataprovider( dataset, **settings ) - - @dataproviders.decorators.dataprovider_factory( 'regex-line', dataproviders.line.RegexLineDataProvider.settings ) - def regex_line_dataprovider( self, dataset, **settings ): - settings[ 'comment_char' ] = '@' - return super( Sam, self ).regex_line_dataprovider( dataset, **settings ) - - @dataproviders.decorators.dataprovider_factory( 'column', dataproviders.column.ColumnarDataProvider.settings ) - def column_dataprovider( self, dataset, **settings ): - settings[ 'comment_char' ] = '@' - return super( Sam, self ).column_dataprovider( dataset, **settings ) - - @dataproviders.decorators.dataprovider_factory( 'dataset-column', - dataproviders.column.ColumnarDataProvider.settings ) - def dataset_column_dataprovider( self, dataset, **settings ): - settings[ 'comment_char' ] = '@' - return super( Sam, self ).dataset_column_dataprovider( dataset, **settings ) - - @dataproviders.decorators.dataprovider_factory( 'dict', dataproviders.column.DictDataProvider.settings ) - def dict_dataprovider( self, dataset, **settings ): - settings[ 'comment_char' ] = '@' - return super( Sam, self ).dict_dataprovider( dataset, **settings ) - - @dataproviders.decorators.dataprovider_factory( 'dataset-dict', dataproviders.column.DictDataProvider.settings ) - def dataset_dict_dataprovider( self, dataset, **settings ): - settings[ 'comment_char' ] = '@' - return super( Sam, self ).dataset_dict_dataprovider( dataset, **settings ) - - @dataproviders.decorators.dataprovider_factory( 'header', dataproviders.line.RegexLineDataProvider.settings ) - def header_dataprovider( self, dataset, **settings ): - dataset_source = dataproviders.dataset.DatasetDataProvider( dataset ) - headers_source = dataproviders.line.RegexLineDataProvider( dataset_source, regex_list=[ '^@' ] ) - return dataproviders.line.RegexLineDataProvider( headers_source, **settings ) - - @dataproviders.decorators.dataprovider_factory( 'id-seq-qual', dict_dataprovider.settings ) - def id_seq_qual_dataprovider( self, dataset, **settings ): + @dataproviders.decorators.dataprovider_factory('line', dataproviders.line.FilteredLineDataProvider.settings) + def line_dataprovider(self, dataset, **settings): + settings['comment_char'] = '@' + return super(Sam, self).line_dataprovider(dataset, **settings) + + @dataproviders.decorators.dataprovider_factory('regex-line', dataproviders.line.RegexLineDataProvider.settings) + def regex_line_dataprovider(self, dataset, **settings): + settings['comment_char'] = '@' + return super(Sam, self).regex_line_dataprovider(dataset, **settings) + + @dataproviders.decorators.dataprovider_factory('column', dataproviders.column.ColumnarDataProvider.settings) + def column_dataprovider(self, dataset, **settings): + settings['comment_char'] = '@' + return super(Sam, self).column_dataprovider(dataset, **settings) + + @dataproviders.decorators.dataprovider_factory('dataset-column', + dataproviders.column.ColumnarDataProvider.settings) + def dataset_column_dataprovider(self, dataset, **settings): + settings['comment_char'] = '@' + return super(Sam, self).dataset_column_dataprovider(dataset, **settings) + + @dataproviders.decorators.dataprovider_factory('dict', dataproviders.column.DictDataProvider.settings) + def dict_dataprovider(self, dataset, **settings): + settings['comment_char'] = '@' + return super(Sam, self).dict_dataprovider(dataset, **settings) + + @dataproviders.decorators.dataprovider_factory('dataset-dict', dataproviders.column.DictDataProvider.settings) + def dataset_dict_dataprovider(self, dataset, **settings): + settings['comment_char'] = '@' + return super(Sam, self).dataset_dict_dataprovider(dataset, **settings) + + @dataproviders.decorators.dataprovider_factory('header', dataproviders.line.RegexLineDataProvider.settings) + def header_dataprovider(self, dataset, **settings): + dataset_source = dataproviders.dataset.DatasetDataProvider(dataset) + headers_source = dataproviders.line.RegexLineDataProvider(dataset_source, regex_list=['^@']) + return dataproviders.line.RegexLineDataProvider(headers_source, **settings) + + @dataproviders.decorators.dataprovider_factory('id-seq-qual', dict_dataprovider.settings) + def id_seq_qual_dataprovider(self, dataset, **settings): # provided as an example of a specified column dict (w/o metadata) - settings[ 'indeces' ] = [ 0, 9, 10 ] - settings[ 'column_names' ] = [ 'id', 'seq', 'qual' ] - return self.dict_dataprovider( dataset, **settings ) - - @dataproviders.decorators.dataprovider_factory( 'genomic-region', - dataproviders.dataset.GenomicRegionDataProvider.settings ) - def genomic_region_dataprovider( self, dataset, **settings ): - settings[ 'comment_char' ] = '@' - return dataproviders.dataset.GenomicRegionDataProvider( dataset, 2, 3, 3, **settings ) - - @dataproviders.decorators.dataprovider_factory( 'genomic-region-dict', - dataproviders.dataset.GenomicRegionDataProvider.settings ) - def genomic_region_dict_dataprovider( self, dataset, **settings ): - settings[ 'comment_char' ] = '@' - return dataproviders.dataset.GenomicRegionDataProvider( dataset, 2, 3, 3, True, **settings ) + settings['indeces'] = [0, 9, 10] + settings['column_names'] = ['id', 'seq', 'qual'] + return self.dict_dataprovider(dataset, **settings) + + @dataproviders.decorators.dataprovider_factory('genomic-region', + dataproviders.dataset.GenomicRegionDataProvider.settings) + def genomic_region_dataprovider(self, dataset, **settings): + settings['comment_char'] = '@' + return dataproviders.dataset.GenomicRegionDataProvider(dataset, 2, 3, 3, **settings) + + @dataproviders.decorators.dataprovider_factory('genomic-region-dict', + dataproviders.dataset.GenomicRegionDataProvider.settings) + def genomic_region_dict_dataprovider(self, dataset, **settings): + settings['comment_char'] = '@' + return dataproviders.dataset.GenomicRegionDataProvider(dataset, 2, 3, 3, True, **settings) # @dataproviders.decorators.dataprovider_factory( 'samtools' ) # def samtools_dataprovider( self, dataset, **settings ): @@ -595,31 +598,31 @@ def genomic_region_dict_dataprovider( self, dataset, **settings ): @dataproviders.decorators.has_dataproviders -class Pileup( Tabular ): +class Pileup(Tabular): """Tab delimited data in pileup (6- or 10-column) format""" edam_format = "format_3015" file_ext = "pileup" line_class = "genomic coordinate" - data_sources = { "data": "tabix" } + data_sources = {"data": "tabix"} """Add metadata elements""" - MetadataElement( name="chromCol", default=1, desc="Chrom column", param=metadata.ColumnParameter ) - MetadataElement( name="startCol", default=2, desc="Start column", param=metadata.ColumnParameter ) - MetadataElement( name="endCol", default=2, desc="End column", param=metadata.ColumnParameter ) - MetadataElement( name="baseCol", default=3, desc="Reference base column", param=metadata.ColumnParameter ) + MetadataElement(name="chromCol", default=1, desc="Chrom column", param=metadata.ColumnParameter) + MetadataElement(name="startCol", default=2, desc="Start column", param=metadata.ColumnParameter) + MetadataElement(name="endCol", default=2, desc="End column", param=metadata.ColumnParameter) + MetadataElement(name="baseCol", default=3, desc="Reference base column", param=metadata.ColumnParameter) - def init_meta( self, dataset, copy_from=None ): - super( Pileup, self ).init_meta( dataset, copy_from=copy_from ) + def init_meta(self, dataset, copy_from=None): + super(Pileup, self).init_meta(dataset, copy_from=copy_from) - def display_peek( self, dataset ): + def display_peek(self, dataset): """Returns formated html of peek""" - return self.make_html_table( dataset, column_parameter_alias={'chromCol': 'Chrom', 'startCol': 'Start', 'baseCol': 'Base'} ) + return self.make_html_table(dataset, column_parameter_alias={'chromCol': 'Chrom', 'startCol': 'Start', 'baseCol': 'Base'}) - def repair_methods( self, dataset ): + def repair_methods(self, dataset): """Return options for removing errors along with a description""" - return [ ("lines", "Remove erroneous lines") ] + return [("lines", "Remove erroneous lines")] - def sniff( self, filename ): + def sniff(self, filename): """ Checks for 'pileup-ness' @@ -638,18 +641,18 @@ def sniff( self, filename ): >>> Pileup().sniff( fname ) True """ - headers = get_headers( filename, '\t' ) + headers = iter_headers(filename, '\t') try: for hdr in headers: - if hdr and not hdr[0].startswith( '#' ): - if len( hdr ) < 5: + if hdr and not hdr[0].startswith('#'): + if len(hdr) < 5: return False try: # chrom start in column 1 (with 0-based columns) # and reference base is in column 2 - chrom = int( hdr[1] ) + chrom = int(hdr[1]) assert chrom >= 0 - assert hdr[2] in [ 'A', 'C', 'G', 'T', 'N', 'a', 'c', 'g', 't', 'n' ] + assert hdr[2] in ['A', 'C', 'G', 'T', 'N', 'a', 'c', 'g', 't', 'n'] except: return False return True @@ -657,54 +660,54 @@ def sniff( self, filename ): return False # Dataproviders - @dataproviders.decorators.dataprovider_factory( 'genomic-region', - dataproviders.dataset.GenomicRegionDataProvider.settings ) - def genomic_region_dataprovider( self, dataset, **settings ): - return dataproviders.dataset.GenomicRegionDataProvider( dataset, **settings ) + @dataproviders.decorators.dataprovider_factory('genomic-region', + dataproviders.dataset.GenomicRegionDataProvider.settings) + def genomic_region_dataprovider(self, dataset, **settings): + return dataproviders.dataset.GenomicRegionDataProvider(dataset, **settings) - @dataproviders.decorators.dataprovider_factory( 'genomic-region-dict', - dataproviders.dataset.GenomicRegionDataProvider.settings ) - def genomic_region_dict_dataprovider( self, dataset, **settings ): - settings[ 'named_columns' ] = True - return self.genomic_region_dataprovider( dataset, **settings ) + @dataproviders.decorators.dataprovider_factory('genomic-region-dict', + dataproviders.dataset.GenomicRegionDataProvider.settings) + def genomic_region_dict_dataprovider(self, dataset, **settings): + settings['named_columns'] = True + return self.genomic_region_dataprovider(dataset, **settings) @dataproviders.decorators.has_dataproviders -class Vcf( Tabular ): +class BaseVcf(Tabular): """ Variant Call Format for describing SNPs and other simple genome variations. """ edam_format = "format_3016" track_type = "VariantTrack" - data_sources = { "data": "tabix", "index": "bigwig" } + data_sources = {"data": "tabix", "index": "bigwig"} file_ext = 'vcf' - column_names = [ 'Chrom', 'Pos', 'ID', 'Ref', 'Alt', 'Qual', 'Filter', 'Info', 'Format', 'data' ] + column_names = ['Chrom', 'Pos', 'ID', 'Ref', 'Alt', 'Qual', 'Filter', 'Info', 'Format', 'data'] - MetadataElement( name="columns", default=10, desc="Number of columns", readonly=True, visible=False ) - MetadataElement( name="column_types", default=['str', 'int', 'str', 'str', 'str', 'int', 'str', 'list', 'str', 'str'], param=metadata.ColumnTypesParameter, desc="Column types", readonly=True, visible=False ) - MetadataElement( name="viz_filter_cols", desc="Score column for visualization", default=[5], param=metadata.ColumnParameter, optional=True, multiple=True, visible=False ) - MetadataElement( name="sample_names", default=[], desc="Sample names", readonly=True, visible=False, optional=True, no_value=[] ) + MetadataElement(name="columns", default=10, desc="Number of columns", readonly=True, visible=False) + MetadataElement(name="column_types", default=['str', 'int', 'str', 'str', 'str', 'int', 'str', 'list', 'str', 'str'], param=metadata.ColumnTypesParameter, desc="Column types", readonly=True, visible=False) + MetadataElement(name="viz_filter_cols", desc="Score column for visualization", default=[5], param=metadata.ColumnParameter, optional=True, multiple=True, visible=False) + MetadataElement(name="sample_names", default=[], desc="Sample names", readonly=True, visible=False, optional=True, no_value=[]) - def sniff( self, filename ): - headers = get_headers( filename, '\n', count=1 ) + def sniff(self, filename): + headers = get_headers(filename, '\n', count=1) return headers[0][0].startswith("##fileformat=VCF") - def display_peek( self, dataset ): + def display_peek(self, dataset): """Returns formated html of peek""" - return self.make_html_table( dataset, column_names=self.column_names ) + return self.make_html_table(dataset, column_names=self.column_names) - def set_meta( self, dataset, **kwd ): - super( Vcf, self ).set_meta( dataset, **kwd ) - source = open( dataset.file_name ) + def set_meta(self, dataset, **kwd): + super(BaseVcf, self).set_meta(dataset, **kwd) + source = open(dataset.file_name) # Skip comments. line = None for line in source: - if not line.startswith( '##' ): + if not line.startswith('##'): break - if line and line.startswith( '#' ): + if line and line.startswith('#'): # Found header line, get sample names. - dataset.metadata.sample_names = line.split()[ 9: ] + dataset.metadata.sample_names = line.split()[9:] @staticmethod def merge(split_files, output_file): @@ -712,7 +715,7 @@ def merge(split_files, output_file): stderr_name = stderr_f.name command = ["bcftools", "concat"] + split_files + ["-o", output_file] log.info("Merging vcf files with command [%s]" % " ".join(command)) - exit_code = subprocess.call( args=command, stderr=open( stderr_name, 'wb' ) ) + exit_code = subprocess.call(args=command, stderr=open(stderr_name, 'wb')) with open(stderr_name, "rb") as f: stderr = f.read().strip() # Did merge succeed? @@ -720,61 +723,101 @@ def merge(split_files, output_file): raise Exception("Error merging VCF files: %s" % stderr) # Dataproviders - @dataproviders.decorators.dataprovider_factory( 'genomic-region', - dataproviders.dataset.GenomicRegionDataProvider.settings ) - def genomic_region_dataprovider( self, dataset, **settings ): - return dataproviders.dataset.GenomicRegionDataProvider( dataset, 0, 1, 1, **settings ) + @dataproviders.decorators.dataprovider_factory('genomic-region', + dataproviders.dataset.GenomicRegionDataProvider.settings) + def genomic_region_dataprovider(self, dataset, **settings): + return dataproviders.dataset.GenomicRegionDataProvider(dataset, 0, 1, 1, **settings) + + @dataproviders.decorators.dataprovider_factory('genomic-region-dict', + dataproviders.dataset.GenomicRegionDataProvider.settings) + def genomic_region_dict_dataprovider(self, dataset, **settings): + settings['named_columns'] = True + return self.genomic_region_dataprovider(dataset, **settings) + + +class Vcf (BaseVcf): + extension = 'vcf' + + +class VcfGz(BaseVcf, binary.Binary): + extension = 'vcf.gz' + compressed = True - @dataproviders.decorators.dataprovider_factory( 'genomic-region-dict', - dataproviders.dataset.GenomicRegionDataProvider.settings ) - def genomic_region_dict_dataprovider( self, dataset, **settings ): - settings[ 'named_columns' ] = True - return self.genomic_region_dataprovider( dataset, **settings ) + MetadataElement(name="tabix_index", desc="Vcf Index File", param=metadata.FileParameter, file_ext="tbi", readonly=True, no_value=None, visible=False, optional=True) + def set_meta(self, dataset, **kwd): + super(BaseVcf, self).set_meta(dataset, **kwd) + """ Creates the index for the VCF file. """ + # These metadata values are not accessible by users, always overwrite + index_file = dataset.metadata.bcf_index + if not index_file: + index_file = dataset.metadata.spec['tabix_index'].param.new_file(dataset=dataset) + # Create the bcf index + # $ bcftools index + # Usage: bcftools index -class Eland( Tabular ): + dataset_symlink = os.path.join(os.path.dirname(index_file.file_name), + '__dataset_%d_%s' % (dataset.id, os.path.basename(index_file.file_name))) + ".vcf.gz" + os.symlink(dataset.file_name, dataset_symlink) + + stderr_name = tempfile.NamedTemporaryFile(prefix="bcf_index_stderr").name + command = ['bcftools', 'index', '-t', dataset_symlink] + try: + subprocess.check_call(args=command, stderr=open(stderr_name, 'wb')) + shutil.move(dataset_symlink + '.tbi', index_file.file_name) # this will fail if bcftools < 1.0 is used, because it creates a .bci index file instead of .csi + except Exception as e: + stderr = open(stderr_name).read().strip() + raise Exception('Error setting BCF metadata: %s' % (stderr or str(e))) + finally: + # Remove temp file and symlink + os.remove(stderr_name) + os.remove(dataset_symlink) + dataset.metadata.tabix_index = index_file + + +class Eland(Tabular): """Support for the export.txt.gz file used by Illumina's ELANDv2e aligner""" file_ext = '_export.txt.gz' - MetadataElement( name="columns", default=0, desc="Number of columns", readonly=True, visible=False ) - MetadataElement( name="column_types", default=[], param=metadata.ColumnTypesParameter, desc="Column types", readonly=True, visible=False, no_value=[] ) - MetadataElement( name="comment_lines", default=0, desc="Number of comments", readonly=True, visible=False ) - MetadataElement( name="tiles", default=[], param=metadata.ListParameter, desc="Set of tiles", readonly=True, visible=False, no_value=[] ) - MetadataElement( name="reads", default=[], param=metadata.ListParameter, desc="Set of reads", readonly=True, visible=False, no_value=[] ) - MetadataElement( name="lanes", default=[], param=metadata.ListParameter, desc="Set of lanes", readonly=True, visible=False, no_value=[] ) - MetadataElement( name="barcodes", default=[], param=metadata.ListParameter, desc="Set of barcodes", readonly=True, visible=False, no_value=[] ) + MetadataElement(name="columns", default=0, desc="Number of columns", readonly=True, visible=False) + MetadataElement(name="column_types", default=[], param=metadata.ColumnTypesParameter, desc="Column types", readonly=True, visible=False, no_value=[]) + MetadataElement(name="comment_lines", default=0, desc="Number of comments", readonly=True, visible=False) + MetadataElement(name="tiles", default=[], param=metadata.ListParameter, desc="Set of tiles", readonly=True, visible=False, no_value=[]) + MetadataElement(name="reads", default=[], param=metadata.ListParameter, desc="Set of reads", readonly=True, visible=False, no_value=[]) + MetadataElement(name="lanes", default=[], param=metadata.ListParameter, desc="Set of lanes", readonly=True, visible=False, no_value=[]) + MetadataElement(name="barcodes", default=[], param=metadata.ListParameter, desc="Set of barcodes", readonly=True, visible=False, no_value=[]) def __init__(self, **kwd): """Initialize taxonomy datatype""" - super( Eland, self ).__init__( **kwd ) + super(Eland, self).__init__(**kwd) self.column_names = ['MACHINE', 'RUN_NO', 'LANE', 'TILE', 'X', 'Y', 'INDEX', 'READ_NO', 'SEQ', 'QUAL', 'CHROM', 'CONTIG', 'POSITION', 'STRAND', 'DESC', 'SRAS', 'PRAS', 'PART_CHROM' 'PART_CONTIG', 'PART_OFFSET', 'PART_STRAND', 'FILT' ] - def make_html_table( self, dataset, skipchars=None ): + def make_html_table(self, dataset, skipchars=None): """Create HTML table, used for displaying peek""" if skipchars is None: skipchars = [] out = [''] try: # Generate column header - out.append( '' ) - for i, name in enumerate( self.column_names ): - out.append( '' % ( str( i + 1 ), name ) ) + out.append('') + for i, name in enumerate(self.column_names): + out.append('' % (str(i + 1), name)) # This data type requires at least 11 columns in the data - if dataset.metadata.columns - len( self.column_names ) > 0: - for i in range( len( self.column_names ), dataset.metadata.columns ): - out.append( '' % str( i + 1 ) ) - out.append( '' ) - out.append( self.make_html_peek_rows( dataset, skipchars=skipchars ) ) - out.append( '
    %s.%s
    %s.%s%s
    ' ) - out = "".join( out ) + if dataset.metadata.columns - len(self.column_names) > 0: + for i in range(len(self.column_names), dataset.metadata.columns): + out.append('%s' % str(i + 1)) + out.append('') + out.append(self.make_html_peek_rows(dataset, skipchars=skipchars)) + out.append('') + out = "".join(out) except Exception as exc: out = "Can't create peek %s" % exc return out - def sniff( self, filename ): + def sniff(self, filename): """ Determines whether the file is in ELAND export format @@ -814,7 +857,7 @@ def sniff( self, filename ): return True return False - def set_meta( self, dataset, overwrite=True, skip=None, max_data_lines=5, **kwd ): + def set_meta(self, dataset, overwrite=True, skip=None, max_data_lines=5, **kwd): if dataset.has_data(): with compression_utils.get_fileobj(dataset.file_name, gzip_only=True) as dataset_fh: lanes = {} @@ -847,24 +890,24 @@ def set_meta( self, dataset, overwrite=True, skip=None, max_data_lines=5, **kwd dataset.metadata.reads = list(reads.keys()) -class ElandMulti( Tabular ): +class ElandMulti(Tabular): file_ext = 'elandmulti' - def sniff( self, filename ): + def sniff(self, filename): return False -class FeatureLocationIndex( Tabular ): +class FeatureLocationIndex(Tabular): """ An index that stores feature locations in tabular format. """ file_ext = 'fli' - MetadataElement( name="columns", default=2, desc="Number of columns", readonly=True, visible=False ) - MetadataElement( name="column_types", default=['str', 'str'], param=metadata.ColumnTypesParameter, desc="Column types", readonly=True, visible=False, no_value=[] ) + MetadataElement(name="columns", default=2, desc="Number of columns", readonly=True, visible=False) + MetadataElement(name="column_types", default=['str', 'str'], param=metadata.ColumnTypesParameter, desc="Column types", readonly=True, visible=False, no_value=[]) @dataproviders.decorators.has_dataproviders -class BaseCSV( TabularData ): +class BaseCSV(TabularData): """ Delimiter-separated table data. This includes CSV, TSV and other dialects understood by the @@ -876,23 +919,23 @@ class BaseCSV( TabularData ): peek_size = 1024 # File chunk used for sniffing CSV dialect big_peek_size = 10240 # Large File chunk used for sniffing CSV dialect - def is_int( self, column_text ): + def is_int(self, column_text): try: - int( column_text ) + int(column_text) return True except: return False - def is_float( self, column_text ): + def is_float(self, column_text): try: - float( column_text ) + float(column_text) return True except: if column_text.strip().lower() == 'na': return True # na is special cased to be a float return False - def guess_type( self, text ): + def guess_type(self, text): if self.is_int(text): return 'int' if self.is_float(text): @@ -900,7 +943,7 @@ def guess_type( self, text ): else: return 'str' - def sniff( self, filename ): + def sniff(self, filename): """ Return True if if recognizes dialect and header. """ try: # check the dialect works @@ -955,7 +998,7 @@ def sniff( self, filename ): # Not readable by Python's csv using this dialect return False - def set_meta( self, dataset, **kwd ): + def set_meta(self, dataset, **kwd): column_types = [] header_row = [] data_row = [] @@ -984,13 +1027,13 @@ def set_meta( self, dataset, **kwd ): dataset.metadata.data_lines = data_lines dataset.metadata.comment_lines = int(bool(header_row)) dataset.metadata.column_types = column_types - dataset.metadata.columns = max( len( header_row ), len( data_row ) ) + dataset.metadata.columns = max(len(header_row), len(data_row)) dataset.metadata.column_names = header_row dataset.metadata.delimiter = self.dialect.delimiter @dataproviders.decorators.has_dataproviders -class CSV( BaseCSV ): +class CSV(BaseCSV): """ Comma-separated table data. Only sniffs comma-separated files with at least 2 rows and 2 columns. @@ -1001,7 +1044,7 @@ class CSV( BaseCSV ): @dataproviders.decorators.has_dataproviders -class TSV( BaseCSV ): +class TSV(BaseCSV): """ Tab-separated table data. Only sniff tab-separated files with at least 2 rows and 2 columns. @@ -1019,23 +1062,23 @@ class TSV( BaseCSV ): strict_width = True # Leave files with different width to tabular -class ConnectivityTable( Tabular ): +class ConnectivityTable(Tabular): edam_format = "format_3309" file_ext = "ct" - header_regexp = re.compile( "^[0-9]+" + "(?:\t|[ ]+)" + ".*?" + "(?:ENERGY|energy|dG)" + "[ \t].*?=") - structure_regexp = re.compile( "^[0-9]+" + "(?:\t|[ ]+)" + "[ACGTURYKMSWBDHVN]+" + "(?:\t|[ ]+)" + "[^\t]+" + "(?:\t|[ ]+)" + "[^\t]+" + "(?:\t|[ ]+)" + "[^\t]+" + "(?:\t|[ ]+)" + "[^\t]+") + header_regexp = re.compile("^[0-9]+" + "(?:\t|[ ]+)" + ".*?" + "(?:ENERGY|energy|dG)" + "[ \t].*?=") + structure_regexp = re.compile("^[0-9]+" + "(?:\t|[ ]+)" + "[ACGTURYKMSWBDHVN]+" + "(?:\t|[ ]+)" + "[^\t]+" + "(?:\t|[ ]+)" + "[^\t]+" + "(?:\t|[ ]+)" + "[^\t]+" + "(?:\t|[ ]+)" + "[^\t]+") def __init__(self, **kwd): - super( ConnectivityTable, self ).__init__( **kwd ) + super(ConnectivityTable, self).__init__(**kwd) self.columns = 6 self.column_names = ['base_index', 'base', 'neighbor_left', 'neighbor_right', 'partner', 'natural_numbering'] self.column_types = ['int', 'str', 'int', 'int', 'int', 'int'] - def set_meta( self, dataset, **kwd ): + def set_meta(self, dataset, **kwd): data_lines = 0 - for line in open( dataset.file_name ): + for line in open(dataset.file_name): data_lines += 1 dataset.metadata.data_lines = data_lines @@ -1076,7 +1119,7 @@ def sniff(self, filename): j = 1 try: - with open( filename ) as handle: + with open(filename) as handle: for line in handle: line = line.strip() @@ -1123,4 +1166,4 @@ def get_chunk(self, trans, dataset, chunk): ck_data_body = re.sub('\n[ \t]+', '\n', ck_data_body) ck_data_body = re.sub('[ ]+', '\t', ck_data_body) - return dumps( { 'ck_data': util.unicodify(ck_data_header + "\n" + ck_data_body ), 'ck_index': ck_index + 1 } ) + return dumps({'ck_data': util.unicodify(ck_data_header + "\n" + ck_data_body), 'ck_index': ck_index + 1}) diff --git a/lib/galaxy/datatypes/test/1.memepsp b/lib/galaxy/datatypes/test/1.memepsp new file mode 100644 index 000000000000..3dec46ab349d --- /dev/null +++ b/lib/galaxy/datatypes/test/1.memepsp @@ -0,0 +1,7 @@ +>ICYA_MANSE 4 +0.075922 0.070764 0.082380 0.030292 0.025101 0.043139 0.032963 +0.086047 0.057445 0.000000 0.000000 0.000000 + +>LACB_BOVIN 4 +0.107099 0.099822 0.116208 0.042731 0.035408 0.060854 0.046499 +0.000000 0.000000 0.000000 diff --git a/lib/galaxy/datatypes/test/1.xls b/lib/galaxy/datatypes/test/1.xls new file mode 100644 index 000000000000..5326aab226cc Binary files /dev/null and b/lib/galaxy/datatypes/test/1.xls differ diff --git a/lib/galaxy/datatypes/text.py b/lib/galaxy/datatypes/text.py index bffb3ad24bc0..525ccb64f96d 100644 --- a/lib/galaxy/datatypes/text.py +++ b/lib/galaxy/datatypes/text.py @@ -12,21 +12,21 @@ from galaxy.datatypes.data import get_file_peek, Text from galaxy.datatypes.metadata import MetadataElement, MetadataParameter -from galaxy.datatypes.sniff import get_headers +from galaxy.datatypes.sniff import iter_headers from galaxy.util import nice_size, string_as_bool log = logging.getLogger(__name__) -class Html( Text ): +class Html(Text): """Class describing an html file""" edam_format = "format_2331" file_ext = "html" - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): if not dataset.dataset.purged: dataset.peek = "HTML file" - dataset.blurb = nice_size( dataset.get_size() ) + dataset.blurb = nice_size(dataset.get_size()) else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disk' @@ -35,7 +35,7 @@ def get_mime(self): """Returns the mime type of the datatype""" return 'text/html' - def sniff( self, filename ): + def sniff(self, filename): """ Determines whether the file is in html format @@ -47,23 +47,23 @@ def sniff( self, filename ): >>> Html().sniff( fname ) True """ - headers = get_headers( filename, None ) + headers = iter_headers(filename, None) try: for i, hdr in enumerate(headers): - if hdr and hdr[0].lower().find( '' ) >= 0: + if hdr and hdr[0].lower().find('') >= 0: return True return False except: return True -class Json( Text ): +class Json(Text): edam_format = "format_3464" file_ext = "json" - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): if not dataset.dataset.purged: - dataset.peek = get_file_peek( dataset.file_name, is_multi_byte=is_multi_byte ) + dataset.peek = get_file_peek(dataset.file_name, is_multi_byte=is_multi_byte) dataset.blurb = "JavaScript Object Notation (JSON)" else: dataset.peek = 'file does not exist' @@ -73,18 +73,18 @@ def get_mime(self): """Returns the mime type of the datatype""" return 'application/json' - def sniff( self, filename ): + def sniff(self, filename): """ Try to load the string with the json module. If successful it's a json file. """ - return self._looks_like_json( filename ) + return self._looks_like_json(filename) - def _looks_like_json( self, filename ): + def _looks_like_json(self, filename): # Pattern used by SequenceSplitLocations if os.path.getsize(filename) < 50000: # If the file is small enough - don't guess just check. try: - json.load( open(filename, "r") ) + json.load(open(filename, "r")) return True except Exception: return False @@ -99,31 +99,31 @@ def _looks_like_json( self, filename ): return start.startswith("[") or start.startswith("{") return False - def display_peek( self, dataset ): + def display_peek(self, dataset): try: return dataset.peek except: - return "JSON file (%s)" % ( nice_size( dataset.get_size() ) ) + return "JSON file (%s)" % (nice_size(dataset.get_size())) -class Ipynb( Json ): +class Ipynb(Json): file_ext = "ipynb" - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): if not dataset.dataset.purged: - dataset.peek = get_file_peek( dataset.file_name, is_multi_byte=is_multi_byte ) + dataset.peek = get_file_peek(dataset.file_name, is_multi_byte=is_multi_byte) dataset.blurb = "Jupyter Notebook" else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disc' - def sniff( self, filename ): + def sniff(self, filename): """ Try to load the string with the json module. If successful it's a json file. """ - if self._looks_like_json( filename ): + if self._looks_like_json(filename): try: - ipynb = json.load( open(filename) ) + ipynb = json.load(open(filename)) if ipynb.get('nbformat', False) is not False and ipynb.get('metadata', False): return True else: @@ -133,14 +133,14 @@ def sniff( self, filename ): def display_data(self, trans, dataset, preview=False, filename=None, to_ext=None, **kwd): config = trans.app.config - trust = getattr( config, 'trust_jupyter_notebook_conversion', False ) + trust = getattr(config, 'trust_jupyter_notebook_conversion', False) if trust: return self._display_data_trusted(trans, dataset, preview=preview, filename=filename, to_ext=to_ext, **kwd) else: - return super(Ipynb, self).display_data( trans, dataset, preview=preview, filename=filename, to_ext=to_ext, **kwd ) + return super(Ipynb, self).display_data(trans, dataset, preview=preview, filename=filename, to_ext=to_ext, **kwd) def _display_data_trusted(self, trans, dataset, preview=False, filename=None, to_ext=None, **kwd): - preview = string_as_bool( preview ) + preview = string_as_bool(preview) if to_ext or not preview: return self._serve_raw(trans, dataset, to_ext, **kwd) else: @@ -154,110 +154,110 @@ def _display_data_trusted(self, trans, dataset, preview=False, filename=None, to ofilename = '%s.html' % ofilename except: ofilename = dataset.file_name - log.exception( 'Command "%s" failed. Could not convert the Jupyter Notebook to HTML, defaulting to plain text.', cmd ) - return open( ofilename ) + log.exception('Command "%s" failed. Could not convert the Jupyter Notebook to HTML, defaulting to plain text.', cmd) + return open(ofilename) - def set_meta( self, dataset, **kwd ): + def set_meta(self, dataset, **kwd): """ Set the number of models in dataset. """ pass -class Biom1( Json ): +class Biom1(Json): """ BIOM version 1.0 file format description http://biom-format.org/documentation/format_versions/biom-1.0.html """ file_ext = "biom1" - MetadataElement( name="table_rows", default=[], desc="table_rows", param=MetadataParameter, readonly=True, visible=False, optional=True, no_value=[] ) - MetadataElement( name="table_matrix_element_type", default="", desc="table_matrix_element_type", param=MetadataParameter, readonly=True, visible=False, optional=True, no_value="" ) - MetadataElement( name="table_format", default="", desc="table_format", param=MetadataParameter, readonly=True, visible=False, optional=True, no_value="" ) - MetadataElement( name="table_generated_by", default="", desc="table_generated_by", param=MetadataParameter, readonly=True, visible=True, optional=True, no_value="" ) - MetadataElement( name="table_matrix_type", default="", desc="table_matrix_type", param=MetadataParameter, readonly=True, visible=False, optional=True, no_value="" ) - MetadataElement( name="table_shape", default=[], desc="table_shape", param=MetadataParameter, readonly=True, visible=False, optional=True, no_value=[] ) - MetadataElement( name="table_format_url", default="", desc="table_format_url", param=MetadataParameter, readonly=True, visible=False, optional=True, no_value="" ) - MetadataElement( name="table_date", default="", desc="table_date", param=MetadataParameter, readonly=True, visible=True, optional=True, no_value="" ) - MetadataElement( name="table_type", default="", desc="table_type", param=MetadataParameter, readonly=True, visible=True, optional=True, no_value="" ) - MetadataElement( name="table_id", default=None, desc="table_id", param=MetadataParameter, readonly=True, visible=True, optional=True, no_value=None ) - MetadataElement( name="table_columns", default=[], desc="table_columns", param=MetadataParameter, readonly=True, visible=False, optional=True, no_value=[] ) - - def set_peek( self, dataset, is_multi_byte=False ): - super( Biom1, self ).set_peek( dataset, is_multi_byte ) + MetadataElement(name="table_rows", default=[], desc="table_rows", param=MetadataParameter, readonly=True, visible=False, optional=True, no_value=[]) + MetadataElement(name="table_matrix_element_type", default="", desc="table_matrix_element_type", param=MetadataParameter, readonly=True, visible=False, optional=True, no_value="") + MetadataElement(name="table_format", default="", desc="table_format", param=MetadataParameter, readonly=True, visible=False, optional=True, no_value="") + MetadataElement(name="table_generated_by", default="", desc="table_generated_by", param=MetadataParameter, readonly=True, visible=True, optional=True, no_value="") + MetadataElement(name="table_matrix_type", default="", desc="table_matrix_type", param=MetadataParameter, readonly=True, visible=False, optional=True, no_value="") + MetadataElement(name="table_shape", default=[], desc="table_shape", param=MetadataParameter, readonly=True, visible=False, optional=True, no_value=[]) + MetadataElement(name="table_format_url", default="", desc="table_format_url", param=MetadataParameter, readonly=True, visible=False, optional=True, no_value="") + MetadataElement(name="table_date", default="", desc="table_date", param=MetadataParameter, readonly=True, visible=True, optional=True, no_value="") + MetadataElement(name="table_type", default="", desc="table_type", param=MetadataParameter, readonly=True, visible=True, optional=True, no_value="") + MetadataElement(name="table_id", default=None, desc="table_id", param=MetadataParameter, readonly=True, visible=True, optional=True, no_value=None) + MetadataElement(name="table_columns", default=[], desc="table_columns", param=MetadataParameter, readonly=True, visible=False, optional=True, no_value=[]) + + def set_peek(self, dataset, is_multi_byte=False): + super(Biom1, self).set_peek(dataset, is_multi_byte) if not dataset.dataset.purged: dataset.blurb = "Biological Observation Matrix v1" - def sniff( self, filename ): + def sniff(self, filename): is_biom = False - if self._looks_like_json( filename ): - is_biom = self._looks_like_biom( filename ) + if self._looks_like_json(filename): + is_biom = self._looks_like_biom(filename) return is_biom - def _looks_like_biom( self, filepath, load_size=50000 ): + def _looks_like_biom(self, filepath, load_size=50000): """ @param filepath: [str] The path to the evaluated file. @param load_size: [int] The size of the file block load in RAM (in bytes). """ is_biom = False - segment_size = int( load_size / 2 ) + segment_size = int(load_size / 2) try: - with open( filepath, "r" ) as fh: + with open(filepath, "r") as fh: prev_str = "" - segment_str = fh.read( segment_size ) - if segment_str.strip().startswith( '{' ): + segment_str = fh.read(segment_size) + if segment_str.strip().startswith('{'): while segment_str: current_str = prev_str + segment_str if '"format"' in current_str: - current_str = re.sub( r'\s', '', current_str ) + current_str = re.sub(r'\s', '', current_str) if '"format":"BiologicalObservationMatrix' in current_str: is_biom = True break prev_str = segment_str - segment_str = fh.read( segment_size ) + segment_str = fh.read(segment_size) except Exception: pass return is_biom - def set_meta( self, dataset, **kwd ): + def set_meta(self, dataset, **kwd): """ Store metadata information from the BIOM file. """ if dataset.has_data(): - with open( dataset.file_name ) as fh: + with open(dataset.file_name) as fh: try: - json_dict = json.load( fh ) + json_dict = json.load(fh) except Exception: return - def _transform_dict_list_ids( dict_list ): + def _transform_dict_list_ids(dict_list): if dict_list: - return [ x.get( 'id', None ) for x in dict_list ] + return [x.get('id', None) for x in dict_list] return [] - b_transform = { 'rows': _transform_dict_list_ids, 'columns': _transform_dict_list_ids } - for ( m_name, b_name ) in [ ('table_rows', 'rows'), - ('table_matrix_element_type', 'matrix_element_type'), - ('table_format', 'format'), - ('table_generated_by', 'generated_by'), - ('table_matrix_type', 'matrix_type'), - ('table_shape', 'shape'), - ('table_format_url', 'format_url'), - ('table_date', 'date'), - ('table_type', 'type'), - ('table_id', 'id'), - ('table_columns', 'columns') ]: + b_transform = {'rows': _transform_dict_list_ids, 'columns': _transform_dict_list_ids} + for (m_name, b_name) in [('table_rows', 'rows'), + ('table_matrix_element_type', 'matrix_element_type'), + ('table_format', 'format'), + ('table_generated_by', 'generated_by'), + ('table_matrix_type', 'matrix_type'), + ('table_shape', 'shape'), + ('table_format_url', 'format_url'), + ('table_date', 'date'), + ('table_type', 'type'), + ('table_id', 'id'), + ('table_columns', 'columns')]: try: - metadata_value = json_dict.get( b_name, None ) + metadata_value = json_dict.get(b_name, None) if b_name in b_transform: - metadata_value = b_transform[ b_name ]( metadata_value ) - setattr( dataset.metadata, m_name, metadata_value ) + metadata_value = b_transform[b_name](metadata_value) + setattr(dataset.metadata, m_name, metadata_value) except Exception: pass -class Obo( Text ): +class Obo(Text): """ OBO file format description http://www.geneontology.org/GO.format.obo-1_2.shtml @@ -266,21 +266,21 @@ class Obo( Text ): edam_format = "format_2549" file_ext = "obo" - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): if not dataset.dataset.purged: - dataset.peek = get_file_peek( dataset.file_name, is_multi_byte=is_multi_byte ) + dataset.peek = get_file_peek(dataset.file_name, is_multi_byte=is_multi_byte) dataset.blurb = "Open Biomedical Ontology (OBO)" else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disc' - def sniff( self, filename ): + def sniff(self, filename): """ Try to guess the Obo filetype. It usually starts with a "format-version:" string and has several stanzas which starts with "id:". """ stanza = re.compile(r'^\[.*\]$') - with open( filename ) as handle: + with open(filename) as handle: first_line = handle.readline() if not first_line.startswith('format-version:'): return False @@ -293,7 +293,7 @@ def sniff( self, filename ): return False -class Arff( Text ): +class Arff(Text): """ An ARFF (Attribute-Relation File Format) file is an ASCII text file that describes a list of instances sharing a set of attributes. http://weka.wikispaces.com/ARFF @@ -302,27 +302,27 @@ class Arff( Text ): file_ext = "arff" """Add metadata elements""" - MetadataElement( name="comment_lines", default=0, desc="Number of comment lines", readonly=True, optional=True, no_value=0 ) - MetadataElement( name="columns", default=0, desc="Number of columns", readonly=True, visible=True, no_value=0 ) + MetadataElement(name="comment_lines", default=0, desc="Number of comment lines", readonly=True, optional=True, no_value=0) + MetadataElement(name="columns", default=0, desc="Number of columns", readonly=True, visible=True, no_value=0) - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): if not dataset.dataset.purged: - dataset.peek = get_file_peek( dataset.file_name, is_multi_byte=is_multi_byte ) + dataset.peek = get_file_peek(dataset.file_name, is_multi_byte=is_multi_byte) dataset.blurb = "Attribute-Relation File Format (ARFF)" - dataset.blurb += ", %s comments, %s attributes" % ( dataset.metadata.comment_lines, dataset.metadata.columns ) + dataset.blurb += ", %s comments, %s attributes" % (dataset.metadata.comment_lines, dataset.metadata.columns) else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disc' - def sniff( self, filename ): + def sniff(self, filename): """ Try to guess the Arff filetype. It usually starts with a "format-version:" string and has several stanzas which starts with "id:". """ - with open( filename ) as handle: + with open(filename) as handle: relation_found = False attribute_found = False - for line_count, line in enumerate( handle ): + for line_count, line in enumerate(handle): if line_count > 1000: # only investigate the first 1000 lines return False @@ -341,7 +341,7 @@ def sniff( self, filename ): return True return False - def set_meta( self, dataset, **kwd ): + def set_meta(self, dataset, **kwd): """ Trying to count the comment lines and the number of columns included. A typical ARFF data block looks like this: @@ -353,7 +353,7 @@ def set_meta( self, dataset, **kwd ): comment_lines = 0 first_real_line = False data_block = False - with open( dataset.file_name ) as handle: + with open(dataset.file_name) as handle: for line in handle: line = line.strip() if not line: @@ -395,17 +395,17 @@ def set_meta( self, dataset, **kwd ): dataset.metadata.columns = column_count -class SnpEffDb( Text ): +class SnpEffDb(Text): """Class describing a SnpEff genome build""" edam_format = "format_3624" file_ext = "snpeffdb" - MetadataElement( name="genome_version", default=None, desc="Genome Version", readonly=True, visible=True, no_value=None ) - MetadataElement( name="snpeff_version", default="SnpEff4.0", desc="SnpEff Version", readonly=True, visible=True, no_value=None ) - MetadataElement( name="regulation", default=[], desc="Regulation Names", readonly=True, visible=True, no_value=[], optional=True) - MetadataElement( name="annotation", default=[], desc="Annotation Names", readonly=True, visible=True, no_value=[], optional=True) + MetadataElement(name="genome_version", default=None, desc="Genome Version", readonly=True, visible=True, no_value=None) + MetadataElement(name="snpeff_version", default="SnpEff4.0", desc="SnpEff Version", readonly=True, visible=True, no_value=None) + MetadataElement(name="regulation", default=[], desc="Regulation Names", readonly=True, visible=True, no_value=[], optional=True) + MetadataElement(name="annotation", default=[], desc="Annotation Names", readonly=True, visible=True, no_value=[], optional=True) - def __init__( self, **kwd ): - Text.__init__( self, **kwd ) + def __init__(self, **kwd): + Text.__init__(self, **kwd) # The SnpEff version line was added in SnpEff version 4.1 def getSnpeffVersionFromFile(self, path): @@ -422,8 +422,8 @@ def getSnpeffVersionFromFile(self, path): pass return snpeff_version - def set_meta( self, dataset, **kwd ): - Text.set_meta(self, dataset, **kwd ) + def set_meta(self, dataset, **kwd): + Text.set_meta(self, dataset, **kwd) data_dir = dataset.extra_files_path # search data_dir/genome_version for files regulation_pattern = 'regulation_(.+).bin' @@ -467,12 +467,12 @@ def set_meta( self, dataset, **kwd ): pass -class SnpSiftDbNSFP( Text ): +class SnpSiftDbNSFP(Text): """Class describing a dbNSFP database prepared fpr use by SnpSift dbnsfp """ - MetadataElement( name='reference_name', default='dbSNFP', desc='Reference Name', readonly=True, visible=True, set_in_upload=True, no_value='dbSNFP' ) - MetadataElement( name="bgzip", default=None, desc="dbNSFP bgzip", readonly=True, visible=True, no_value=None ) - MetadataElement( name="index", default=None, desc="Tabix Index File", readonly=True, visible=True, no_value=None) - MetadataElement( name="annotation", default=[], desc="Annotation Names", readonly=True, visible=True, no_value=[] ) + MetadataElement(name='reference_name', default='dbSNFP', desc='Reference Name', readonly=True, visible=True, set_in_upload=True, no_value='dbSNFP') + MetadataElement(name="bgzip", default=None, desc="dbNSFP bgzip", readonly=True, visible=True, no_value=None) + MetadataElement(name="index", default=None, desc="Tabix Index File", readonly=True, visible=True, no_value=None) + MetadataElement(name="annotation", default=[], desc="Annotation Names", readonly=True, visible=True, no_value=[]) file_ext = "snpsiftdbnsfp" composite_type = 'auto_primary_file' allow_datatype_change = False @@ -487,20 +487,21 @@ class SnpSiftDbNSFP( Text ): ## Create tabix index tabix -s 1 -b 2 -e 2 dbNSFP2.3.txt.gz """ - def __init__( self, **kwd ): - Text.__init__( self, **kwd ) - self.add_composite_file( '%s.gz', description='dbNSFP bgzip', substitute_name_with_metadata='reference_name', is_binary=True ) - self.add_composite_file( '%s.gz.tbi', description='Tabix Index File', substitute_name_with_metadata='reference_name', is_binary=True ) - def init_meta( self, dataset, copy_from=None ): - Text.init_meta( self, dataset, copy_from=copy_from ) + def __init__(self, **kwd): + Text.__init__(self, **kwd) + self.add_composite_file('%s.gz', description='dbNSFP bgzip', substitute_name_with_metadata='reference_name', is_binary=True) + self.add_composite_file('%s.gz.tbi', description='Tabix Index File', substitute_name_with_metadata='reference_name', is_binary=True) - def generate_primary_file( self, dataset=None ): + def init_meta(self, dataset, copy_from=None): + Text.init_meta(self, dataset, copy_from=copy_from) + + def generate_primary_file(self, dataset=None): """ This is called only at upload to write the html file cannot rename the datasets here - they come with the default unfortunately """ - self.regenerate_primary_file( dataset ) + self.regenerate_primary_file(dataset) def regenerate_primary_file(self, dataset): """ @@ -515,7 +516,7 @@ def regenerate_primary_file(self, dataset): f.write(annotations) f.close() - def set_meta( self, dataset, overwrite=True, **kwd ): + def set_meta(self, dataset, overwrite=True, **kwd): try: efp = dataset.extra_files_path if os.path.exists(efp): @@ -539,7 +540,7 @@ def set_meta( self, dataset, overwrite=True, **kwd ): except Exception as e: log.warning("set_meta fname: %s %s" % (dataset.file_name if dataset and dataset.file_name else 'Unkwown', str(e))) - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): if not dataset.dataset.purged: dataset.peek = '%s : %s' % (dataset.metadata.reference_name, ','.join(dataset.metadata.annotation)) dataset.blurb = '%s' % dataset.metadata.reference_name diff --git a/lib/galaxy/datatypes/tracks.py b/lib/galaxy/datatypes/tracks.py index c14bf4420067..6468737a49f1 100644 --- a/lib/galaxy/datatypes/tracks.py +++ b/lib/galaxy/datatypes/tracks.py @@ -12,13 +12,13 @@ # GeneTrack is no longer supported but leaving the datatype since # files of this type may still exist -class GeneTrack( binary.Binary ): +class GeneTrack(binary.Binary): edam_data = "data_3002" edam_format = "format_2919" file_ext = "genetrack" def __init__(self, **kwargs): - super( GeneTrack, self ).__init__( **kwargs ) + super(GeneTrack, self).__init__(**kwargs) # self.add_display_app( 'genetrack', 'View in', '', 'genetrack_link' ) # def get_display_links( self, dataset, type, app, base_url, target_frame='galaxy_main', **kwd ): #Force target_frame to be 'galaxy_main' # return binary.Binary.get_display_links( self, dataset, type, app, base_url, target_frame=target_frame, **kwd ) @@ -40,7 +40,7 @@ def __init__(self, **kwargs): # return ret_val -class UCSCTrackHub( Html ): +class UCSCTrackHub(Html): """ Datatype for UCSC TrackHub """ @@ -51,7 +51,7 @@ class UCSCTrackHub( Html ): def __init__(self, **kwd): Html.__init__(self, **kwd) - def generate_primary_file( self, dataset=None ): + def generate_primary_file(self, dataset=None): """ This is called only at upload to write the html file cannot rename the datasets here - they come with the default unfortunately @@ -60,26 +60,26 @@ def generate_primary_file( self, dataset=None ): 'Files for Composite Dataset (%s)

    \ This composite dataset is composed of the following files:

      ' % ( self.file_ext)] - for composite_name, composite_file in self.get_composite_files( dataset=dataset ).items(): + for composite_name, composite_file in self.get_composite_files(dataset=dataset).items(): opt_text = '' if composite_file.optional: opt_text = ' (optional)' - rval.append('
    • %s%s' % ( composite_name, composite_name, opt_text) ) + rval.append('
    • %s%s' % (composite_name, composite_name, opt_text)) rval.append('
    ') return "\n".join(rval) - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): if not dataset.dataset.purged: dataset.peek = "Track Hub structure: Visualization in UCSC Track Hub" else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disk' - def display_peek( self, dataset ): + def display_peek(self, dataset): try: return dataset.peek except: return "Track Hub structure: Visualization in UCSC Track Hub" - def sniff( self, filename ): + def sniff(self, filename): return False diff --git a/lib/galaxy/datatypes/triples.py b/lib/galaxy/datatypes/triples.py index 1b8df11dd5df..377a47efd3df 100644 --- a/lib/galaxy/datatypes/triples.py +++ b/lib/galaxy/datatypes/triples.py @@ -14,7 +14,7 @@ log = logging.getLogger(__name__) -class Triples( data.Data ): +class Triples(data.Data): """ The abstract base class for the file format that can contain triples """ @@ -22,90 +22,90 @@ class Triples( data.Data ): edam_format = "format_2376" file_ext = "triples" - def sniff( self, filename ): + def sniff(self, filename): """ Returns false and the user must manually set. """ return False - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): """Set the peek and blurb text""" if not dataset.dataset.purged: - dataset.peek = data.get_file_peek( dataset.file_name, is_multi_byte=is_multi_byte ) + dataset.peek = data.get_file_peek(dataset.file_name, is_multi_byte=is_multi_byte) dataset.blurb = 'Triple data' else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disk' -class NTriples( data.Text, Triples ): +class NTriples(data.Text, Triples): """ The N-Triples triple data format """ edam_format = "format_3256" file_ext = "nt" - def sniff( self, filename ): + def sniff(self, filename): with open(filename, "r") as f: # . - if re.compile( r'<[^>]*>\s<[^>]*>\s<[^>]*>\s\.' ).search( f.readline( 1024 ) ): + if re.compile(r'<[^>]*>\s<[^>]*>\s<[^>]*>\s\.').search(f.readline(1024)): return True return False - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): """Set the peek and blurb text""" if not dataset.dataset.purged: - dataset.peek = data.get_file_peek( dataset.file_name, is_multi_byte=is_multi_byte ) + dataset.peek = data.get_file_peek(dataset.file_name, is_multi_byte=is_multi_byte) dataset.blurb = 'N-Triples triple data' else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disk' -class N3( data.Text, Triples ): +class N3(data.Text, Triples): """ The N3 triple data format """ edam_format = "format_3257" file_ext = "n3" - def sniff( self, filename ): + def sniff(self, filename): """ Returns false and the user must manually set. """ return False - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): """Set the peek and blurb text""" if not dataset.dataset.purged: - dataset.peek = data.get_file_peek( dataset.file_name, is_multi_byte=is_multi_byte ) + dataset.peek = data.get_file_peek(dataset.file_name, is_multi_byte=is_multi_byte) dataset.blurb = 'Notation-3 Triple data' else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disk' -class Turtle( data.Text, Triples ): +class Turtle(data.Text, Triples): """ The Turtle triple data format """ edam_format = "format_3255" file_ext = "ttl" - def sniff( self, filename ): + def sniff(self, filename): with open(filename, "r") as f: # @prefix rdf: . - line = f.readline( 1024 ) - if re.compile( r'@prefix\s+[^:]*:\s+<[^>]*>\s\.' ).search( line ): + line = f.readline(1024) + if re.compile(r'@prefix\s+[^:]*:\s+<[^>]*>\s\.').search(line): return True - if re.compile( r'@base\s+<[^>]*>\s\.' ).search( line ): + if re.compile(r'@base\s+<[^>]*>\s\.').search(line): return True return False - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): """Set the peek and blurb text""" if not dataset.dataset.purged: - dataset.peek = data.get_file_peek( dataset.file_name, is_multi_byte=is_multi_byte ) + dataset.peek = data.get_file_peek(dataset.file_name, is_multi_byte=is_multi_byte) dataset.blurb = 'Turtle triple data' else: dataset.peek = 'file does not exist' @@ -113,33 +113,33 @@ def set_peek( self, dataset, is_multi_byte=False ): # TODO: we might want to look at rdflib or a similar, larger lib/egg -class Rdf( xml.GenericXml, Triples ): +class Rdf(xml.GenericXml, Triples): """ Resource Description Framework format (http://www.w3.org/RDF/). """ edam_format = "format_3261" file_ext = "rdf" - def sniff( self, filename ): + def sniff(self, filename): with open(filename, "r") as f: - firstlines = "".join( f.readlines( 5000 ) ) + firstlines = "".join(f.readlines(5000)) # = self.nfields: - raise MissingFieldError( "No field for feature_col (%d)" % feature_col ) - self.feature = self.fields[ self.feature_col ] + raise MissingFieldError("No field for feature_col (%d)" % feature_col) + self.feature = self.fields[self.feature_col] self.score_col = score_col if self.score_col >= self.nfields: - raise MissingFieldError( "No field for score_col (%d)" % score_col ) - self.score = self.fields[ self.score_col ] + raise MissingFieldError("No field for score_col (%d)" % score_col) + self.score = self.fields[self.score_col] # GFF attributes. - self.attributes = parse_gff_attributes( fields[8] ) + self.attributes = parse_gff_attributes(fields[8]) - def copy( self ): - return GFFInterval(self.reader, list( self.fields ), self.chrom_col, self.feature_col, self.start_col, + def copy(self): + return GFFInterval(self.reader, list(self.fields), self.chrom_col, self.feature_col, self.start_col, self.end_col, self.strand_col, self.score_col, self.strand) -class GFFFeature( GFFInterval ): +class GFFFeature(GFFInterval): """ A GFF feature, which can include multiple intervals. """ - def __init__( self, reader, chrom_col=0, feature_col=2, start_col=3, end_col=4, - strand_col=6, score_col=5, default_strand='.', fix_strand=False, intervals=[], - raw_size=0 ): + + def __init__(self, reader, chrom_col=0, feature_col=2, start_col=3, end_col=4, + strand_col=6, score_col=5, default_strand='.', fix_strand=False, intervals=[], + raw_size=0): # Use copy so that first interval and feature do not share fields. - GFFInterval.__init__( self, reader, copy.deepcopy( intervals[0].fields ), chrom_col, feature_col, - start_col, end_col, strand_col, score_col, default_strand, - fix_strand=fix_strand ) + GFFInterval.__init__(self, reader, copy.deepcopy(intervals[0].fields), chrom_col, feature_col, + start_col, end_col, strand_col, score_col, default_strand, + fix_strand=fix_strand) self.intervals = intervals self.raw_size = raw_size # Use intervals to set feature attributes. for interval in self.intervals: # Error checking. NOTE: intervals need not share the same strand. if interval.chrom != self.chrom: - raise ValueError( "interval chrom does not match self chrom: %s != %s" % - ( interval.chrom, self.chrom ) ) + raise ValueError("interval chrom does not match self chrom: %s != %s" % + (interval.chrom, self.chrom)) # Set start, end of interval. if interval.start < self.start: self.start = interval.start if interval.end > self.end: self.end = interval.end - def name( self ): + def name(self): """ Returns feature's name. """ name = None # Preference for name: GTF, GFF3, GFF. @@ -83,43 +85,43 @@ def name( self ): # GFF3: 'ID', 'id', # GFF (TODO): - 'group' ]: - name = self.attributes.get( attr_name, None ) + 'group']: + name = self.attributes.get(attr_name, None) if name is not None: break return name - def copy( self ): + def copy(self): intervals_copy = [] for interval in self.intervals: - intervals_copy.append( interval.copy() ) + intervals_copy.append(interval.copy()) return GFFFeature(self.reader, self.chrom_col, self.feature_col, self.start_col, self.end_col, self.strand_col, - self.score_col, self.strand, intervals=intervals_copy ) + self.score_col, self.strand, intervals=intervals_copy) - def lines( self ): + def lines(self): lines = [] for interval in self.intervals: - lines.append( '\t'.join( interval.fields ) ) + lines.append('\t'.join(interval.fields)) return lines -class GFFIntervalToBEDReaderWrapper( NiceReaderWrapper ): +class GFFIntervalToBEDReaderWrapper(NiceReaderWrapper): """ Reader wrapper that reads GFF intervals/lines and automatically converts them to BED format. """ - def parse_row( self, line ): + def parse_row(self, line): # HACK: this should return a GFF interval, but bx-python operations # require GenomicInterval objects and subclasses will not work. - interval = GenomicInterval( self, line.split( "\t" ), self.chrom_col, self.start_col, - self.end_col, self.strand_col, self.default_strand, - fix_strand=self.fix_strand ) - interval = convert_gff_coords_to_bed( interval ) + interval = GenomicInterval(self, line.split("\t"), self.chrom_col, self.start_col, + self.end_col, self.strand_col, self.default_strand, + fix_strand=self.fix_strand) + interval = convert_gff_coords_to_bed(interval) return interval -class GFFReaderWrapper( Iterator, NiceReaderWrapper ): # Iterator can be removed after bx-python library is ported to Python3 +class GFFReaderWrapper(Iterator, NiceReaderWrapper): # Iterator can be removed after bx-python library is ported to Python3 """ Reader wrapper for GFF files. @@ -133,10 +135,10 @@ class GFFReaderWrapper( Iterator, NiceReaderWrapper ): # Iterator can be remove expect traditional interval format. """ - def __init__( self, reader, chrom_col=0, feature_col=2, start_col=3, - end_col=4, strand_col=6, score_col=5, fix_strand=False, convert_to_bed_coord=False, **kwargs ): - NiceReaderWrapper.__init__( self, reader, chrom_col=chrom_col, start_col=start_col, end_col=end_col, - strand_col=strand_col, fix_strand=fix_strand, **kwargs ) + def __init__(self, reader, chrom_col=0, feature_col=2, start_col=3, + end_col=4, strand_col=6, score_col=5, fix_strand=False, convert_to_bed_coord=False, **kwargs): + NiceReaderWrapper.__init__(self, reader, chrom_col=chrom_col, start_col=start_col, end_col=end_col, + strand_col=strand_col, fix_strand=fix_strand, **kwargs) self.feature_col = feature_col self.score_col = score_col self.convert_to_bed_coord = convert_to_bed_coord @@ -145,28 +147,28 @@ def __init__( self, reader, chrom_col=0, feature_col=2, start_col=3, self.seed_interval = None self.seed_interval_line_len = 0 - def parse_row( self, line ): - interval = GFFInterval( self, line.split( "\t" ), self.chrom_col, self.feature_col, - self.start_col, self.end_col, self.strand_col, self.score_col, - self.default_strand, fix_strand=self.fix_strand ) + def parse_row(self, line): + interval = GFFInterval(self, line.split("\t"), self.chrom_col, self.feature_col, + self.start_col, self.end_col, self.strand_col, self.score_col, + self.default_strand, fix_strand=self.fix_strand) return interval - def __next__( self ): + def __next__(self): """ Returns next GFFFeature. """ # # Helper function. # - def handle_parse_error( e ): + def handle_parse_error(e): """ Actions to take when ParseError found. """ if self.outstream: if self.print_delegate and hasattr(self.print_delegate, "__call__"): - self.print_delegate( self.outstream, e, self ) + self.print_delegate(self.outstream, e, self) self.skipped += 1 # no reason to stuff an entire bad file into memmory if self.skipped < 10: - self.skipped_lines.append( ( self.linenum, self.current_line, str( e ) ) ) + self.skipped_lines.append((self.linenum, self.current_line, str(e))) # For debugging, uncomment this to propogate parsing exceptions up. # I.e. the underlying reason for an unexpected StopIteration exception @@ -183,130 +185,130 @@ def handle_parse_error( e ): if not self.seed_interval: while not self.seed_interval: try: - self.seed_interval = GenomicIntervalReader.next( self ) + self.seed_interval = GenomicIntervalReader.next(self) except ParseError as e: - handle_parse_error( e ) + handle_parse_error(e) # TODO: When no longer supporting python 2.4 use finally: # finally: - raw_size += len( self.current_line ) + raw_size += len(self.current_line) # If header or comment, clear seed interval and return it with its size. - if isinstance( self.seed_interval, ( Header, Comment ) ): + if isinstance(self.seed_interval, (Header, Comment)): return_val = self.seed_interval - return_val.raw_size = len( self.current_line ) + return_val.raw_size = len(self.current_line) self.seed_interval = None self.seed_interval_line_len = 0 return return_val # Initialize feature identifier from seed. - feature_group = self.seed_interval.attributes.get( 'group', None ) # For GFF + feature_group = self.seed_interval.attributes.get('group', None) # For GFF # For GFF3 - feature_id = self.seed_interval.attributes.get( 'ID', None ) + feature_id = self.seed_interval.attributes.get('ID', None) # For GTF. - feature_transcript_id = self.seed_interval.attributes.get( 'transcript_id', None ) + feature_transcript_id = self.seed_interval.attributes.get('transcript_id', None) # Read all intervals associated with seed. feature_intervals = [] - feature_intervals.append( self.seed_interval ) + feature_intervals.append(self.seed_interval) while True: try: - interval = GenomicIntervalReader.next( self ) - raw_size += len( self.current_line ) + interval = GenomicIntervalReader.next(self) + raw_size += len(self.current_line) except StopIteration as e: # No more intervals to read, but last feature needs to be # returned. interval = None - raw_size += len( self.current_line ) + raw_size += len(self.current_line) break except ParseError as e: - handle_parse_error( e ) - raw_size += len( self.current_line ) + handle_parse_error(e) + raw_size += len(self.current_line) continue # TODO: When no longer supporting python 2.4 use finally: # finally: # raw_size += len( self.current_line ) # Ignore comments. - if isinstance( interval, Comment ): + if isinstance(interval, Comment): continue # Determine if interval is part of feature. part_of = False - group = interval.attributes.get( 'group', None ) + group = interval.attributes.get('group', None) # GFF test: if group and feature_group == group: part_of = True # GFF3 test: - parent_id = interval.attributes.get( 'Parent', None ) - cur_id = interval.attributes.get( 'ID', None ) - if ( cur_id and cur_id == feature_id ) or ( parent_id and parent_id == feature_id ): + parent_id = interval.attributes.get('Parent', None) + cur_id = interval.attributes.get('ID', None) + if (cur_id and cur_id == feature_id) or (parent_id and parent_id == feature_id): part_of = True # GTF test: - transcript_id = interval.attributes.get( 'transcript_id', None ) + transcript_id = interval.attributes.get('transcript_id', None) if transcript_id and transcript_id == feature_transcript_id: part_of = True # If interval is not part of feature, clean up and break. if not part_of: # Adjust raw size because current line is not part of feature. - raw_size -= len( self.current_line ) + raw_size -= len(self.current_line) break # Interval associated with feature. - feature_intervals.append( interval ) + feature_intervals.append(interval) # Last interval read is the seed for the next interval. self.seed_interval = interval - self.seed_interval_line_len = len( self.current_line ) + self.seed_interval_line_len = len(self.current_line) # Return feature. - feature = GFFFeature( self, self.chrom_col, self.feature_col, self.start_col, - self.end_col, self.strand_col, self.score_col, - self.default_strand, fix_strand=self.fix_strand, - intervals=feature_intervals, raw_size=raw_size ) + feature = GFFFeature(self, self.chrom_col, self.feature_col, self.start_col, + self.end_col, self.strand_col, self.score_col, + self.default_strand, fix_strand=self.fix_strand, + intervals=feature_intervals, raw_size=raw_size) # Convert to BED coords? if self.convert_to_bed_coord: - convert_gff_coords_to_bed( feature ) + convert_gff_coords_to_bed(feature) return feature -def convert_bed_coords_to_gff( interval ): +def convert_bed_coords_to_gff(interval): """ Converts an interval object's coordinates from BED format to GFF format. Accepted object types include GenomicInterval and list (where the first element in the list is the interval's start, and the second element is the interval's end). """ - if isinstance( interval, GenomicInterval ): + if isinstance(interval, GenomicInterval): interval.start += 1 - if isinstance( interval, GFFFeature ): + if isinstance(interval, GFFFeature): for subinterval in interval.intervals: - convert_bed_coords_to_gff( subinterval ) + convert_bed_coords_to_gff(subinterval) elif isinstance(interval, list): - interval[ 0 ] += 1 + interval[0] += 1 return interval -def convert_gff_coords_to_bed( interval ): +def convert_gff_coords_to_bed(interval): """ Converts an interval object's coordinates from GFF format to BED format. Accepted object types include GFFFeature, GenomicInterval, and list (where the first element in the list is the interval's start, and the second element is the interval's end). """ - if isinstance( interval, GenomicInterval ): + if isinstance(interval, GenomicInterval): interval.start -= 1 - if isinstance( interval, GFFFeature ): + if isinstance(interval, GFFFeature): for subinterval in interval.intervals: - convert_gff_coords_to_bed( subinterval ) + convert_gff_coords_to_bed(subinterval) elif isinstance(interval, list): - interval[ 0 ] -= 1 + interval[0] -= 1 return interval -def parse_gff_attributes( attr_str ): +def parse_gff_attributes(attr_str): """ Parses a GFF/GTF attribute string and returns a dictionary of name-value pairs. The general format for a GFF3 attributes string is @@ -327,9 +329,9 @@ def parse_gff_attributes( attr_str ): # Try splitting by '=' (GFF3) first because spaces are allowed in GFF3 # attribute; next, try double quotes for GTF. pair = name_value_pair.strip().split("=") - if len( pair ) == 1: + if len(pair) == 1: pair = name_value_pair.strip().split("\"") - if len( pair ) == 1: + if len(pair) == 1: # Could not split for some reason -- raise exception? continue if pair == '': @@ -339,16 +341,16 @@ def parse_gff_attributes( attr_str ): continue # Need to strip double quote from values value = pair[1].strip(" \"") - attributes[ name ] = value + attributes[name] = value - if len( attributes ) == 0: + if len(attributes) == 0: # Could not split attributes string, so entire string must be # 'group' attribute. This is the case for strictly GFF files. attributes['group'] = attr_str return attributes -def parse_gff3_attributes( attr_str ): +def parse_gff3_attributes(attr_str): """ Parses a GFF3 attribute string and returns a dictionary of name-value pairs. The general format for a GFF3 attributes string is @@ -371,7 +373,7 @@ def parse_gff3_attributes( attr_str ): return attributes -def gff_attributes_to_str( attrs, gff_format ): +def gff_attributes_to_str(attrs, gff_format): """ Convert GFF attributes to string. Supported formats are GFF3, GTF. """ @@ -391,11 +393,11 @@ def gff_attributes_to_str( attrs, gff_format ): format_string = '%s=%s' attrs_strs = [] for name, value in attrs.items(): - attrs_strs.append( format_string % ( name, value ) ) - return " ; ".join( attrs_strs ) + attrs_strs.append(format_string % (name, value)) + return " ; ".join(attrs_strs) -def read_unordered_gtf( iterator, strict=False ): +def read_unordered_gtf(iterator, strict=False): """ Returns GTF features found in an iterator. GTF lines need not be ordered or clustered for reader to work. Reader returns GFFFeature objects sorted @@ -404,7 +406,7 @@ def read_unordered_gtf( iterator, strict=False ): # -- Get function that generates line/feature key. -- def get_transcript_id(fields): - return parse_gff_attributes( fields[8] )[ 'transcript_id' ] + return parse_gff_attributes(fields[8])['transcript_id'] if strict: # Strict GTF parsing uses transcript_id only to group lines into feature. key_fn = get_transcript_id @@ -413,33 +415,33 @@ def get_transcript_id(fields): # transcripts with same ID on different chromosomes; this occurs in some popular # datasources, such as RefGenes in UCSC. def key_fn(fields): - return fields[0] + '_' + get_transcript_id( fields ) + return fields[0] + '_' + get_transcript_id(fields) # Aggregate intervals by transcript_id and collect comments. feature_intervals = odict() comments = [] - for count, line in enumerate( iterator ): - if line.startswith( '#' ): - comments.append( Comment( line ) ) + for count, line in enumerate(iterator): + if line.startswith('#'): + comments.append(Comment(line)) continue - line_key = key_fn( line.split('\t') ) + line_key = key_fn(line.split('\t')) if line_key in feature_intervals: - feature = feature_intervals[ line_key ] + feature = feature_intervals[line_key] else: feature = [] - feature_intervals[ line_key ] = feature - feature.append( GFFInterval( None, line.split( '\t' ) ) ) + feature_intervals[line_key] = feature + feature.append(GFFInterval(None, line.split('\t'))) # Create features. chroms_features = {} - for count, intervals in enumerate( feature_intervals.values() ): + for count, intervals in enumerate(feature_intervals.values()): # Sort intervals by start position. intervals.sort(key=lambda _: _.start) - feature = GFFFeature( None, intervals=intervals ) + feature = GFFFeature(None, intervals=intervals) if feature.chrom not in chroms_features: - chroms_features[ feature.chrom ] = [] - chroms_features[ feature.chrom ].append( feature ) + chroms_features[feature.chrom] = [] + chroms_features[feature.chrom].append(feature) # Sort features by chrom, start position. chroms_features_sorted = sorted(chroms_features.values(), key=lambda _: _[0].chrom) diff --git a/lib/galaxy/datatypes/xml.py b/lib/galaxy/datatypes/xml.py index 4a1c22b949df..aba2272c2a7e 100644 --- a/lib/galaxy/datatypes/xml.py +++ b/lib/galaxy/datatypes/xml.py @@ -13,21 +13,21 @@ @dataproviders.decorators.has_dataproviders -class GenericXml( data.Text ): +class GenericXml(data.Text): """Base format class for any XML file.""" edam_format = "format_2332" file_ext = "xml" - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): """Set the peek and blurb text""" if not dataset.dataset.purged: - dataset.peek = data.get_file_peek( dataset.file_name, is_multi_byte=is_multi_byte ) + dataset.peek = data.get_file_peek(dataset.file_name, is_multi_byte=is_multi_byte) dataset.blurb = 'XML data' else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disk' - def sniff( self, filename ): + def sniff(self, filename): """ Determines whether the file is XML or not @@ -55,81 +55,81 @@ def merge(split_files, output_file): data.Text.merge(split_files, output_file) merge = staticmethod(merge) - @dataproviders.decorators.dataprovider_factory( 'xml', dataproviders.hierarchy.XMLDataProvider.settings ) - def xml_dataprovider( self, dataset, **settings ): - dataset_source = dataproviders.dataset.DatasetDataProvider( dataset ) - return dataproviders.hierarchy.XMLDataProvider( dataset_source, **settings ) + @dataproviders.decorators.dataprovider_factory('xml', dataproviders.hierarchy.XMLDataProvider.settings) + def xml_dataprovider(self, dataset, **settings): + dataset_source = dataproviders.dataset.DatasetDataProvider(dataset) + return dataproviders.hierarchy.XMLDataProvider(dataset_source, **settings) -class MEMEXml( GenericXml ): +class MEMEXml(GenericXml): """MEME XML Output data""" file_ext = "memexml" - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): """Set the peek and blurb text""" if not dataset.dataset.purged: - dataset.peek = data.get_file_peek( dataset.file_name, is_multi_byte=is_multi_byte ) + dataset.peek = data.get_file_peek(dataset.file_name, is_multi_byte=is_multi_byte) dataset.blurb = 'MEME XML data' else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disk' - def sniff( self, filename ): + def sniff(self, filename): return False -class CisML( GenericXml ): +class CisML(GenericXml): """CisML XML data""" # see: http://www.ncbi.nlm.nih.gov/pubmed/15001475 file_ext = "cisml" - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): """Set the peek and blurb text""" if not dataset.dataset.purged: - dataset.peek = data.get_file_peek( dataset.file_name, is_multi_byte=is_multi_byte ) + dataset.peek = data.get_file_peek(dataset.file_name, is_multi_byte=is_multi_byte) dataset.blurb = 'CisML data' else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disk' - def sniff( self, filename ): + def sniff(self, filename): return False -class Phyloxml( GenericXml ): +class Phyloxml(GenericXml): """Format for defining phyloxml data http://www.phyloxml.org/""" edam_data = "data_0872" edam_format = "format_3159" file_ext = "phyloxml" - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): """Set the peek and blurb text""" if not dataset.dataset.purged: - dataset.peek = data.get_file_peek( dataset.file_name, is_multi_byte=is_multi_byte ) + dataset.peek = data.get_file_peek(dataset.file_name, is_multi_byte=is_multi_byte) dataset.blurb = 'Phyloxml data' else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disk' - def sniff( self, filename ): + def sniff(self, filename): """"Checking for keyword - 'phyloxml' always in lowercase in the first few lines""" - f = open( filename, "r" ) - firstlines = "".join( f.readlines(5) ) + f = open(filename, "r") + firstlines = "".join(f.readlines(5)) f.close() if "phyloxml" in firstlines: return True return False - def get_visualizations( self, dataset ): + def get_visualizations(self, dataset): """ Returns a list of visualizations for datatype. """ - return [ 'phyloviz' ] + return ['phyloviz'] -class Owl( GenericXml ): +class Owl(GenericXml): """ Web Ontology Language OWL format description http://www.w3.org/TR/owl-ref/ @@ -137,23 +137,23 @@ class Owl( GenericXml ): edam_format = "format_3262" file_ext = "owl" - def set_peek( self, dataset, is_multi_byte=False ): + def set_peek(self, dataset, is_multi_byte=False): if not dataset.dataset.purged: - dataset.peek = data.get_file_peek( dataset.file_name, is_multi_byte=is_multi_byte ) + dataset.peek = data.get_file_peek(dataset.file_name, is_multi_byte=is_multi_byte) dataset.blurb = "Web Ontology Language OWL" else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disc' - def sniff( self, filename ): + def sniff(self, filename): """ Checking for keyword - ' tags in to a dictionary suitable for persistence. @@ -470,13 +471,13 @@ def __get_envs(self, parent): """ rval = [] for param in parent.findall('env'): - rval.append( dict( + rval.append(dict( name=param.get('id'), file=param.get('file'), execute=param.get('exec'), value=param.text, raw=util.asbool(param.get('raw', 'false')) - ) ) + )) return rval def __get_resubmits(self, parent): @@ -489,12 +490,12 @@ def __get_resubmits(self, parent): """ rval = [] for resubmit in parent.findall('resubmit'): - rval.append( dict( + rval.append(dict( condition=resubmit.get('condition'), destination=resubmit.get('destination'), handler=resubmit.get('handler'), delay=resubmit.get('delay'), - ) ) + )) return rval def __is_enabled(self, params): @@ -591,11 +592,11 @@ def get_job_runner_plugins(self, handler_id): """ rval = {} if handler_id in self.handler_runner_plugins: - plugins_to_load = [ rp for rp in self.runner_plugins if rp['id'] in self.handler_runner_plugins[handler_id] ] - log.info( "Handler '%s' will load specified runner plugins: %s", handler_id, ', '.join( [ rp['id'] for rp in plugins_to_load ] ) ) + plugins_to_load = [rp for rp in self.runner_plugins if rp['id'] in self.handler_runner_plugins[handler_id]] + log.info("Handler '%s' will load specified runner plugins: %s", handler_id, ', '.join([rp['id'] for rp in plugins_to_load])) else: plugins_to_load = self.runner_plugins - log.info( "Handler '%s' will load all configured runner plugins", handler_id ) + log.info("Handler '%s' will load all configured runner plugins", handler_id) for runner in plugins_to_load: class_names = [] module = None @@ -604,38 +605,38 @@ def get_job_runner_plugins(self, handler_id): if ':' in load: # Name to load was specified as ':' module_name, class_name = load.rsplit(':', 1) - class_names = [ class_name ] - module = __import__( module_name ) + class_names = [class_name] + module = __import__(module_name) else: # Name to load was specified as '' if '.' not in load: # For legacy reasons, try from galaxy.jobs.runners first if there's no '.' in the name module_name = 'galaxy.jobs.runners.' + load try: - module = __import__( module_name ) + module = __import__(module_name) except ImportError: # No such module, we'll retry without prepending galaxy.jobs.runners. # All other exceptions (e.g. something wrong with the module code) will raise pass if module is None: # If the name included a '.' or loading from the static runners path failed, try the original name - module = __import__( load ) + module = __import__(load) module_name = load if module is None: # Module couldn't be loaded, error should have already been displayed continue - for comp in module_name.split( "." )[1:]: - module = getattr( module, comp ) + for comp in module_name.split(".")[1:]: + module = getattr(module, comp) if not class_names: # If there's not a ':', we check .__all__ for class names try: assert module.__all__ class_names = module.__all__ except AssertionError: - log.error( 'Runner "%s" does not contain a list of exported classes in __all__' % load ) + log.error('Runner "%s" does not contain a list of exported classes in __all__' % load) continue for class_name in class_names: - runner_class = getattr( module, class_name ) + runner_class = getattr(module, class_name) try: assert issubclass(runner_class, BaseJobRunner) except TypeError: @@ -645,12 +646,12 @@ def get_job_runner_plugins(self, handler_id): log.warning("Job runner classes must be subclassed from BaseJobRunner, %s has bases: %s" % (id, runner_class.__bases__)) continue try: - rval[id] = runner_class( self.app, runner[ 'workers' ], **runner.get( 'kwds', {} ) ) + rval[id] = runner_class(self.app, runner['workers'], **runner.get('kwds', {})) except TypeError: - log.exception( "Job runner '%s:%s' has not been converted to a new-style runner or encountered TypeError on load", - module_name, class_name ) - rval[id] = runner_class( self.app ) - log.debug( "Loaded job runner '%s:%s' as '%s'" % ( module_name, class_name, id ) ) + log.exception("Job runner '%s:%s' has not been converted to a new-style runner or encountered TypeError on load", + module_name, class_name) + rval[id] = runner_class(self.app) + log.debug("Loaded job runner '%s:%s' as '%s'" % (module_name, class_name, id)) return rval def is_id(self, collection): @@ -679,7 +680,7 @@ def convert_legacy_destinations(self, job_runners): :param job_runners: All loaded job runner plugins. :type job_runners: list of job runner plugins """ - for id, destination in [ ( id, destinations[0] ) for id, destinations in self.destinations.items() if self.is_id(destinations) ]: + for id, destination in [(id, destinations[0]) for id, destinations in self.destinations.items() if self.is_id(destinations)]: # Only need to deal with real destinations, not members of tags if destination.legacy and not destination.converted: if destination.runner in job_runners: @@ -697,7 +698,7 @@ def convert_legacy_destinations(self, job_runners): class HasResourceParameters: - def get_resource_parameters( self, job=None ): + def get_resource_parameters(self, job=None): # Find the dymically inserted resource parameters and give them # to rule. @@ -705,29 +706,30 @@ def get_resource_parameters( self, job=None ): job = self.get_job() app = self.app - param_values = job.get_param_values( app, ignore_errors=True ) + param_values = job.get_param_values(app, ignore_errors=True) resource_params = {} try: - resource_params_raw = param_values[ "__job_resource" ] - if resource_params_raw[ "__job_resource__select" ].lower() in [ "1", "yes", "true" ]: + resource_params_raw = param_values["__job_resource"] + if resource_params_raw["__job_resource__select"].lower() in ["1", "yes", "true"]: for key, value in resource_params_raw.items(): - resource_params[ key ] = value + resource_params[key] = value except KeyError: pass return resource_params -class JobWrapper( object, HasResourceParameters ): +class JobWrapper(object, HasResourceParameters): """ Wraps a 'model.Job' with convenience methods for running processes and state management. """ - def __init__( self, job, queue, use_persisted_destination=False ): + + def __init__(self, job, queue, use_persisted_destination=False): self.job_id = job.id self.session_id = job.session_id self.user_id = job.user_id - self.tool = queue.app.toolbox.get_tool( job.tool_id, job.tool_version, exact=True ) + self.tool = queue.app.toolbox.get_tool(job.tool_id, job.tool_version, exact=True) self.queue = queue self.app = queue.app self.sa_session = self.app.model.context @@ -743,29 +745,29 @@ def __init__( self, job, queue, use_persisted_destination=False ): # and job recovery fail. # Create the working dir if necessary self._create_working_directory() - self.dataset_path_rewriter = self._job_dataset_path_rewriter( self.working_directory ) + self.dataset_path_rewriter = self._job_dataset_path_rewriter(self.working_directory) self.output_paths = None self.output_hdas_and_paths = None self.tool_provided_job_metadata = None # Wrapper holding the info required to restore and clean up from files used for setting metadata externally - self.external_output_metadata = metadata.JobExternalOutputMetadataWrapper( job ) - self.job_runner_mapper = JobRunnerMapper( self, queue.dispatcher.url_to_destination, self.app.job_config ) + self.external_output_metadata = metadata.JobExternalOutputMetadataWrapper(job) + self.job_runner_mapper = JobRunnerMapper(self, queue.dispatcher.url_to_destination, self.app.job_config) self.params = None if job.params: - self.params = loads( job.params ) + self.params = loads(job.params) if use_persisted_destination: - self.job_runner_mapper.cached_job_destination = JobDestination( from_job=job ) + self.job_runner_mapper.cached_job_destination = JobDestination(from_job=job) self.__commands_in_new_shell = True self.__user_system_pwent = None self.__galaxy_system_pwent = None - def _job_dataset_path_rewriter( self, working_directory ): + def _job_dataset_path_rewriter(self, working_directory): outputs_to_working_directory = util.asbool(self.get_destination_configuration("outputs_to_working_directory", False)) if outputs_to_working_directory: - dataset_path_rewriter = OutputsToWorkingDirectoryPathRewriter( working_directory ) + dataset_path_rewriter = OutputsToWorkingDirectoryPathRewriter(working_directory) else: - dataset_path_rewriter = NullDatasetPathRewriter( ) + dataset_path_rewriter = NullDatasetPathRewriter() return dataset_path_rewriter @property @@ -778,11 +780,11 @@ def cleanup_job(self): def requires_containerization(self): return util.asbool(self.get_destination_configuration("require_container", "False")) - def can_split( self ): + def can_split(self): # Should the job handler split this job up? return self.app.config.use_tasked_jobs and self.tool.parallelism - def get_job_runner_url( self ): + def get_job_runner_url(self): log.warning('(%s) Job runner URLs are deprecated, use destinations instead.' % self.job_id) return self.job_destination.url @@ -810,7 +812,7 @@ def commands_in_new_shell(self): @property def galaxy_lib_dir(self): if self.__galaxy_lib_dir is None: - self.__galaxy_lib_dir = os.path.abspath( "lib" ) # cwd = galaxy root + self.__galaxy_lib_dir = os.path.abspath("lib") # cwd = galaxy root return self.__galaxy_lib_dir @property @@ -834,46 +836,46 @@ def job_destination(self): """ return self.job_runner_mapper.get_job_destination(self.params) - def get_job( self ): - return self.sa_session.query( model.Job ).get( self.job_id ) + def get_job(self): + return self.sa_session.query(model.Job).get(self.job_id) def get_id_tag(self): # For compatability with drmaa, which uses job_id right now, and TaskWrapper return self.get_job().get_id_tag() - def get_param_dict( self ): + def get_param_dict(self): """ Restore the dictionary of parameters from the database. """ job = self.get_job() - param_dict = dict( [ ( p.name, p.value ) for p in job.parameters ] ) - param_dict = self.tool.params_from_strings( param_dict, self.app ) + param_dict = dict([(p.name, p.value) for p in job.parameters]) + param_dict = self.tool.params_from_strings(param_dict, self.app) return param_dict - def get_version_string_path( self ): + def get_version_string_path(self): return os.path.abspath(os.path.join(self.app.config.new_file_path, "GALAXY_VERSION_STRING_%s" % self.job_id)) - def prepare( self, compute_environment=None ): + def prepare(self, compute_environment=None): """ Prepare the job to run by creating the working directory and the config files. """ self.sa_session.expunge_all() # this prevents the metadata reverting that has been seen in conjunction with the PBS job runner - if not os.path.exists( self.working_directory ): - os.mkdir( self.working_directory ) + if not os.path.exists(self.working_directory): + os.mkdir(self.working_directory) job = self._load_job() - def get_special( ): - special = self.sa_session.query( model.JobExportHistoryArchive ).filter_by( job=job ).first() + def get_special(): + special = self.sa_session.query(model.JobExportHistoryArchive).filter_by(job=job).first() if not special: - special = self.sa_session.query( model.GenomeIndexToolData ).filter_by( job=job ).first() + special = self.sa_session.query(model.GenomeIndexToolData).filter_by(job=job).first() return special - tool_evaluator = self._get_tool_evaluator( job ) - compute_environment = compute_environment or self.default_compute_environment( job ) - tool_evaluator.set_compute_environment( compute_environment, get_special=get_special ) + tool_evaluator = self._get_tool_evaluator(job) + compute_environment = compute_environment or self.default_compute_environment(job) + tool_evaluator.set_compute_environment(compute_environment, get_special=get_special) self.sa_session.flush() @@ -886,73 +888,73 @@ def get_special( ): # if the server was stopped and restarted before the job finished job.command_line = unicodify(self.command_line) job.dependencies = self.tool.dependencies - self.sa_session.add( job ) + self.sa_session.add(job) self.sa_session.flush() # Return list of all extra files self.param_dict = tool_evaluator.param_dict version_string_cmd_raw = self.tool.version_string_cmd if version_string_cmd_raw: version_command_template = string.Template(version_string_cmd_raw) - version_string_cmd = version_command_template.safe_substitute({"__tool_directory__": compute_environment.tool_directory() }) - self.write_version_cmd = "%s > %s 2>&1" % ( version_string_cmd, compute_environment.version_path() ) + version_string_cmd = version_command_template.safe_substitute({"__tool_directory__": compute_environment.tool_directory()}) + self.write_version_cmd = "%s > %s 2>&1" % (version_string_cmd, compute_environment.version_path()) else: self.write_version_cmd = None return self.extra_filenames - def _create_working_directory( self ): + def _create_working_directory(self): job = self.get_job() try: self.app.object_store.create( - job, base_dir='job_work', dir_only=True, obj_dir=True ) + job, base_dir='job_work', dir_only=True, obj_dir=True) self.working_directory = self.app.object_store.get_filename( - job, base_dir='job_work', dir_only=True, obj_dir=True ) + job, base_dir='job_work', dir_only=True, obj_dir=True) # The tool execution is given a working directory beneath the # "job" working directory. self.tool_working_directory = os.path.join(self.working_directory, "working") safe_makedirs(self.tool_working_directory) - log.debug( '(%s) Working directory for job is: %s', - self.job_id, self.working_directory ) + log.debug('(%s) Working directory for job is: %s', + self.job_id, self.working_directory) except ObjectInvalid: - raise Exception( '(%s) Unable to create job working directory', - job.id ) + raise Exception('(%s) Unable to create job working directory', + job.id) - def clear_working_directory( self ): + def clear_working_directory(self): job = self.get_job() - if not os.path.exists( self.working_directory ): - log.warning( '(%s): Working directory clear requested but %s does ' - 'not exist', - self.job_id, - self.working_directory ) + if not os.path.exists(self.working_directory): + log.warning('(%s): Working directory clear requested but %s does ' + 'not exist', + self.job_id, + self.working_directory) return self.app.object_store.create( job, base_dir='job_work', dir_only=True, obj_dir=True, - extra_dir='_cleared_contents', extra_dir_at_root=True ) + extra_dir='_cleared_contents', extra_dir_at_root=True) base = self.app.object_store.get_filename( job, base_dir='job_work', dir_only=True, obj_dir=True, - extra_dir='_cleared_contents', extra_dir_at_root=True ) - date_str = datetime.datetime.now().strftime( '%Y%m%d-%H%M%S' ) - arc_dir = os.path.join( base, date_str ) - shutil.move( self.working_directory, arc_dir ) + extra_dir='_cleared_contents', extra_dir_at_root=True) + date_str = datetime.datetime.now().strftime('%Y%m%d-%H%M%S') + arc_dir = os.path.join(base, date_str) + shutil.move(self.working_directory, arc_dir) self._create_working_directory() - log.debug( '(%s) Previous working directory moved to %s', - self.job_id, arc_dir ) + log.debug('(%s) Previous working directory moved to %s', + self.job_id, arc_dir) - def default_compute_environment( self, job=None ): + def default_compute_environment(self, job=None): if not job: job = self.get_job() - return SharedComputeEnvironment( self, job ) + return SharedComputeEnvironment(self, job) - def _load_job( self ): + def _load_job(self): # Load job from database and verify it has user or session. # Restore parameters from the database job = self.get_job() if job.user is None and job.galaxy_session is None: - raise Exception( 'Job %s has no user and no session.' % job.id ) + raise Exception('Job %s has no user and no session.' % job.id) return job - def _get_tool_evaluator( self, job ): + def _get_tool_evaluator(self, job): # Hacky way to avoid cirular import for now. # Placing ToolEvaluator in either jobs or tools # result in ciruclar dependency. @@ -966,13 +968,13 @@ def _get_tool_evaluator( self, job ): ) return tool_evaluator - def fail( self, message, exception=False, stdout="", stderr="", exit_code=None ): + def fail(self, message, exception=False, stdout="", stderr="", exit_code=None): """ Indicate job failure by setting state and message on all output datasets. """ job = self.get_job() - self.sa_session.refresh( job ) + self.sa_session.refresh(job) # if the job was deleted, don't fail it if not job.state == job.states.DELETED: # Check if the failure is due to an exception @@ -988,13 +990,13 @@ def fail( self, message, exception=False, stdout="", stderr="", exit_code=None ) if outputs_to_working_directory: for dataset_path in self.get_output_fnames(): try: - shutil.move( dataset_path.false_path, dataset_path.real_path ) - log.debug( "fail(): Moved %s to %s" % ( dataset_path.false_path, dataset_path.real_path ) ) - except ( IOError, OSError ) as e: - log.error( "fail(): Missing output file in working directory: %s" % e ) + shutil.move(dataset_path.false_path, dataset_path.real_path) + log.debug("fail(): Moved %s to %s" % (dataset_path.false_path, dataset_path.real_path)) + except (IOError, OSError) as e: + log.error("fail(): Missing output file in working directory: %s" % e) for dataset_assoc in job.output_datasets + job.output_library_datasets: dataset = dataset_assoc.dataset - self.sa_session.refresh( dataset ) + self.sa_session.refresh(dataset) dataset.state = dataset.states.ERROR dataset.blurb = 'tool error' dataset.info = message @@ -1006,20 +1008,20 @@ def fail( self, message, exception=False, stdout="", stderr="", exit_code=None ) self.__update_output(job, dataset) # Pause any dependent jobs (and those jobs' outputs) for dep_job_assoc in dataset.dependent_jobs: - self.pause( dep_job_assoc.job, "Execution of this dataset's job is paused because its input datasets are in an error state." ) - self.sa_session.add( dataset ) + self.pause(dep_job_assoc.job, "Execution of this dataset's job is paused because its input datasets are in an error state.") + self.sa_session.add(dataset) self.sa_session.flush() - job.set_final_state( job.states.ERROR ) + job.set_final_state(job.states.ERROR) job.command_line = unicodify(self.command_line) job.info = message # TODO: Put setting the stdout, stderr, and exit code in one place # (not duplicated with the finish method). - job.set_streams( stdout, stderr ) + job.set_streams(stdout, stderr) # Let the exit code be Null if one is not provided: - if ( exit_code is not None ): + if (exit_code is not None): job.exit_code = exit_code - self.sa_session.add( job ) + self.sa_session.add(job) self.sa_session.flush() else: for dataset_assoc in job.output_datasets: @@ -1028,18 +1030,18 @@ def fail( self, message, exception=False, stdout="", stderr="", exit_code=None ) # the partial files to the object store regardless of whether job.state == DELETED self.__update_output(job, dataset, clean_only=True) - self._report_error_to_sentry() + self._report_error() # Perform email action even on failure. for pja in [pjaa.post_job_action for pjaa in job.post_job_actions if pjaa.post_job_action.action_type == "EmailAction"]: ActionBox.execute(self.app, self.sa_session, pja, job) # If the job was deleted, call tool specific fail actions (used for e.g. external metadata) and clean up if self.tool: - self.tool.job_failed( self, message, exception ) + self.tool.job_failed(self, message, exception) cleanup_job = self.cleanup_job delete_files = cleanup_job == 'always' or (cleanup_job == 'onsuccess' and job.state == job.states.DELETED) - self.cleanup( delete_files=delete_files ) + self.cleanup(delete_files=delete_files) - def pause( self, job=None, message=None ): + def pause(self, job=None, message=None): if job is None: job = self.get_job() if message is None: @@ -1048,11 +1050,11 @@ def pause( self, job=None, message=None ): for dataset_assoc in job.output_datasets + job.output_library_datasets: dataset_assoc.dataset.dataset.state = dataset_assoc.dataset.dataset.states.PAUSED dataset_assoc.dataset.info = message - self.sa_session.add( dataset_assoc.dataset ) - job.set_state( job.states.PAUSED ) - self.sa_session.add( job ) + self.sa_session.add(dataset_assoc.dataset) + job.set_state(job.states.PAUSED) + self.sa_session.add(job) - def is_ready_for_resubmission( self, job=None ): + def is_ready_for_resubmission(self, job=None): if job is None: job = self.get_job() @@ -1064,53 +1066,53 @@ def is_ready_for_resubmission( self, job=None ): return True - def mark_as_resubmitted( self, info=None ): + def mark_as_resubmitted(self, info=None): job = self.get_job() - self.sa_session.refresh( job ) + self.sa_session.refresh(job) if info is not None: job.info = info - job.set_state( model.Job.states.RESUBMITTED ) - self.sa_session.add( job ) + job.set_state(model.Job.states.RESUBMITTED) + self.sa_session.add(job) self.sa_session.flush() - def change_state( self, state, info=False, flush=True, job=None ): + def change_state(self, state, info=False, flush=True, job=None): job_supplied = job is not None if not job_supplied: job = self.get_job() - self.sa_session.refresh( job ) + self.sa_session.refresh(job) # Else: # If this is a new job (e.g. initially queued) - we are in the same # thread and no other threads are working on the job yet - so don't refresh. if job.state in model.Job.terminal_states: - log.warning( "(%s) Ignoring state change from '%s' to '%s' for job " - "that is already terminal", job.id, job.state, state ) + log.warning("(%s) Ignoring state change from '%s' to '%s' for job " + "that is already terminal", job.id, job.state, state) return for dataset_assoc in job.output_datasets + job.output_library_datasets: dataset = dataset_assoc.dataset if not job_supplied: - self.sa_session.refresh( dataset ) - dataset.raw_set_dataset_state( state ) + self.sa_session.refresh(dataset) + dataset.raw_set_dataset_state(state) if info: dataset.info = info - self.sa_session.add( dataset ) + self.sa_session.add(dataset) if info: job.info = info - job.set_state( state ) - self.sa_session.add( job ) + job.set_state(state) + self.sa_session.add(job) if flush: self.sa_session.flush() - def get_state( self ): + def get_state(self): job = self.get_job() - self.sa_session.refresh( job ) + self.sa_session.refresh(job) return job.state - def set_runner( self, runner_url, external_id ): + def set_runner(self, runner_url, external_id): log.warning('set_runner() is deprecated, use set_job_destination()') self.set_job_destination(self.job_destination, external_id) - def set_job_destination( self, job_destination, external_id=None, flush=True, job=None ): + def set_job_destination(self, job_destination, external_id=None, flush=True, job=None): """ Persist job destination params in the database for recovery. @@ -1161,8 +1163,8 @@ def finish( try: self.reclaim_ownership() except: - log.exception( '(%s) Failed to change ownership of %s, failing' % ( job.id, self.working_directory ) ) - return self.fail( job.info, stdout=stdout, stderr=stderr, exit_code=tool_exit_code ) + log.exception('(%s) Failed to change ownership of %s, failing' % (job.id, self.working_directory)) + return self.fail(job.info, stdout=stdout, stderr=stderr, exit_code=tool_exit_code) # if the job was deleted, don't finish it if job.state == job.states.DELETED or job.state == job.states.ERROR: @@ -1171,7 +1173,7 @@ def finish( # was deleted by an administrator (based on old comments), but it # could also mean that a job was broken up into tasks and one of # the tasks failed. So include the stderr, stdout, and exit code: - return self.fail( job.info, stderr=stderr, stdout=stdout, exit_code=tool_exit_code ) + return self.fail(job.info, stderr=stderr, stdout=stdout, exit_code=tool_exit_code) # Check the tool's stdout, stderr, and exit code for errors, but only # if the job has not already been marked as having an error. @@ -1179,7 +1181,7 @@ def finish( # We set final_job_state to use for dataset management, but *don't* set # job.state until after dataset collection to prevent history issues - if ( self.check_tool_output( stdout, stderr, tool_exit_code, job ) ): + if (self.check_tool_output(stdout, stderr, tool_exit_code, job)): final_job_state = job.states.OK else: final_job_state = job.states.ERROR @@ -1194,23 +1196,23 @@ def finish( if outputs_to_working_directory and not self.__link_file_check(): for dataset_path in self.get_output_fnames(): try: - shutil.move( dataset_path.false_path, dataset_path.real_path ) - log.debug( "finish(): Moved %s to %s" % ( dataset_path.false_path, dataset_path.real_path ) ) - except ( IOError, OSError ): + shutil.move(dataset_path.false_path, dataset_path.real_path) + log.debug("finish(): Moved %s to %s" % (dataset_path.false_path, dataset_path.real_path)) + except (IOError, OSError): # this can happen if Galaxy is restarted during the job's # finish method - the false_path file has already moved, # and when the job is recovered, it won't be found. - if os.path.exists( dataset_path.real_path ) and os.stat( dataset_path.real_path ).st_size > 0: - log.warning( "finish(): %s not found, but %s is not empty, so it will be used instead" - % ( dataset_path.false_path, dataset_path.real_path ) ) + if os.path.exists(dataset_path.real_path) and os.stat(dataset_path.real_path).st_size > 0: + log.warning("finish(): %s not found, but %s is not empty, so it will be used instead" + % (dataset_path.false_path, dataset_path.real_path)) else: # Prior to fail we need to set job.state - job.set_state( final_job_state ) - return self.fail( "Job %s's output dataset(s) could not be read" % job.id ) + job.set_state(final_job_state) + return self.fail("Job %s's output dataset(s) could not be read" % job.id) - job_context = ExpressionContext( dict( stdout=job.stdout, stderr=job.stderr ) ) + job_context = ExpressionContext(dict(stdout=job.stdout, stderr=job.stderr)) for dataset_assoc in job.output_datasets + job.output_library_datasets: - context = self.get_dataset_finish_context( job_context, dataset_assoc.dataset.dataset ) + context = self.get_dataset_finish_context(job_context, dataset_assoc) # should this also be checking library associations? - can a library item be added from a history before the job has ended? - # lets not allow this to occur # need to update all associated output hdas, i.e. history was shared with job running @@ -1221,14 +1223,14 @@ def finish( while trynum < self.app.config.retry_job_output_collection: try: # Attempt to short circuit NFS attribute caching - os.stat( dataset.dataset.file_name ) - os.chown( dataset.dataset.file_name, os.getuid(), -1 ) + os.stat(dataset.dataset.file_name) + os.chown(dataset.dataset.file_name, os.getuid(), -1) trynum = self.app.config.retry_job_output_collection - except ( OSError, ObjectNotFound ) as e: + except (OSError, ObjectNotFound) as e: trynum += 1 - log.warning( 'Error accessing %s, will retry: %s', dataset.dataset.file_name, e ) - time.sleep( 2 ) - if getattr( dataset, "hidden_beneath_collection_instance", None ): + log.warning('Error accessing %s, will retry: %s', dataset.dataset.file_name, e) + time.sleep(2) + if getattr(dataset, "hidden_beneath_collection_instance", None): dataset.visible = False dataset.blurb = 'done' dataset.peek = 'no peek' @@ -1253,35 +1255,35 @@ def finish( if dataset.datatype.composite_type == 'auto_primary_file' and not dataset.has_data(): try: with NamedTemporaryFile() as temp_fh: - temp_fh.write( dataset.datatype.generate_primary_file( dataset ) ) + temp_fh.write(dataset.datatype.generate_primary_file(dataset)) temp_fh.flush() - self.app.object_store.update_from_file( dataset.dataset, file_name=temp_fh.name, create=True ) + self.app.object_store.update_from_file(dataset.dataset, file_name=temp_fh.name, create=True) dataset.set_size() except Exception as e: - log.warning( 'Unable to generate primary composite file automatically for %s: %s', dataset.dataset.id, e ) + log.warning('Unable to generate primary composite file automatically for %s: %s', dataset.dataset.id, e) if job.states.ERROR == final_job_state: dataset.blurb = "error" dataset.mark_unhidden() elif not purged and dataset.has_data(): # If the tool was expected to set the extension, attempt to retrieve it if dataset.ext == 'auto': - dataset.extension = context.get( 'ext', 'data' ) - dataset.init_meta( copy_from=dataset ) + dataset.extension = context.get('ext', 'data') + dataset.init_meta(copy_from=dataset) # if a dataset was copied, it won't appear in our dictionary: # either use the metadata from originating output dataset, or call set_meta on the copies # it would be quicker to just copy the metadata from the originating output dataset, # but somewhat trickier (need to recurse up the copied_from tree), for now we'll call set_meta() retry_internally = util.asbool(self.get_destination_configuration("retry_metadata_internally", True)) - if retry_internally and not self.external_output_metadata.external_metadata_set_successfully(dataset, self.sa_session ): + if retry_internally and not self.external_output_metadata.external_metadata_set_successfully(dataset, self.sa_session): # If Galaxy was expected to sniff type and didn't - do so. if dataset.ext == "_sniff_": - extension = sniff.handle_uploaded_dataset_file( dataset.dataset.file_name, self.app.datatypes_registry ) + extension = sniff.handle_uploaded_dataset_file(dataset.dataset.file_name, self.app.datatypes_registry) dataset.extension = extension # call datatype.set_meta directly for the initial set_meta call during dataset creation - dataset.datatype.set_meta( dataset, overwrite=False ) - elif ( job.states.ERROR != final_job_state and - not self.external_output_metadata.external_metadata_set_successfully( dataset, self.sa_session ) ): + dataset.datatype.set_meta(dataset, overwrite=False) + elif (job.states.ERROR != final_job_state and + not self.external_output_metadata.external_metadata_set_successfully(dataset, self.sa_session)): dataset._state = model.Dataset.states.FAILED_METADATA else: # load metadata from file @@ -1289,30 +1291,30 @@ def finish( # since if it is edited, the metadata changed on the running output will no longer match # the metadata that was stored to disk for use via the external process, # and the changes made by the user will be lost, without warning or notice - output_filename = self.external_output_metadata.get_output_filenames_by_dataset( dataset, self.sa_session ).filename_out + output_filename = self.external_output_metadata.get_output_filenames_by_dataset(dataset, self.sa_session).filename_out - def path_rewriter( path ): + def path_rewriter(path): if not path: return path - normalized_remote_working_directory = remote_working_directory and os.path.normpath( remote_working_directory ) - normalized_remote_metadata_directory = remote_metadata_directory and os.path.normpath( remote_metadata_directory ) - normalized_path = os.path.normpath( path ) - if remote_working_directory and normalized_path.startswith( normalized_remote_working_directory ): - return normalized_path.replace( normalized_remote_working_directory, self.working_directory, 1 ) - if remote_metadata_directory and normalized_path.startswith( normalized_remote_metadata_directory ): - return normalized_path.replace( normalized_remote_metadata_directory, self.working_directory, 1 ) + normalized_remote_working_directory = remote_working_directory and os.path.normpath(remote_working_directory) + normalized_remote_metadata_directory = remote_metadata_directory and os.path.normpath(remote_metadata_directory) + normalized_path = os.path.normpath(path) + if remote_working_directory and normalized_path.startswith(normalized_remote_working_directory): + return normalized_path.replace(normalized_remote_working_directory, self.working_directory, 1) + if remote_metadata_directory and normalized_path.startswith(normalized_remote_metadata_directory): + return normalized_path.replace(normalized_remote_metadata_directory, self.working_directory, 1) return path - dataset.metadata.from_JSON_dict( output_filename, path_rewriter=path_rewriter ) + dataset.metadata.from_JSON_dict(output_filename, path_rewriter=path_rewriter) try: - assert context.get( 'line_count', None ) is not None - if ( not dataset.datatype.composite_type and dataset.dataset.is_multi_byte() ) or self.tool.is_multi_byte: - dataset.set_peek( line_count=context['line_count'], is_multi_byte=True ) + assert context.get('line_count', None) is not None + if (not dataset.datatype.composite_type and dataset.dataset.is_multi_byte()) or self.tool.is_multi_byte: + dataset.set_peek(line_count=context['line_count'], is_multi_byte=True) else: - dataset.set_peek( line_count=context['line_count'] ) + dataset.set_peek(line_count=context['line_count']) except: - if ( not dataset.datatype.composite_type and dataset.dataset.is_multi_byte() ) or self.tool.is_multi_byte: - dataset.set_peek( is_multi_byte=True ) + if (not dataset.datatype.composite_type and dataset.dataset.is_multi_byte()) or self.tool.is_multi_byte: + dataset.set_peek(is_multi_byte=True) else: dataset.set_peek() for context_key in ['name', 'info', 'dbkey']: @@ -1323,14 +1325,14 @@ def path_rewriter( path ): dataset.blurb = "empty" if dataset.ext == 'auto': dataset.extension = 'txt' - self.sa_session.add( dataset ) + self.sa_session.add(dataset) if job.states.ERROR == final_job_state: - log.debug( "(%s) setting dataset %s state to ERROR", job.id, dataset_assoc.dataset.dataset.id ) + log.debug("(%s) setting dataset %s state to ERROR", job.id, dataset_assoc.dataset.dataset.id) # TODO: This is where the state is being set to error. Change it! dataset_assoc.dataset.dataset.state = model.Dataset.states.ERROR # Pause any dependent jobs (and those jobs' outputs) for dep_job_assoc in dataset_assoc.dataset.dependent_jobs: - self.pause( dep_job_assoc.job, "Execution of this dataset's job is paused because its input datasets are in an error state." ) + self.pause(dep_job_assoc.job, "Execution of this dataset's job is paused because its input datasets are in an error state.") else: dataset_assoc.dataset.dataset.state = model.Dataset.states.OK # If any of the rest of the finish method below raises an @@ -1348,7 +1350,7 @@ def path_rewriter( path ): self.sa_session.flush() # Shrink streams and ensure unicode. - job.set_streams( job.stdout, job.stderr ) + job.set_streams(job.stdout, job.stderr) # The exit code will be null if there is no exit code to be set. # This is so that we don't assign an exit code, such as 0, that @@ -1356,14 +1358,14 @@ def path_rewriter( path ): if tool_exit_code is not None: job.exit_code = tool_exit_code # custom post process setup - inp_data = dict( [ ( da.name, da.dataset ) for da in job.input_datasets ] ) - out_data = dict( [ ( da.name, da.dataset ) for da in job.output_datasets ] ) - inp_data.update( [ ( da.name, da.dataset ) for da in job.input_library_datasets ] ) - out_data.update( [ ( da.name, da.dataset ) for da in job.output_library_datasets ] ) + inp_data = dict([(da.name, da.dataset) for da in job.input_datasets]) + out_data = dict([(da.name, da.dataset) for da in job.output_datasets]) + inp_data.update([(da.name, da.dataset) for da in job.input_library_datasets]) + out_data.update([(da.name, da.dataset) for da in job.output_library_datasets]) # TODO: eliminate overlap with tools/evaluation.py - out_collections = dict( [ ( obj.name, obj.dataset_collection_instance ) for obj in job.output_dataset_collection_instances ] ) - out_collections.update( [ ( obj.name, obj.dataset_collection ) for obj in job.output_dataset_collections ] ) + out_collections = dict([(obj.name, obj.dataset_collection_instance) for obj in job.output_dataset_collection_instances]) + out_collections.update([(obj.name, obj.dataset_collection) for obj in job.output_dataset_collections]) input_ext = 'data' input_dbkey = '?' @@ -1374,8 +1376,8 @@ def path_rewriter( path ): input_ext = data.ext input_dbkey = data.dbkey or '?' # why not re-use self.param_dict here? - param_dict = dict( [ ( p.name, p.value ) for p in job.parameters ] ) - param_dict = self.tool.params_from_strings( param_dict, self.app ) + param_dict = dict([(p.name, p.value) for p in job.parameters]) + param_dict = self.tool.params_from_strings(param_dict, self.app) # Create generated output children and primary datasets and add to param_dict tool_working_directory = self.tool_working_directory # LEGACY: Remove in 17.XX @@ -1384,10 +1386,11 @@ def path_rewriter( path ): tool_working_directory = self.working_directory collected_datasets = { 'children': self.tool.collect_child_datasets(out_data, tool_working_directory), - 'primary': self.tool.collect_primary_datasets(out_data, tool_working_directory, input_ext, input_dbkey) + 'primary': self.tool.collect_primary_datasets(out_data, self.get_tool_provided_job_metadata(), tool_working_directory, input_ext, input_dbkey) } self.tool.collect_dynamic_collections( out_collections, + self.get_tool_provided_job_metadata(), job_working_directory=tool_working_directory, inp_data=inp_data, job=job, @@ -1396,11 +1399,11 @@ def path_rewriter( path ): param_dict.update({'__collected_datasets__': collected_datasets}) # Certain tools require tasks to be completed after job execution # ( this used to be performed in the "exec_after_process" hook, but hooks are deprecated ). - self.tool.exec_after_process( self.queue.app, inp_data, out_data, param_dict, job=job ) + self.tool.exec_after_process(self.queue.app, inp_data, out_data, param_dict, job=job) # Call 'exec_after_process' hook - self.tool.call_hook( 'exec_after_process', self.queue.app, inp_data=inp_data, - out_data=out_data, param_dict=param_dict, - tool=self.tool, stdout=job.stdout, stderr=job.stderr ) + self.tool.call_hook('exec_after_process', self.queue.app, inp_data=inp_data, + out_data=out_data, param_dict=param_dict, + tool=self.tool, stdout=job.stdout, stderr=job.stderr) job.command_line = unicodify(self.command_line) collected_bytes = 0 @@ -1424,52 +1427,52 @@ def path_rewriter( path ): self.sa_session.flush() # fix permissions - for path in [ dp.real_path for dp in self.get_mutable_output_fnames() ]: + for path in [dp.real_path for dp in self.get_mutable_output_fnames()]: if os.path.exists(path): - util.umask_fix_perms( path, self.app.config.umask, 0o666, self.app.config.gid ) + util.umask_fix_perms(path, self.app.config.umask, 0o666, self.app.config.gid) # Finally set the job state. This should only happen *after* all # dataset creation, and will allow us to eliminate force_history_refresh. - job.set_final_state( final_job_state ) + job.set_final_state(final_job_state) if not job.tasks: # If job was composed of tasks, don't attempt to recollect statisitcs - self._collect_metrics( job ) + self._collect_metrics(job) self.sa_session.flush() - log.debug( 'job %d ended (finish() executed in %s)' % (self.job_id, finish_timer) ) + log.debug('job %d ended (finish() executed in %s)' % (self.job_id, finish_timer)) if job.state == job.states.ERROR: - self._report_error_to_sentry() + self._report_error() cleanup_job = self.cleanup_job - delete_files = cleanup_job == 'always' or ( job.state == job.states.OK and cleanup_job == 'onsuccess' ) - self.cleanup( delete_files=delete_files ) + delete_files = cleanup_job == 'always' or (job.state == job.states.OK and cleanup_job == 'onsuccess') + self.cleanup(delete_files=delete_files) - def check_tool_output( self, stdout, stderr, tool_exit_code, job ): - return check_output( self.tool, stdout, stderr, tool_exit_code, job ) + def check_tool_output(self, stdout, stderr, tool_exit_code, job): + return check_output(self.tool, stdout, stderr, tool_exit_code, job) - def cleanup( self, delete_files=True ): + def cleanup(self, delete_files=True): # At least one of these tool cleanup actions (job import), is needed # for thetool to work properly, that is why one might want to run # cleanup but not delete files. try: if delete_files: for fname in self.extra_filenames: - os.remove( fname ) - self.external_output_metadata.cleanup_external_metadata( self.sa_session ) - galaxy.tools.imp_exp.JobExportHistoryArchiveWrapper( self.job_id ).cleanup_after_job( self.sa_session ) - galaxy.tools.imp_exp.JobImportHistoryArchiveWrapper( self.app, self.job_id ).cleanup_after_job() + os.remove(fname) + self.external_output_metadata.cleanup_external_metadata(self.sa_session) + galaxy.tools.imp_exp.JobExportHistoryArchiveWrapper(self.job_id).cleanup_after_job(self.sa_session) + galaxy.tools.imp_exp.JobImportHistoryArchiveWrapper(self.app, self.job_id).cleanup_after_job() if delete_files: self.app.object_store.delete(self.get_job(), base_dir='job_work', entire_dir=True, dir_only=True, obj_dir=True) except: - log.exception( "Unable to cleanup job %d", self.job_id ) + log.exception("Unable to cleanup job %d", self.job_id) def _collect_extra_files(self, dataset, job_working_directory): - temp_file_path = os.path.join( job_working_directory, "dataset_%s_files" % ( dataset.id ) ) + temp_file_path = os.path.join(job_working_directory, "dataset_%s_files" % (dataset.id)) extra_dir = None try: # This skips creation of directories - object store # automatically creates them. However, empty directories will # not be created in the object store at all, which might be a # problem. - for root, dirs, files in os.walk( temp_file_path ): + for root, dirs, files in os.walk(temp_file_path): extra_dir = root.replace(job_working_directory, '', 1).lstrip(os.path.sep) for f in files: self.app.object_store.update_from_file( @@ -1481,71 +1484,71 @@ def _collect_extra_files(self, dataset, job_working_directory): preserve_symlinks=True ) except Exception as e: - log.debug( "Error in collect_associated_files: %s" % ( e ) ) + log.debug("Error in collect_associated_files: %s" % (e)) - def _collect_metrics( self, has_metrics ): + def _collect_metrics(self, has_metrics): job = has_metrics.get_job() - per_plugin_properties = self.app.job_metrics.collect_properties( job.destination_id, self.job_id, self.working_directory ) + per_plugin_properties = self.app.job_metrics.collect_properties(job.destination_id, self.job_id, self.working_directory) if per_plugin_properties: - log.info( "Collecting metrics for %s %s" % ( type(has_metrics).__name__, getattr( has_metrics, 'id', None ) ) ) + log.info("Collecting metrics for %s %s" % (type(has_metrics).__name__, getattr(has_metrics, 'id', None))) for plugin, properties in per_plugin_properties.items(): for metric_name, metric_value in properties.items(): if metric_value is not None: - has_metrics.add_metric( plugin, metric_name, metric_value ) + has_metrics.add_metric(plugin, metric_name, metric_value) - def get_output_sizes( self ): + def get_output_sizes(self): sizes = [] output_paths = self.get_output_fnames() - for outfile in [ str( o ) for o in output_paths ]: - if os.path.exists( outfile ): - sizes.append( ( outfile, os.stat( outfile ).st_size ) ) + for outfile in [unicodify(o) for o in output_paths]: + if os.path.exists(outfile): + sizes.append((outfile, os.stat(outfile).st_size)) else: - sizes.append( ( outfile, 0 ) ) + sizes.append((outfile, 0)) return sizes def check_limits(self, runtime=None): if self.app.job_config.limits.output_size > 0: for outfile, size in self.get_output_sizes(): if size > self.app.job_config.limits.output_size: - log.warning( '(%s) Job output size %s has exceeded the global output size limit', self.get_id_tag(), os.path.basename( outfile ) ) - return ( JobState.runner_states.OUTPUT_SIZE_LIMIT, - 'Job output file grew too large (greater than %s), please try different inputs or parameters' - % util.nice_size( self.app.job_config.limits.output_size ) ) + log.warning('(%s) Job output size %s has exceeded the global output size limit', self.get_id_tag(), os.path.basename(outfile)) + return (JobState.runner_states.OUTPUT_SIZE_LIMIT, + 'Job output file grew too large (greater than %s), please try different inputs or parameters' + % util.nice_size(self.app.job_config.limits.output_size)) if self.app.job_config.limits.walltime_delta is not None and runtime is not None: if runtime > self.app.job_config.limits.walltime_delta: - log.warning( '(%s) Job runtime %s has exceeded the global walltime, it will be terminated', self.get_id_tag(), runtime ) - return ( JobState.runner_states.GLOBAL_WALLTIME_REACHED, - 'Job ran longer than the maximum allowed execution time (runtime: %s, limit: %s), please try different inputs or parameters' - % ( str(runtime).split('.')[0], self.app.job_config.limits.walltime ) ) + log.warning('(%s) Job runtime %s has exceeded the global walltime, it will be terminated', self.get_id_tag(), runtime) + return (JobState.runner_states.GLOBAL_WALLTIME_REACHED, + 'Job ran longer than the maximum allowed execution time (runtime: %s, limit: %s), please try different inputs or parameters' + % (str(runtime).split('.')[0], self.app.job_config.limits.walltime)) return None - def has_limits( self ): + def has_limits(self): has_output_limit = self.app.job_config.limits.output_size > 0 has_walltime_limit = self.app.job_config.limits.walltime_delta is not None return has_output_limit or has_walltime_limit - def get_command_line( self ): + def get_command_line(self): return self.command_line - def get_session_id( self ): + def get_session_id(self): return self.session_id - def get_env_setup_clause( self ): + def get_env_setup_clause(self): if self.app.config.environment_setup_file is None: return '' - return '[ -f "%s" ] && . %s' % ( self.app.config.environment_setup_file, self.app.config.environment_setup_file ) + return '[ -f "%s" ] && . %s' % (self.app.config.environment_setup_file, self.app.config.environment_setup_file) - def get_input_dataset_fnames( self, ds ): + def get_input_dataset_fnames(self, ds): filenames = [] - filenames = [ ds.file_name ] + filenames = [ds.file_name] # we will need to stage in metadata file names also # TODO: would be better to only stage in metadata files that are actually needed (found in command line, referenced in config files, etc.) for key, value in ds.metadata.items(): - if isinstance( value, model.MetadataFile ): - filenames.append( value.file_name ) + if isinstance(value, model.MetadataFile): + filenames.append(value.file_name) return filenames - def get_input_fnames( self ): + def get_input_fnames(self): job = self.get_job() filenames = [] for da in job.input_datasets + job.input_library_datasets: # da is JobToInputDatasetAssociation object @@ -1553,7 +1556,7 @@ def get_input_fnames( self ): filenames.extend(self.get_input_dataset_fnames(da.dataset)) return filenames - def get_input_paths( self, job=None ): + def get_input_paths(self, job=None): if job is None: job = self.get_job() paths = [] @@ -1561,118 +1564,97 @@ def get_input_paths( self, job=None ): if da.dataset: filenames = self.get_input_dataset_fnames(da.dataset) for real_path in filenames: - false_path = self.dataset_path_rewriter.rewrite_dataset_path( da.dataset, 'input' ) - paths.append( DatasetPath( da.id, real_path=real_path, false_path=false_path, mutable=False ) ) + false_path = self.dataset_path_rewriter.rewrite_dataset_path(da.dataset, 'input') + paths.append(DatasetPath(da.id, real_path=real_path, false_path=false_path, mutable=False)) return paths - def get_output_fnames( self ): + def get_output_fnames(self): if self.output_paths is None: self.compute_outputs() return self.output_paths - def get_mutable_output_fnames( self ): + def get_mutable_output_fnames(self): if self.output_paths is None: self.compute_outputs() return [dsp for dsp in self.output_paths if dsp.mutable] - def get_output_hdas_and_fnames( self ): + def get_output_hdas_and_fnames(self): if self.output_hdas_and_paths is None: self.compute_outputs() return self.output_hdas_and_paths - def compute_outputs( self ): + def compute_outputs(self): dataset_path_rewriter = self.dataset_path_rewriter job = self.get_job() # Job output datasets are combination of history, library, and jeha datasets. - special = self.sa_session.query( model.JobExportHistoryArchive ).filter_by( job=job ).first() + special = self.sa_session.query(model.JobExportHistoryArchive).filter_by(job=job).first() false_path = None results = [] for da in job.output_datasets + job.output_library_datasets: - da_false_path = dataset_path_rewriter.rewrite_dataset_path( da.dataset, 'output' ) + da_false_path = dataset_path_rewriter.rewrite_dataset_path(da.dataset, 'output') mutable = da.dataset.dataset.external_filename is None - dataset_path = DatasetPath( da.dataset.dataset.id, da.dataset.file_name, false_path=da_false_path, mutable=mutable ) - results.append( ( da.name, da.dataset, dataset_path ) ) + dataset_path = DatasetPath(da.dataset.dataset.id, da.dataset.file_name, false_path=da_false_path, mutable=mutable) + results.append((da.name, da.dataset, dataset_path)) self.output_paths = [t[2] for t in results] self.output_hdas_and_paths = dict([(t[0], t[1:]) for t in results]) if special: - false_path = dataset_path_rewriter.rewrite_dataset_path( special.dataset, 'output' ) - dsp = DatasetPath( special.dataset.id, special.dataset.file_name, false_path ) - self.output_paths.append( dsp ) + false_path = dataset_path_rewriter.rewrite_dataset_path(special.dataset, 'output') + dsp = DatasetPath(special.dataset.id, special.dataset.file_name, false_path) + self.output_paths.append(dsp) return self.output_paths - def get_output_file_id( self, file ): + def get_output_file_id(self, file): if self.output_paths is None: self.get_output_fnames() for dp in self.output_paths: outputs_to_working_directory = util.asbool(self.get_destination_configuration("outputs_to_working_directory", False)) - if outputs_to_working_directory and os.path.basename( dp.false_path ) == file: + if outputs_to_working_directory and os.path.basename(dp.false_path) == file: return dp.dataset_id - elif os.path.basename( dp.real_path ) == file: + elif os.path.basename(dp.real_path) == file: return dp.dataset_id return None - def get_tool_provided_job_metadata( self ): + def get_tool_provided_job_metadata(self): if self.tool_provided_job_metadata is not None: return self.tool_provided_job_metadata - # Look for JSONified job metadata - self.tool_provided_job_metadata = [] - meta_file = os.path.join( self.tool_working_directory, TOOL_PROVIDED_JOB_METADATA_FILE ) - # LEGACY: Remove in 17.XX - if not os.path.exists( meta_file ): - # Maybe this is a legacy job, use the job working directory instead - meta_file = os.path.join( self.working_directory, TOOL_PROVIDED_JOB_METADATA_FILE ) - - if os.path.exists( meta_file ): - for line in open( meta_file, 'r' ): - try: - line = loads( line ) - assert 'type' in line - except: - log.exception( '(%s) Got JSON data from tool, but data is improperly formatted or no "type" key in data' % self.job_id ) - log.debug( 'Offending data was: %s' % line ) - continue - # Set the dataset id if it's a dataset entry and isn't set. - # This isn't insecure. We loop the job's output datasets in - # the finish method, so if a tool writes out metadata for a - # dataset id that it doesn't own, it'll just be ignored. - if line['type'] == 'dataset' and 'dataset_id' not in line: - try: - line['dataset_id'] = self.get_output_file_id( line['dataset'] ) - except KeyError: - log.warning( '(%s) Tool provided job dataset-specific metadata without specifying a dataset' % self.job_id ) - continue - self.tool_provided_job_metadata.append( line ) + self.tool_provided_job_metadata = self.tool.tool_provided_metadata(self) return self.tool_provided_job_metadata - def get_dataset_finish_context( self, job_context, dataset ): - for meta in self.get_tool_provided_job_metadata(): - if meta['type'] == 'dataset' and meta['dataset_id'] == dataset.id: - return ExpressionContext( meta, job_context ) + def get_dataset_finish_context(self, job_context, output_dataset_assoc): + meta = {} + tool_provided_metadata = self.get_tool_provided_job_metadata() + if hasattr(tool_provided_metadata, "get_meta_by_dataset_id"): + meta = tool_provided_metadata.get_meta_by_dataset_id(output_dataset_assoc.dataset.dataset.id) + elif hasattr(tool_provided_metadata, "get_meta_by_name"): + meta = tool_provided_metadata.get_meta_by_name(output_dataset_assoc.name) + + if meta: + return ExpressionContext(meta, job_context) return job_context - def invalidate_external_metadata( self ): + def invalidate_external_metadata(self): job = self.get_job() - self.external_output_metadata.invalidate_external_metadata( [ output_dataset_assoc.dataset for - output_dataset_assoc in - job.output_datasets + job.output_library_datasets ], - self.sa_session ) - - def setup_external_metadata( self, exec_dir=None, tmp_dir=None, - dataset_files_path=None, config_root=None, - config_file=None, datatypes_config=None, - resolve_metadata_dependencies=False, - set_extension=True, **kwds ): + self.external_output_metadata.invalidate_external_metadata([output_dataset_assoc.dataset for + output_dataset_assoc in + job.output_datasets + job.output_library_datasets], + self.sa_session) + + def setup_external_metadata(self, exec_dir=None, tmp_dir=None, + dataset_files_path=None, config_root=None, + config_file=None, datatypes_config=None, + resolve_metadata_dependencies=False, + set_extension=True, **kwds): # extension could still be 'auto' if this is the upload tool. job = self.get_job() if set_extension: for output_dataset_assoc in job.output_datasets: if output_dataset_assoc.dataset.ext == 'auto': - context = self.get_dataset_finish_context( dict(), output_dataset_assoc.dataset.dataset ) - output_dataset_assoc.dataset.extension = context.get( 'ext', 'data' ) + context = self.get_dataset_finish_context(dict(), output_dataset_assoc) + output_dataset_assoc.dataset.extension = context.get('ext', 'data') self.sa_session.flush() if tmp_dir is None: # this dir should should relative to the exec_dir @@ -1685,19 +1667,19 @@ def setup_external_metadata( self, exec_dir=None, tmp_dir=None, config_file = self.app.config.config_file if datatypes_config is None: datatypes_config = self.app.datatypes_registry.integrated_datatypes_configs - command = self.external_output_metadata.setup_external_metadata( [ output_dataset_assoc.dataset for - output_dataset_assoc in - job.output_datasets + job.output_library_datasets ], - self.sa_session, - exec_dir=exec_dir, - tmp_dir=tmp_dir, - dataset_files_path=dataset_files_path, - config_root=config_root, - config_file=config_file, - datatypes_config=datatypes_config, - job_metadata=os.path.join( self.tool_working_directory, TOOL_PROVIDED_JOB_METADATA_FILE ), - max_metadata_value_size=self.app.config.max_metadata_value_size, - **kwds ) + command = self.external_output_metadata.setup_external_metadata([output_dataset_assoc.dataset for + output_dataset_assoc in + job.output_datasets + job.output_library_datasets], + self.sa_session, + exec_dir=exec_dir, + tmp_dir=tmp_dir, + dataset_files_path=dataset_files_path, + config_root=config_root, + config_file=config_file, + datatypes_config=datatypes_config, + job_metadata=os.path.join(self.tool_working_directory, self.tool.provided_metadata_file), + max_metadata_value_size=self.app.config.max_metadata_value_size, + **kwds) if resolve_metadata_dependencies: metadata_tool = self.app.toolbox.get_tool("__SET_METADATA__") if metadata_tool is not None: @@ -1710,7 +1692,7 @@ def setup_external_metadata( self, exec_dir=None, tmp_dir=None, return command @property - def user( self ): + def user(self): job = self.get_job() if job.user is not None: return job.user.email @@ -1741,10 +1723,10 @@ def __update_output(self, job, dataset, clean_only=False): # is a bit of hack - our object store abstractions would be stronger # and more consistent if tools weren't writing there directly. target = dataset.file_name - if os.path.exists( target ): - os.remove( target ) + if os.path.exists(target): + os.remove(target) - def __link_file_check( self ): + def __link_file_check(self): """ outputs_to_working_directory breaks library uploads where data is linked. This method is a hack that solves that problem, but is specific to the upload tool and relies on an injected job param. This @@ -1752,51 +1734,51 @@ def __link_file_check( self ): and stateful way of determining link-only datasets. -nate """ job = self.get_job() - param_dict = job.get_param_values( self.app ) - return self.tool.id == 'upload1' and param_dict.get( 'link_data_only', None ) == 'link_to_files' + param_dict = job.get_param_values(self.app) + return self.tool.id == 'upload1' and param_dict.get('link_data_only', None) == 'link_to_files' - def _change_ownership( self, username, gid ): + def _change_ownership(self, username, gid): job = self.get_job() external_chown_script = self.get_destination_configuration("external_chown_script", None) if external_chown_script is not None: cmd = shlex.split(external_chown_script) - cmd.extend( [ self.working_directory, username, str( gid ) ] ) - log.debug( '(%s) Changing ownership of working directory with: %s' % ( job.id, ' '.join( cmd ) ) ) - p = subprocess.Popen( cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) + cmd.extend([self.working_directory, username, str(gid)]) + log.debug('(%s) Changing ownership of working directory with: %s' % (job.id, ' '.join(cmd))) + p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # TODO: log stdout/stderr stdout, stderr = p.communicate() assert p.returncode == 0 - def change_ownership_for_run( self ): + def change_ownership_for_run(self): job = self.get_job() external_chown_script = self.get_destination_configuration("external_chown_script", None) if external_chown_script and job.user is not None: try: - self._change_ownership( self.user_system_pwent[0], str( self.user_system_pwent[3] ) ) + self._change_ownership(self.user_system_pwent[0], str(self.user_system_pwent[3])) except: - log.exception( '(%s) Failed to change ownership of %s, making world-writable instead' % ( job.id, self.working_directory ) ) - os.chmod( self.working_directory, 0o777 ) + log.exception('(%s) Failed to change ownership of %s, making world-writable instead' % (job.id, self.working_directory)) + os.chmod(self.working_directory, 0o777) - def reclaim_ownership( self ): + def reclaim_ownership(self): job = self.get_job() external_chown_script = self.get_destination_configuration("external_chown_script", None) if external_chown_script and job.user is not None: - self._change_ownership( self.galaxy_system_pwent[0], str( self.galaxy_system_pwent[3] ) ) + self._change_ownership(self.galaxy_system_pwent[0], str(self.galaxy_system_pwent[3])) @property - def user_system_pwent( self ): + def user_system_pwent(self): if self.__user_system_pwent is None: job = self.get_job() self.__user_system_pwent = job.user.system_user_pwent(self.app.config.real_system_username) return self.__user_system_pwent @property - def galaxy_system_pwent( self ): + def galaxy_system_pwent(self): if self.__galaxy_system_pwent is None: self.__galaxy_system_pwent = pwd.getpwuid(os.getuid()) return self.__galaxy_system_pwent - def get_output_destination( self, output_path ): + def get_output_destination(self, output_path): """ Destination for outputs marked as from_work_dir. This is the normal case, just copy these files directly to the ulimate destination. @@ -1804,32 +1786,16 @@ def get_output_destination( self, output_path ): return output_path @property - def requires_setting_metadata( self ): + def requires_setting_metadata(self): if self.tool: return self.tool.requires_setting_metadata return False - def _report_error_to_sentry( self ): + def _report_error(self): job = self.get_job() tool = self.app.toolbox.get_tool(job.tool_id, tool_version=job.tool_version) or None - if self.app.sentry_client and job.state == job.states.ERROR: - self.app.sentry_client.capture( - 'raven.events.Message', - message="Galaxy Job Error: %s v.%s" % (job.tool_id, job.tool_version), - extra={ - 'info' : job.info, - 'id' : job.id, - 'command_line' : job.command_line, - 'stderr' : job.stderr, - 'traceback': job.traceback, - 'exit_code': job.exit_code, - 'stdout': job.stdout, - 'handler': job.handler, - 'user': self.user, - 'tool_version': job.tool_version, - 'tool_xml': tool.config_file if tool else None - } - ) + for dataset in job.output_datasets: + self.app.error_reports.default_error_plugin.submit_report(dataset, job, tool, user_submission=False) class TaskWrapper(JobWrapper): @@ -1844,42 +1810,42 @@ def __init__(self, task, queue): self.task_id = task.id working_directory = task.working_directory self.working_directory = working_directory - job_dataset_path_rewriter = self._job_dataset_path_rewriter( self.working_directory ) - self.dataset_path_rewriter = TaskPathRewriter( working_directory, job_dataset_path_rewriter ) + job_dataset_path_rewriter = self._job_dataset_path_rewriter(self.working_directory) + self.dataset_path_rewriter = TaskPathRewriter(working_directory, job_dataset_path_rewriter) if task.prepare_input_files_cmd is not None: - self.prepare_input_files_cmds = [ task.prepare_input_files_cmd ] + self.prepare_input_files_cmds = [task.prepare_input_files_cmd] else: self.prepare_input_files_cmds = None self.status = task.states.NEW - def can_split( self ): + def can_split(self): # Should the job handler split this job up? TaskWrapper should # always return False as the job has already been split. return False - def get_job( self ): + def get_job(self): if self.job_id: - return self.sa_session.query( model.Job ).get( self.job_id ) + return self.sa_session.query(model.Job).get(self.job_id) else: return None - def get_task( self ): + def get_task(self): return self.sa_session.query(model.Task).get(self.task_id) def get_id_tag(self): # For compatibility with drmaa job runner and TaskWrapper, instead of using job_id directly return self.get_task().get_id_tag() - def get_param_dict( self ): + def get_param_dict(self): """ Restore the dictionary of parameters from the database. """ - job = self.sa_session.query( model.Job ).get( self.job_id ) - param_dict = dict( [ ( p.name, p.value ) for p in job.parameters ] ) - param_dict = self.tool.params_from_strings( param_dict, self.app ) + job = self.sa_session.query(model.Job).get(self.job_id) + param_dict = dict([(p.name, p.value) for p in job.parameters]) + param_dict = self.tool.params_from_strings(param_dict, self.app) return param_dict - def prepare( self, compute_environment=None ): + def prepare(self, compute_environment=None): """ Prepare the job to run by creating the working directory and the config files. @@ -1890,9 +1856,9 @@ def prepare( self, compute_environment=None ): # DBTODO New method for generating command line for a task? - tool_evaluator = self._get_tool_evaluator( job ) - compute_environment = compute_environment or self.default_compute_environment( job ) - tool_evaluator.set_compute_environment( compute_environment ) + tool_evaluator = self._get_tool_evaluator(job) + compute_environment = compute_environment or self.default_compute_environment(job) + tool_evaluator.set_compute_environment(compute_environment) self.sa_session.flush() @@ -1905,47 +1871,47 @@ def prepare( self, compute_environment=None ): # We need command_line persisted to the db in order for Galaxy to re-queue the job # if the server was stopped and restarted before the job finished task.command_line = self.command_line - self.sa_session.add( task ) + self.sa_session.add(task) self.sa_session.flush() self.param_dict = tool_evaluator.param_dict self.status = 'prepared' return self.extra_filenames - def fail( self, message, exception=False ): + def fail(self, message, exception=False): log.error("TaskWrapper Failure %s" % message) self.status = 'error' # How do we want to handle task failure? Fail the job and let it clean up? - def change_state( self, state, info=False, flush=True, job=None ): + def change_state(self, state, info=False, flush=True, job=None): task = self.get_task() - self.sa_session.refresh( task ) + self.sa_session.refresh(task) if info: task.info = info task.state = state - self.sa_session.add( task ) + self.sa_session.add(task) self.sa_session.flush() - def get_state( self ): + def get_state(self): task = self.get_task() - self.sa_session.refresh( task ) + self.sa_session.refresh(task) return task.state - def get_exit_code( self ): + def get_exit_code(self): task = self.get_task() - self.sa_session.refresh( task ) + self.sa_session.refresh(task) return task.exit_code - def set_runner( self, runner_url, external_id ): + def set_runner(self, runner_url, external_id): task = self.get_task() - self.sa_session.refresh( task ) + self.sa_session.refresh(task) task.task_runner_name = runner_url task.task_runner_external_id = external_id # DBTODO Check task job_runner_stuff - self.sa_session.add( task ) + self.sa_session.add(task) self.sa_session.flush() - def finish( self, stdout, stderr, tool_exit_code=None ): + def finish(self, stdout, stderr, tool_exit_code=None): # DBTODO integrate previous finish logic. # Simple finish for tasks. Just set the flag OK. """ @@ -1955,115 +1921,115 @@ def finish( self, stdout, stderr, tool_exit_code=None ): """ # This may have ended too soon - log.debug( 'task %s for job %d ended; exit code: %d' - % (self.task_id, self.job_id, - tool_exit_code if tool_exit_code is not None else -256 ) ) + log.debug('task %s for job %d ended; exit code: %d' + % (self.task_id, self.job_id, + tool_exit_code if tool_exit_code is not None else -256)) # default post job setup_external_metadata self.sa_session.expunge_all() task = self.get_task() # if the job was deleted, don't finish it if task.state == task.states.DELETED: # Job was deleted by an administrator - delete_files = self.cleanup_job in ( 'always', 'onsuccess' ) - self.cleanup( delete_files=delete_files ) + delete_files = self.cleanup_job in ('always', 'onsuccess') + self.cleanup(delete_files=delete_files) return elif task.state == task.states.ERROR: - self.fail( task.info ) + self.fail(task.info) return # Check what the tool returned. If the stdout or stderr matched # regular expressions that indicate errors, then set an error. # The same goes if the tool's exit code was in a given range. - if ( self.check_tool_output( stdout, stderr, tool_exit_code, task ) ): + if (self.check_tool_output(stdout, stderr, tool_exit_code, task)): task.state = task.states.OK else: task.state = task.states.ERROR # Save stdout and stderr - task.set_streams( stdout, stderr ) - self._collect_metrics( task ) + task.set_streams(stdout, stderr) + self._collect_metrics(task) task.exit_code = tool_exit_code task.command_line = self.command_line self.sa_session.flush() - def cleanup( self, delete_files=True ): + def cleanup(self, delete_files=True): # There is no task cleanup. The job cleans up for all tasks. pass - def get_command_line( self ): + def get_command_line(self): return self.command_line - def get_session_id( self ): + def get_session_id(self): return self.session_id - def get_output_file_id( self, file ): + def get_output_file_id(self, file): # There is no permanent output file for tasks. return None - def get_tool_provided_job_metadata( self ): + def get_tool_provided_job_metadata(self): # DBTODO Handle this as applicable for tasks. return None - def get_dataset_finish_context( self, job_context, dataset ): + def get_dataset_finish_context(self, job_context, dataset): # Handled at the parent job level. Do nothing here. pass - def setup_external_metadata( self, exec_dir=None, tmp_dir=None, dataset_files_path=None, - config_root=None, config_file=None, datatypes_config=None, - set_extension=True, **kwds ): + def setup_external_metadata(self, exec_dir=None, tmp_dir=None, dataset_files_path=None, + config_root=None, config_file=None, datatypes_config=None, + set_extension=True, **kwds): # There is no metadata setting for tasks. This is handled after the merge, at the job level. return "" - def get_output_destination( self, output_path ): + def get_output_destination(self, output_path): """ Destination for outputs marked as from_work_dir. These must be copied with the same basenme as the path for the ultimate output destination. This is required in the task case so they can be merged. """ - return os.path.join( self.working_directory, os.path.basename( output_path ) ) + return os.path.join(self.working_directory, os.path.basename(output_path)) @six.add_metaclass(ABCMeta) -class ComputeEnvironment( object ): +class ComputeEnvironment(object): """ Definition of the job as it will be run on the (potentially) remote compute server. """ @abstractmethod - def output_paths( self ): + def output_paths(self): """ Output DatasetPaths defined by job. """ @abstractmethod - def input_paths( self ): + def input_paths(self): """ Input DatasetPaths defined by job. """ @abstractmethod - def working_directory( self ): + def working_directory(self): """ Job working directory (potentially remote) """ @abstractmethod - def config_directory( self ): + def config_directory(self): """ Directory containing config files (potentially remote) """ @abstractmethod - def sep( self ): + def sep(self): """ os.path.sep for the platform this job will execute in. """ @abstractmethod - def new_file_path( self ): + def new_file_path(self): """ Absolute path to dump new files for this job on compute server. """ @abstractmethod - def tool_directory( self ): + def tool_directory(self): """ Absolute path to tool files for this job on compute server. """ @abstractmethod - def version_path( self ): + def version_path(self): """ Location of the version file for the underlying tool. """ @abstractmethod - def unstructured_path_rewriter( self ): + def unstructured_path_rewriter(self): """ Return a function that takes in a value, determines if it is path to be rewritten (will be passed non-path values as well - onus is on this function to determine both if its input is a path and if it should @@ -2071,59 +2037,60 @@ def unstructured_path_rewriter( self ): """ -class SimpleComputeEnvironment( object ): +class SimpleComputeEnvironment(object): - def config_directory( self ): - return self.working_directory( ) + def config_directory(self): + return self.working_directory() - def sep( self ): + def sep(self): return os.path.sep - def unstructured_path_rewriter( self ): + def unstructured_path_rewriter(self): return lambda v: v -class SharedComputeEnvironment( SimpleComputeEnvironment ): +class SharedComputeEnvironment(SimpleComputeEnvironment): """ Default ComputeEnviornment for job and task wrapper to pass to ToolEvaluator - valid when Galaxy and compute share all the relevant file systems. """ - def __init__( self, job_wrapper, job ): + def __init__(self, job_wrapper, job): self.app = job_wrapper.app self.job_wrapper = job_wrapper self.job = job - def output_paths( self ): + def output_paths(self): return self.job_wrapper.get_output_fnames() - def input_paths( self ): - return self.job_wrapper.get_input_paths( self.job ) + def input_paths(self): + return self.job_wrapper.get_input_paths(self.job) - def working_directory( self ): + def working_directory(self): return self.job_wrapper.working_directory - def new_file_path( self ): - return os.path.abspath( self.app.config.new_file_path ) + def new_file_path(self): + return os.path.abspath(self.app.config.new_file_path) - def version_path( self ): + def version_path(self): return self.job_wrapper.get_version_string_path() - def tool_directory( self ): + def tool_directory(self): return os.path.abspath(self.job_wrapper.tool.tool_dir) -class NoopQueue( object ): +class NoopQueue(object): """ Implements the JobQueue / JobStopQueue interface but does nothing """ - def put( self, *args, **kwargs ): + + def put(self, *args, **kwargs): return - def put_stop( self, *args ): + def put_stop(self, *args): return - def shutdown( self ): + def shutdown(self): return @@ -2132,13 +2099,14 @@ class ParallelismInfo(object): Stores the information (if any) for running multiple instances of the tool in parallel on the same set of inputs. """ + def __init__(self, tag): self.method = tag.get('method') if isinstance(tag, dict): items = tag.items() else: items = tag.attrib.items() - self.attributes = dict( [ item for item in items if item[ 0 ] != 'method' ]) + self.attributes = dict([item for item in items if item[0] != 'method']) if len(self.attributes) == 0: # legacy basic mode - provide compatible defaults self.attributes['split_size'] = 20 diff --git a/lib/galaxy/jobs/actions/post.py b/lib/galaxy/jobs/actions/post.py index 38f5178bd78f..2f7402f399ca 100644 --- a/lib/galaxy/jobs/actions/post.py +++ b/lib/galaxy/jobs/actions/post.py @@ -10,7 +10,7 @@ from galaxy.util import send_mail -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) class DefaultJobAction(object): @@ -51,9 +51,9 @@ def execute(cls, app, sa_session, action, job, replacement_dict): to = job.user.email subject = "Galaxy workflow step notification '%s'" % (job.history.name) outdata = ', '.join(ds.dataset.display_name() for ds in job.output_datasets) - body = "Your Galaxy job generating dataset '%s' is complete as of %s." % (outdata, datetime.datetime.now().strftime( "%I:%M" )) + body = "Your Galaxy job generating dataset '%s' is complete as of %s." % (outdata, datetime.datetime.now().strftime("%I:%M")) try: - send_mail( frm, to, subject, body, app.config ) + send_mail(frm, to, subject, body, app.config) except Exception as e: log.error("EmailAction PJA Failed, exception: %s" % e) @@ -73,7 +73,7 @@ class ChangeDatatypeAction(DefaultJobAction): def execute(cls, app, sa_session, action, job, replacement_dict): for dataset_assoc in job.output_datasets: if action.output_name == '' or dataset_assoc.name == action.output_name: - app.datatypes_registry.change_datatype( dataset_assoc.dataset, action.action_arguments['newtype']) + app.datatypes_registry.change_datatype(dataset_assoc.dataset, action.action_arguments['newtype']) @classmethod def get_short_str(cls, pja): @@ -195,7 +195,7 @@ class HideDatasetAction(DefaultJobAction): @classmethod def execute(cls, app, sa_session, action, job, replacement_dict): for dataset_assoc in job.output_datasets: - if dataset_assoc.dataset.state != dataset_assoc.dataset.states.ERROR and ( action.output_name == '' or dataset_assoc.name == action.output_name ): + if dataset_assoc.dataset.state != dataset_assoc.dataset.states.ERROR and (action.output_name == '' or dataset_assoc.name == action.output_name): dataset_assoc.dataset.visible = False @classmethod @@ -248,7 +248,7 @@ class SetMetadataAction(DefaultJobAction): @classmethod def execute(cls, app, sa_session, action, job, replacement_dict): for data in job.output_datasets: - data.set_metadata( action.action_arguments['newtype'] ) + data.set_metadata(action.action_arguments['newtype']) class DeleteIntermediatesAction(DefaultJobAction): @@ -326,6 +326,8 @@ def get_short_str(cls, pja): class TagDatasetAction(DefaultJobAction): name = "TagDatasetAction" verbose_name = "Add tag to dataset" + action = "Add" + direction = "to" @classmethod def execute(cls, app, sa_session, action, job, replacement_dict): @@ -334,33 +336,51 @@ def execute(cls, app, sa_session, action, job, replacement_dict): if tags: for dataset_assoc in job.output_datasets: if action.output_name == '' or dataset_assoc.name == action.output_name: - app.tag_handler.add_tags_from_list( job.user, dataset_assoc.dataset, tags) + cls._execute(app, job.user, dataset_assoc.dataset, tags) sa_session.flush() + @classmethod + def _execute(cls, app, user, dataset, tags): + app.tag_handler.add_tags_from_list(user, dataset, tags) + @classmethod def get_short_str(cls, pja): if pja.action_arguments and pja.action_arguments.get('tags', ''): - return "Add tag(s) '%s' to '%s'." % (escape(pja.action_arguments['tags']), - escape(pja.output_name)) + return "%s tag(s) '%s' %s '%s'." % (cls.action, + escape(pja.action_arguments['tags']), + cls.direction, + escape(pja.output_name)) else: - return "Tag addition action used without a tag specified. No tag will be added." + return "%s Tag action used without a tag specified. No tag will be added." % cls.action + + +class RemoveTagDatasetAction(TagDatasetAction): + name = "RemoveTagDatasetAction" + verbose_name = "Remove tag from dataset" + action = "Remove" + direction = "from" + + @classmethod + def _execute(cls, app, user, dataset, tags): + app.tag_handler.remove_tags_from_list(user, dataset, tags) class ActionBox(object): - actions = { "RenameDatasetAction": RenameDatasetAction, - "HideDatasetAction": HideDatasetAction, - "ChangeDatatypeAction": ChangeDatatypeAction, - "ColumnSetAction": ColumnSetAction, - "EmailAction": EmailAction, - "DeleteIntermediatesAction": DeleteIntermediatesAction, - "TagDatasetAction": TagDatasetAction, - } + actions = {"RenameDatasetAction": RenameDatasetAction, + "HideDatasetAction": HideDatasetAction, + "ChangeDatatypeAction": ChangeDatatypeAction, + "ColumnSetAction": ColumnSetAction, + "EmailAction": EmailAction, + "DeleteIntermediatesAction": DeleteIntermediatesAction, + "TagDatasetAction": TagDatasetAction, + "RemoveTagDatasetAction": RemoveTagDatasetAction} public_actions = ['RenameDatasetAction', 'ChangeDatatypeAction', 'ColumnSetAction', 'EmailAction', - 'DeleteIntermediatesAction', 'TagDatasetAction'] + 'DeleteIntermediatesAction', 'TagDatasetAction', + 'RemoveTagDatasetAction'] immediate_actions = ['ChangeDatatypeAction', 'RenameDatasetAction', - 'TagDatasetAction'] + 'TagDatasetAction', 'RemoveTagDatasetAction'] @classmethod def get_short_str(cls, action): diff --git a/lib/galaxy/jobs/command_factory.py b/lib/galaxy/jobs/command_factory.py index 896461059841..b1eaef62ac18 100644 --- a/lib/galaxy/jobs/command_factory.py +++ b/lib/galaxy/jobs/command_factory.py @@ -12,7 +12,7 @@ write_script, ) -log = getLogger( __name__ ) +log = getLogger(__name__) CAPTURE_RETURN_CODE = "return_code=$?" YIELD_CAPTURED_CODE = 'sh -c "exit $return_code"' @@ -66,8 +66,13 @@ def build_command( if not commands_builder.commands: return None + # Version, dependency resolution, and task splitting are prepended to the + # command - so they need to appear in the following order to ensure that + # the underlying application used by version command is available in the + # environment after dependency resolution, but the task splitting command + # is still executed in Galaxy's Python environment. + __handle_version_command(commands_builder, job_wrapper) - __handle_task_splitting(commands_builder, job_wrapper) # One could imagine also allowing dependencies inside of the container but # that is too sophisticated for a first crack at this - build your @@ -75,6 +80,8 @@ def build_command( if not container or container.resolve_dependencies: __handle_dependency_resolution(commands_builder, job_wrapper, remote_command_params) + __handle_task_splitting(commands_builder, job_wrapper) + if (container and modify_command_for_container) or job_wrapper.commands_in_new_shell: if container and modify_command_for_container: # Many Docker containers do not have /bin/bash. @@ -92,9 +99,9 @@ def build_command( run_in_container_command = container.containerize_command( externalized_commands ) - commands_builder = CommandsBuilder( run_in_container_command ) + commands_builder = CommandsBuilder(run_in_container_command) else: - commands_builder = CommandsBuilder( externalized_commands ) + commands_builder = CommandsBuilder(externalized_commands) # Don't need to create a separate tool working directory for Pulsar # jobs - that is handled by Pulsar. @@ -120,7 +127,7 @@ def build_command( def __externalize_commands(job_wrapper, shell, commands_builder, remote_command_params, script_name="tool_script.sh"): - local_container_script = join( job_wrapper.working_directory, script_name ) + local_container_script = join(job_wrapper.working_directory, script_name) tool_commands = commands_builder.build() config = job_wrapper.app.config integrity_injection = "" @@ -174,7 +181,7 @@ def __handle_work_dir_outputs(commands_builder, job_wrapper, runner, remote_comm work_dir_outputs_kwds = {} if 'working_directory' in remote_command_params: work_dir_outputs_kwds['job_working_directory'] = remote_command_params['working_directory'] - work_dir_outputs = runner.get_work_dir_outputs( job_wrapper, **work_dir_outputs_kwds ) + work_dir_outputs = runner.get_work_dir_outputs(job_wrapper, **work_dir_outputs_kwds) if work_dir_outputs: commands_builder.capture_return_code() copy_commands = map(__copy_if_exists_command, work_dir_outputs) @@ -185,14 +192,14 @@ def __handle_metadata(commands_builder, job_wrapper, runner, remote_command_para # Append metadata setting commands, we don't want to overwrite metadata # that was copied over in init_meta(), as per established behavior metadata_kwds = remote_command_params.get('metadata_kwds', {}) - exec_dir = metadata_kwds.get( 'exec_dir', abspath( getcwd() ) ) - tmp_dir = metadata_kwds.get( 'tmp_dir', job_wrapper.working_directory ) - dataset_files_path = metadata_kwds.get( 'dataset_files_path', runner.app.model.Dataset.file_path ) - output_fnames = metadata_kwds.get( 'output_fnames', job_wrapper.get_output_fnames() ) - config_root = metadata_kwds.get( 'config_root', None ) - config_file = metadata_kwds.get( 'config_file', None ) - datatypes_config = metadata_kwds.get( 'datatypes_config', None ) - compute_tmp_dir = metadata_kwds.get( 'compute_tmp_dir', None ) + exec_dir = metadata_kwds.get('exec_dir', abspath(getcwd())) + tmp_dir = metadata_kwds.get('tmp_dir', job_wrapper.working_directory) + dataset_files_path = metadata_kwds.get('dataset_files_path', runner.app.model.Dataset.file_path) + output_fnames = metadata_kwds.get('output_fnames', job_wrapper.get_output_fnames()) + config_root = metadata_kwds.get('config_root', None) + config_file = metadata_kwds.get('config_file', None) + datatypes_config = metadata_kwds.get('datatypes_config', None) + compute_tmp_dir = metadata_kwds.get('compute_tmp_dir', None) resolve_metadata_dependencies = job_wrapper.commands_in_new_shell metadata_command = job_wrapper.setup_external_metadata( exec_dir=exec_dir, @@ -205,7 +212,7 @@ def __handle_metadata(commands_builder, job_wrapper, runner, remote_command_para datatypes_config=datatypes_config, compute_tmp_dir=compute_tmp_dir, resolve_metadata_dependencies=resolve_metadata_dependencies, - kwds={ 'overwrite': False } + kwds={'overwrite': False} ) or '' metadata_command = metadata_command.strip() if metadata_command: @@ -217,7 +224,7 @@ def __handle_metadata(commands_builder, job_wrapper, runner, remote_command_para def __copy_if_exists_command(work_dir_output): source_file, destination = work_dir_output - return "if [ -f %s ] ; then cp %s %s ; fi" % ( source_file, source_file, destination ) + return "if [ -f %s ] ; then cp %s %s ; fi" % (source_file, source_file, destination) class CommandsBuilder(object): @@ -263,4 +270,4 @@ def build(self): return self.commands -__all__ = ( "build_command", ) +__all__ = ("build_command", ) diff --git a/lib/galaxy/jobs/datasets.py b/lib/galaxy/jobs/datasets.py index 36643482bb73..9caa5cff1a51 100644 --- a/lib/galaxy/jobs/datasets.py +++ b/lib/galaxy/jobs/datasets.py @@ -10,12 +10,12 @@ import six -def dataset_path_rewrites( dataset_paths ): +def dataset_path_rewrites(dataset_paths): dataset_paths_with_rewrites = [path for path in dataset_paths if getattr(path, "false_path", None)] - return dict( ( dp.real_path, dp ) for dp in dataset_paths_with_rewrites ) + return dict((dp.real_path, dp) for dp in dataset_paths_with_rewrites) -class DatasetPath( object ): +class DatasetPath(object): def __init__( self, @@ -31,13 +31,13 @@ def __init__( self.false_extra_files_path = false_extra_files_path self.mutable = mutable - def __str__( self ): + def __str__(self): if self.false_path is None: return self.real_path else: return self.false_path - def with_path_for_job( self, false_path, false_extra_files_path=None ): + def with_path_for_job(self, false_path, false_extra_files_path=None): """ Clone the dataset path but with a new false_path. """ @@ -54,60 +54,60 @@ def with_path_for_job( self, false_path, false_extra_files_path=None ): @six.add_metaclass(ABCMeta) -class DatasetPathRewriter( object ): +class DatasetPathRewriter(object): """ Used by runner to rewrite paths. """ @abstractmethod - def rewrite_dataset_path( self, dataset, dataset_type ): + def rewrite_dataset_path(self, dataset, dataset_type): """ Dataset type is 'input' or 'output'. Return None to indicate not to rewrite this path. """ -class NullDatasetPathRewriter( object ): +class NullDatasetPathRewriter(object): """ Used by default for jobwrapper, do not rewrite anything. """ - def rewrite_dataset_path( self, dataset, dataset_type ): + def rewrite_dataset_path(self, dataset, dataset_type): """ Keep path the same. """ return None -class OutputsToWorkingDirectoryPathRewriter( object ): +class OutputsToWorkingDirectoryPathRewriter(object): """ Rewrites all paths to place them in the specified working directory for normal jobs when Galaxy is configured with app.config.outputs_to_working_directory. Job runner base class is responsible for copying these out after job is complete. """ - def __init__( self, working_directory ): + def __init__(self, working_directory): self.working_directory = working_directory - def rewrite_dataset_path( self, dataset, dataset_type ): + def rewrite_dataset_path(self, dataset, dataset_type): """ Keep path the same. """ if dataset_type == 'output': - false_path = os.path.abspath( os.path.join( self.working_directory, "galaxy_dataset_%d.dat" % dataset.id ) ) + false_path = os.path.abspath(os.path.join(self.working_directory, "galaxy_dataset_%d.dat" % dataset.id)) return false_path else: return None -class TaskPathRewriter( object ): +class TaskPathRewriter(object): """ Rewrites all paths to place them in the specified working directory for TaskWrapper. TaskWrapper is responsible for putting them there and pulling them out. """ - def __init__( self, working_directory, job_dataset_path_rewriter ): + def __init__(self, working_directory, job_dataset_path_rewriter): self.working_directory = working_directory self.job_dataset_path_rewriter = job_dataset_path_rewriter - def rewrite_dataset_path( self, dataset, dataset_type ): + def rewrite_dataset_path(self, dataset, dataset_type): """ """ dataset_file_name = dataset.file_name - job_file_name = self.job_dataset_path_rewriter.rewrite_dataset_path( dataset, dataset_type ) or dataset_file_name - return os.path.join( self.working_directory, os.path.basename( job_file_name ) ) + job_file_name = self.job_dataset_path_rewriter.rewrite_dataset_path(dataset, dataset_type) or dataset_file_name + return os.path.join(self.working_directory, os.path.basename(job_file_name)) diff --git a/lib/galaxy/jobs/deferred/__init__.py b/lib/galaxy/jobs/deferred/__init__.py index 90bfa9167f51..f8ab70a20a47 100644 --- a/lib/galaxy/jobs/deferred/__init__.py +++ b/lib/galaxy/jobs/deferred/__init__.py @@ -10,15 +10,15 @@ from galaxy.util.bunch import Bunch from galaxy.util.sleeper import Sleeper -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class DeferredJobQueue( object ): - job_states = Bunch( READY='ready', - WAIT='wait', - INVALID='invalid' ) +class DeferredJobQueue(object): + job_states = Bunch(READY='ready', + WAIT='wait', + INVALID='invalid') - def __init__( self, app ): + def __init__(self, app): self.app = app self.sa_session = app.model.context.current self.queue = Queue() @@ -28,132 +28,133 @@ def __init__( self, app ): self.running = True self.waiting_jobs = [] self.__check_jobs_at_startup() - self.monitor_thread = threading.Thread( target=self.__monitor ) + self.monitor_thread = threading.Thread(target=self.__monitor) self.monitor_thread.start() - log.info( 'Deferred job queue started' ) + log.info('Deferred job queue started') - def _load_plugins( self ): - for fname in os.listdir( os.path.dirname( __file__ ) ): - if not fname.startswith( '_' ) and fname.endswith( '.py' ): + def _load_plugins(self): + for fname in os.listdir(os.path.dirname(__file__)): + if not fname.startswith('_') and fname.endswith('.py'): name = fname[:-3] module_name = 'galaxy.jobs.deferred.' + name try: - module = __import__( module_name ) + module = __import__(module_name) except: - log.exception( 'Deferred job plugin appears to exist but is not loadable: %s', module_name ) + log.exception('Deferred job plugin appears to exist but is not loadable: %s', module_name) continue - for comp in module_name.split( "." )[1:]: - module = getattr( module, comp ) - if '__all__' not in dir( module ): - log.error( 'Plugin "%s" does not contain a list of exported classes in __all__' % module_name ) + for comp in module_name.split(".")[1:]: + module = getattr(module, comp) + if '__all__' not in dir(module): + log.error('Plugin "%s" does not contain a list of exported classes in __all__' % module_name) continue for obj in module.__all__: - display_name = ':'.join( ( module_name, obj ) ) - plugin = getattr( module, obj ) - for name in ( 'check_job', 'run_job' ): - if name not in dir( plugin ): - log.error( 'Plugin "%s" does not contain required method "%s()"' % ( display_name, name ) ) + display_name = ':'.join((module_name, obj)) + plugin = getattr(module, obj) + for name in ('check_job', 'run_job'): + if name not in dir(plugin): + log.error('Plugin "%s" does not contain required method "%s()"' % (display_name, name)) break else: - self.plugins[obj] = plugin( self.app ) + self.plugins[obj] = plugin(self.app) self.plugins[obj].job_states = self.job_states - log.debug( 'Loaded deferred job plugin: %s' % display_name ) + log.debug('Loaded deferred job plugin: %s' % display_name) - def __check_jobs_at_startup( self ): - waiting_jobs = self.sa_session.query( model.DeferredJob ) \ - .filter( model.DeferredJob.state == model.DeferredJob.states.WAITING ).all() + def __check_jobs_at_startup(self): + waiting_jobs = self.sa_session.query(model.DeferredJob) \ + .filter(model.DeferredJob.state == model.DeferredJob.states.WAITING).all() for job in waiting_jobs: - if not self.__check_job_plugin( job ): + if not self.__check_job_plugin(job): continue - if 'check_interval' in dir( self.plugins[job.plugin] ): + if 'check_interval' in dir(self.plugins[job.plugin]): job.check_interval = self.plugins[job.plugin].check_interval - log.info( 'Recovered deferred job (id: %s) at startup' % job.id ) + log.info('Recovered deferred job (id: %s) at startup' % job.id) # Pass the job ID as opposed to the job, since the monitor thread # needs to load it in its own threadlocal scoped session. - self.waiting_jobs.append( job.id ) + self.waiting_jobs.append(job.id) - def __monitor( self ): + def __monitor(self): while self.running: try: self.__monitor_step() except: - log.exception( 'Exception in monitor_step' ) - self.sleeper.sleep( 1 ) - log.info( 'job queue stopped' ) + log.exception('Exception in monitor_step') + self.sleeper.sleep(1) + log.info('job queue stopped') - def __monitor_step( self ): + def __monitor_step(self): # TODO: Querying the database with this frequency is bad, we need message passing - new_jobs = self.sa_session.query( model.DeferredJob ) \ - .filter( model.DeferredJob.state == model.DeferredJob.states.NEW ).all() + new_jobs = self.sa_session.query(model.DeferredJob) \ + .filter(model.DeferredJob.state == model.DeferredJob.states.NEW).all() for job in new_jobs: - if not self.__check_job_plugin( job ): + if not self.__check_job_plugin(job): continue job.state = model.DeferredJob.states.WAITING - self.sa_session.add( job ) + self.sa_session.add(job) self.sa_session.flush() - if 'check_interval' in dir( self.plugins[job.plugin] ): + if 'check_interval' in dir(self.plugins[job.plugin]): job.check_interval = self.plugins[job.plugin].check_interval - self.waiting_jobs.append( job ) + self.waiting_jobs.append(job) new_waiting = [] for job in self.waiting_jobs: try: # Recovered jobs are passed in by ID - assert type( job ) is int - job = self.sa_session.query( model.DeferredJob ).get( job ) + assert type(job) is int + job = self.sa_session.query(model.DeferredJob).get(job) except: pass if job.is_check_time: try: - job_state = self.plugins[job.plugin].check_job( job ) + job_state = self.plugins[job.plugin].check_job(job) except Exception: - self.__fail_job( job ) - log.exception( 'Set deferred job %s to error because of an exception in check_job()' % job.id ) + self.__fail_job(job) + log.exception('Set deferred job %s to error because of an exception in check_job()' % job.id) continue if job_state == self.job_states.READY: try: - self.plugins[job.plugin].run_job( job ) + self.plugins[job.plugin].run_job(job) except Exception: - self.__fail_job( job ) - log.exception( 'Set deferred job %s to error because of an exception in run_job()' % job.id ) + self.__fail_job(job) + log.exception('Set deferred job %s to error because of an exception in run_job()' % job.id) continue elif job_state == self.job_states.INVALID: - self.__fail_job( job ) - log.error( 'Unable to run deferred job (id: %s): Plugin "%s" marked it as invalid' % ( job.id, job.plugin ) ) + self.__fail_job(job) + log.error('Unable to run deferred job (id: %s): Plugin "%s" marked it as invalid' % (job.id, job.plugin)) continue else: - new_waiting.append( job ) + new_waiting.append(job) job.last_check = 'now' else: - new_waiting.append( job ) + new_waiting.append(job) self.waiting_jobs = new_waiting - def __check_job_plugin( self, job ): + def __check_job_plugin(self, job): if job.plugin not in self.plugins: - log.error( 'Invalid deferred job plugin: %s' ) % job.plugin + log.error('Invalid deferred job plugin: %s') % job.plugin job.state = model.DeferredJob.states.ERROR - self.sa_session.add( job ) + self.sa_session.add(job) self.sa_session.flush() return False return True - def __check_if_ready_to_run( self, job ): - return self.plugins[job.plugin].check_job( job ) + def __check_if_ready_to_run(self, job): + return self.plugins[job.plugin].check_job(job) - def __fail_job( self, job ): + def __fail_job(self, job): job.state = model.DeferredJob.states.ERROR - self.sa_session.add( job ) + self.sa_session.add(job) self.sa_session.flush() - def shutdown( self ): + def shutdown(self): self.running = False self.sleeper.wake() -class FakeTrans( object ): +class FakeTrans(object): """A fake trans for calling the external set metadata tool""" - def __init__( self, app, history=None, user=None): - class Dummy( object ): - def __init__( self ): + + def __init__(self, app, history=None, user=None): + class Dummy(object): + def __init__(self): self.id = None self.app = app self.sa_session = app.model.context.current @@ -168,23 +169,23 @@ def __init__( self ): self.user = user self.model = app.model - def get_galaxy_session( self ): + def get_galaxy_session(self): return self.dummy - def log_event( self, message, tool_id=None ): + def log_event(self, message, tool_id=None): pass - def get_current_user_roles( self ): + def get_current_user_roles(self): if self.user: return self.user.all_roles() else: return [] - def db_dataset_for( self, dbkey ): + def db_dataset_for(self, dbkey): if self.history is None: return None - datasets = self.sa_session.query( self.app.model.HistoryDatasetAssociation ) \ - .filter_by( deleted=False, history_id=self.history.id, extension="len" ) + datasets = self.sa_session.query(self.app.model.HistoryDatasetAssociation) \ + .filter_by(deleted=False, history_id=self.history.id, extension="len") for ds in datasets: if dbkey == ds.dbkey: return ds diff --git a/lib/galaxy/jobs/deferred/data_transfer.py b/lib/galaxy/jobs/deferred/data_transfer.py index ee8ec8bfb639..4b7e93d8ce25 100644 --- a/lib/galaxy/jobs/deferred/data_transfer.py +++ b/lib/galaxy/jobs/deferred/data_transfer.py @@ -16,160 +16,160 @@ from galaxy.workflow.modules import module_factory -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -__all__ = ( 'DataTransfer', ) +__all__ = ('DataTransfer', ) -class DataTransfer( object ): +class DataTransfer(object): check_interval = 15 - dataset_name_re = re.compile( '(dataset\d+)_(name)' ) - dataset_datatype_re = re.compile( '(dataset\d+)_(datatype)' ) + dataset_name_re = re.compile('(dataset\d+)_(name)') + dataset_datatype_re = re.compile('(dataset\d+)_(datatype)') - def __init__( self, app ): + def __init__(self, app): self.app = app self.sa_session = app.model.context.current - def create_job( self, trans, **kwd ): - raise Exception( "Unimplemented Method" ) + def create_job(self, trans, **kwd): + raise Exception("Unimplemented Method") - def check_job( self, job ): - raise Exception( "Unimplemented Method" ) + def check_job(self, job): + raise Exception("Unimplemented Method") - def run_job( self, job ): - if job.params[ 'type' ] == 'init_transfer': + def run_job(self, job): + if job.params['type'] == 'init_transfer': # TODO: don't create new downloads on restart. - if job.params[ 'protocol' ] in [ 'http', 'https' ]: + if job.params['protocol'] in ['http', 'https']: results = [] - for result in job.params[ 'results' ].values(): - result[ 'transfer_job' ] = self.app.transfer_manager.new( protocol=job.params[ 'protocol' ], - name=result[ 'name' ], - datatype=result[ 'datatype' ], - url=result[ 'url' ] ) - results.append( result ) - elif job.params[ 'protocol' ] == 'scp': + for result in job.params['results'].values(): + result['transfer_job'] = self.app.transfer_manager.new(protocol=job.params['protocol'], + name=result['name'], + datatype=result['datatype'], + url=result['url']) + results.append(result) + elif job.params['protocol'] == 'scp': results = [] result = {} - sample_datasets_dict = job.params[ 'sample_datasets_dict' ] + sample_datasets_dict = job.params['sample_datasets_dict'] # sample_datasets_dict looks something like the following. The outer dictionary keys are SampleDataset ids. # {'7': {'status': 'Not started', 'name': '3.bed', 'file_path': '/tmp/library/3.bed', 'sample_id': 7, # 'external_service_id': 2, 'error_msg': '', 'size': '8.0K'}} for sample_dataset_id, sample_dataset_info_dict in sample_datasets_dict.items(): result = {} - result[ 'transfer_job' ] = self.app.transfer_manager.new( protocol=job.params[ 'protocol' ], - host=job.params[ 'host' ], - user_name=job.params[ 'user_name' ], - password=job.params[ 'password' ], - sample_dataset_id=sample_dataset_id, - status=sample_dataset_info_dict[ 'status' ], - name=sample_dataset_info_dict[ 'name' ], - file_path=sample_dataset_info_dict[ 'file_path' ], - sample_id=sample_dataset_info_dict[ 'sample_id' ], - external_service_id=sample_dataset_info_dict[ 'external_service_id' ], - error_msg=sample_dataset_info_dict[ 'error_msg' ], - size=sample_dataset_info_dict[ 'size' ] ) - results.append( result ) - self.app.transfer_manager.run( [ r[ 'transfer_job' ] for r in results ] ) + result['transfer_job'] = self.app.transfer_manager.new(protocol=job.params['protocol'], + host=job.params['host'], + user_name=job.params['user_name'], + password=job.params['password'], + sample_dataset_id=sample_dataset_id, + status=sample_dataset_info_dict['status'], + name=sample_dataset_info_dict['name'], + file_path=sample_dataset_info_dict['file_path'], + sample_id=sample_dataset_info_dict['sample_id'], + external_service_id=sample_dataset_info_dict['external_service_id'], + error_msg=sample_dataset_info_dict['error_msg'], + size=sample_dataset_info_dict['size']) + results.append(result) + self.app.transfer_manager.run([r['transfer_job'] for r in results]) for result in results: - transfer_job = result.pop( 'transfer_job' ) - self.create_job( None, - transfer_job_id=transfer_job.id, - result=transfer_job.params, - sample_id=job.params[ 'sample_id' ] ) + transfer_job = result.pop('transfer_job') + self.create_job(None, + transfer_job_id=transfer_job.id, + result=transfer_job.params, + sample_id=job.params['sample_id']) # Update the state of the relevant SampleDataset new_status = self.app.model.SampleDataset.transfer_status.IN_QUEUE - self._update_sample_dataset_status( protocol=job.params[ 'protocol' ], - sample_id=job.params[ 'sample_id' ], - result_dict=transfer_job.params, - new_status=new_status, - error_msg='' ) + self._update_sample_dataset_status(protocol=job.params['protocol'], + sample_id=job.params['sample_id'], + result_dict=transfer_job.params, + new_status=new_status, + error_msg='') job.state = self.app.model.DeferredJob.states.OK - self.sa_session.add( job ) + self.sa_session.add(job) self.sa_session.flush() # TODO: Error handling: failure executing, or errors returned from the manager - if job.params[ 'type' ] == 'finish_transfer': - protocol = job.params[ 'protocol' ] + if job.params['type'] == 'finish_transfer': + protocol = job.params['protocol'] # Update the state of the relevant SampleDataset new_status = self.app.model.SampleDataset.transfer_status.ADD_TO_LIBRARY - if protocol in [ 'http', 'https' ]: - result_dict = job.params[ 'result' ] - library_dataset_name = result_dict[ 'name' ] - extension = result_dict[ 'datatype' ] - elif protocol in [ 'scp' ]: + if protocol in ['http', 'https']: + result_dict = job.params['result'] + library_dataset_name = result_dict['name'] + extension = result_dict['datatype'] + elif protocol in ['scp']: # In this case, job.params will be a dictionary that contains a key named 'result'. The value # of the result key is a dictionary that looks something like: # {'sample_dataset_id': '8', 'status': 'Not started', 'protocol': 'scp', 'name': '3.bed', # 'file_path': '/data/library/3.bed', 'host': '127.0.0.1', 'sample_id': 8, 'external_service_id': 2, # 'local_path': '/tmp/kjl2Ss4', 'password': 'galaxy', 'user_name': 'gvk', 'error_msg': '', 'size': '8.0K'} try: - tj = self.sa_session.query( self.app.model.TransferJob ).get( int( job.params['transfer_job_id'] ) ) + tj = self.sa_session.query(self.app.model.TransferJob).get(int(job.params['transfer_job_id'])) result_dict = tj.params result_dict['local_path'] = tj.path except Exception as e: - log.error( "Updated transfer result unavailable, using old result. Error was: %s" % str( e ) ) - result_dict = job.params[ 'result' ] - library_dataset_name = result_dict[ 'name' ] + log.error("Updated transfer result unavailable, using old result. Error was: %s" % str(e)) + result_dict = job.params['result'] + library_dataset_name = result_dict['name'] # Determine the data format (see the relevant TODO item in the manual_data_transfer plugin).. - extension = sniff.guess_ext( result_dict[ 'local_path' ], sniff_order=self.app.datatypes_registry.sniff_order ) - self._update_sample_dataset_status( protocol=job.params[ 'protocol' ], - sample_id=int( job.params[ 'sample_id' ] ), - result_dict=result_dict, - new_status=new_status, - error_msg='' ) - sample = self.sa_session.query( self.app.model.Sample ).get( int( job.params[ 'sample_id' ] ) ) - ld = self.app.model.LibraryDataset( folder=sample.folder, name=library_dataset_name ) - self.sa_session.add( ld ) + extension = sniff.guess_ext(result_dict['local_path'], sniff_order=self.app.datatypes_registry.sniff_order) + self._update_sample_dataset_status(protocol=job.params['protocol'], + sample_id=int(job.params['sample_id']), + result_dict=result_dict, + new_status=new_status, + error_msg='') + sample = self.sa_session.query(self.app.model.Sample).get(int(job.params['sample_id'])) + ld = self.app.model.LibraryDataset(folder=sample.folder, name=library_dataset_name) + self.sa_session.add(ld) self.sa_session.flush() - self.app.security_agent.copy_library_permissions( FakeTrans( self.app ), sample.folder, ld ) - ldda = self.app.model.LibraryDatasetDatasetAssociation( name=library_dataset_name, - extension=extension, - dbkey='?', - library_dataset=ld, - create_dataset=True, - sa_session=self.sa_session ) + self.app.security_agent.copy_library_permissions(FakeTrans(self.app), sample.folder, ld) + ldda = self.app.model.LibraryDatasetDatasetAssociation(name=library_dataset_name, + extension=extension, + dbkey='?', + library_dataset=ld, + create_dataset=True, + sa_session=self.sa_session) ldda.message = 'Transferred by the Data Transfer Plugin' - self.sa_session.add( ldda ) + self.sa_session.add(ldda) self.sa_session.flush() ldda.state = ldda.states.QUEUED # flushed in the set property ld.library_dataset_dataset_association_id = ldda.id - self.sa_session.add( ld ) + self.sa_session.add(ld) self.sa_session.flush() try: # Move the dataset from its temporary location - shutil.move( job.transfer_job.path, ldda.file_name ) + shutil.move(job.transfer_job.path, ldda.file_name) ldda.init_meta() for name, spec in ldda.metadata.spec.items(): - if name not in [ 'name', 'info', 'dbkey', 'base_name' ]: - if spec.get( 'default' ): - setattr( ldda.metadata, name, spec.unwrap( spec.get( 'default' ) ) ) - self.app.datatypes_registry.set_external_metadata_tool.tool_action.execute( self.app.datatypes_registry.set_external_metadata_tool, - FakeTrans( self.app, - history=sample.history, - user=sample.request.user ), - incoming={ 'input1': ldda } ) + if name not in ['name', 'info', 'dbkey', 'base_name']: + if spec.get('default'): + setattr(ldda.metadata, name, spec.unwrap(spec.get('default'))) + self.app.datatypes_registry.set_external_metadata_tool.tool_action.execute(self.app.datatypes_registry.set_external_metadata_tool, + FakeTrans(self.app, + history=sample.history, + user=sample.request.user), + incoming={'input1': ldda}) ldda.state = ldda.states.OK # TODO: not sure if this flush is necessary - self.sa_session.add( ldda ) + self.sa_session.add(ldda) self.sa_session.flush() except Exception as e: - log.exception( 'Failure preparing library dataset for finished transfer job (id: %s) via deferred job (id: %s):' % - ( str( job.transfer_job.id ), str( job.id ) ) ) + log.exception('Failure preparing library dataset for finished transfer job (id: %s) via deferred job (id: %s):' % + (str(job.transfer_job.id), str(job.id))) ldda.state = ldda.states.ERROR if sample.workflow: - log.debug( "\n\nLogging sample mappings as: %s" % sample.workflow[ 'mappings' ] ) - log.debug( "job.params: %s" % job.params ) + log.debug("\n\nLogging sample mappings as: %s" % sample.workflow['mappings']) + log.debug("job.params: %s" % job.params) # We have a workflow. Update all mappings to ldda's, and when the final one is done # execute_workflow with either the provided history, or a new one. sub_done = True rep_done = False - for k, v in sample.workflow[ 'mappings' ].items(): - if 'hda' not in v and v[ 'ds_tag' ].startswith( 'hi|' ): - sample.workflow[ 'mappings' ][ k ][ 'hda' ] = self.app.security.decode_id( v[ 'ds_tag' ][3:] ) - for key, value in sample.workflow[ 'mappings' ].items(): - if 'url' in value and value[ 'url' ] == job.params[ 'result' ][ 'url' ]: + for k, v in sample.workflow['mappings'].items(): + if 'hda' not in v and v['ds_tag'].startswith('hi|'): + sample.workflow['mappings'][k]['hda'] = self.app.security.decode_id(v['ds_tag'][3:]) + for key, value in sample.workflow['mappings'].items(): + if 'url' in value and value['url'] == job.params['result']['url']: # DBTODO Make sure all ds| mappings get the URL of the dataset, for linking to later. # If this dataset maps to what we just finished, update the ldda id in the sample. - sample.workflow[ 'mappings' ][ key ][ 'ldda' ] = ldda.id + sample.workflow['mappings'][key]['ldda'] = ldda.id rep_done = True # DBTODO replace the hi| mappings with the hda here. Just rip off the first three chars. elif 'ldda' not in value and 'hda' not in value: @@ -177,74 +177,74 @@ def run_job( self, job ): sub_done = False if sub_done and rep_done: if not sample.history: - new_history = self.app.model.History( name="New History From %s" % sample.name, user=sample.request.user ) - self.sa_session.add( new_history ) + new_history = self.app.model.History(name="New History From %s" % sample.name, user=sample.request.user) + self.sa_session.add(new_history) sample.history = new_history self.sa_session.flush() - self._execute_workflow( sample ) + self._execute_workflow(sample) # Check the workflow for substitution done-ness - self.sa_session.add( sample ) + self.sa_session.add(sample) self.sa_session.flush() elif sample.history: # We don't have a workflow, but a history was provided. # No processing, go ahead and chunk everything in the history. - if ldda.dataset.state in [ 'new', 'upload', 'queued', 'running', 'empty', 'discarded' ]: - log.error("Cannot import dataset '%s' to user history since its state is '%s'. " % ( ldda.name, ldda.dataset.state )) - elif ldda.dataset.state in [ 'ok', 'error' ]: - ldda.to_history_dataset_association( target_history=sample.history, add_to_history=True ) + if ldda.dataset.state in ['new', 'upload', 'queued', 'running', 'empty', 'discarded']: + log.error("Cannot import dataset '%s' to user history since its state is '%s'. " % (ldda.name, ldda.dataset.state)) + elif ldda.dataset.state in ['ok', 'error']: + ldda.to_history_dataset_association(target_history=sample.history, add_to_history=True) # Finished job.state = self.app.model.DeferredJob.states.OK - self.sa_session.add( job ) + self.sa_session.add(job) self.sa_session.flush() # Update the state of the relevant SampleDataset new_status = self.app.model.SampleDataset.transfer_status.COMPLETE - self._update_sample_dataset_status( protocol=job.params[ 'protocol' ], - sample_id=int( job.params[ 'sample_id' ] ), - result_dict=job.params[ 'result' ], - new_status=new_status, - error_msg='' ) + self._update_sample_dataset_status(protocol=job.params['protocol'], + sample_id=int(job.params['sample_id']), + result_dict=job.params['result'], + new_status=new_status, + error_msg='') if sample.datasets and not sample.untransferred_dataset_files: # Update the state of the sample to the sample's request type's final state. new_state = sample.request.type.final_sample_state - self._update_sample_state( sample.id, new_state ) + self._update_sample_state(sample.id, new_state) # Update the state of the request, if possible - self._update_request_state( sample.request.id ) + self._update_request_state(sample.request.id) - def _missing_params( self, params, required_params ): + def _missing_params(self, params, required_params): missing_params = [x for x in required_params if x not in params] if missing_params: - log.error( 'Job parameters missing required keys: %s' % ', '.join( missing_params ) ) + log.error('Job parameters missing required keys: %s' % ', '.join(missing_params)) return True return False - def _update_sample_dataset_status( self, protocol, sample_id, result_dict, new_status, error_msg=None ): + def _update_sample_dataset_status(self, protocol, sample_id, result_dict, new_status, error_msg=None): # result_dict looks something like: # {'url': '127.0.0.1/data/filtered_subreads.fa', 'name': 'Filtered reads'} # TODO: error checking on valid new_status value - if protocol in [ 'http', 'https' ]: - sample_dataset = self.sa_session.query( self.app.model.SampleDataset ) \ - .filter( and_( self.app.model.SampleDataset.table.c.sample_id == sample_id, - self.app.model.SampleDataset.table.c.name == result_dict[ 'name' ], - self.app.model.SampleDataset.table.c.file_path == result_dict[ 'url' ] ) ) \ + if protocol in ['http', 'https']: + sample_dataset = self.sa_session.query(self.app.model.SampleDataset) \ + .filter(and_(self.app.model.SampleDataset.table.c.sample_id == sample_id, + self.app.model.SampleDataset.table.c.name == result_dict['name'], + self.app.model.SampleDataset.table.c.file_path == result_dict['url'])) \ .first() - elif protocol in [ 'scp' ]: - sample_dataset = self.sa_session.query( self.app.model.SampleDataset ).get( int( result_dict[ 'sample_dataset_id' ] ) ) + elif protocol in ['scp']: + sample_dataset = self.sa_session.query(self.app.model.SampleDataset).get(int(result_dict['sample_dataset_id'])) sample_dataset.status = new_status sample_dataset.error_msg = error_msg - self.sa_session.add( sample_dataset ) + self.sa_session.add(sample_dataset) self.sa_session.flush() - def _update_sample_state( self, sample_id, new_state, comment=None ): - sample = self.sa_session.query( self.app.model.Sample ).get( sample_id ) + def _update_sample_state(self, sample_id, new_state, comment=None): + sample = self.sa_session.query(self.app.model.Sample).get(sample_id) if comment is None: - comment = 'Sample state set to %s' % str( new_state ) - event = self.app.model.SampleEvent( sample, new_state, comment ) - self.sa_session.add( event ) + comment = 'Sample state set to %s' % str(new_state) + event = self.app.model.SampleEvent(sample, new_state, comment) + self.sa_session.add(event) self.sa_session.flush() - def _update_request_state( self, request_id ): - request = self.sa_session.query( self.app.model.Request ).get( request_id ) + def _update_request_state(self, request_id): + request = self.sa_session.query(self.app.model.Request).get(request_id) # Make sure all the samples of the current request have the same state common_state = request.samples_have_common_state if not common_state: @@ -252,8 +252,8 @@ def _update_request_state( self, request_id ): # the final sample state, then move the request state to In-progress if request.is_complete: message = "At least 1 sample state moved from the final sample state, so now the request's state is (%s)" % request.states.SUBMITTED - event = self.app.model.RequestEvent( request, request.states.SUBMITTED, message ) - self.sa_session.add( event ) + event = self.app.model.RequestEvent(request, request.states.SUBMITTED, message) + self.sa_session.add(event) self.sa_session.flush() else: request_type_state = request.type.final_sample_state @@ -264,22 +264,22 @@ def _update_request_state( self, request_id ): else: comment = "All samples of this sequencing request are in the (%s) sample state. " % common_state.name state = request.states.SUBMITTED - event = self.app.model.RequestEvent( request, state, comment ) - self.sa_session.add( event ) + event = self.app.model.RequestEvent(request, state, comment) + self.sa_session.add(event) self.sa_session.flush() # TODO: handle email notification if it is configured to be sent when the samples are in this state. - def _execute_workflow( self, sample): + def _execute_workflow(self, sample): for key, value in sample.workflow['mappings'].items(): if 'hda' not in value and 'ldda' in value: # If HDA is already here, it's an external input, we're not copying anything. - ldda = self.sa_session.query( self.app.model.LibraryDatasetDatasetAssociation ).get( value['ldda'] ) - if ldda.dataset.state in [ 'new', 'upload', 'queued', 'running', 'empty', 'discarded' ]: - log.error("Cannot import dataset '%s' to user history since its state is '%s'. " % ( ldda.name, ldda.dataset.state )) - elif ldda.dataset.state in [ 'ok', 'error' ]: - hda = ldda.to_history_dataset_association( target_history=sample.history, add_to_history=True ) + ldda = self.sa_session.query(self.app.model.LibraryDatasetDatasetAssociation).get(value['ldda']) + if ldda.dataset.state in ['new', 'upload', 'queued', 'running', 'empty', 'discarded']: + log.error("Cannot import dataset '%s' to user history since its state is '%s'. " % (ldda.name, ldda.dataset.state)) + elif ldda.dataset.state in ['ok', 'error']: + hda = ldda.to_history_dataset_association(target_history=sample.history, add_to_history=True) sample.workflow['mappings'][key]['hda'] = hda.id - self.sa_session.add( sample ) + self.sa_session.add(sample) self.sa_session.flush() workflow_dict = sample.workflow import copy @@ -293,14 +293,14 @@ def _execute_workflow( self, sample): if not workflow: log.error("Workflow mapping failure.") return - if len( workflow.steps ) == 0: - log.error( "Workflow cannot be run because it does not have any steps" ) + if len(workflow.steps) == 0: + log.error("Workflow cannot be run because it does not have any steps") return if workflow.has_cycles: - log.error( "Workflow cannot be run because it contains cycles" ) + log.error("Workflow cannot be run because it contains cycles") return if workflow.has_errors: - log.error( "Workflow cannot be run because of validation errors in some steps" ) + log.error("Workflow cannot be run because of validation errors in some steps") return # Build the state for each step errors = {} @@ -311,12 +311,12 @@ def _execute_workflow( self, sample): # Contruct modules if step.type == 'tool' or step.type is None: # Restore the tool state for the step - step.module = module_factory.from_workflow_step( fk_trans, step ) + step.module = module_factory.from_workflow_step(fk_trans, step) # Fix any missing parameters step.upgrade_messages = step.module.check_and_update_state() # Any connected input needs to have value DummyDataset (these # are not persisted so we need to do it every time) - step.module.add_dummy_datasets( connections=step.input_connections ) + step.module.add_dummy_datasets(connections=step.input_connections) # Store state with the step step.state = step.module.state # Error dict @@ -324,24 +324,24 @@ def _execute_workflow( self, sample): errors[step.id] = step.tool_errors else: # Non-tool specific stuff? - step.module = module_factory.from_workflow_step( fk_trans, step ) + step.module = module_factory.from_workflow_step(fk_trans, step) step.state = step.module.get_runtime_state() # Connections by input name - step.input_connections_by_name = dict( ( conn.input_name, conn ) for conn in step.input_connections ) + step.input_connections_by_name = dict((conn.input_name, conn) for conn in step.input_connections) for step in workflow.steps: step.upgrade_messages = {} # Connections by input name step.input_connections_by_name = \ - dict( ( conn.input_name, conn ) for conn in step.input_connections ) + dict((conn.input_name, conn) for conn in step.input_connections) # Extract just the arguments for this step by prefix step_errors = None if step.type == 'tool' or step.type is None: - module = module_factory.from_workflow_step( fk_trans, step ) + module = module_factory.from_workflow_step(fk_trans, step) # Fix any missing parameters step.upgrade_messages = module.check_and_update_state() # Any connected input needs to have value DummyDataset (these # are not persisted so we need to do it every time) - module.add_dummy_datasets( connections=step.input_connections ) + module.add_dummy_datasets(connections=step.input_connections) # Get the tool tool = module.tool # Get the state @@ -352,33 +352,33 @@ def _execute_workflow( self, sample): workflow_invocation = self.app.model.WorkflowInvocation() workflow_invocation.workflow = workflow outputs = odict() - for i, step in enumerate( workflow.steps ): + for i, step in enumerate(workflow.steps): job = None if step.type == 'tool' or step.type is None: - tool = self.app.toolbox.get_tool( step.tool_id ) + tool = self.app.toolbox.get_tool(step.tool_id) - def callback( input, prefixed_name, **kwargs ): - if isinstance( input, DataToolParameter ): + def callback(input, prefixed_name, **kwargs): + if isinstance(input, DataToolParameter): if prefixed_name in step.input_connections_by_name: - conn = step.input_connections_by_name[ prefixed_name ] - return outputs[ conn.output_step.id ][ conn.output_name ] - visit_input_values( tool.inputs, step.state.inputs, callback ) - job, out_data = tool.execute( fk_trans, step.state.inputs, history=sample.history) - outputs[ step.id ] = out_data + conn = step.input_connections_by_name[prefixed_name] + return outputs[conn.output_step.id][conn.output_name] + visit_input_values(tool.inputs, step.state.inputs, callback) + job, out_data = tool.execute(fk_trans, step.state.inputs, history=sample.history) + outputs[step.id] = out_data for pja in step.post_job_actions: if pja.action_type in ActionBox.immediate_actions: ActionBox.execute(self.app, self.sa_session, pja, job, replacement_dict=None) else: job.add_post_job_action(pja) else: - job, out_data = step.module.execute( fk_trans, step.state) - outputs[ step.id ] = out_data + job, out_data = step.module.execute(fk_trans, step.state) + outputs[step.id] = out_data if step.id in workflow_dict['mappings']: - data = self.sa_session.query( self.app.model.HistoryDatasetAssociation ).get( workflow_dict['mappings'][str(step.id)]['hda'] ) - outputs[ step.id ]['output'] = data + data = self.sa_session.query(self.app.model.HistoryDatasetAssociation).get(workflow_dict['mappings'][str(step.id)]['hda']) + outputs[step.id]['output'] = data workflow_invocation_step = self.app.model.WorkflowInvocationStep() workflow_invocation_step.workflow_invocation = workflow_invocation workflow_invocation_step.workflow_step = step workflow_invocation_step.job = job - self.sa_session.add( workflow_invocation ) + self.sa_session.add(workflow_invocation) self.sa_session.flush() diff --git a/lib/galaxy/jobs/deferred/manual_data_transfer.py b/lib/galaxy/jobs/deferred/manual_data_transfer.py index 306f8974e213..2b3abaee68a5 100644 --- a/lib/galaxy/jobs/deferred/manual_data_transfer.py +++ b/lib/galaxy/jobs/deferred/manual_data_transfer.py @@ -6,26 +6,26 @@ from .data_transfer import DataTransfer -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -__all__ = ( 'ManualDataTransferPlugin', ) +__all__ = ('ManualDataTransferPlugin', ) -class ManualDataTransferPlugin( DataTransfer ): - def __init__( self, app ): - super( ManualDataTransferPlugin, self ).__init__( app ) +class ManualDataTransferPlugin(DataTransfer): + def __init__(self, app): + super(ManualDataTransferPlugin, self).__init__(app) - def create_job( self, trans, **kwd ): + def create_job(self, trans, **kwd): if 'sample' in kwd and 'sample_datasets' in kwd and 'external_service' in kwd and 'external_service_type' in kwd: - sample = kwd[ 'sample' ] - sample_datasets = kwd[ 'sample_datasets' ] - external_service = kwd[ 'external_service' ] - external_service_type = kwd[ 'external_service_type' ] + sample = kwd['sample'] + sample_datasets = kwd['sample_datasets'] + external_service = kwd['external_service'] + external_service_type = kwd['external_service_type'] # TODO: is there a better way to store the protocol? protocol = next(iter(external_service_type.data_transfer.keys())) - host = external_service.form_values.content[ 'host' ] - user_name = external_service.form_values.content[ 'user_name' ] - password = external_service.form_values.content[ 'password' ] + host = external_service.form_values.content['host'] + user_name = external_service.form_values.content['user_name'] + password = external_service.form_values.content['password'] # TODO: In the future, we may want to implement a way for the user to associate a selected file with one of # the run outputs configured in the section of the external service config file. The # following was a first pass at implementing something (the datatype was included in the sample_dataset_dict), @@ -44,64 +44,64 @@ def create_job( self, trans, **kwd ): sample_datasets_dict = {} for sample_dataset in sample_datasets: sample_dataset_id = sample_dataset.id - sample_dataset_dict = dict( sample_id=sample_dataset.sample.id, - name=sample_dataset.name, - file_path=sample_dataset.file_path, - status=sample_dataset.status, - error_msg=sample_dataset.error_msg, - size=sample_dataset.size, - external_service_id=sample_dataset.external_service.id ) - sample_datasets_dict[ sample_dataset_id ] = sample_dataset_dict - params = { 'type' : 'init_transfer', - 'sample_id' : sample.id, - 'sample_datasets_dict' : sample_datasets_dict, - 'protocol' : protocol, - 'host' : host, - 'user_name' : user_name, - 'password' : password } + sample_dataset_dict = dict(sample_id=sample_dataset.sample.id, + name=sample_dataset.name, + file_path=sample_dataset.file_path, + status=sample_dataset.status, + error_msg=sample_dataset.error_msg, + size=sample_dataset.size, + external_service_id=sample_dataset.external_service.id) + sample_datasets_dict[sample_dataset_id] = sample_dataset_dict + params = {'type' : 'init_transfer', + 'sample_id' : sample.id, + 'sample_datasets_dict' : sample_datasets_dict, + 'protocol' : protocol, + 'host' : host, + 'user_name' : user_name, + 'password' : password} elif 'transfer_job_id' in kwd: - params = { 'type' : 'finish_transfer', - 'protocol' : kwd[ 'result' ][ 'protocol' ], - 'sample_id' : kwd[ 'sample_id' ], - 'result' : kwd[ 'result' ], - 'transfer_job_id' : kwd[ 'transfer_job_id' ] } + params = {'type' : 'finish_transfer', + 'protocol' : kwd['result']['protocol'], + 'sample_id' : kwd['sample_id'], + 'result' : kwd['result'], + 'transfer_job_id' : kwd['transfer_job_id']} else: - log.error( 'No job was created because kwd does not include "samples" and "sample_datasets" or "transfer_job_id".' ) + log.error('No job was created because kwd does not include "samples" and "sample_datasets" or "transfer_job_id".') return - deferred_job = self.app.model.DeferredJob( state=self.app.model.DeferredJob.states.NEW, - plugin='ManualDataTransferPlugin', - params=params ) - self.sa_session.add( deferred_job ) + deferred_job = self.app.model.DeferredJob(state=self.app.model.DeferredJob.states.NEW, + plugin='ManualDataTransferPlugin', + params=params) + self.sa_session.add(deferred_job) self.sa_session.flush() - log.debug( 'Created a deferred job in the ManualDataTransferPlugin of type: %s' % params[ 'type' ] ) + log.debug('Created a deferred job in the ManualDataTransferPlugin of type: %s' % params['type']) # TODO: error reporting to caller (if possible?) - def check_job( self, job ): - if self._missing_params( job.params, [ 'type' ] ): + def check_job(self, job): + if self._missing_params(job.params, ['type']): return self.job_states.INVALID - if job.params[ 'type' ] == 'init_transfer': - if job.params[ 'protocol' ] in [ 'http', 'https' ]: - raise Exception( "Manual data transfer is not yet supported for http(s)." ) - elif job.params[ 'protocol' ] == 'scp': - if self._missing_params( job.params, [ 'protocol', 'host', 'user_name', 'password', 'sample_id', 'sample_datasets_dict' ] ): + if job.params['type'] == 'init_transfer': + if job.params['protocol'] in ['http', 'https']: + raise Exception("Manual data transfer is not yet supported for http(s).") + elif job.params['protocol'] == 'scp': + if self._missing_params(job.params, ['protocol', 'host', 'user_name', 'password', 'sample_id', 'sample_datasets_dict']): return self.job_states.INVALID # TODO: what kind of checks do we need here? return self.job_states.READY return self.job_states.WAIT - if job.params[ 'type' ] == 'finish_transfer': - if self._missing_params( job.params, [ 'transfer_job_id' ] ): + if job.params['type'] == 'finish_transfer': + if self._missing_params(job.params, ['transfer_job_id']): return self.job_states.INVALID # Get the TransferJob object and add it to the DeferredJob so we only look it up once. - if not hasattr( job, 'transfer_job' ): - job.transfer_job = self.sa_session.query( self.app.model.TransferJob ).get( int( job.params[ 'transfer_job_id' ] ) ) - state = self.app.transfer_manager.get_state( job.transfer_job ) + if not hasattr(job, 'transfer_job'): + job.transfer_job = self.sa_session.query(self.app.model.TransferJob).get(int(job.params['transfer_job_id'])) + state = self.app.transfer_manager.get_state(job.transfer_job) if not state: - log.error( 'No state for transfer job id: %s' % job.transfer_job.id ) + log.error('No state for transfer job id: %s' % job.transfer_job.id) return self.job_states.WAIT - if state[ 'state' ] in self.app.model.TransferJob.terminal_states: + if state['state'] in self.app.model.TransferJob.terminal_states: return self.job_states.READY - log.debug( "Checked on finish transfer job %s, not done yet." % job.id ) + log.debug("Checked on finish transfer job %s, not done yet." % job.id) return self.job_states.WAIT else: - log.error( 'Unknown job type for ManualDataTransferPlugin: %s' % str( job.params[ 'type' ] ) ) + log.error('Unknown job type for ManualDataTransferPlugin: %s' % str(job.params['type'])) return self.job_states.INVALID diff --git a/lib/galaxy/jobs/deferred/pacific_biosciences_smrt_portal.py b/lib/galaxy/jobs/deferred/pacific_biosciences_smrt_portal.py index 6d03ee8e110c..a0e2707c951f 100644 --- a/lib/galaxy/jobs/deferred/pacific_biosciences_smrt_portal.py +++ b/lib/galaxy/jobs/deferred/pacific_biosciences_smrt_portal.py @@ -10,123 +10,123 @@ from .data_transfer import DataTransfer -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -__all__ = ( 'SMRTPortalPlugin', ) +__all__ = ('SMRTPortalPlugin', ) -class SMRTPortalPlugin( DataTransfer ): +class SMRTPortalPlugin(DataTransfer): api_path = '/smrtportal/api' - def __init__( self, app ): - super( SMRTPortalPlugin, self ).__init__( app ) + def __init__(self, app): + super(SMRTPortalPlugin, self).__init__(app) - def create_job( self, trans, **kwd ): + def create_job(self, trans, **kwd): if 'secondary_analysis_job_id' in kwd: - sample = kwd[ 'sample' ] - smrt_job_id = kwd[ 'secondary_analysis_job_id' ] - external_service = sample.request.type.get_external_service( 'pacific_biosciences_smrt_portal' ) - external_service.load_data_transfer_settings( trans ) - http_configs = external_service.data_transfer[ trans.model.ExternalService.data_transfer_protocol.HTTP ] - if not http_configs[ 'automatic_transfer' ]: - raise Exception( "Manual data transfer using http is not yet supported." ) - smrt_host = external_service.form_values.content[ 'host' ] - external_service_type = external_service.get_external_service_type( trans ) + sample = kwd['sample'] + smrt_job_id = kwd['secondary_analysis_job_id'] + external_service = sample.request.type.get_external_service('pacific_biosciences_smrt_portal') + external_service.load_data_transfer_settings(trans) + http_configs = external_service.data_transfer[trans.model.ExternalService.data_transfer_protocol.HTTP] + if not http_configs['automatic_transfer']: + raise Exception("Manual data transfer using http is not yet supported.") + smrt_host = external_service.form_values.content['host'] + external_service_type = external_service.get_external_service_type(trans) # TODO: is there a better way to store the protocol? # external_service_type.data_transfer looks somethng like # {'http': } protocol = next(iter(external_service_type.data_transfer.keys())) results = {} for k, v in external_service.form_values.content.items(): - match = self.dataset_name_re.match( k ) or self.dataset_datatype_re.match( k ) + match = self.dataset_name_re.match(k) or self.dataset_datatype_re.match(k) if match: id, field = match.groups() if id in results: - results[ id ][ field ] = v + results[id][field] = v else: - results[ id ] = { field : v } + results[id] = {field : v} for id, attrs in results.items(): - url_template = external_service_type.run_details[ 'results_urls' ].get( id + '_name' ) - url = Template( url_template ).substitute( host=smrt_host, secondary_analysis_job_id=kwd[ 'secondary_analysis_job_id' ] ) - results[ id ][ 'url' ] = url + url_template = external_service_type.run_details['results_urls'].get(id + '_name') + url = Template(url_template).substitute(host=smrt_host, secondary_analysis_job_id=kwd['secondary_analysis_job_id']) + results[id]['url'] = url if sample.workflow: # DBTODO Make sure all ds| mappings get the URL of the dataset, for linking to later. - for k, v in sample.workflow[ 'mappings' ].items(): + for k, v in sample.workflow['mappings'].items(): if 'ds|%s' % id in v.values(): sample.workflow['mappings'][k]['url'] = url self.sa_session.add(sample) self.sa_session.flush() - params = { 'type' : 'init_transfer', - 'protocol' : protocol, - 'sample_id' : sample.id, - 'results' : results, - 'smrt_host' : smrt_host, - 'smrt_job_id' : smrt_job_id } + params = {'type' : 'init_transfer', + 'protocol' : protocol, + 'sample_id' : sample.id, + 'results' : results, + 'smrt_host' : smrt_host, + 'smrt_job_id' : smrt_job_id} # Create a new SampleDataset for each run result dataset - self._associate_untransferred_datasets_with_sample( sample, external_service, results ) + self._associate_untransferred_datasets_with_sample(sample, external_service, results) elif 'transfer_job_id' in kwd: - params = { 'type' : 'finish_transfer', - 'protocol' : kwd[ 'result' ][ 'protocol' ], - 'sample_id' : kwd[ 'sample_id' ], - 'result' : kwd[ 'result' ], - 'transfer_job_id' : kwd[ 'transfer_job_id' ] } + params = {'type' : 'finish_transfer', + 'protocol' : kwd['result']['protocol'], + 'sample_id' : kwd['sample_id'], + 'result' : kwd['result'], + 'transfer_job_id' : kwd['transfer_job_id']} else: - log.error( 'No job was created because kwd does not include "secondary_analysis_job_id" or "transfer_job_id".' ) + log.error('No job was created because kwd does not include "secondary_analysis_job_id" or "transfer_job_id".') return - deferred_job = self.app.model.DeferredJob( state=self.app.model.DeferredJob.states.NEW, - plugin='SMRTPortalPlugin', - params=params ) - self.sa_session.add( deferred_job ) + deferred_job = self.app.model.DeferredJob(state=self.app.model.DeferredJob.states.NEW, + plugin='SMRTPortalPlugin', + params=params) + self.sa_session.add(deferred_job) self.sa_session.flush() - log.debug( 'Created a deferred job in the SMRTPortalPlugin of type: %s' % params[ 'type' ] ) + log.debug('Created a deferred job in the SMRTPortalPlugin of type: %s' % params['type']) # TODO: error reporting to caller (if possible?) - def check_job( self, job ): - if self._missing_params( job.params, [ 'type' ] ): + def check_job(self, job): + if self._missing_params(job.params, ['type']): return self.job_states.INVALID - if job.params[ 'type' ] == 'init_transfer': - if self._missing_params( job.params, [ 'smrt_host', 'smrt_job_id' ] ): + if job.params['type'] == 'init_transfer': + if self._missing_params(job.params, ['smrt_host', 'smrt_job_id']): return self.job_states.INVALID - url = 'http://' + job.params[ 'smrt_host' ] + self.api_path + '/Jobs/' + job.params[ 'smrt_job_id' ] + '/Status' - r = urlopen( url ) - status = json.loads( r.read() ) + url = 'http://' + job.params['smrt_host'] + self.api_path + '/Jobs/' + job.params['smrt_job_id'] + '/Status' + r = urlopen(url) + status = json.loads(r.read()) # TODO: error handling: unexpected json or bad response, bad url, etc. - if status[ 'Code' ] == 'Completed': - log.debug( "SMRT Portal job '%s' is Completed. Initiating transfer." % job.params[ 'smrt_job_id' ] ) + if status['Code'] == 'Completed': + log.debug("SMRT Portal job '%s' is Completed. Initiating transfer." % job.params['smrt_job_id']) return self.job_states.READY return self.job_states.WAIT - if job.params[ 'type' ] == 'finish_transfer': - if self._missing_params( job.params, [ 'transfer_job_id' ] ): + if job.params['type'] == 'finish_transfer': + if self._missing_params(job.params, ['transfer_job_id']): return self.job_states.INVALID # Get the TransferJob object and add it to the DeferredJob so we only look it up once. - if not hasattr( job, 'transfer_job' ): - job.transfer_job = self.sa_session.query( self.app.model.TransferJob ).get( int( job.params[ 'transfer_job_id' ] ) ) - state = self.app.transfer_manager.get_state( job.transfer_job ) + if not hasattr(job, 'transfer_job'): + job.transfer_job = self.sa_session.query(self.app.model.TransferJob).get(int(job.params['transfer_job_id'])) + state = self.app.transfer_manager.get_state(job.transfer_job) if not state: - log.error( 'No state for transfer job id: %s' % job.transfer_job.id ) + log.error('No state for transfer job id: %s' % job.transfer_job.id) return self.job_states.WAIT - if state[ 'state' ] in self.app.model.TransferJob.terminal_states: + if state['state'] in self.app.model.TransferJob.terminal_states: return self.job_states.READY - log.debug( "Checked on finish transfer job %s, not done yet." % job.id ) + log.debug("Checked on finish transfer job %s, not done yet." % job.id) return self.job_states.WAIT else: - log.error( 'Unknown job type for SMRTPortalPlugin: %s' % str( job.params[ 'type' ] ) ) + log.error('Unknown job type for SMRTPortalPlugin: %s' % str(job.params['type'])) return self.job_states.INVALID - def _associate_untransferred_datasets_with_sample( self, sample, external_service, results_dict ): + def _associate_untransferred_datasets_with_sample(self, sample, external_service, results_dict): # results_dict looks something like: # {'dataset2': {'datatype': 'fasta', 'url': '127.0.0.1:8080/data/filtered_subreads.fa', 'name': 'Filtered reads'} } for key, val in results_dict.items(): - file_path = val[ 'url' ] + file_path = val['url'] status = self.app.model.SampleDataset.transfer_status.NOT_STARTED - name = val[ 'name' ] + name = val['name'] size = 'unknown' - sample_dataset = self.app.model.SampleDataset( sample=sample, - file_path=file_path, - status=status, - name=name, - error_msg='', - size=size, - external_service=external_service ) - self.sa_session.add( sample_dataset ) + sample_dataset = self.app.model.SampleDataset(sample=sample, + file_path=file_path, + status=status, + name=name, + error_msg='', + size=size, + external_service=external_service) + self.sa_session.add(sample_dataset) self.sa_session.flush() diff --git a/lib/galaxy/jobs/dynamic_tool_destination.py b/lib/galaxy/jobs/dynamic_tool_destination.py index 5c2187087be3..34117d57903c 100755 --- a/lib/galaxy/jobs/dynamic_tool_destination.py +++ b/lib/galaxy/jobs/dynamic_tool_destination.py @@ -1243,7 +1243,7 @@ def map_tool_to_destination( if priority in config['default_destination']['priority']: destination = config['default_destination']['priority'][priority] else: - destination = ( config['default_destination']['priority'][default_priority]) + destination = (config['default_destination']['priority'][default_priority]) config = config['tools'] if str(tool.old_id) in config: if 'rules' in config[str(tool.old_id)]: diff --git a/lib/galaxy/jobs/error_level.py b/lib/galaxy/jobs/error_level.py index 5b4d6340caf8..e8bce87f06bf 100644 --- a/lib/galaxy/jobs/error_level.py +++ b/lib/galaxy/jobs/error_level.py @@ -3,7 +3,7 @@ # These determine stdio-based error levels from matching on regular expressions # and exit codes. They are meant to be used comparatively, such as showing # that warning < fatal. This is really meant to just be an enum. -class StdioErrorLevel( object ): +class StdioErrorLevel(object): NO_ERROR = 0 LOG = 1 WARNING = 2 @@ -17,9 +17,8 @@ class StdioErrorLevel( object ): } @staticmethod - def desc( error_level ): + def desc(error_level): err_msg = "Unknown error" - if ( error_level > 0 and - error_level <= StdioErrorLevel.MAX ): - err_msg = StdioErrorLevel.descs[ error_level ] + if error_level > 0 and error_level <= StdioErrorLevel.MAX: + err_msg = StdioErrorLevel.descs[error_level] return err_msg diff --git a/lib/galaxy/jobs/handler.py b/lib/galaxy/jobs/handler.py index 93c80c438932..399987f4ba25 100644 --- a/lib/galaxy/jobs/handler.py +++ b/lib/galaxy/jobs/handler.py @@ -16,41 +16,42 @@ from galaxy.jobs import JobWrapper, TaskWrapper, JobDestination from galaxy.jobs.mapper import JobNotReadyException -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) # States for running a job. These are NOT the same as data states JOB_WAIT, JOB_ERROR, JOB_INPUT_ERROR, JOB_INPUT_DELETED, JOB_READY, JOB_DELETED, JOB_ADMIN_DELETED, JOB_USER_OVER_QUOTA, JOB_USER_OVER_TOTAL_WALLTIME = 'wait', 'error', 'input_error', 'input_deleted', 'ready', 'deleted', 'admin_deleted', 'user_over_quota', 'user_over_total_walltime' DEFAULT_JOB_PUT_FAILURE_MESSAGE = 'Unable to run job due to a misconfiguration of the Galaxy job running system. Please contact a site administrator.' -class JobHandler( object ): +class JobHandler(object): """ Handle the preparation, running, tracking, and finishing of jobs """ - def __init__( self, app ): + + def __init__(self, app): self.app = app # The dispatcher launches the underlying job runners - self.dispatcher = DefaultJobDispatcher( app ) + self.dispatcher = DefaultJobDispatcher(app) # Queues for starting and stopping jobs - self.job_queue = JobHandlerQueue( app, self.dispatcher ) - self.job_stop_queue = JobHandlerStopQueue( app, self.dispatcher ) + self.job_queue = JobHandlerQueue(app, self.dispatcher) + self.job_stop_queue = JobHandlerStopQueue(app, self.dispatcher) - def start( self ): + def start(self): self.job_queue.start() - def shutdown( self ): + def shutdown(self): self.job_queue.shutdown() self.job_stop_queue.shutdown() -class JobHandlerQueue( object ): +class JobHandlerQueue(object): """ Job Handler's Internal Queue, this is what actually implements waiting for jobs to be runnable and dispatching to a JobRunner. """ STOP_SIGNAL = object() - def __init__( self, app, dispatcher ): + def __init__(self, app, dispatcher): """Initializes the Job Handler Queue, creates (unstarted) monitoring thread""" self.app = app self.dispatcher = dispatcher @@ -73,10 +74,10 @@ def __init__( self, app, dispatcher ): # Helper for interruptable sleep self.sleeper = Sleeper() self.running = True - self.monitor_thread = threading.Thread( name="JobHandlerQueue.monitor_thread", target=self.__monitor ) - self.monitor_thread.setDaemon( True ) + self.monitor_thread = threading.Thread(name="JobHandlerQueue.monitor_thread", target=self.__monitor) + self.monitor_thread.setDaemon(True) - def start( self ): + def start(self): """ Starts the JobHandler's thread after checking for any unhandled jobs. """ @@ -84,16 +85,16 @@ def start( self ): self.__check_jobs_at_startup() # Start the queue self.monitor_thread.start() - log.info( "job handler queue started" ) + log.info("job handler queue started") - def job_wrapper( self, job, use_persisted_destination=False ): - return JobWrapper( job, self, use_persisted_destination=use_persisted_destination ) + def job_wrapper(self, job, use_persisted_destination=False): + return JobWrapper(job, self, use_persisted_destination=use_persisted_destination) - def job_pair_for_id( self, id ): - job = self.sa_session.query( model.Job ).get( id ) - return job, self.job_wrapper( job, use_persisted_destination=True ) + def job_pair_for_id(self, id): + job = self.sa_session.query(model.Job).get(id) + return job, self.job_wrapper(job, use_persisted_destination=True) - def __check_jobs_at_startup( self ): + def __check_jobs_at_startup(self): """ Checks all jobs that are in the 'new', 'queued' or 'running' state in the database and requeues or cleans up as necessary. Only run as the @@ -102,75 +103,75 @@ def __check_jobs_at_startup( self ): """ jobs_at_startup = [] if self.track_jobs_in_database: - in_list = ( model.Job.states.QUEUED, - model.Job.states.RUNNING ) + in_list = (model.Job.states.QUEUED, + model.Job.states.RUNNING) else: - in_list = ( model.Job.states.NEW, - model.Job.states.QUEUED, - model.Job.states.RUNNING ) + in_list = (model.Job.states.NEW, + model.Job.states.QUEUED, + model.Job.states.RUNNING) if self.app.config.user_activation_on: - jobs_at_startup = self.sa_session.query( model.Job ).enable_eagerloads( False ) \ - .outerjoin( model.User ) \ - .filter( model.Job.state.in_( in_list ) & - ( model.Job.handler == self.app.config.server_name ) & - or_( ( model.Job.user_id == null() ), ( model.User.active == true() ) ) ).all() + jobs_at_startup = self.sa_session.query(model.Job).enable_eagerloads(False) \ + .outerjoin(model.User) \ + .filter(model.Job.state.in_(in_list) & + (model.Job.handler == self.app.config.server_name) & + or_((model.Job.user_id == null()), (model.User.active == true()))).all() else: - jobs_at_startup = self.sa_session.query( model.Job ).enable_eagerloads( False ) \ - .filter( model.Job.state.in_( in_list ) & - ( model.Job.handler == self.app.config.server_name ) ).all() + jobs_at_startup = self.sa_session.query(model.Job).enable_eagerloads(False) \ + .filter(model.Job.state.in_(in_list) & + (model.Job.handler == self.app.config.server_name)).all() for job in jobs_at_startup: - if not self.app.toolbox.has_tool( job.tool_id, job.tool_version, exact=True ): - log.warning( "(%s) Tool '%s' removed from tool config, unable to recover job" % ( job.id, job.tool_id ) ) - self.job_wrapper( job ).fail( 'This tool was disabled before the job completed. Please contact your Galaxy administrator.' ) + if not self.app.toolbox.has_tool(job.tool_id, job.tool_version, exact=True): + log.warning("(%s) Tool '%s' removed from tool config, unable to recover job" % (job.id, job.tool_id)) + self.job_wrapper(job).fail('This tool was disabled before the job completed. Please contact your Galaxy administrator.') elif job.job_runner_name is not None and job.job_runner_external_id is None: # This could happen during certain revisions of Galaxy where a runner URL was persisted before the job was dispatched to a runner. - log.debug( "(%s) Job runner assigned but no external ID recorded, adding to the job handler queue" % job.id ) + log.debug("(%s) Job runner assigned but no external ID recorded, adding to the job handler queue" % job.id) job.job_runner_name = None if self.track_jobs_in_database: - job.set_state( model.Job.states.NEW ) + job.set_state(model.Job.states.NEW) else: - self.queue.put( ( job.id, job.tool_id ) ) + self.queue.put((job.id, job.tool_id)) elif job.job_runner_name is not None and job.job_runner_external_id is not None and job.destination_id is None: # This is the first start after upgrading from URLs to destinations, convert the URL to a destination and persist - job_wrapper = self.job_wrapper( job ) + job_wrapper = self.job_wrapper(job) job_destination = self.dispatcher.url_to_destination(job.job_runner_name) if job_destination.id is None: job_destination.id = 'legacy_url' job_wrapper.set_job_destination(job_destination, job.job_runner_external_id) - self.dispatcher.recover( job, job_wrapper ) + self.dispatcher.recover(job, job_wrapper) log.info('(%s) Converted job from a URL to a destination and recovered' % (job.id)) elif job.job_runner_name is None: # Never (fully) dispatched - log.debug( "(%s) No job runner assigned and job still in '%s' state, adding to the job handler queue" % ( job.id, job.state ) ) + log.debug("(%s) No job runner assigned and job still in '%s' state, adding to the job handler queue" % (job.id, job.state)) if self.track_jobs_in_database: - job.set_state( model.Job.states.NEW ) + job.set_state(model.Job.states.NEW) else: - self.queue.put( ( job.id, job.tool_id ) ) + self.queue.put((job.id, job.tool_id)) else: # Already dispatched and running - job_wrapper = self.__recover_job_wrapper( job ) - self.dispatcher.recover( job, job_wrapper ) + job_wrapper = self.__recover_job_wrapper(job) + self.dispatcher.recover(job, job_wrapper) if self.sa_session.dirty: self.sa_session.flush() def __recover_job_wrapper(self, job): # Already dispatched and running - job_wrapper = self.job_wrapper( job ) + job_wrapper = self.job_wrapper(job) # Use the persisted destination as its params may differ from # what's in the job_conf xml job_destination = JobDestination(id=job.destination_id, runner=job.job_runner_name, params=job.destination_params) # resubmits are not persisted (it's a good thing) so they # should be added back to the in-memory destination on startup try: - config_job_destination = self.app.job_config.get_destination( job.destination_id ) + config_job_destination = self.app.job_config.get_destination(job.destination_id) job_destination.resubmit = config_job_destination.resubmit except KeyError: - log.debug( '(%s) Recovered destination id (%s) does not exist in job config (but this may be normal in the case of a dynamically generated destination)', job.id, job.destination_id ) + log.debug('(%s) Recovered destination id (%s) does not exist in job config (but this may be normal in the case of a dynamically generated destination)', job.id, job.destination_id) job_wrapper.job_runner_mapper.cached_job_destination = job_destination return job_wrapper - def __monitor( self ): + def __monitor(self): """ Continually iterate the waiting jobs, checking is each is ready to run and dispatching if so. @@ -182,11 +183,11 @@ def __monitor( self ): if not self.app.job_manager.job_lock: self.__monitor_step() except: - log.exception( "Exception in monitor_step" ) + log.exception("Exception in monitor_step") # Sleep - self.sleeper.sleep( 1 ) + self.sleeper.sleep(1) - def __monitor_step( self ): + def __monitor_step(self): """ Called repeatedly by `monitor` to process waiting jobs. Gets any new jobs (either from the database or from its own queue), then iterates @@ -208,11 +209,11 @@ def __monitor_step( self ): .join(model.JobToInputDatasetAssociation) \ .join(model.HistoryDatasetAssociation) \ .join(model.Dataset) \ - .filter(and_( (model.Job.state == model.Job.states.NEW ), - or_( ( model.HistoryDatasetAssociation._state == model.HistoryDatasetAssociation.states.FAILED_METADATA ), - ( model.HistoryDatasetAssociation.deleted == true() ), - ( model.Dataset.state != model.Dataset.states.OK ), - ( model.Dataset.deleted == true() ) ) ) ).subquery() + .filter(and_((model.Job.state == model.Job.states.NEW), + or_((model.HistoryDatasetAssociation._state == model.HistoryDatasetAssociation.states.FAILED_METADATA), + (model.HistoryDatasetAssociation.deleted == true()), + (model.Dataset.state != model.Dataset.states.OK), + (model.Dataset.deleted == true())))).subquery() ldda_not_ready = self.sa_session.query(model.Job.id).enable_eagerloads(False) \ .join(model.JobToInputLibraryDatasetAssociation) \ .join(model.LibraryDatasetDatasetAssociation) \ @@ -224,7 +225,7 @@ def __monitor_step( self ): (model.Dataset.deleted == true())))).subquery() if self.app.config.user_activation_on: jobs_to_check = self.sa_session.query(model.Job).enable_eagerloads(False) \ - .outerjoin( model.User ) \ + .outerjoin(model.User) \ .filter(and_((model.Job.state == model.Job.states.NEW), or_((model.Job.user_id == null()), (model.User.active == true())), (model.Job.handler == self.app.config.server_name), @@ -247,7 +248,7 @@ def __monitor_step( self ): # Get job objects and append to watch queue for any which were # previously waiting for job_id in self.waiting_jobs: - jobs_to_check.append( self.sa_session.query( model.Job ).get( job_id ) ) + jobs_to_check.append(self.sa_session.query(model.Job).get(job_id)) try: while 1: message = self.queue.get_nowait() @@ -256,19 +257,19 @@ def __monitor_step( self ): # Unpack the message job_id, tool_id = message # Get the job object and append to watch queue - jobs_to_check.append( self.sa_session.query( model.Job ).get( job_id ) ) + jobs_to_check.append(self.sa_session.query(model.Job).get(job_id)) except Empty: pass # Ensure that we get new job counts on each iteration self.__clear_job_count() # Check resubmit jobs first so that limits of new jobs will still be enforced for job in resubmit_jobs: - log.debug( '(%s) Job was resubmitted and is being dispatched immediately', job.id ) + log.debug('(%s) Job was resubmitted and is being dispatched immediately', job.id) # Reassemble resubmit job destination from persisted value - jw = self.__recover_job_wrapper( job ) + jw = self.__recover_job_wrapper(job) if jw.is_ready_for_resubmission(job): self.increase_running_job_count(job.user_id, jw.job_destination.id) - self.dispatcher.put( jw ) + self.dispatcher.put(jw) # Iterate over new and waiting jobs and look for any that are # ready to run new_waiting_jobs = [] @@ -276,40 +277,40 @@ def __monitor_step( self ): try: # Check the job's dependencies, requeue if they're not done. # Some of these states will only happen when using the in-memory job queue - job_state = self.__check_job_state( job ) + job_state = self.__check_job_state(job) if job_state == JOB_WAIT: - new_waiting_jobs.append( job.id ) + new_waiting_jobs.append(job.id) elif job_state == JOB_INPUT_ERROR: - log.info( "(%d) Job unable to run: one or more inputs in error state" % job.id ) + log.info("(%d) Job unable to run: one or more inputs in error state" % job.id) elif job_state == JOB_INPUT_DELETED: - log.info( "(%d) Job unable to run: one or more inputs deleted" % job.id ) + log.info("(%d) Job unable to run: one or more inputs deleted" % job.id) elif job_state == JOB_READY: - self.dispatcher.put( self.job_wrappers.pop( job.id ) ) - log.info( "(%d) Job dispatched" % job.id ) + self.dispatcher.put(self.job_wrappers.pop(job.id)) + log.info("(%d) Job dispatched" % job.id) elif job_state == JOB_DELETED: - log.info( "(%d) Job deleted by user while still queued" % job.id ) + log.info("(%d) Job deleted by user while still queued" % job.id) elif job_state == JOB_ADMIN_DELETED: - log.info( "(%d) Job deleted by admin while still queued" % job.id ) - elif job_state in ( JOB_USER_OVER_QUOTA, - JOB_USER_OVER_TOTAL_WALLTIME ): + log.info("(%d) Job deleted by admin while still queued" % job.id) + elif job_state in (JOB_USER_OVER_QUOTA, + JOB_USER_OVER_TOTAL_WALLTIME): if job_state == JOB_USER_OVER_QUOTA: - log.info( "(%d) User (%s) is over quota: job paused" % ( job.id, job.user_id ) ) + log.info("(%d) User (%s) is over quota: job paused" % (job.id, job.user_id)) else: - log.info( "(%d) User (%s) is over total walltime limit: job paused" % ( job.id, job.user_id ) ) + log.info("(%d) User (%s) is over total walltime limit: job paused" % (job.id, job.user_id)) - job.set_state( model.Job.states.PAUSED ) + job.set_state(model.Job.states.PAUSED) for dataset_assoc in job.output_datasets + job.output_library_datasets: dataset_assoc.dataset.dataset.state = model.Dataset.states.PAUSED dataset_assoc.dataset.info = "Execution of this dataset's job is paused because you were over your disk quota at the time it was ready to run" - self.sa_session.add( dataset_assoc.dataset.dataset ) - self.sa_session.add( job ) + self.sa_session.add(dataset_assoc.dataset.dataset) + self.sa_session.add(job) elif job_state == JOB_ERROR: - log.error( "(%d) Error checking job readiness" % job.id ) + log.error("(%d) Error checking job readiness" % job.id) else: - log.error( "(%d) Job in unknown state '%s'" % ( job.id, job_state ) ) - new_waiting_jobs.append( job.id ) + log.error("(%d) Job in unknown state '%s'" % (job.id, job_state)) + new_waiting_jobs.append(job.id) except Exception: - log.exception( "failure running job %d", job.id ) + log.exception("failure running job %d", job.id) # Update the waiting list if not self.track_jobs_in_database: self.waiting_jobs = new_waiting_jobs @@ -322,7 +323,7 @@ def __monitor_step( self ): # Done with the session self.sa_session.remove() - def __check_job_state( self, job ): + def __check_job_state(self, job): """ Check if a job is ready to run by verifying that each of its input datasets is ready (specifically in the OK state). If any input dataset @@ -333,7 +334,7 @@ def __check_job_state( self, job ): datasets are still being prepared. """ if not self.track_jobs_in_database: - in_memory_not_ready_state = self.__verify_in_memory_job_inputs( job ) + in_memory_not_ready_state = self.__verify_in_memory_job_inputs(job) if in_memory_not_ready_state: return in_memory_not_ready_state @@ -342,22 +343,22 @@ def __check_job_state( self, job ): # Create the job wrapper so that the destination can be set job_id = job.id - job_wrapper = self.job_wrappers.get( job_id, None ) + job_wrapper = self.job_wrappers.get(job_id, None) if not job_wrapper: - job_wrapper = self.job_wrapper( job ) - self.job_wrappers[ job_id ] = job_wrapper + job_wrapper = self.job_wrapper(job) + self.job_wrappers[job_id] = job_wrapper # If state == JOB_READY, assume job_destination also set - otherwise # in case of various error or cancelled states do not assume # destination has been set. - state, job_destination = self.__verify_job_ready( job, job_wrapper ) + state, job_destination = self.__verify_job_ready(job, job_wrapper) if state == JOB_READY: # PASS. increase usage by one job (if caching) so that multiple jobs aren't dispatched on this queue iteration - self.increase_running_job_count(job.user_id, job_destination.id ) + self.increase_running_job_count(job.user_id, job_destination.id) return state - def __verify_job_ready( self, job, job_wrapper ): + def __verify_job_ready(self, job, job_wrapper): """ Compute job destination and verify job is ready at that destination by checking job limits and quota. If this method return a job state of JOB_READY - it MUST also return a job @@ -369,39 +370,39 @@ def __verify_job_ready( self, job, job_wrapper ): # Cause the job_destination to be set and cached by the mapper job_destination = job_wrapper.job_destination except AssertionError as e: - log.warning( "(%s) Tool '%s' removed from tool config, unable to run job" % ( job.id, job.tool_id ) ) - job_wrapper.fail( e ) + log.warning("(%s) Tool '%s' removed from tool config, unable to run job" % (job.id, job.tool_id)) + job_wrapper.fail(e) return JOB_ERROR, job_destination except JobNotReadyException as e: job_state = e.job_state or JOB_WAIT return job_state, None except Exception as e: - failure_message = getattr( e, 'failure_message', DEFAULT_JOB_PUT_FAILURE_MESSAGE ) + failure_message = getattr(e, 'failure_message', DEFAULT_JOB_PUT_FAILURE_MESSAGE) if failure_message == DEFAULT_JOB_PUT_FAILURE_MESSAGE: - log.exception( 'Failed to generate job destination' ) + log.exception('Failed to generate job destination') else: - log.debug( "Intentionally failing job with message (%s)" % failure_message ) - job_wrapper.fail( failure_message ) + log.debug("Intentionally failing job with message (%s)" % failure_message) + job_wrapper.fail(failure_message) return JOB_ERROR, job_destination # job is ready to run, check limits # TODO: these checks should be refactored to minimize duplication and made more modular/pluggable - state = self.__check_destination_jobs( job, job_wrapper ) + state = self.__check_destination_jobs(job, job_wrapper) if state == JOB_READY: - state = self.__check_user_jobs( job, job_wrapper ) + state = self.__check_user_jobs(job, job_wrapper) if state == JOB_READY and self.app.config.enable_quotas: - quota = self.app.quota_agent.get_quota( job.user ) + quota = self.app.quota_agent.get_quota(job.user) if quota is not None: try: - usage = self.app.quota_agent.get_usage( user=job.user, history=job.history ) + usage = self.app.quota_agent.get_usage(user=job.user, history=job.history) if usage > quota: return JOB_USER_OVER_QUOTA, job_destination except AssertionError as e: pass # No history, should not happen with an anon user # Check total walltime limits - if ( state == JOB_READY and - "delta" in self.app.job_config.limits.total_walltime ): - jobs_to_check = self.sa_session.query( model.Job ).filter( + if (state == JOB_READY and + "delta" in self.app.job_config.limits.total_walltime): + jobs_to_check = self.sa_session.query(model.Job).filter( model.Job.user_id == job.user.id, model.Job.update_time >= datetime.datetime.now() - datetime.timedelta( @@ -416,7 +417,7 @@ def __verify_job_ready( self, job, job_wrapper ): finished = None for history in sorted( job.state_history, - key=lambda history: history.update_time ): + key=lambda history: history.update_time): if history.state == "running": started = history.create_time elif history.state == "ok": @@ -429,7 +430,7 @@ def __verify_job_ready( self, job, job_wrapper ): return state, job_destination - def __verify_in_memory_job_inputs( self, job ): + def __verify_in_memory_job_inputs(self, job): """ Perform the same checks that happen via SQL for in-memory managed jobs. """ @@ -443,23 +444,23 @@ def __verify_in_memory_job_inputs( self, job ): continue # don't run jobs for which the input dataset was deleted if idata.deleted: - self.job_wrappers.pop(job.id, self.job_wrapper( job )).fail( "input data %s (file: %s) was deleted before the job started" % ( idata.hid, idata.file_name ) ) + self.job_wrappers.pop(job.id, self.job_wrapper(job)).fail("input data %s (file: %s) was deleted before the job started" % (idata.hid, idata.file_name)) return JOB_INPUT_DELETED # an error in the input data causes us to bail immediately elif idata.state == idata.states.ERROR: - self.job_wrappers.pop(job.id, self.job_wrapper( job )).fail( "input data %s is in error state" % ( idata.hid ) ) + self.job_wrappers.pop(job.id, self.job_wrapper(job)).fail("input data %s is in error state" % (idata.hid)) return JOB_INPUT_ERROR elif idata.state == idata.states.FAILED_METADATA: - self.job_wrappers.pop(job.id, self.job_wrapper( job )).fail( "input data %s failed to properly set metadata" % ( idata.hid ) ) + self.job_wrappers.pop(job.id, self.job_wrapper(job)).fail("input data %s failed to properly set metadata" % (idata.hid)) return JOB_INPUT_ERROR - elif idata.state != idata.states.OK and not ( idata.state == idata.states.SETTING_METADATA and job.tool_id is not None and job.tool_id == self.app.datatypes_registry.set_external_metadata_tool.id ): + elif idata.state != idata.states.OK and not (idata.state == idata.states.SETTING_METADATA and job.tool_id is not None and job.tool_id == self.app.datatypes_registry.set_external_metadata_tool.id): # need to requeue return JOB_WAIT # All inputs ready to go. return None - def __clear_job_count( self ): + def __clear_job_count(self): self.user_job_count = None self.user_job_count_per_destination = None self.total_job_count_per_destination = None @@ -479,7 +480,7 @@ def get_user_job_count(self, user_id): rval += row[0] return rval - def __cache_user_job_count( self ): + def __cache_user_job_count(self): # Cache the job count if necessary if self.user_job_count is None and self.app.config.cache_user_job_count: self.user_job_count = {} @@ -545,7 +546,7 @@ def increase_running_job_count(self, user_id, destination_id): self.total_job_count_per_destination = {} self.total_job_count_per_destination[destination_id] = self.total_job_count_per_destination.get(destination_id, 0) + 1 - def __check_user_jobs( self, job, job_wrapper ): + def __check_user_jobs(self, job, job_wrapper): # TODO: Update output datasets' _state = LIMITED or some such new # state, so the UI can reflect what jobs are waiting due to concurrency # limits @@ -571,7 +572,7 @@ def __check_user_jobs( self, job, job_wrapper ): if tag in self.app.job_config.limits.destination_user_concurrent_jobs: # Only if there's a limit defined for this tag count = 0 - for id in [ d.id for d in self.app.job_config.get_destinations(tag) ]: + for id in [d.id for d in self.app.job_config.get_destinations(tag)]: # Add up the aggregate job total for this tag count += count_per_id.get(id, 0) if count >= self.app.job_config.limits.destination_user_concurrent_jobs[tag]: @@ -579,17 +580,17 @@ def __check_user_jobs( self, job, job_wrapper ): elif job.galaxy_session: # Anonymous users only get the hard limit if self.app.job_config.limits.anonymous_user_concurrent_jobs: - count = self.sa_session.query( model.Job ).enable_eagerloads( False ) \ - .filter( and_( model.Job.session_id == job.galaxy_session.id, - or_( model.Job.state == model.Job.states.RUNNING, - model.Job.state == model.Job.states.QUEUED ) ) ).count() + count = self.sa_session.query(model.Job).enable_eagerloads(False) \ + .filter(and_(model.Job.session_id == job.galaxy_session.id, + or_(model.Job.state == model.Job.states.RUNNING, + model.Job.state == model.Job.states.QUEUED))).count() if count >= self.app.job_config.limits.anonymous_user_concurrent_jobs: return JOB_WAIT else: - log.warning( 'Job %s is not associated with a user or session so job concurrency limit cannot be checked.' % job.id ) + log.warning('Job %s is not associated with a user or session so job concurrency limit cannot be checked.' % job.id) return JOB_READY - def __cache_total_job_count_per_destination( self ): + def __cache_total_job_count_per_destination(self): # Cache the job count if necessary if self.total_job_count_per_destination is None: self.total_job_count_per_destination = {} @@ -606,7 +607,7 @@ def get_total_job_count_per_destination(self): # insofar as FIFO would be fair...) return self.total_job_count_per_destination - def __check_destination_jobs( self, job, job_wrapper ): + def __check_destination_jobs(self, job, job_wrapper): if self.app.job_config.limits.destination_total_concurrent_jobs: id = job_wrapper.job_destination.id count_per_id = self.get_total_job_count_per_destination() @@ -622,41 +623,41 @@ def __check_destination_jobs( self, job, job_wrapper ): if tag in self.app.job_config.limits.destination_total_concurrent_jobs: # Only if there's a limit defined for this tag count = 0 - for id in [ d.id for d in self.app.job_config.get_destinations(tag) ]: + for id in [d.id for d in self.app.job_config.get_destinations(tag)]: # Add up the aggregate job total for this tag count += count_per_id.get(id, 0) if count >= self.app.job_config.limits.destination_total_concurrent_jobs[tag]: return JOB_WAIT return JOB_READY - def put( self, job_id, tool_id ): + def put(self, job_id, tool_id): """Add a job to the queue (by job identifier)""" if not self.track_jobs_in_database: - self.queue.put( ( job_id, tool_id ) ) + self.queue.put((job_id, tool_id)) self.sleeper.wake() - def shutdown( self ): + def shutdown(self): """Attempts to gracefully shut down the worker thread""" if self.parent_pid != os.getpid(): # We're not the real job queue, do nothing return else: - log.info( "sending stop signal to worker thread" ) + log.info("sending stop signal to worker thread") self.running = False if not self.app.config.track_jobs_in_database: - self.queue.put( self.STOP_SIGNAL ) + self.queue.put(self.STOP_SIGNAL) self.sleeper.wake() - log.info( "job handler queue stopped" ) + log.info("job handler queue stopped") self.dispatcher.shutdown() -class JobHandlerStopQueue( object ): +class JobHandlerStopQueue(object): """ A queue for jobs which need to be terminated prematurely. """ STOP_SIGNAL = object() - def __init__( self, app, dispatcher ): + def __init__(self, app, dispatcher): self.app = app self.dispatcher = dispatcher @@ -674,26 +675,26 @@ def __init__( self, app, dispatcher ): # Helper for interruptable sleep self.sleeper = Sleeper() self.running = True - self.monitor_thread = threading.Thread( name="JobHandlerStopQueue.monitor_thread", target=self.monitor ) - self.monitor_thread.setDaemon( True ) + self.monitor_thread = threading.Thread(name="JobHandlerStopQueue.monitor_thread", target=self.monitor) + self.monitor_thread.setDaemon(True) self.monitor_thread.start() - log.info( "job handler stop queue started" ) + log.info("job handler stop queue started") - def monitor( self ): + def monitor(self): """ Continually iterate the waiting jobs, stop any that are found. """ # HACK: Delay until after forking, we need a way to do post fork notification!!! - time.sleep( 10 ) + time.sleep(10) while self.running: try: self.monitor_step() except: - log.exception( "Exception in monitor_step" ) + log.exception("Exception in monitor_step") # Sleep - self.sleeper.sleep( 1 ) + self.sleeper.sleep(1) - def monitor_step( self ): + def monitor_step(self): """ Called repeatedly by `monitor` to stop jobs. """ @@ -703,11 +704,11 @@ def monitor_step( self ): # Clear the session so we get fresh states for job and all datasets self.sa_session.expunge_all() # Fetch all new jobs - newly_deleted_jobs = self.sa_session.query( model.Job ).enable_eagerloads( False ) \ - .filter( ( model.Job.state == model.Job.states.DELETED_NEW ) & - ( model.Job.handler == self.app.config.server_name ) ).all() + newly_deleted_jobs = self.sa_session.query(model.Job).enable_eagerloads(False) \ + .filter((model.Job.state == model.Job.states.DELETED_NEW) & + (model.Job.handler == self.app.config.server_name)).all() for job in newly_deleted_jobs: - jobs_to_check.append( ( job, job.stderr ) ) + jobs_to_check.append((job, job.stderr)) # Also pull from the queue (in the case of Administrative stopped jobs) try: while 1: @@ -717,14 +718,14 @@ def monitor_step( self ): # Unpack the message job_id, error_msg = message # Get the job object and append to watch queue - jobs_to_check.append( ( self.sa_session.query( model.Job ).get( job_id ), error_msg ) ) + jobs_to_check.append((self.sa_session.query(model.Job).get(job_id), error_msg)) except Empty: pass for job, error_msg in jobs_to_check: - if ( job.state not in - ( job.states.DELETED_NEW, - job.states.DELETED ) and - job.finished ): + if (job.state not in + (job.states.DELETED_NEW, + job.states.DELETED) and + job.finished): # terminated before it got here log.debug('Job %s already finished, not deleting or stopping', job.id) continue @@ -732,50 +733,50 @@ def monitor_step( self ): if error_msg is not None: final_state = job.states.ERROR job.info = error_msg - job.set_final_state( final_state ) - self.sa_session.add( job ) + job.set_final_state(final_state) + self.sa_session.add(job) self.sa_session.flush() if job.job_runner_name is not None: # tell the dispatcher to stop the job - self.dispatcher.stop( job ) + self.dispatcher.stop(job) - def put( self, job_id, error_msg=None ): + def put(self, job_id, error_msg=None): if not self.app.config.track_jobs_in_database: - self.queue.put( ( job_id, error_msg ) ) + self.queue.put((job_id, error_msg)) - def shutdown( self ): + def shutdown(self): """Attempts to gracefully shut down the worker thread""" if self.parent_pid != os.getpid(): # We're not the real job queue, do nothing return else: - log.info( "sending stop signal to worker thread" ) + log.info("sending stop signal to worker thread") self.running = False if not self.app.config.track_jobs_in_database: - self.queue.put( self.STOP_SIGNAL ) + self.queue.put(self.STOP_SIGNAL) self.sleeper.wake() - log.info( "job handler stop queue stopped" ) + log.info("job handler stop queue stopped") -class DefaultJobDispatcher( object ): +class DefaultJobDispatcher(object): - def __init__( self, app ): + def __init__(self, app): self.app = app - self.job_runners = self.app.job_config.get_job_runner_plugins( self.app.config.server_name ) + self.job_runners = self.app.job_config.get_job_runner_plugins(self.app.config.server_name) # Once plugins are loaded, all job destinations that were created from # URLs can have their URL params converted to the destination's param # dict by the plugin. self.app.job_config.convert_legacy_destinations(self.job_runners) - log.debug( "Loaded job runners plugins: " + ':'.join( self.job_runners.keys() ) ) + log.debug("Loaded job runners plugins: " + ':'.join(self.job_runners.keys())) - def __get_runner_name( self, job_wrapper ): + def __get_runner_name(self, job_wrapper): if job_wrapper.can_split(): runner_name = "tasks" else: runner_name = job_wrapper.job_destination.runner return runner_name - def url_to_destination( self, url ): + def url_to_destination(self, url): """This is used by the runner mapper (a.k.a. dynamic runner) and recovery methods to have runners convert URLs to destinations. @@ -788,20 +789,20 @@ def url_to_destination( self, url ): log.exception("Unable to convert legacy job runner URL '%s' to job destination, destination will be the '%s' runner with no params", url, runner_name) return JobDestination(runner=runner_name) - def put( self, job_wrapper ): - runner_name = self.__get_runner_name( job_wrapper ) + def put(self, job_wrapper): + runner_name = self.__get_runner_name(job_wrapper) try: if isinstance(job_wrapper, TaskWrapper): # DBTODO Refactor - log.debug( "(%s) Dispatching task %s to %s runner" % ( job_wrapper.job_id, job_wrapper.task_id, runner_name ) ) + log.debug("(%s) Dispatching task %s to %s runner" % (job_wrapper.job_id, job_wrapper.task_id, runner_name)) else: - log.debug( "(%s) Dispatching to %s runner" % ( job_wrapper.job_id, runner_name ) ) - self.job_runners[runner_name].put( job_wrapper ) + log.debug("(%s) Dispatching to %s runner" % (job_wrapper.job_id, runner_name)) + self.job_runners[runner_name].put(job_wrapper) except KeyError: - log.error( 'put(): (%s) Invalid job runner: %s' % ( job_wrapper.job_id, runner_name ) ) - job_wrapper.fail( DEFAULT_JOB_PUT_FAILURE_MESSAGE ) + log.error('put(): (%s) Invalid job runner: %s' % (job_wrapper.job_id, runner_name)) + job_wrapper.fail(DEFAULT_JOB_PUT_FAILURE_MESSAGE) - def stop( self, job ): + def stop(self, job): """ Stop the given job. The input variable job may be either a Job or a Task. """ @@ -810,39 +811,39 @@ def stop( self, job ): # Note that Jobs and Tasks have runner_names, which are distinct from # the job_runner_name and task_runner_name. - if ( isinstance( job, model.Job ) ): - log.debug( "Stopping job %d:", job.get_id() ) - elif( isinstance( job, model.Task ) ): - log.debug( "Stopping job %d, task %d" - % ( job.get_job().get_id(), job.get_id() ) ) + if (isinstance(job, model.Job)): + log.debug("Stopping job %d:", job.get_id()) + elif(isinstance(job, model.Task)): + log.debug("Stopping job %d, task %d" + % (job.get_job().get_id(), job.get_id())) else: - log.debug( "Unknown job to stop" ) + log.debug("Unknown job to stop") # The runner name is not set until the job has started. # If we're stopping a task, then the runner_name may be # None, in which case it hasn't been scheduled. - if ( job.get_job_runner_name() is not None ): - runner_name = ( job.get_job_runner_name().split( ":", 1 ) )[ 0 ] - if ( isinstance( job, model.Job ) ): - log.debug( "stopping job %d in %s runner" % ( job.get_id(), runner_name ) ) - elif ( isinstance( job, model.Task ) ): - log.debug( "Stopping job %d, task %d in %s runner" - % ( job.get_job().get_id(), job.get_id(), runner_name ) ) + if (job.get_job_runner_name() is not None): + runner_name = (job.get_job_runner_name().split(":", 1))[0] + if (isinstance(job, model.Job)): + log.debug("stopping job %d in %s runner" % (job.get_id(), runner_name)) + elif (isinstance(job, model.Task)): + log.debug("Stopping job %d, task %d in %s runner" + % (job.get_job().get_id(), job.get_id(), runner_name)) try: - self.job_runners[runner_name].stop_job( job ) + self.job_runners[runner_name].stop_job(job) except KeyError: - log.error( 'stop(): (%s) Invalid job runner: %s' % ( job.get_id(), runner_name ) ) + log.error('stop(): (%s) Invalid job runner: %s' % (job.get_id(), runner_name)) # Job and output dataset states have already been updated, so nothing is done here. - def recover( self, job, job_wrapper ): - runner_name = ( job.job_runner_name.split(":", 1) )[0] - log.debug( "recovering job %d in %s runner" % ( job.get_id(), runner_name ) ) + def recover(self, job, job_wrapper): + runner_name = (job.job_runner_name.split(":", 1))[0] + log.debug("recovering job %d in %s runner" % (job.get_id(), runner_name)) try: - self.job_runners[runner_name].recover( job, job_wrapper ) + self.job_runners[runner_name].recover(job, job_wrapper) except KeyError: - log.error( 'recover(): (%s) Invalid job runner: %s' % ( job_wrapper.job_id, runner_name ) ) - job_wrapper.fail( DEFAULT_JOB_PUT_FAILURE_MESSAGE ) + log.error('recover(): (%s) Invalid job runner: %s' % (job_wrapper.job_id, runner_name)) + job_wrapper.fail(DEFAULT_JOB_PUT_FAILURE_MESSAGE) - def shutdown( self ): + def shutdown(self): for runner in self.job_runners.itervalues(): runner.shutdown() diff --git a/lib/galaxy/jobs/manager.py b/lib/galaxy/jobs/manager.py index 4cf56040f575..811cb637f5e3 100644 --- a/lib/galaxy/jobs/manager.py +++ b/lib/galaxy/jobs/manager.py @@ -6,21 +6,22 @@ from galaxy.jobs import handler, NoopQueue -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class JobManager( object ): +class JobManager(object): """ Highest level interface to job management. TODO: Currently the app accesses "job_queue" and "job_stop_queue" directly. This should be decoupled. """ - def __init__( self, app ): + + def __init__(self, app): self.app = app if self.app.is_job_handler(): log.debug("Starting job handler") - self.job_handler = handler.JobHandler( app ) + self.job_handler = handler.JobHandler(app) self.job_queue = self.job_handler.job_queue self.job_stop_queue = self.job_handler.job_stop_queue else: @@ -28,20 +29,20 @@ def __init__( self, app ): self.job_queue = self.job_stop_queue = NoopQueue() self.job_lock = False - def start( self ): + def start(self): self.job_handler.start() - def shutdown( self ): + def shutdown(self): self.job_handler.shutdown() -class NoopHandler( object ): - def __init__( self, *args, **kwargs ): +class NoopHandler(object): + def __init__(self, *args, **kwargs): self.job_queue = NoopQueue() self.job_stop_queue = NoopQueue() - def start( self ): + def start(self): pass - def shutdown( self, *args ): + def shutdown(self, *args): pass diff --git a/lib/galaxy/jobs/mapper.py b/lib/galaxy/jobs/mapper.py index 70bedbe6bfbb..7a2cbe6284bb 100644 --- a/lib/galaxy/jobs/mapper.py +++ b/lib/galaxy/jobs/mapper.py @@ -8,7 +8,7 @@ from galaxy.jobs.dynamic_tool_destination import map_tool_to_destination from .rule_helper import RuleHelper -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) DYNAMIC_RUNNER_NAME = "dynamic" DYNAMIC_DESTINATION_ID = "dynamic_legacy_from_url" @@ -17,15 +17,15 @@ ERROR_MESSAGE_RULE_FUNCTION_NOT_FOUND = "Galaxy misconfigured - no rule function named %s found in dynamic rule modules." -class JobMappingException( Exception ): +class JobMappingException(Exception): - def __init__( self, failure_message ): + def __init__(self, failure_message): self.failure_message = failure_message -class JobNotReadyException( Exception ): +class JobNotReadyException(Exception): - def __init__( self, job_state=None, message=None ): + def __init__(self, job_state=None, message=None): self.job_state = job_state self.message = message @@ -38,13 +38,13 @@ def __init__( self, job_state=None, message=None ): ) -class JobRunnerMapper( object ): +class JobRunnerMapper(object): """ This class is responsible to managing the mapping of jobs (in the form of job_wrappers) to job runner url strings. """ - def __init__( self, job_wrapper, url_to_destination, job_config ): + def __init__(self, job_wrapper, url_to_destination, job_config): self.job_wrapper = job_wrapper self.url_to_destination = url_to_destination self.job_config = job_config @@ -56,44 +56,44 @@ def __init__( self, job_wrapper, url_to_destination, job_config ): __import__(rules_module_name) self.rules_module = sys.modules[rules_module_name] - def __get_rule_modules( self ): - unsorted_module_names = self.__get_rule_module_names( ) + def __get_rule_modules(self): + unsorted_module_names = self.__get_rule_module_names() # Load modules in reverse order to allow hierarchical overrides # i.e. 000_galaxy_rules.py, 100_site_rules.py, 200_instance_rules.py - module_names = sorted( unsorted_module_names, reverse=True ) + module_names = sorted(unsorted_module_names, reverse=True) modules = [] for rule_module_name in module_names: try: - module = __import__( rule_module_name ) - for comp in rule_module_name.split( "." )[1:]: - module = getattr( module, comp ) - modules.append( module ) + module = __import__(rule_module_name) + for comp in rule_module_name.split(".")[1:]: + module = getattr(module, comp) + modules.append(module) except BaseException as exception: - exception_str = str( exception ) - message = "%s rule module could not be loaded: %s" % ( rule_module_name, exception_str ) - log.debug( message ) + exception_str = str(exception) + message = "%s rule module could not be loaded: %s" % (rule_module_name, exception_str) + log.debug(message) continue return modules - def __get_rule_module_names( self ): + def __get_rule_module_names(self): rules_dir = self.rules_module.__path__[0] names = [] - for fname in os.listdir( rules_dir ): - if not( fname.startswith( "_" ) ) and fname.endswith( ".py" ): + for fname in os.listdir(rules_dir): + if not(fname.startswith("_")) and fname.endswith(".py"): base_name = self.rules_module.__name__ rule_module_name = "%s.%s" % (base_name, fname[:-len(".py")]) - names.append( rule_module_name ) + names.append(rule_module_name) return names - def __invoke_expand_function( self, expand_function, destination_params ): - function_arg_names = inspect.getargspec( expand_function ).args + def __invoke_expand_function(self, expand_function, destination_params): + function_arg_names = inspect.getargspec(expand_function).args app = self.job_wrapper.app possible_args = { "job_id": self.job_wrapper.job_id, "tool": self.job_wrapper.tool, "tool_id": self.job_wrapper.tool.id, "job_wrapper": self.job_wrapper, - "rule_helper": RuleHelper( app ), + "rule_helper": RuleHelper(app), "app": app } @@ -102,12 +102,12 @@ def __invoke_expand_function( self, expand_function, destination_params ): # Send through any job_conf.xml defined args to function for destination_param in destination_params.keys(): if destination_param in function_arg_names: - actual_args[ destination_param ] = destination_params[ destination_param ] + actual_args[destination_param] = destination_params[destination_param] # Populate needed args for possible_arg_name in possible_args: if possible_arg_name in function_arg_names: - actual_args[ possible_arg_name ] = possible_args[ possible_arg_name ] + actual_args[possible_arg_name] = possible_args[possible_arg_name] # Don't hit the DB to load the job object if not needed require_db = False @@ -121,30 +121,30 @@ def __invoke_expand_function( self, expand_function, destination_params ): user_email = user and str(user.email) if "job" in function_arg_names: - actual_args[ "job" ] = job + actual_args["job"] = job if "user" in function_arg_names: - actual_args[ "user" ] = user + actual_args["user"] = user if "user_email" in function_arg_names: - actual_args[ "user_email" ] = user_email + actual_args["user_email"] = user_email if "resource_params" in function_arg_names: - actual_args[ "resource_params" ] = self.job_wrapper.get_resource_parameters( job ) + actual_args["resource_params"] = self.job_wrapper.get_resource_parameters(job) if "workflow_invocation_uuid" in function_arg_names: - param_values = job.raw_param_dict( ) - workflow_invocation_uuid = param_values.get( "__workflow_invocation_uuid__", None ) - actual_args[ "workflow_invocation_uuid" ] = workflow_invocation_uuid + param_values = job.raw_param_dict() + workflow_invocation_uuid = param_values.get("__workflow_invocation_uuid__", None) + actual_args["workflow_invocation_uuid"] = workflow_invocation_uuid - return expand_function( **actual_args ) + return expand_function(**actual_args) - def __job_params( self, job ): + def __job_params(self, job): app = self.job_wrapper.app - param_values = job.get_param_values( app, ignore_errors=True ) + param_values = job.get_param_values(app, ignore_errors=True) return param_values - def __convert_url_to_destination( self, url ): + def __convert_url_to_destination(self, url): """ Job runner URLs are deprecated, but dynamic mapper functions may still be returning them. Runners are expected to be able to convert these to @@ -154,56 +154,56 @@ def __convert_url_to_destination( self, url ): JobHandlerQueue.DefaultJobDispatcher.url_to_destination, which in turn calls the url_to_destination method for the appropriate runner. """ - dest = self.url_to_destination( url ) + dest = self.url_to_destination(url) dest['id'] = DYNAMIC_DESTINATION_ID return dest - def __determine_expand_function_name( self, destination ): + def __determine_expand_function_name(self, destination): # default look for function with name matching an id of tool, unless one specified expand_function_name = destination.params.get('function', None) if not expand_function_name: for tool_id in self.job_wrapper.tool.all_ids: - if self.__last_rule_module_with_function( tool_id ): + if self.__last_rule_module_with_function(tool_id): expand_function_name = tool_id break return expand_function_name - def __get_expand_function( self, expand_function_name ): - matching_rule_module = self.__last_rule_module_with_function( expand_function_name ) + def __get_expand_function(self, expand_function_name): + matching_rule_module = self.__last_rule_module_with_function(expand_function_name) if matching_rule_module: - expand_function = getattr( matching_rule_module, expand_function_name ) + expand_function = getattr(matching_rule_module, expand_function_name) return expand_function else: - message = ERROR_MESSAGE_RULE_FUNCTION_NOT_FOUND % ( expand_function_name ) - raise Exception( message ) + message = ERROR_MESSAGE_RULE_FUNCTION_NOT_FOUND % (expand_function_name) + raise Exception(message) - def __last_rule_module_with_function( self, function_name ): + def __last_rule_module_with_function(self, function_name): # self.rule_modules is sorted in reverse order, so find first # wiht function - for rule_module in self.__get_rule_modules( ): - if hasattr( rule_module, function_name ): + for rule_module in self.__get_rule_modules(): + if hasattr(rule_module, function_name): return rule_module return None - def __handle_dynamic_job_destination( self, destination ): + def __handle_dynamic_job_destination(self, destination): expand_type = destination.params.get('type', "python") expand_function = None if expand_type == "python": - expand_function_name = self.__determine_expand_function_name( destination ) + expand_function_name = self.__determine_expand_function_name(destination) if not expand_function_name: message = ERROR_MESSAGE_NO_RULE_FUNCTION % destination - raise Exception( message ) + raise Exception(message) - expand_function = self.__get_expand_function( expand_function_name ) + expand_function = self.__get_expand_function(expand_function_name) elif expand_type in STOCK_RULES: - expand_function = STOCK_RULES[ expand_type ] + expand_function = STOCK_RULES[expand_type] else: - raise Exception( "Unhandled dynamic job runner type specified - %s" % expand_type ) + raise Exception("Unhandled dynamic job runner type specified - %s" % expand_type) - return self.__handle_rule( expand_function, destination ) + return self.__handle_rule(expand_function, destination) - def __handle_rule( self, rule_function, destination ): - job_destination = self.__invoke_expand_function( rule_function, destination.params ) + def __handle_rule(self, rule_function, destination): + job_destination = self.__invoke_expand_function(rule_function, destination.params) if not isinstance(job_destination, galaxy.jobs.JobDestination): job_destination_rep = str(job_destination) # Should be either id or url if '://' in job_destination_rep: @@ -212,23 +212,23 @@ def __handle_rule( self, rule_function, destination ): job_destination = self.job_config.get_destination(job_destination_rep) return job_destination - def __cache_job_destination( self, params, raw_job_destination=None ): + def __cache_job_destination(self, params, raw_job_destination=None): if raw_job_destination is None: - raw_job_destination = self.job_wrapper.tool.get_job_destination( params ) + raw_job_destination = self.job_wrapper.tool.get_job_destination(params) if raw_job_destination.runner == DYNAMIC_RUNNER_NAME: - job_destination = self.__handle_dynamic_job_destination( raw_job_destination ) + job_destination = self.__handle_dynamic_job_destination(raw_job_destination) else: job_destination = raw_job_destination self.cached_job_destination = job_destination - def get_job_destination( self, params ): + def get_job_destination(self, params): """ Cache the job_destination to avoid recalculation. """ - if not hasattr( self, 'cached_job_destination' ): - self.__cache_job_destination( params ) + if not hasattr(self, 'cached_job_destination'): + self.__cache_job_destination(params) return self.cached_job_destination - def cache_job_destination( self, raw_job_destination ): - self.__cache_job_destination( None, raw_job_destination=raw_job_destination ) + def cache_job_destination(self, raw_job_destination): + self.__cache_job_destination(None, raw_job_destination=raw_job_destination) return self.cached_job_destination diff --git a/lib/galaxy/jobs/metrics/__init__.py b/lib/galaxy/jobs/metrics/__init__.py index e6efb58f5df0..ac6c932e4db1 100644 --- a/lib/galaxy/jobs/metrics/__init__.py +++ b/lib/galaxy/jobs/metrics/__init__.py @@ -37,7 +37,7 @@ def __init__(self, conf_file=None, **kwargs): def format(self, plugin, key, value): """Find :class:`formatting.JobMetricFormatter` corresponding to instrumented plugin value.""" if plugin in self.plugin_classes: - plugin_class = self.plugin_classes[ plugin ] + plugin_class = self.plugin_classes[plugin] formatter = plugin_class.formatter else: formatter = DEFAULT_FORMATTER @@ -54,10 +54,10 @@ def set_destination_conf_element(self, destination_id, element): def set_destination_instrumenter(self, destination_id, job_instrumenter=None): if job_instrumenter is None: job_instrumenter = NULL_JOB_INSTRUMENTER - self.job_instrumenters[ destination_id ] = job_instrumenter + self.job_instrumenters[destination_id] = job_instrumenter def collect_properties(self, destination_id, job_id, job_directory): - return self.job_instrumenters[ destination_id ].collect_properties(job_id, job_directory) + return self.job_instrumenters[destination_id].collect_properties(job_id, job_directory) def __plugins_dict(self): import galaxy.jobs.metrics.instrumenters @@ -95,7 +95,7 @@ def pre_execute_commands(self, job_directory): commands.extend(util.listify(plugin_commands)) except Exception: log.exception("Failed to generate pre-execute commands for plugin %s", plugin) - return "\n".join([ c for c in commands if c ]) + return "\n".join([c for c in commands if c]) def post_execute_commands(self, job_directory): commands = [] @@ -106,7 +106,7 @@ def post_execute_commands(self, job_directory): commands.extend(util.listify(plugin_commands)) except Exception: log.exception("Failed to generate post-execute commands for plugin %s", plugin) - return "\n".join([ c for c in commands if c ]) + return "\n".join([c for c in commands if c]) def collect_properties(self, job_id, job_directory): per_plugin_properites = {} @@ -114,7 +114,7 @@ def collect_properties(self, job_id, job_directory): try: properties = plugin.job_properties(job_id, job_directory) if properties: - per_plugin_properites[ plugin.plugin_type ] = properties + per_plugin_properites[plugin.plugin_type] = properties except Exception: log.exception("Failed to collect job properties for plugin %s", plugin) return per_plugin_properites diff --git a/lib/galaxy/jobs/metrics/collectl/cli.py b/lib/galaxy/jobs/metrics/collectl/cli.py index 90271d6bee76..0bad1d0f1872 100644 --- a/lib/galaxy/jobs/metrics/collectl/cli.py +++ b/lib/galaxy/jobs/metrics/collectl/cli.py @@ -3,7 +3,7 @@ import subprocess from string import Template -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) COMMAND_LINE_TEMPLATE = Template( "$collectl_path $destination_arg $mode_arg $subsystems_arg $interval_arg $procfilt_arg $flush_arg $sep_arg" @@ -12,7 +12,7 @@ MODE_PLAYBACK = "playback" -class CollectlCli( object ): +class CollectlCli(object): """ Abstraction over (some of) the command-line arguments of collectl. Ideally this will be useful for building up command line arguments for @@ -61,81 +61,81 @@ class CollectlCli( object ): Optional flush interval (defaults to None). """ - def __init__( self, **kwargs ): + def __init__(self, **kwargs): command_args = {} - command_args[ "collectl_path" ] = kwargs.get( "collectl_path", "collectl" ) - playback_path = kwargs.get( "playback_path", None ) + command_args["collectl_path"] = kwargs.get("collectl_path", "collectl") + playback_path = kwargs.get("playback_path", None) self.mode = MODE_RECORD if not playback_path else MODE_PLAYBACK if self.mode == MODE_RECORD: mode_arg = "" elif self.mode == MODE_PLAYBACK: mode_arg = "-P -p '%s'" % playback_path else: - raise Exception( "Invalid mode supplied to CollectlCli - %s" % self.mode ) - command_args[ "mode_arg" ] = mode_arg - command_args[ "interval_arg" ] = self.__interval_arg( kwargs ) - destination = kwargs.get( "destination_path", None ) + raise Exception("Invalid mode supplied to CollectlCli - %s" % self.mode) + command_args["mode_arg"] = mode_arg + command_args["interval_arg"] = self.__interval_arg(kwargs) + destination = kwargs.get("destination_path", None) if destination: destination_arg = "-f '%s'" % destination else: destination_arg = "" - command_args[ "destination_arg" ] = destination_arg - procfilt = kwargs.get( "procfilt", None ) - command_args[ "procfilt_arg" ] = "" if not procfilt else "--procfilt %s" % procfilt - command_args[ "subsystems_arg" ] = self.__subsystems_arg( kwargs.get( "subsystems", [] ) ) - flush = kwargs.get( "flush", None ) - command_args[ "flush_arg"] = "--flush %s" % flush if flush else "" - sep = kwargs.get( "sep", None ) - command_args[ "sep_arg" ] = "--sep=%s" % sep if sep else "" + command_args["destination_arg"] = destination_arg + procfilt = kwargs.get("procfilt", None) + command_args["procfilt_arg"] = "" if not procfilt else "--procfilt %s" % procfilt + command_args["subsystems_arg"] = self.__subsystems_arg(kwargs.get("subsystems", [])) + flush = kwargs.get("flush", None) + command_args["flush_arg"] = "--flush %s" % flush if flush else "" + sep = kwargs.get("sep", None) + command_args["sep_arg"] = "--sep=%s" % sep if sep else "" self.command_args = command_args - def __subsystems_arg( self, subsystems ): + def __subsystems_arg(self, subsystems): if subsystems: - return "-s%s" % "".join( [ s.command_line_arg for s in subsystems ] ) + return "-s%s" % "".join([s.command_line_arg for s in subsystems]) else: return "" - def __interval_arg( self, kwargs ): + def __interval_arg(self, kwargs): if self.mode != MODE_RECORD: return "" - interval = kwargs.get( "interval", None ) + interval = kwargs.get("interval", None) if not interval: return "" - self.__validate_interval_arg( interval ) + self.__validate_interval_arg(interval) interval_arg = "-i %s" % interval - interval2 = kwargs.get( "interval2", None ) + interval2 = kwargs.get("interval2", None) if not interval2: return interval_arg - self.__validate_interval_arg( interval2, multiple_of=int( interval ) ) - interval_arg = "%s:%s" % ( interval_arg, interval2 ) + self.__validate_interval_arg(interval2, multiple_of=int(interval)) + interval_arg = "%s:%s" % (interval_arg, interval2) - interval3 = kwargs.get( "interval3", None ) + interval3 = kwargs.get("interval3", None) if not interval3: return interval_arg - self.__validate_interval_arg( interval3, multiple_of=int( interval ) ) - interval_arg = "%s:%s" % ( interval_arg, interval3 ) + self.__validate_interval_arg(interval3, multiple_of=int(interval)) + interval_arg = "%s:%s" % (interval_arg, interval3) return interval_arg - def __validate_interval_arg( self, value, multiple_of=None ): + def __validate_interval_arg(self, value, multiple_of=None): if value and not str(value).isdigit(): - raise Exception( "Invalid interval argument supplied, must be integer %s" % value ) + raise Exception("Invalid interval argument supplied, must be integer %s" % value) if multiple_of: - if int( value ) % multiple_of != 0: - raise Exception( "Invalid interval argument supplied, must multiple of %s" % multiple_of ) + if int(value) % multiple_of != 0: + raise Exception("Invalid interval argument supplied, must multiple of %s" % multiple_of) - def build_command_line( self ): - return COMMAND_LINE_TEMPLATE.substitute( **self.command_args ) + def build_command_line(self): + return COMMAND_LINE_TEMPLATE.substitute(**self.command_args) - def run( self, stdout=subprocess.PIPE, stderr=subprocess.PIPE ): + def run(self, stdout=subprocess.PIPE, stderr=subprocess.PIPE): command_line = self.build_command_line() - log.info( "Executing %s" % command_line ) - proc = subprocess.Popen( command_line, shell=True, stdout=stdout, stderr=stderr ) + log.info("Executing %s" % command_line) + proc = subprocess.Popen(command_line, shell=True, stdout=stdout, stderr=stderr) return_code = proc.wait() if return_code: - raise Exception( "Problem running collectl command." ) + raise Exception("Problem running collectl command.") -__all__ = ( 'CollectlCli', ) +__all__ = ('CollectlCli', ) diff --git a/lib/galaxy/jobs/metrics/collectl/processes.py b/lib/galaxy/jobs/metrics/collectl/processes.py index a10c3d9bc890..c3c4fdcddc3c 100644 --- a/lib/galaxy/jobs/metrics/collectl/processes.py +++ b/lib/galaxy/jobs/metrics/collectl/processes.py @@ -14,7 +14,7 @@ if sys.version_info > (3,): long = int -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) # Collectl process information cheat sheet: # @@ -67,11 +67,11 @@ ] # Types of statistics this module can summarize -STATISTIC_TYPES = [ "max", "min", "sum", "count", "avg" ] +STATISTIC_TYPES = ["max", "min", "sum", "count", "avg"] -COLUMN_INDICES = dict( [ ( col, i ) for i, col in enumerate( PROCESS_COLUMNS ) ] ) -PID_INDEX = COLUMN_INDICES[ "PID" ] -PARENT_PID_INDEX = COLUMN_INDICES[ "PPID" ] +COLUMN_INDICES = dict([(col, i) for i, col in enumerate(PROCESS_COLUMNS)]) +PID_INDEX = COLUMN_INDICES["PID"] +PARENT_PID_INDEX = COLUMN_INDICES["PPID"] DEFAULT_STATISTICS = [ ("max", "VmSize"), @@ -88,7 +88,7 @@ ] -def parse_process_statistics( statistics ): +def parse_process_statistics(statistics): """ Turn string or list of strings into list of tuples in format ( stat, resource ) where stat is a value from STATISTIC_TYPES and resource is a value from PROCESS_COLUMNS. @@ -96,86 +96,86 @@ def parse_process_statistics( statistics ): if statistics is None: statistics = DEFAULT_STATISTICS - statistics = util.listify( statistics ) - statistics = [ _tuplize_statistic(_) for _ in statistics ] + statistics = util.listify(statistics) + statistics = [_tuplize_statistic(_) for _ in statistics] # Check for validity... for statistic in statistics: - if statistic[ 0 ] not in STATISTIC_TYPES: - raise Exception( "Unknown statistic type encountered %s" % statistic[ 0 ] ) - if statistic[ 1 ] not in PROCESS_COLUMNS: - raise Exception( "Unknown process column encountered %s" % statistic[ 1 ] ) + if statistic[0] not in STATISTIC_TYPES: + raise Exception("Unknown statistic type encountered %s" % statistic[0]) + if statistic[1] not in PROCESS_COLUMNS: + raise Exception("Unknown process column encountered %s" % statistic[1]) return statistics -def generate_process_statistics( collectl_playback_cli, pid, statistics=DEFAULT_STATISTICS ): +def generate_process_statistics(collectl_playback_cli, pid, statistics=DEFAULT_STATISTICS): """ Playback collectl file and generate summary statistics. """ - with tempfile.NamedTemporaryFile( ) as tmp_tsv: - collectl_playback_cli.run( stdout=tmp_tsv ) - with open( tmp_tsv.name, "r" ) as tsv_file: - return _read_process_statistics( tsv_file, pid, statistics ) + with tempfile.NamedTemporaryFile() as tmp_tsv: + collectl_playback_cli.run(stdout=tmp_tsv) + with open(tmp_tsv.name, "r") as tsv_file: + return _read_process_statistics(tsv_file, pid, statistics) -def _read_process_statistics( tsv_file, pid, statistics ): - process_summarizer = CollectlProcessSummarizer( pid, statistics ) +def _read_process_statistics(tsv_file, pid, statistics): + process_summarizer = CollectlProcessSummarizer(pid, statistics) current_interval = None - for row in csv.reader( tsv_file, dialect="excel-tab" ): + for row in csv.reader(tsv_file, dialect="excel-tab"): if current_interval is None: - for header, expected_header in zip( row, PROCESS_COLUMNS ): + for header, expected_header in zip(row, PROCESS_COLUMNS): if header.lower() != expected_header.lower(): - raise Exception( "Unknown header value encountered while processing collectl playback - %s" % header ) + raise Exception("Unknown header value encountered while processing collectl playback - %s" % header) # First row, check contains correct header. current_interval = CollectlProcessInterval() continue - if current_interval.row_is_in( row ): - current_interval.add_row( row ) + if current_interval.row_is_in(row): + current_interval.add_row(row) else: - process_summarizer.handle_interval( current_interval ) + process_summarizer.handle_interval(current_interval) current_interval = CollectlProcessInterval() # Do we have unsummarized rows... if current_interval and current_interval.rows: - process_summarizer.handle_interval( current_interval ) + process_summarizer.handle_interval(current_interval) return process_summarizer.get_statistics() -class CollectlProcessSummarizer( object ): +class CollectlProcessSummarizer(object): - def __init__( self, pid, statistics ): + def __init__(self, pid, statistics): self.pid = pid self.statistics = statistics - self.columns_of_interest = set( [ s[ 1 ] for s in statistics ] ) - self.tree_statistics = collections.defaultdict( stats.StatisticsTracker ) - self.process_accum_statistics = collections.defaultdict( stats.StatisticsTracker ) + self.columns_of_interest = set([s[1] for s in statistics]) + self.tree_statistics = collections.defaultdict(stats.StatisticsTracker) + self.process_accum_statistics = collections.defaultdict(stats.StatisticsTracker) self.interval_count = 0 - def handle_interval( self, interval ): + def handle_interval(self, interval): self.interval_count += 1 - rows = self.__rows_for_process( interval.rows, self.pid ) + rows = self.__rows_for_process(interval.rows, self.pid) for column_name in self.columns_of_interest: - column_index = COLUMN_INDICES[ column_name ] + column_index = COLUMN_INDICES[column_name] if column_name == "AccumT": # Should not sum this across pids each interval, sum max at end... for r in rows: - pid_seconds = self.__time_to_seconds( r[ column_index ] ) - self.process_accum_statistics[ r[ PID_INDEX ] ].track( pid_seconds ) + pid_seconds = self.__time_to_seconds(r[column_index]) + self.process_accum_statistics[r[PID_INDEX]].track(pid_seconds) else: # All other stastics should be summed across whole process tree # at each interval I guess. - if column_name in [ "SysT", "UsrT", "PCT" ]: + if column_name in ["SysT", "UsrT", "PCT"]: to_num = float else: to_num = long - interval_stat = sum( to_num( r[ column_index ] ) for r in rows ) - self.tree_statistics[ column_name ].track( interval_stat ) + interval_stat = sum(to_num(r[column_index]) for r in rows) + self.tree_statistics[column_name].track(interval_stat) - def get_statistics( self ): + def get_statistics(self): if self.interval_count == 0: return [] @@ -185,72 +185,72 @@ def get_statistics( self ): if column == "AccumT": # Only thing that makes sense is sum if statistic_type != "max": - log.warning( "Only statistic max makes sense for AccumT" ) + log.warning("Only statistic max makes sense for AccumT") continue - value = sum( v.max for v in self.process_accum_statistics.values() ) + value = sum(v.max for v in self.process_accum_statistics.values()) else: - statistics_tracker = self.tree_statistics[ column ] - value = getattr( statistics_tracker, statistic_type ) + statistics_tracker = self.tree_statistics[column] + value = getattr(statistics_tracker, statistic_type) - computed_statistic = ( statistic, value ) - computed_statistics.append( computed_statistic ) + computed_statistic = (statistic, value) + computed_statistics.append(computed_statistic) return computed_statistics - def __rows_for_process( self, rows, pid ): + def __rows_for_process(self, rows, pid): process_rows = [] - pids = self.__all_child_pids( rows, pid ) + pids = self.__all_child_pids(rows, pid) for row in rows: - if row[ PID_INDEX ] in pids: - process_rows.append( row ) + if row[PID_INDEX] in pids: + process_rows.append(row) return process_rows - def __all_child_pids( self, rows, pid ): - pids_in_process_tree = set( [ str( self.pid ) ] ) + def __all_child_pids(self, rows, pid): + pids_in_process_tree = set([str(self.pid)]) added = True while added: added = False for row in rows: - pid = row[ PID_INDEX ] - parent_pid = row[ PARENT_PID_INDEX ] + pid = row[PID_INDEX] + parent_pid = row[PARENT_PID_INDEX] if parent_pid in pids_in_process_tree and pid not in pids_in_process_tree: - pids_in_process_tree.add( pid ) + pids_in_process_tree.add(pid) added = True return pids_in_process_tree - def __time_to_seconds( self, minutes_str ): - parts = minutes_str.split( ":" ) + def __time_to_seconds(self, minutes_str): + parts = minutes_str.split(":") seconds = 0.0 - for i, val in enumerate( parts ): - seconds += float(val) * ( 60 ** ( len( parts ) - ( i + 1 ) ) ) + for i, val in enumerate(parts): + seconds += float(val) * (60 ** (len(parts) - (i + 1))) return seconds -class CollectlProcessInterval( object ): +class CollectlProcessInterval(object): """ Represent all rows in collectl playback file for given time slice with ability to filter out just rows corresponding to the process tree corresponding to a given pid. """ - def __init__( self ): + def __init__(self): self.rows = [] - def row_is_in( self, row ): + def row_is_in(self, row): if not self.rows: # No rows, this row defines interval. return True - first_row = self.rows[ 0 ] - return first_row[ 0 ] == row[ 0 ] and first_row[ 1 ] == row[ 1 ] + first_row = self.rows[0] + return first_row[0] == row[0] and first_row[1] == row[1] - def add_row( self, row ): - self.rows.append( row ) + def add_row(self, row): + self.rows.append(row) -def _tuplize_statistic( statistic ): - if not isinstance( statistic, tuple ): - statistic_split = statistic.split( "_", 1 ) - statistic = ( statistic_split[ 0 ].lower(), statistic_split[ 1 ] ) +def _tuplize_statistic(statistic): + if not isinstance(statistic, tuple): + statistic_split = statistic.split("_", 1) + statistic = (statistic_split[0].lower(), statistic_split[1]) return statistic -__all__ = ( 'generate_process_statistics', ) +__all__ = ('generate_process_statistics', ) diff --git a/lib/galaxy/jobs/metrics/collectl/stats.py b/lib/galaxy/jobs/metrics/collectl/stats.py index 7bf79f523610..3f14c0e6cbc1 100644 --- a/lib/galaxy/jobs/metrics/collectl/stats.py +++ b/lib/galaxy/jobs/metrics/collectl/stats.py @@ -3,15 +3,15 @@ """ -class StatisticsTracker( object ): +class StatisticsTracker(object): - def __init__( self ): + def __init__(self): self.min = None self.max = None self.count = 0 self.sum = 0 - def track( self, value ): + def track(self, value): if self.min is None or value < self.min: self.min = value if self.max is None or value > self.max: @@ -20,7 +20,7 @@ def track( self, value ): self.sum += value @property - def avg( self ): + def avg(self): if self.count > 0: return self.sum / self.count else: diff --git a/lib/galaxy/jobs/metrics/collectl/subsystems.py b/lib/galaxy/jobs/metrics/collectl/subsystems.py index 942c5b36be98..18bf64ec5439 100644 --- a/lib/galaxy/jobs/metrics/collectl/subsystems.py +++ b/lib/galaxy/jobs/metrics/collectl/subsystems.py @@ -11,49 +11,49 @@ @six.add_metaclass(ABCMeta) -class CollectlSubsystem( object ): +class CollectlSubsystem(object): """ Class providing an abstraction of collectl subsytems. """ @property @abstractmethod - def command_line_arg( self ): + def command_line_arg(self): """ Return single letter command-line argument used by collectl CLI. """ @property @abstractmethod - def name( self, job_directory ): + def name(self, job_directory): """ High-level name for subsystem as consumed by this module. """ -class ProcessesSubsystem( CollectlSubsystem ): +class ProcessesSubsystem(CollectlSubsystem): command_line_arg = "Z" name = "process" -class CpuSubsystem( CollectlSubsystem ): +class CpuSubsystem(CollectlSubsystem): command_line_arg = "C" name = "cpu" -class DiskSubsystem( CollectlSubsystem ): +class DiskSubsystem(CollectlSubsystem): command_line_arg = "D" name = "disk" -class NetworkSubsystem( CollectlSubsystem ): +class NetworkSubsystem(CollectlSubsystem): command_line_arg = "N" name = "network" -class EnvironmentSubsystem( CollectlSubsystem ): +class EnvironmentSubsystem(CollectlSubsystem): command_line_arg = "E" name = "environment" -class MemorySubsystem( CollectlSubsystem ): +class MemorySubsystem(CollectlSubsystem): command_line_arg = "M" name = "memory" @@ -66,16 +66,16 @@ class MemorySubsystem( CollectlSubsystem ): EnvironmentSubsystem(), MemorySubsystem(), ] -SUBSYSTEM_DICT = dict( [ (s.name, s) for s in SUBSYSTEMS ] ) +SUBSYSTEM_DICT = dict([(s.name, s) for s in SUBSYSTEMS]) -def get_subsystem( name ): +def get_subsystem(name): """ >>> get_subsystem( "process" ).command_line_arg == "Z" True """ - return SUBSYSTEM_DICT[ name ] + return SUBSYSTEM_DICT[name] -__all__ = ( 'get_subsystem', ) +__all__ = ('get_subsystem', ) diff --git a/lib/galaxy/jobs/metrics/instrumenters/__init__.py b/lib/galaxy/jobs/metrics/instrumenters/__init__.py index 6e772a1ae63a..952ec6f4b552 100644 --- a/lib/galaxy/jobs/metrics/instrumenters/__init__.py +++ b/lib/galaxy/jobs/metrics/instrumenters/__init__.py @@ -16,23 +16,23 @@ @six.add_metaclass(ABCMeta) -class InstrumentPlugin( object ): +class InstrumentPlugin(object): """Describes how to instrument job scripts and retrieve collected metrics.""" formatter = formatting.JobMetricFormatter() @property @abstractmethod - def plugin_type( self ): + def plugin_type(self): """ Short string providing labelling this plugin """ - def pre_execute_instrument( self, job_directory ): + def pre_execute_instrument(self, job_directory): """ Optionally return one or more commands to instrument job. These commands will be executed on the compute server prior to the job running. """ return None - def post_execute_instrument( self, job_directory ): + def post_execute_instrument(self, job_directory): """ Optionally return one or more commands to instrument job. These commands will be executed on the compute server after the tool defined command is ran. @@ -40,18 +40,18 @@ def post_execute_instrument( self, job_directory ): return None @abstractmethod - def job_properties( self, job_id, job_directory ): + def job_properties(self, job_id, job_directory): """ Collect properties for this plugin from specified job directory. This method will run on the Galaxy server and can assume files created in job_directory with pre_execute_instrument and post_execute_instrument are available. """ - def _instrument_file_name( self, name ): + def _instrument_file_name(self, name): """ Provide a common pattern for naming files used by instrumentation plugins - to ease their staging out of remote job directories. """ - return "%s_%s_%s" % ( INSTRUMENT_FILE_PREFIX, self.plugin_type, name ) + return "%s_%s_%s" % (INSTRUMENT_FILE_PREFIX, self.plugin_type, name) - def _instrument_file_path( self, job_directory, name ): - return os.path.join( job_directory, self._instrument_file_name( name ) ) + def _instrument_file_path(self, job_directory, name): + return os.path.join(job_directory, self._instrument_file_name(name)) diff --git a/lib/galaxy/jobs/metrics/instrumenters/collectl.py b/lib/galaxy/jobs/metrics/instrumenters/collectl.py index 49d49b8e3e6c..4a12b69464fe 100644 --- a/lib/galaxy/jobs/metrics/instrumenters/collectl.py +++ b/lib/galaxy/jobs/metrics/instrumenters/collectl.py @@ -13,7 +13,7 @@ from ..instrumenters import InstrumentPlugin from ...metrics import formatting -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) # By default, only grab statistics for user processes (as identified by # username). @@ -31,119 +31,119 @@ EMPTY_COLLECTL_FILE_MESSAGE = "Skipping process summary due to empty file... job probably did not run long enough for collectl to gather data." -class CollectlFormatter( formatting.JobMetricFormatter ): +class CollectlFormatter(formatting.JobMetricFormatter): - def format( self, key, value ): + def format(self, key, value): if key == "pid": - return ( "Process ID", int( value ) ) + return ("Process ID", int(value)) elif key == "raw_log_path": - return ( "Relative Path of Full Collectl Log", value ) + return ("Relative Path of Full Collectl Log", value) elif key == "process_max_AccumT": - return ( "Job Runtime (System+User)", formatting.seconds_to_str( float( value ) ) ) + return ("Job Runtime (System+User)", formatting.seconds_to_str(float(value))) else: - _, stat_type, resource_type = key.split( "_", 2 ) - if resource_type.startswith( "Vm"): - value_str = "%s KB" % int( value ) - elif resource_type in [ "RSYS", "WSYS" ] and stat_type in [ "count", "max", "sum" ]: - value_str = "%d (# system calls)" % int( value ) + _, stat_type, resource_type = key.split("_", 2) + if resource_type.startswith("Vm"): + value_str = "%s KB" % int(value) + elif resource_type in ["RSYS", "WSYS"] and stat_type in ["count", "max", "sum"]: + value_str = "%d (# system calls)" % int(value) else: - value_str = str( value ) - resource_title = FORMATTED_RESOURCE_TITLES.get( resource_type, resource_type ) - return ( "%s (%s)" % ( resource_title, stat_type ), value_str ) + value_str = str(value) + resource_title = FORMATTED_RESOURCE_TITLES.get(resource_type, resource_type) + return ("%s (%s)" % (resource_title, stat_type), value_str) -class CollectlPlugin( InstrumentPlugin ): +class CollectlPlugin(InstrumentPlugin): """ Run collectl along with job to capture system and/or process data according to specified collectl subsystems. """ plugin_type = "collectl" formatter = CollectlFormatter() - def __init__( self, **kwargs ): - self.__configure_paths( kwargs ) - self.__configure_subsystems( kwargs ) - saved_logs_path = kwargs.get( "saved_logs_path", "" ) + def __init__(self, **kwargs): + self.__configure_paths(kwargs) + self.__configure_subsystems(kwargs) + saved_logs_path = kwargs.get("saved_logs_path", "") if "app" in kwargs: log.debug("Found path for saved logs: %s" % saved_logs_path) - saved_logs_path = kwargs[ "app" ].config.resolve_path( saved_logs_path ) + saved_logs_path = kwargs["app"].config.resolve_path(saved_logs_path) self.saved_logs_path = saved_logs_path - self.__configure_collectl_recorder_args( kwargs ) - self.summarize_process_data = util.asbool( kwargs.get( "summarize_process_data", True ) ) - self.log_collectl_program_output = util.asbool( kwargs.get( "log_collectl_program_output", False ) ) + self.__configure_collectl_recorder_args(kwargs) + self.summarize_process_data = util.asbool(kwargs.get("summarize_process_data", True)) + self.log_collectl_program_output = util.asbool(kwargs.get("log_collectl_program_output", False)) if self.summarize_process_data: - if subsystems.get_subsystem( "process" ) not in self.subsystems: - raise Exception( "Collectl plugin misconfigured - cannot summarize_process_data without process subsystem being enabled." ) + if subsystems.get_subsystem("process") not in self.subsystems: + raise Exception("Collectl plugin misconfigured - cannot summarize_process_data without process subsystem being enabled.") - process_statistics = kwargs.get( "process_statistics", None ) + process_statistics = kwargs.get("process_statistics", None) # None will let processes module use default set of statistics # defined there. - self.process_statistics = processes.parse_process_statistics( process_statistics ) + self.process_statistics = processes.parse_process_statistics(process_statistics) - def pre_execute_instrument( self, job_directory ): + def pre_execute_instrument(self, job_directory): commands = [] # Capture PID of process so we can walk its ancestors when building # statistics for the whole job. - commands.append( '''echo "$$" > '%s' ''' % self.__pid_file( job_directory ) ) + commands.append('''echo "$$" > '%s' ''' % self.__pid_file(job_directory)) # Run collectl in record mode to capture process and system level # statistics according to supplied subsystems. - commands.append( self.__collectl_record_command( job_directory ) ) + commands.append(self.__collectl_record_command(job_directory)) return commands - def post_execute_instrument( self, job_directory ): + def post_execute_instrument(self, job_directory): commands = [] # collectl dies when job script completes, perhaps capture pid of # collectl above and check if it is still alive to allow tracking if # collectl ran successfully through the whole job. return commands - def job_properties( self, job_id, job_directory ): - pid = open( self.__pid_file( job_directory ), "r" ).read().strip() - contents = os.listdir( job_directory ) + def job_properties(self, job_id, job_directory): + pid = open(self.__pid_file(job_directory), "r").read().strip() + contents = os.listdir(job_directory) try: - rel_path = filter( self._is_instrumented_collectl_log, contents )[ 0 ] - path = os.path.join( job_directory, rel_path ) + rel_path = filter(self._is_instrumented_collectl_log, contents)[0] + path = os.path.join(job_directory, rel_path) except IndexError: - message = "Failed to find collectl log in directory %s, files were %s" % ( job_directory, contents ) - raise Exception( message ) + message = "Failed to find collectl log in directory %s, files were %s" % (job_directory, contents) + raise Exception(message) properties = dict( - pid=int( pid ), + pid=int(pid), ) if self.saved_logs_path: - destination_rel_dir = os.path.join( *util.directory_hash_id( job_id ) ) - destination_rel_path = os.path.join( destination_rel_dir, rel_path ) - destination_path = os.path.join( self.saved_logs_path, destination_rel_path ) - destination_dir = os.path.dirname( destination_path ) - if not os.path.isdir( destination_dir ): - os.makedirs( destination_dir ) - shutil.copyfile( path, destination_path ) - properties[ "raw_log_path" ] = destination_rel_path + destination_rel_dir = os.path.join(*util.directory_hash_id(job_id)) + destination_rel_path = os.path.join(destination_rel_dir, rel_path) + destination_path = os.path.join(self.saved_logs_path, destination_rel_path) + destination_dir = os.path.dirname(destination_path) + if not os.path.isdir(destination_dir): + os.makedirs(destination_dir) + shutil.copyfile(path, destination_path) + properties["raw_log_path"] = destination_rel_path if self.summarize_process_data: # Run collectl in playback and generate statistics of interest - summary_statistics = self.__summarize_process_data( pid, path ) + summary_statistics = self.__summarize_process_data(pid, path) for statistic, value in summary_statistics: - properties[ "process_%s" % "_".join( statistic ) ] = value + properties["process_%s" % "_".join(statistic)] = value return properties - def __configure_paths( self, kwargs ): + def __configure_paths(self, kwargs): # 95% of time I would expect collectl to just be installed with apt or # yum, but if it is manually installed on not on path, allow # configuration of explicit path - and allow path to be different # between galaxy job handler (local_collectl_path) and compute node # (remote_collectl_path). - collectl_path = kwargs.get( "collectl_path", "collectl" ) - self.remote_collectl_path = kwargs.get( "remote_collectl_path", collectl_path ) - self.local_collectl_path = kwargs.get( "local_collectl_path", collectl_path ) + collectl_path = kwargs.get("collectl_path", "collectl") + self.remote_collectl_path = kwargs.get("remote_collectl_path", collectl_path) + self.local_collectl_path = kwargs.get("local_collectl_path", collectl_path) - def __configure_subsystems( self, kwargs ): - raw_subsystems_str = kwargs.get( "subsystems", DEFAULT_SUBSYSTEMS ) - raw_subsystems = util.listify( raw_subsystems_str, do_strip=True ) - self.subsystems = [ subsystems.get_subsystem(_) for _ in raw_subsystems ] + def __configure_subsystems(self, kwargs): + raw_subsystems_str = kwargs.get("subsystems", DEFAULT_SUBSYSTEMS) + raw_subsystems = util.listify(raw_subsystems_str, do_strip=True) + self.subsystems = [subsystems.get_subsystem(_) for _ in raw_subsystems] - def __configure_collectl_recorder_args( self, kwargs ): + def __configure_collectl_recorder_args(self, kwargs): collectl_recorder_args = kwargs.copy() # Allow deployer to configure separate system and process intervals, @@ -151,44 +151,44 @@ def __configure_collectl_recorder_args( self, kwargs ): # plugin's most useful feature is the process level information so # this is likely what the deployer is attempting to configure. if "interval" in kwargs and "interval2" not in kwargs: - collectl_recorder_args[ "interval2" ] = kwargs[ "interval"] + collectl_recorder_args["interval2"] = kwargs["interval"] if "flush" not in kwargs: - collectl_recorder_args[ "flush" ] = DEFAULT_FLUSH_INTERVAL + collectl_recorder_args["flush"] = DEFAULT_FLUSH_INTERVAL - procfilt_on = kwargs.get( "procfilt_on", DEFAULT_PROCFILT_ON ).lower() + procfilt_on = kwargs.get("procfilt_on", DEFAULT_PROCFILT_ON).lower() # Calculate explicit arguments, rest can just be passed through from # constructor arguments. explicit_args = dict( collectl_path=self.remote_collectl_path, - procfilt=procfilt_argument( procfilt_on ), + procfilt=procfilt_argument(procfilt_on), subsystems=self.subsystems, ) - collectl_recorder_args.update( explicit_args ) + collectl_recorder_args.update(explicit_args) self.collectl_recorder_args = collectl_recorder_args - def __summarize_process_data( self, pid, collectl_log_path ): + def __summarize_process_data(self, pid, collectl_log_path): playback_cli_args = dict( collectl_path=self.local_collectl_path, playback_path=collectl_log_path, sep="9" ) - if not os.stat( collectl_log_path ).st_size: - log.debug( EMPTY_COLLECTL_FILE_MESSAGE ) - return [ ] + if not os.stat(collectl_log_path).st_size: + log.debug(EMPTY_COLLECTL_FILE_MESSAGE) + return [] - playback_cli = cli.CollectlCli( **playback_cli_args ) - return processes.generate_process_statistics( playback_cli, pid, self.process_statistics ) + playback_cli = cli.CollectlCli(**playback_cli_args) + return processes.generate_process_statistics(playback_cli, pid, self.process_statistics) - def __collectl_recorder_cli( self, job_directory ): + def __collectl_recorder_cli(self, job_directory): cli_args = self.collectl_recorder_args.copy() - cli_args[ "destination_path" ] = self._instrument_file_path( job_directory, "log" ) - return cli.CollectlCli( **cli_args ) + cli_args["destination_path"] = self._instrument_file_path(job_directory, "log") + return cli.CollectlCli(**cli_args) - def __collectl_record_command( self, job_directory ): - collectl_cli = self.__collectl_recorder_cli( job_directory ) + def __collectl_record_command(self, job_directory): + collectl_cli = self.__collectl_recorder_cli(job_directory) if self.log_collectl_program_output: - redirect_to = self._instrument_file_path( job_directory, "program_output" ) + redirect_to = self._instrument_file_path(job_directory, "program_output") else: redirect_to = "/dev/null" return "%s > %s 2>&1 &" % ( @@ -196,15 +196,15 @@ def __collectl_record_command( self, job_directory ): redirect_to, ) - def __pid_file( self, job_directory ): - return self._instrument_file_path( job_directory, "pid" ) + def __pid_file(self, job_directory): + return self._instrument_file_path(job_directory, "pid") - def _is_instrumented_collectl_log( self, filename ): - prefix = self._instrument_file_name( "log" ) - return filename.startswith( prefix ) and filename.endswith( ".raw.gz" ) + def _is_instrumented_collectl_log(self, filename): + prefix = self._instrument_file_name("log") + return filename.startswith(prefix) and filename.endswith(".raw.gz") -def procfilt_argument( procfilt_on ): +def procfilt_argument(procfilt_on): if procfilt_on == "username": return "U$USER" elif procfilt_on == "uid": @@ -212,8 +212,8 @@ def procfilt_argument( procfilt_on ): else: # Ensure it is empty of None if procfilt_on or procfilt_on.lower() != "none": - raise Exception( "Invalid procfilt_on argument encountered") + raise Exception("Invalid procfilt_on argument encountered") return "" -__all__ = ( 'CollectlPlugin', ) +__all__ = ('CollectlPlugin', ) diff --git a/lib/galaxy/jobs/metrics/instrumenters/core.py b/lib/galaxy/jobs/metrics/instrumenters/core.py index ce5534fb9017..86c923d69b74 100644 --- a/lib/galaxy/jobs/metrics/instrumenters/core.py +++ b/lib/galaxy/jobs/metrics/instrumenters/core.py @@ -5,7 +5,7 @@ from ..instrumenters import InstrumentPlugin from ...metrics import formatting -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) GALAXY_SLOTS_KEY = "galaxy_slots" START_EPOCH_KEY = "start_epoch" @@ -13,76 +13,76 @@ RUNTIME_SECONDS_KEY = "runtime_seconds" -class CorePluginFormatter( formatting.JobMetricFormatter ): +class CorePluginFormatter(formatting.JobMetricFormatter): - def format( self, key, value ): - value = int( value ) + def format(self, key, value): + value = int(value) if key == GALAXY_SLOTS_KEY: - return ( "Cores Allocated", "%d" % value ) + return ("Cores Allocated", "%d" % value) elif key == RUNTIME_SECONDS_KEY: - return ( "Job Runtime (Wall Clock)", formatting.seconds_to_str( value ) ) + return ("Job Runtime (Wall Clock)", formatting.seconds_to_str(value)) else: # TODO: Use localized version of this from galaxy.ini title = "Job Start Time" if key == START_EPOCH_KEY else "Job End Time" - return (title, time.strftime( '%Y-%m-%d %H:%M:%S', time.localtime( value ) ) ) + return (title, time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(value))) -class CorePlugin( InstrumentPlugin ): +class CorePlugin(InstrumentPlugin): """ Simple plugin that collects data without external dependencies. In particular it currently collects value set for Galaxy slots. """ plugin_type = "core" formatter = CorePluginFormatter() - def __init__( self, **kwargs ): + def __init__(self, **kwargs): pass - def pre_execute_instrument( self, job_directory ): + def pre_execute_instrument(self, job_directory): commands = [] - commands.append( self.__record_galaxy_slots_command( job_directory ) ) - commands.append( self.__record_seconds_since_epoch_to_file( job_directory, "start" ) ) + commands.append(self.__record_galaxy_slots_command(job_directory)) + commands.append(self.__record_seconds_since_epoch_to_file(job_directory, "start")) return commands - def post_execute_instrument( self, job_directory ): + def post_execute_instrument(self, job_directory): commands = [] - commands.append( self.__record_seconds_since_epoch_to_file( job_directory, "end" ) ) + commands.append(self.__record_seconds_since_epoch_to_file(job_directory, "end")) return commands - def job_properties( self, job_id, job_directory ): - galaxy_slots_file = self.__galaxy_slots_file( job_directory ) + def job_properties(self, job_id, job_directory): + galaxy_slots_file = self.__galaxy_slots_file(job_directory) properties = {} - properties[ GALAXY_SLOTS_KEY ] = self.__read_integer( galaxy_slots_file ) - start = self.__read_seconds_since_epoch( job_directory, "start" ) - end = self.__read_seconds_since_epoch( job_directory, "end" ) + properties[GALAXY_SLOTS_KEY] = self.__read_integer(galaxy_slots_file) + start = self.__read_seconds_since_epoch(job_directory, "start") + end = self.__read_seconds_since_epoch(job_directory, "end") if start is not None and end is not None: - properties[ START_EPOCH_KEY ] = start - properties[ END_EPOCH_KEY ] = end - properties[ RUNTIME_SECONDS_KEY ] = end - start + properties[START_EPOCH_KEY] = start + properties[END_EPOCH_KEY] = end + properties[RUNTIME_SECONDS_KEY] = end - start return properties - def __record_galaxy_slots_command( self, job_directory ): - galaxy_slots_file = self.__galaxy_slots_file( job_directory ) + def __record_galaxy_slots_command(self, job_directory): + galaxy_slots_file = self.__galaxy_slots_file(job_directory) return '''echo "$GALAXY_SLOTS" > '%s' ''' % galaxy_slots_file - def __record_seconds_since_epoch_to_file( self, job_directory, name ): - path = self._instrument_file_path( job_directory, "epoch_%s" % name ) + def __record_seconds_since_epoch_to_file(self, job_directory, name): + path = self._instrument_file_path(job_directory, "epoch_%s" % name) return 'date +"%s" > ' + path - def __read_seconds_since_epoch( self, job_directory, name ): - path = self._instrument_file_path( job_directory, "epoch_%s" % name ) - return self.__read_integer( path ) + def __read_seconds_since_epoch(self, job_directory, name): + path = self._instrument_file_path(job_directory, "epoch_%s" % name) + return self.__read_integer(path) - def __galaxy_slots_file( self, job_directory ): - return self._instrument_file_path( job_directory, "galaxy_slots" ) + def __galaxy_slots_file(self, job_directory): + return self._instrument_file_path(job_directory, "galaxy_slots") - def __read_integer( self, path ): + def __read_integer(self, path): value = None try: - value = int( open( path, "r" ).read() ) + value = int(open(path, "r").read()) except Exception: pass return value -__all__ = ( 'CorePlugin', ) +__all__ = ('CorePlugin', ) diff --git a/lib/galaxy/jobs/metrics/instrumenters/cpuinfo.py b/lib/galaxy/jobs/metrics/instrumenters/cpuinfo.py index 1ce31cb2ea62..b54d13df059d 100644 --- a/lib/galaxy/jobs/metrics/instrumenters/cpuinfo.py +++ b/lib/galaxy/jobs/metrics/instrumenters/cpuinfo.py @@ -7,58 +7,58 @@ from ..instrumenters import InstrumentPlugin from ...metrics import formatting -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) PROCESSOR_LINE = re.compile(r"processor\s*\:\s*(\d+)") -class CpuInfoFormatter( formatting.JobMetricFormatter ): +class CpuInfoFormatter(formatting.JobMetricFormatter): - def format( self, key, value ): + def format(self, key, value): if key == "processor_count": - return "Processor Count", "%s" % int( value ) + return "Processor Count", "%s" % int(value) else: return key, value -class CpuInfoPlugin( InstrumentPlugin ): +class CpuInfoPlugin(InstrumentPlugin): """ Gather information about processor configuration from /proc/cpuinfo. Linux only. """ plugin_type = "cpuinfo" formatter = CpuInfoFormatter() - def __init__( self, **kwargs ): - self.verbose = util.asbool( kwargs.get( "verbose", False ) ) + def __init__(self, **kwargs): + self.verbose = util.asbool(kwargs.get("verbose", False)) - def pre_execute_instrument( self, job_directory ): - return "cat /proc/cpuinfo > '%s'" % self.__instrument_cpuinfo_path( job_directory ) + def pre_execute_instrument(self, job_directory): + return "cat /proc/cpuinfo > '%s'" % self.__instrument_cpuinfo_path(job_directory) - def job_properties( self, job_id, job_directory ): + def job_properties(self, job_id, job_directory): properties = {} processor_count = 0 - with open( self.__instrument_cpuinfo_path( job_directory ) ) as f: + with open(self.__instrument_cpuinfo_path(job_directory)) as f: current_processor = None for line in f: line = line.strip().lower() if not line: # Skip empty lines continue - processor_line_match = PROCESSOR_LINE.match( line ) + processor_line_match = PROCESSOR_LINE.match(line) if processor_line_match: processor_count += 1 - current_processor = processor_line_match.group( 1 ) + current_processor = processor_line_match.group(1) elif current_processor and self.verbose: # If verbose, dump information about each processor # into database... - key, value = line.split( ":", 1 ) - key = "processor_%s_%s" % ( current_processor, key.strip() ) + key, value = line.split(":", 1) + key = "processor_%s_%s" % (current_processor, key.strip()) value = value - properties[ "processor_count" ] = processor_count + properties["processor_count"] = processor_count return properties - def __instrument_cpuinfo_path( self, job_directory ): - return self._instrument_file_path( job_directory, "cpuinfo" ) + def __instrument_cpuinfo_path(self, job_directory): + return self._instrument_file_path(job_directory, "cpuinfo") -__all__ = ( 'CpuInfoPlugin', ) +__all__ = ('CpuInfoPlugin', ) diff --git a/lib/galaxy/jobs/metrics/instrumenters/env.py b/lib/galaxy/jobs/metrics/instrumenters/env.py index 3c2adb90386a..66257f5feca5 100644 --- a/lib/galaxy/jobs/metrics/instrumenters/env.py +++ b/lib/galaxy/jobs/metrics/instrumenters/env.py @@ -5,67 +5,67 @@ from ..instrumenters import InstrumentPlugin from ...metrics import formatting -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class EnvFormatter( formatting.JobMetricFormatter ): +class EnvFormatter(formatting.JobMetricFormatter): pass -class EnvPlugin( InstrumentPlugin ): +class EnvPlugin(InstrumentPlugin): """ Instrumentation plugin capable of recording all or specific environment variables for a job at runtime. """ plugin_type = "env" formatter = EnvFormatter() - def __init__( self, **kwargs ): - variables_str = kwargs.get( "variables", None ) + def __init__(self, **kwargs): + variables_str = kwargs.get("variables", None) if variables_str: - variables = [ v.strip() for v in variables_str.split(",") ] + variables = [v.strip() for v in variables_str.split(",")] else: variables = None self.variables = variables - def pre_execute_instrument( self, job_directory ): + def pre_execute_instrument(self, job_directory): """ Use env to dump all environment variables to a file. """ - return "env > '%s'" % self.__env_file( job_directory ) + return "env > '%s'" % self.__env_file(job_directory) - def post_execute_instrument( self, job_directory ): + def post_execute_instrument(self, job_directory): return None - def job_properties( self, job_id, job_directory ): + def job_properties(self, job_id, job_directory): """ Recover environment variables dumped out on compute server and filter out specific variables if needed. """ variables = self.variables properties = {} - env_string = ''.join( open( self.__env_file( job_directory ) ).readlines() ) + env_string = ''.join(open(self.__env_file(job_directory)).readlines()) while env_string: # Check if the next lines contain a shell function. # We use '\n\}\n' as regex termination because shell # functions can be nested. # We use the non-greedy '.+?' because of re.DOTALL . - m = re.match( '([^=]+)=(\(\) \{.+?\n\})\n', env_string, re.DOTALL ) + m = re.match('([^=]+)=(\(\) \{.+?\n\})\n', env_string, re.DOTALL) if m is None: - m = re.match( '([^=]+)=(.*)\n', env_string ) + m = re.match('([^=]+)=(.*)\n', env_string) if m is None: # Some problem recording or reading back env output. message_template = "Problem parsing env metric output for job %s - properties will be incomplete" message = message_template % job_id - log.debug( message ) + log.debug(message) break (var, value) = m.groups() if not variables or var in variables: - properties[ var ] = value + properties[var] = value env_string = env_string[m.end():] return properties - def __env_file( self, job_directory ): - return self._instrument_file_path( job_directory, "vars" ) + def __env_file(self, job_directory): + return self._instrument_file_path(job_directory, "vars") -__all__ = ( 'EnvPlugin', ) +__all__ = ('EnvPlugin', ) diff --git a/lib/galaxy/jobs/metrics/instrumenters/meminfo.py b/lib/galaxy/jobs/metrics/instrumenters/meminfo.py index 1eac6a69dee7..984e57f57fdc 100644 --- a/lib/galaxy/jobs/metrics/instrumenters/meminfo.py +++ b/lib/galaxy/jobs/metrics/instrumenters/meminfo.py @@ -20,47 +20,47 @@ } -class MemInfoFormatter( formatting.JobMetricFormatter ): +class MemInfoFormatter(formatting.JobMetricFormatter): - def format( self, key, value ): - title = MEMINFO_TITLES.get( key, key ) - return title, util.nice_size( value * 1000 ) # kB = *1000, KB = *1024 - wikipedia + def format(self, key, value): + title = MEMINFO_TITLES.get(key, key) + return title, util.nice_size(value * 1000) # kB = *1000, KB = *1024 - wikipedia -class MemInfoPlugin( InstrumentPlugin ): +class MemInfoPlugin(InstrumentPlugin): """ Gather information about processor configuration from /proc/cpuinfo. Linux only. """ plugin_type = "meminfo" formatter = MemInfoFormatter() - def __init__( self, **kwargs ): - self.verbose = util.asbool( kwargs.get( "verbose", False ) ) + def __init__(self, **kwargs): + self.verbose = util.asbool(kwargs.get("verbose", False)) - def pre_execute_instrument( self, job_directory ): - return "cat /proc/meminfo > '%s'" % self.__instrument_meminfo_path( job_directory ) + def pre_execute_instrument(self, job_directory): + return "cat /proc/meminfo > '%s'" % self.__instrument_meminfo_path(job_directory) - def job_properties( self, job_id, job_directory ): + def job_properties(self, job_id, job_directory): properties = {} - with open( self.__instrument_meminfo_path( job_directory ) ) as f: + with open(self.__instrument_meminfo_path(job_directory)) as f: for line in f: line = line.strip() if not line: # Skip empty lines continue - line_match = MEMINFO_LINE.match( line ) + line_match = MEMINFO_LINE.match(line) if not line_match: continue - key = line_match.group( 1 ).lower() + key = line_match.group(1).lower() # By default just grab important meminfo properties with titles # defined for formatter. Grab everything in verbose mode for # an arbitrary snapshot of memory at beginning of run. if key in MEMINFO_TITLES or self.verbose: - value = long( line_match.group( 2 ) ) - properties[ key ] = value + value = long(line_match.group(2)) + properties[key] = value return properties - def __instrument_meminfo_path( self, job_directory ): - return self._instrument_file_path( job_directory, "meminfo" ) + def __instrument_meminfo_path(self, job_directory): + return self._instrument_file_path(job_directory, "meminfo") -__all__ = ( 'MemInfoPlugin', ) +__all__ = ('MemInfoPlugin', ) diff --git a/lib/galaxy/jobs/metrics/instrumenters/uname.py b/lib/galaxy/jobs/metrics/instrumenters/uname.py index fc10fb820dd6..577b7adf7954 100644 --- a/lib/galaxy/jobs/metrics/instrumenters/uname.py +++ b/lib/galaxy/jobs/metrics/instrumenters/uname.py @@ -3,33 +3,33 @@ from ...metrics import formatting -class UnameFormatter( formatting.JobMetricFormatter ): +class UnameFormatter(formatting.JobMetricFormatter): - def format( self, key, value ): + def format(self, key, value): return "Operating System", value -class UnamePlugin( InstrumentPlugin ): +class UnamePlugin(InstrumentPlugin): """ Use uname to gather operating system information about remote system job is running on. Linux only. """ plugin_type = "uname" formatter = UnameFormatter() - def __init__( self, **kwargs ): - self.uname_args = kwargs.get( "args", "-a" ) + def __init__(self, **kwargs): + self.uname_args = kwargs.get("args", "-a") - def pre_execute_instrument( self, job_directory ): - return "uname %s > '%s'" % ( self.uname_args, self.__instrument_uname_path( job_directory ) ) + def pre_execute_instrument(self, job_directory): + return "uname %s > '%s'" % (self.uname_args, self.__instrument_uname_path(job_directory)) - def job_properties( self, job_id, job_directory ): + def job_properties(self, job_id, job_directory): properties = {} - with open( self.__instrument_uname_path( job_directory ) ) as f: - properties[ "uname" ] = f.read() + with open(self.__instrument_uname_path(job_directory)) as f: + properties["uname"] = f.read() return properties - def __instrument_uname_path( self, job_directory ): - return self._instrument_file_path( job_directory, "uname" ) + def __instrument_uname_path(self, job_directory): + return self._instrument_file_path(job_directory, "uname") -__all__ = ( 'UnamePlugin', ) +__all__ = ('UnamePlugin', ) diff --git a/lib/galaxy/jobs/output_checker.py b/lib/galaxy/jobs/output_checker.py index 987d5c6aa967..14072957a65b 100644 --- a/lib/galaxy/jobs/output_checker.py +++ b/lib/galaxy/jobs/output_checker.py @@ -3,10 +3,11 @@ import traceback from logging import getLogger -log = getLogger( __name__ ) +log = getLogger(__name__) -def check_output( tool, stdout, stderr, tool_exit_code, job ): + +def check_output(tool, stdout, stderr, tool_exit_code, job): """ Check the output of a tool - given the stdout, stderr, and the tool's exit code, return True if the tool exited succesfully and False @@ -31,8 +32,7 @@ def check_output( tool, stdout, stderr, tool_exit_code, job ): # then we assume that the tool writer overwrote the default # behavior of just setting an error if there is *anything* on # stderr. - if ( len( tool.stdio_regexes ) > 0 or - len( tool.stdio_exit_codes ) > 0 ): + if len(tool.stdio_regexes) > 0 or len(tool.stdio_exit_codes) > 0: # Check the exit code ranges in the order in which # they were specified. Each exit_code is a StdioExitCode # that includes an applicable range. If the exit code was in @@ -41,24 +41,23 @@ def check_output( tool, stdout, stderr, tool_exit_code, job ): max_error_level = StdioErrorLevel.NO_ERROR if tool_exit_code is not None: for stdio_exit_code in tool.stdio_exit_codes: - if ( tool_exit_code >= stdio_exit_code.range_start and - tool_exit_code <= stdio_exit_code.range_end ): + if (tool_exit_code >= stdio_exit_code.range_start and + tool_exit_code <= stdio_exit_code.range_end): # Tack on a generic description of the code # plus a specific code description. For example, # this might prepend "Job 42: Warning (Out of Memory)\n". code_desc = stdio_exit_code.desc - if ( None is code_desc ): + if None is code_desc: code_desc = "" - tool_msg = ( "%s: Exit code %d (%s)" % ( - StdioErrorLevel.desc( stdio_exit_code.error_level ), - tool_exit_code, - code_desc ) ) - log.info( "Job %s: %s" % (job.get_id_tag(), tool_msg) ) + tool_msg = ("%s: Exit code %d (%s)" % ( + StdioErrorLevel.desc(stdio_exit_code.error_level), + tool_exit_code, + code_desc)) + log.info("Job %s: %s" % (job.get_id_tag(), tool_msg)) stderr = tool_msg + "\n" + stderr - max_error_level = max( max_error_level, - stdio_exit_code.error_level ) - if ( max_error_level >= - StdioErrorLevel.FATAL ): + max_error_level = max(max_error_level, + stdio_exit_code.error_level) + if max_error_level >= StdioErrorLevel.FATAL: break if max_error_level < StdioErrorLevel.FATAL: @@ -78,32 +77,30 @@ def check_output( tool, stdout, stderr, tool_exit_code, job ): # o If it was fatal, then we're done - break. # Repeat the stdout stuff for stderr. # TODO: Collapse this into a single function. - if ( regex.stdout_match ): - regex_match = re.search( regex.match, stdout, - re.IGNORECASE ) - if ( regex_match ): - rexmsg = __regex_err_msg( regex_match, regex) - log.info( "Job %s: %s" - % ( job.get_id_tag(), rexmsg ) ) + if regex.stdout_match: + regex_match = re.search(regex.match, stdout, + re.IGNORECASE) + if regex_match: + rexmsg = __regex_err_msg(regex_match, regex) + log.info("Job %s: %s" + % (job.get_id_tag(), rexmsg)) stdout = rexmsg + "\n" + stdout - max_error_level = max( max_error_level, - regex.error_level ) - if ( max_error_level >= - StdioErrorLevel.FATAL ): + max_error_level = max(max_error_level, + regex.error_level) + if max_error_level >= StdioErrorLevel.FATAL: break - if ( regex.stderr_match ): - regex_match = re.search( regex.match, stderr, - re.IGNORECASE ) - if ( regex_match ): - rexmsg = __regex_err_msg( regex_match, regex) - log.info( "Job %s: %s" - % ( job.get_id_tag(), rexmsg ) ) + if regex.stderr_match: + regex_match = re.search(regex.match, stderr, + re.IGNORECASE) + if regex_match: + rexmsg = __regex_err_msg(regex_match, regex) + log.info("Job %s: %s" + % (job.get_id_tag(), rexmsg)) stderr = rexmsg + "\n" + stderr - max_error_level = max( max_error_level, - regex.error_level ) - if ( max_error_level >= - StdioErrorLevel.FATAL ): + max_error_level = max(max_error_level, + regex.error_level) + if max_error_level >= StdioErrorLevel.FATAL: break # If we encountered a fatal error, then we'll need to set the @@ -131,25 +128,25 @@ def check_output( tool, stdout, stderr, tool_exit_code, job ): # On any exception, return True. except: tb = traceback.format_exc() - log.warning( "Tool check encountered unexpected exception; " + - "assuming tool was successful: " + tb ) + log.warning("Tool check encountered unexpected exception; " + + "assuming tool was successful: " + tb) success = True # Store the modified stdout and stderr in the job: if job is not None: - job.set_streams( stdout, stderr ) + job.set_streams(stdout, stderr) return success -def __regex_err_msg( match, regex ): +def __regex_err_msg(match, regex): """ Return a message about the match on tool output using the given ToolStdioRegex regex object. The regex_match is a MatchObject that will contain the string matched on. """ # Get the description for the error level: - err_msg = StdioErrorLevel.desc( regex.error_level ) + ": " + err_msg = StdioErrorLevel.desc(regex.error_level) + ": " # If there's a description for the regular expression, then use it. # Otherwise, we'll take the first 256 characters of the match. if None is not regex.desc: @@ -160,7 +157,7 @@ def __regex_err_msg( match, regex ): err_msg += "Matched on " # TODO: Move the constant 256 somewhere else besides here. if mend - mstart > 256: - err_msg += match.string[ mstart : mstart + 256 ] + "..." + err_msg += match.string[mstart : mstart + 256] + "..." else: - err_msg += match.string[ mstart: mend ] + err_msg += match.string[mstart: mend] return err_msg diff --git a/lib/galaxy/jobs/rule_helper.py b/lib/galaxy/jobs/rule_helper.py index b1825346e8ff..2a76715bb6f2 100644 --- a/lib/galaxy/jobs/rule_helper.py +++ b/lib/galaxy/jobs/rule_helper.py @@ -8,12 +8,12 @@ from galaxy import util import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) VALID_JOB_HASH_STRATEGIES = ["job", "user", "history", "workflow_invocation"] -class RuleHelper( object ): +class RuleHelper(object): """ Utility to allow job rules to interface cleanly with the rest of Galaxy and shield them from low-level details of models, metrics, etc.... @@ -21,10 +21,10 @@ class RuleHelper( object ): could interface with other stuff as well. """ - def __init__( self, app ): + def __init__(self, app): self.app = app - def supports_docker( self, job_or_tool ): + def supports_docker(self, job_or_tool): """ Job rules can pass this function a job, job_wrapper, or tool and determine if the underlying tool believes it can be containered. """ @@ -32,14 +32,14 @@ def supports_docker( self, job_or_tool ): # developers from the details and they shouldn't have to know how to # interrogate tool or job to figure out if it can be run in a # container. - if hasattr( job_or_tool, 'containers' ): + if hasattr(job_or_tool, 'containers'): tool = job_or_tool - elif hasattr( job_or_tool, 'tool' ): + elif hasattr(job_or_tool, 'tool'): # Have a JobWrapper-like tool = job_or_tool.tool else: # Have a Job object. - tool = self.app.toolbox.get_tool( job_or_tool.tool_id ) + tool = self.app.toolbox.get_tool(job_or_tool.tool_id, tool_version=job_or_tool.tool_version) # Can't import at top because circular import between galaxy.tools and galaxy.jobs. import galaxy.tools.deps.containers tool_info = galaxy.tools.deps.containers.ToolInfo(tool.containers, tool.requirements, tool.requires_galaxy_python_environment) @@ -50,8 +50,8 @@ def job_count( self, **kwds ): - query = self.query( model.Job ) - return self._filter_job_query( query, **kwds ).count() + query = self.query(model.Job) + return self._filter_job_query(query, **kwds).count() def sum_job_runtime( self, @@ -60,22 +60,22 @@ def sum_job_runtime( # TODO: Consider sum_core_hours or something that scales runtime by # by calculated cores per job. query = self.metric_query( - select=func.sum( model.JobMetricNumeric.table.c.metric_value ), + select=func.sum(model.JobMetricNumeric.table.c.metric_value), metric_name="runtime_seconds", plugin="core", ) - query = query.join( model.Job ) - return float( self._filter_job_query( query, **kwds ).first()[ 0 ] ) + query = query.join(model.Job) + return float(self._filter_job_query(query, **kwds).first()[0]) - def metric_query( self, select, metric_name, plugin, numeric=True ): + def metric_query(self, select, metric_name, plugin, numeric=True): metric_class = model.JobMetricNumeric if numeric else model.JobMetricText - query = self.query( select ) - query = query.filter( metric_class.table.c.plugin == plugin ) - query = query.filter( metric_class.table.c.metric_name == metric_name ) + query = self.query(select) + query = query.filter(metric_class.table.c.plugin == plugin) + query = query.filter(metric_class.table.c.metric_name == metric_name) return query - def query( self, select_expression ): - return self.app.model.context.query( select_expression ) + def query(self, select_expression): + return self.app.model.context.query(select_expression) def _filter_job_query( self, @@ -88,40 +88,40 @@ def _filter_job_query( updated_in_last=None, ): if for_destination is not None: - for_destinations = [ for_destination ] + for_destinations = [for_destination] - query = query.join( model.User ) + query = query.join(model.User) if for_user_email is not None: - query = query.filter( model.User.table.c.email == for_user_email ) + query = query.filter(model.User.table.c.email == for_user_email) if for_destinations is not None: - if len( for_destinations ) == 1: - query = query.filter( model.Job.table.c.destination_id == for_destinations[ 0 ] ) + if len(for_destinations) == 1: + query = query.filter(model.Job.table.c.destination_id == for_destinations[0]) else: - query = query.filter( model.Job.table.c.destination_id.in_( for_destinations ) ) + query = query.filter(model.Job.table.c.destination_id.in_(for_destinations)) if created_in_last is not None: end_date = datetime.now() start_date = end_date - created_in_last - query = query.filter( model.Job.table.c.create_time >= start_date ) + query = query.filter(model.Job.table.c.create_time >= start_date) if updated_in_last is not None: end_date = datetime.now() start_date = end_date - updated_in_last - log.info( end_date ) - log.info( start_date ) - query = query.filter( model.Job.table.c.update_time >= start_date ) + log.info(end_date) + log.info(start_date) + query = query.filter(model.Job.table.c.update_time >= start_date) if for_job_states is not None: # Optimize the singleton case - can be much more performant in my experience. - if len( for_job_states ) == 1: - query = query.filter( model.Job.table.c.state == for_job_states[ 0 ] ) + if len(for_job_states) == 1: + query = query.filter(model.Job.table.c.state == for_job_states[0]) else: - query = query.filter( model.Job.table.c.state.in_( for_job_states ) ) + query = query.filter(model.Job.table.c.state.in_(for_job_states)) return query - def should_burst( self, destination_ids, num_jobs, job_states=None ): + def should_burst(self, destination_ids, num_jobs, job_states=None): """ Check if the specified destinations ``destination_ids`` have at least ``num_jobs`` assigned to it - send in ``job_state`` as ``queued`` to limit this check to number of jobs queued. @@ -135,30 +135,30 @@ def should_burst( self, destination_ids, num_jobs, job_states=None ): job_states = "queued,running" from_destination_job_count = self.job_count( for_destinations=destination_ids, - for_job_states=util.listify( job_states ) + for_job_states=util.listify(job_states) ) # Would this job push us over maximum job count before requiring # bursting (roughly... very roughly given many handler threads may be # scheduling jobs). - return ( from_destination_job_count + 1 ) > int( num_jobs ) + return (from_destination_job_count + 1) > int(num_jobs) - def choose_one( self, lst, hash_value=None ): + def choose_one(self, lst, hash_value=None): """ Choose a random value from supplied list. If hash_value is passed in then every request with that same hash_value would produce the same choice from the supplied list. """ if hash_value is None: - return random.choice( lst ) + return random.choice(lst) - if not isinstance( hash_value, int ): + if not isinstance(hash_value, int): # Convert hash_value string into index - as_hex = hashlib.md5( hash_value ).hexdigest() + as_hex = hashlib.md5(hash_value).hexdigest() hash_value = int(as_hex, 16) # else assumed to be 'random' int from 0-~Inf - random_index = hash_value % len( lst ) - return lst[ random_index ] + random_index = hash_value % len(lst) + return lst[random_index] - def job_hash( self, job, hash_by=None ): + def job_hash(self, job, hash_by=None): """ Produce a reproducible hash for the given job on various criteria - for instance if hash_by is "workflow_invocation,history" - all jobs within the same workflow invocation will receive the same @@ -170,27 +170,27 @@ def job_hash( self, job, hash_by=None ): route or schedule related jobs. """ if hash_by is None: - hash_by = [ "job" ] - hash_bys = util.listify( hash_by ) + hash_by = ["job"] + hash_bys = util.listify(hash_by) for hash_by in hash_bys: - job_hash = self._try_hash_for_job( job, hash_by ) + job_hash = self._try_hash_for_job(job, hash_by) if job_hash: return job_hash # Fall back to just hashing by job id, should always return a value. - return self._try_hash_for_job( job, "job" ) + return self._try_hash_for_job(job, "job") - def _try_hash_for_job( self, job, hash_by ): + def _try_hash_for_job(self, job, hash_by): """ May return False or None if hash type is invalid for that job - e.g. attempting to hash by user for anonymous job or by workflow invocation for jobs outside of workflows. """ if hash_by not in VALID_JOB_HASH_STRATEGIES: - message = "Do not know how to hash jobs by %s, must be one of %s" % ( hash_by, VALID_JOB_HASH_STRATEGIES ) - raise Exception( message ) + message = "Do not know how to hash jobs by %s, must be one of %s" % (hash_by, VALID_JOB_HASH_STRATEGIES) + raise Exception(message) if hash_by == "workflow_invocation": - return job.raw_param_dict().get( "__workflow_invocation_uuid__", None ) + return job.raw_param_dict().get("__workflow_invocation_uuid__", None) elif hash_by == "history": return job.history_id elif hash_by == "user": diff --git a/lib/galaxy/jobs/runners/__init__.py b/lib/galaxy/jobs/runners/__init__.py index 6ee51c87608d..cd30907061d8 100644 --- a/lib/galaxy/jobs/runners/__init__.py +++ b/lib/galaxy/jobs/runners/__init__.py @@ -26,7 +26,7 @@ from .state_handler_factory import build_state_handlers -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) STOP_SIGNAL = object() @@ -39,22 +39,22 @@ GALAXY_VENV_TEMPLATE = """GALAXY_VIRTUAL_ENV="%s"; if [ "$GALAXY_VIRTUAL_ENV" != "None" -a -z "$VIRTUAL_ENV" -a -f "$GALAXY_VIRTUAL_ENV/bin/activate" ]; then . "$GALAXY_VIRTUAL_ENV/bin/activate"; fi;""" -class RunnerParams( ParamsWithSpecs ): +class RunnerParams(ParamsWithSpecs): - def _param_unknown_error( self, name ): - raise Exception( JOB_RUNNER_PARAMETER_UNKNOWN_MESSAGE % name ) + def _param_unknown_error(self, name): + raise Exception(JOB_RUNNER_PARAMETER_UNKNOWN_MESSAGE % name) - def _param_map_error( self, name, value ): - raise Exception( JOB_RUNNER_PARAMETER_MAP_PROBLEM_MESSAGE % ( name, value ) ) + def _param_map_error(self, name, value): + raise Exception(JOB_RUNNER_PARAMETER_MAP_PROBLEM_MESSAGE % (name, value)) - def _param_vaildation_error( self, name, value ): - raise Exception( JOB_RUNNER_PARAMETER_VALIDATION_FAILED_MESSAGE % name ) + def _param_vaildation_error(self, name, value): + raise Exception(JOB_RUNNER_PARAMETER_VALIDATION_FAILED_MESSAGE % name) -class BaseJobRunner( object ): - DEFAULT_SPECS = dict( recheck_missing_job_retries=dict( map=int, valid=lambda x: x >= 0, default=0 ) ) +class BaseJobRunner(object): + DEFAULT_SPECS = dict(recheck_missing_job_retries=dict(map=int, valid=lambda x: x >= 0, default=0)) - def __init__( self, app, nworkers, **kwargs ): + def __init__(self, app, nworkers, **kwargs): """Start the job runner """ self.app = app @@ -62,10 +62,10 @@ def __init__( self, app, nworkers, **kwargs ): self.nworkers = nworkers runner_param_specs = self.DEFAULT_SPECS.copy() if 'runner_param_specs' in kwargs: - runner_param_specs.update( kwargs.pop( 'runner_param_specs' ) ) + runner_param_specs.update(kwargs.pop('runner_param_specs')) if kwargs: - log.debug( 'Loading %s with params: %s', self.runner_name, kwargs ) - self.runner_params = RunnerParams( specs=runner_param_specs, params=kwargs ) + log.debug('Loading %s with params: %s', self.runner_name, kwargs) + self.runner_params = RunnerParams(specs=runner_param_specs, params=kwargs) self.runner_state_handlers = build_state_handlers() def _init_worker_threads(self): @@ -75,16 +75,16 @@ def _init_worker_threads(self): self.work_threads = [] log.debug('Starting %s %s workers' % (self.nworkers, self.runner_name)) for i in range(self.nworkers): - worker = threading.Thread( name="%s.work_thread-%d" % (self.runner_name, i), target=self.run_next ) - worker.setDaemon( True ) + worker = threading.Thread(name="%s.work_thread-%d" % (self.runner_name, i), target=self.run_next) + worker.setDaemon(True) worker.start() - self.work_threads.append( worker ) + self.work_threads.append(worker) def run_next(self): """Run the next item in the work queue (a job waiting to run) """ while True: - ( method, arg ) = self.work_queue.get() + (method, arg) = self.work_queue.get() if method is STOP_SIGNAL: return # id and name are collected first so that the call of method() is the last exception. @@ -103,7 +103,7 @@ def run_next(self): try: method(arg) except: - log.exception( "(%s) Unhandled exception calling %s" % ( job_id, name ) ) + log.exception("(%s) Unhandled exception calling %s" % (job_id, name)) # Causes a runner's `queue_job` method to be called from a worker thread def put(self, job_wrapper): @@ -112,22 +112,22 @@ def put(self, job_wrapper): put_timer = ExecutionTimer() job = job_wrapper.get_job() # Change to queued state before handing to worker thread so the runner won't pick it up again - job_wrapper.change_state( model.Job.states.QUEUED, flush=False, job=job ) + job_wrapper.change_state(model.Job.states.QUEUED, flush=False, job=job) # Persist the destination so that the job will be included in counts if using concurrency limits - job_wrapper.set_job_destination( job_wrapper.job_destination, None, flush=False, job=job ) + job_wrapper.set_job_destination(job_wrapper.job_destination, None, flush=False, job=job) self.sa_session.flush() self.mark_as_queued(job_wrapper) log.debug("Job [%s] queued %s" % (job_wrapper.job_id, put_timer)) def mark_as_queued(self, job_wrapper): - self.work_queue.put( ( self.queue_job, job_wrapper ) ) + self.work_queue.put((self.queue_job, job_wrapper)) - def shutdown( self ): + def shutdown(self): """Attempts to gracefully shut down the worker threads """ - log.info( "%s: Sending stop signal to %s worker threads" % ( self.runner_name, len( self.work_threads ) ) ) - for i in range( len( self.work_threads ) ): - self.work_queue.put( ( STOP_SIGNAL, None ) ) + log.info("%s: Sending stop signal to %s worker threads" % (self.runner_name, len(self.work_threads))) + for i in range(len(self.work_threads)): + self.work_queue.put((STOP_SIGNAL, None)) # Most runners should override the legacy URL handler methods and destination param method def url_to_destination(self, url): @@ -156,12 +156,12 @@ def prepare_job(self, job_wrapper, include_metadata=False, include_work_dir_outp # Make sure the job hasn't been deleted if job_state == model.Job.states.DELETED: - log.debug( "(%s) Job deleted by user before it entered the %s queue" % ( job_id, self.runner_name ) ) - if self.app.config.cleanup_job in ( "always", "onsuccess" ): + log.debug("(%s) Job deleted by user before it entered the %s queue" % (job_id, self.runner_name)) + if self.app.config.cleanup_job in ("always", "onsuccess"): job_wrapper.cleanup() return False elif job_state != model.Job.states.QUEUED: - log.info( "(%s) Job is in state %s, skipping execution" % ( job_id, job_state ) ) + log.info("(%s) Job is in state %s, skipping execution" % (job_id, job_state)) # cleanup may not be safe in all states return False @@ -176,11 +176,11 @@ def prepare_job(self, job_wrapper, include_metadata=False, include_work_dir_outp ) except Exception as e: log.exception("(%s) Failure preparing job" % job_id) - job_wrapper.fail( e.message if hasattr( e, 'message' ) else "Job preparation failed", exception=True ) + job_wrapper.fail(e.message if hasattr(e, 'message') else "Job preparation failed", exception=True) return False if not job_wrapper.runner_command_line: - job_wrapper.finish( '', '' ) + job_wrapper.finish('', '') return False return True @@ -195,9 +195,9 @@ def stop_job(self, job): def recover(self, job, job_wrapper): raise NotImplementedError() - def build_command_line( self, job_wrapper, include_metadata=False, include_work_dir_outputs=True, - modify_command_for_container=True ): - container = self._find_container( job_wrapper ) + def build_command_line(self, job_wrapper, include_metadata=False, include_work_dir_outputs=True, + modify_command_for_container=True): + container = self._find_container(job_wrapper) if not container and job_wrapper.requires_containerization: raise Exception("Failed to find a container when required, contact Galaxy admin.") return build_command( @@ -209,7 +209,7 @@ def build_command_line( self, job_wrapper, include_metadata=False, include_work_ container=container ) - def get_work_dir_outputs( self, job_wrapper, job_working_directory=None, tool_working_directory=None ): + def get_work_dir_outputs(self, job_wrapper, job_working_directory=None, tool_working_directory=None): """ Returns list of pairs (source_file, destination) describing path to work_dir output file and ultimate destination. @@ -219,7 +219,7 @@ def get_work_dir_outputs( self, job_wrapper, job_working_directory=None, tool_wo if tool_working_directory is None: if not job_working_directory: - job_working_directory = os.path.abspath( job_wrapper.working_directory ) + job_working_directory = os.path.abspath(job_wrapper.working_directory) tool_working_directory = os.path.join(job_working_directory, "working") # Set up dict of dataset id --> output path; output path can be real or @@ -229,39 +229,39 @@ def get_work_dir_outputs( self, job_wrapper, job_working_directory=None, tool_wo path = dataset_path.real_path if self.app.config.outputs_to_working_directory: path = dataset_path.false_path - output_paths[ dataset_path.dataset_id ] = path + output_paths[dataset_path.dataset_id] = path output_pairs = [] # Walk job's output associations to find and use from_work_dir attributes. job = job_wrapper.get_job() job_tool = job_wrapper.tool - for (joda, dataset) in self._walk_dataset_outputs( job ): + for (joda, dataset) in self._walk_dataset_outputs(job): if joda and job_tool: - hda_tool_output = job_tool.find_output_def( joda.name ) + hda_tool_output = job_tool.find_output_def(joda.name) if hda_tool_output and hda_tool_output.from_work_dir: # Copy from working dir to HDA. # TODO: move instead of copy to save time? - source_file = os.path.join( tool_working_directory, hda_tool_output.from_work_dir ) - destination = job_wrapper.get_output_destination( output_paths[ dataset.dataset_id ] ) - if in_directory( source_file, tool_working_directory ): - output_pairs.append( ( source_file, destination ) ) + source_file = os.path.join(tool_working_directory, hda_tool_output.from_work_dir) + destination = job_wrapper.get_output_destination(output_paths[dataset.dataset_id]) + if in_directory(source_file, tool_working_directory): + output_pairs.append((source_file, destination)) else: # Security violation. - log.exception( "from_work_dir specified a location not in the working directory: %s, %s", source_file, job_wrapper.working_directory ) + log.exception("from_work_dir specified a location not in the working directory: %s, %s", source_file, job_wrapper.working_directory) return output_pairs - def _walk_dataset_outputs( self, job ): + def _walk_dataset_outputs(self, job): for dataset_assoc in job.output_datasets + job.output_library_datasets: for dataset in dataset_assoc.dataset.dataset.history_associations + dataset_assoc.dataset.dataset.library_associations: - if isinstance( dataset, self.app.model.HistoryDatasetAssociation ): - joda = self.sa_session.query( self.app.model.JobToOutputDatasetAssociation ).filter_by( job=job, dataset=dataset ).first() + if isinstance(dataset, self.app.model.HistoryDatasetAssociation): + joda = self.sa_session.query(self.app.model.JobToOutputDatasetAssociation).filter_by(job=job, dataset=dataset).first() yield (joda, dataset) # TODO: why is this not just something easy like: # for dataset_assoc in job.output_datasets + job.output_library_datasets: # yield (dataset_assoc, dataset_assoc.dataset) # I don't understand the reworking it backwards. -John - def _handle_metadata_externally( self, job_wrapper, resolve_requirements=False ): + def _handle_metadata_externally(self, job_wrapper, resolve_requirements=False): """ Set metadata externally. Used by the Pulsar job runner where this shouldn't be attached to command line to execute. @@ -269,49 +269,49 @@ def _handle_metadata_externally( self, job_wrapper, resolve_requirements=False ) # run the metadata setting script here # this is terminate-able when output dataset/job is deleted # so that long running set_meta()s can be canceled without having to reboot the server - if job_wrapper.get_state() not in [ model.Job.states.ERROR, model.Job.states.DELETED ] and job_wrapper.output_paths: + if job_wrapper.get_state() not in [model.Job.states.ERROR, model.Job.states.DELETED] and job_wrapper.output_paths: lib_adjust = GALAXY_LIB_ADJUST_TEMPLATE % job_wrapper.galaxy_lib_dir venv = GALAXY_VENV_TEMPLATE % job_wrapper.galaxy_virtual_env - external_metadata_script = job_wrapper.setup_external_metadata( output_fnames=job_wrapper.get_output_fnames(), - set_extension=True, - tmp_dir=job_wrapper.working_directory, - # We don't want to overwrite metadata that was copied over in init_meta(), as per established behavior - kwds={ 'overwrite' : False } ) + external_metadata_script = job_wrapper.setup_external_metadata(output_fnames=job_wrapper.get_output_fnames(), + set_extension=True, + tmp_dir=job_wrapper.working_directory, + # We don't want to overwrite metadata that was copied over in init_meta(), as per established behavior + kwds={'overwrite' : False}) external_metadata_script = "%s %s %s" % (lib_adjust, venv, external_metadata_script) if resolve_requirements: dependency_shell_commands = self.app.datatypes_registry.set_external_metadata_tool.build_dependency_shell_commands(job_directory=job_wrapper.working_directory) if dependency_shell_commands: - if isinstance( dependency_shell_commands, list ): - dependency_shell_commands = "&&".join( dependency_shell_commands ) - external_metadata_script = "%s&&%s" % ( dependency_shell_commands, external_metadata_script ) - log.debug( 'executing external set_meta script for job %d: %s' % ( job_wrapper.job_id, external_metadata_script ) ) - external_metadata_proc = subprocess.Popen( args=external_metadata_script, - shell=True, - cwd=job_wrapper.working_directory, - env=os.environ, - preexec_fn=os.setpgrp ) - job_wrapper.external_output_metadata.set_job_runner_external_pid( external_metadata_proc.pid, self.sa_session ) + if isinstance(dependency_shell_commands, list): + dependency_shell_commands = "&&".join(dependency_shell_commands) + external_metadata_script = "%s&&%s" % (dependency_shell_commands, external_metadata_script) + log.debug('executing external set_meta script for job %d: %s' % (job_wrapper.job_id, external_metadata_script)) + external_metadata_proc = subprocess.Popen(args=external_metadata_script, + shell=True, + cwd=job_wrapper.working_directory, + env=os.environ, + preexec_fn=os.setpgrp) + job_wrapper.external_output_metadata.set_job_runner_external_pid(external_metadata_proc.pid, self.sa_session) external_metadata_proc.wait() - log.debug( 'execution of external set_meta for job %d finished' % job_wrapper.job_id ) + log.debug('execution of external set_meta for job %d finished' % job_wrapper.job_id) def get_job_file(self, job_wrapper, **kwds): job_metrics = job_wrapper.app.job_metrics - job_instrumenter = job_metrics.job_instrumenters[ job_wrapper.job_destination.id ] + job_instrumenter = job_metrics.job_instrumenters[job_wrapper.job_destination.id] - env_setup_commands = kwds.get( 'env_setup_commands', [] ) - env_setup_commands.append( job_wrapper.get_env_setup_clause() or '' ) + env_setup_commands = kwds.get('env_setup_commands', []) + env_setup_commands.append(job_wrapper.get_env_setup_clause() or '') destination = job_wrapper.job_destination or {} - envs = destination.get( "env", [] ) - envs.extend( job_wrapper.environment_variables ) + envs = destination.get("env", []) + envs.extend(job_wrapper.environment_variables) for env in envs: - env_setup_commands.append( env_to_statement( env ) ) + env_setup_commands.append(env_to_statement(env)) command_line = job_wrapper.runner_command_line options = dict( job_instrumenter=job_instrumenter, galaxy_lib=job_wrapper.galaxy_lib_dir, galaxy_virtual_env=job_wrapper.galaxy_virtual_env, env_setup_commands=env_setup_commands, - working_directory=os.path.abspath( job_wrapper.working_directory ), + working_directory=os.path.abspath(job_wrapper.working_directory), command=command_line, shell=job_wrapper.shell, preserve_python_environment=job_wrapper.tool.requires_galaxy_python_environment, @@ -319,12 +319,12 @@ def get_job_file(self, job_wrapper, **kwds): # Additional logging to enable if debugging from_work_dir handling, metadata # commands, etc... (or just peak in the job script.) job_id = job_wrapper.job_id - log.debug( '(%s) command is: %s' % ( job_id, command_line ) ) + log.debug('(%s) command is: %s' % (job_id, command_line)) options.update(**kwds) return job_script(**options) - def write_executable_script( self, path, contents, mode=0o755 ): - write_script( path, contents, self.app.config, mode=mode ) + def write_executable_script(self, path, contents, mode=0o755): + write_script(path, contents, self.app.config, mode=mode) def _find_container( self, @@ -360,7 +360,7 @@ def _find_container( job_info ) - def _handle_runner_state( self, runner_state, job_state ): + def _handle_runner_state(self, runner_state, job_state): try: for handler in self.runner_state_handlers.get(runner_state, []): handler(self.app, self, job_state) @@ -369,25 +369,25 @@ def _handle_runner_state( self, runner_state, job_state ): except: log.exception('Caught exception in runner state handler:') - def fail_job( self, job_state, exception=False ): - if getattr( job_state, 'stop_job', True ): - self.stop_job( self.sa_session.query( self.app.model.Job ).get( job_state.job_wrapper.job_id ) ) - self._handle_runner_state( 'failure', job_state ) + def fail_job(self, job_state, exception=False): + if getattr(job_state, 'stop_job', True): + self.stop_job(self.sa_session.query(self.app.model.Job).get(job_state.job_wrapper.job_id)) + self._handle_runner_state('failure', job_state) # Not convinced this is the best way to indicate this state, but # something necessary if not job_state.runner_state_handled: - job_state.job_wrapper.fail( getattr( job_state, 'fail_message', 'Job failed' ), exception=exception ) + job_state.job_wrapper.fail(getattr(job_state, 'fail_message', 'Job failed'), exception=exception) if job_state.job_wrapper.cleanup_job == "always": job_state.cleanup() - def mark_as_resubmitted( self, job_state, info=None ): - job_state.job_wrapper.mark_as_resubmitted( info=info ) + def mark_as_resubmitted(self, job_state, info=None): + job_state.job_wrapper.mark_as_resubmitted(info=info) if not self.app.config.track_jobs_in_database: - job_state.job_wrapper.change_state( model.Job.states.QUEUED ) - self.app.job_manager.job_handler.dispatcher.put( job_state.job_wrapper ) + job_state.job_wrapper.change_state(model.Job.states.QUEUED) + self.app.job_manager.job_handler.dispatcher.put(job_state.job_wrapper) -class JobState( object ): +class JobState(object): """ Encapsulate state of jobs. """ @@ -399,59 +399,59 @@ class JobState( object ): OUTPUT_SIZE_LIMIT='output_size_limit' ) - def __init__( self, job_wrapper, job_destination ): + def __init__(self, job_wrapper, job_destination): self.runner_state_handled = False self.job_wrapper = job_wrapper self.job_destination = job_destination - self.cleanup_file_attributes = [ 'job_file', 'output_file', 'error_file', 'exit_code_file' ] + self.cleanup_file_attributes = ['job_file', 'output_file', 'error_file', 'exit_code_file'] - def set_defaults( self, files_dir ): + def set_defaults(self, files_dir): if self.job_wrapper is not None: id_tag = self.job_wrapper.get_id_tag() if files_dir is not None: - self.job_file = JobState.default_job_file( files_dir, id_tag ) - self.output_file = os.path.join( files_dir, 'galaxy_%s.o' % id_tag ) - self.error_file = os.path.join( files_dir, 'galaxy_%s.e' % id_tag ) - self.exit_code_file = os.path.join( files_dir, 'galaxy_%s.ec' % id_tag ) + self.job_file = JobState.default_job_file(files_dir, id_tag) + self.output_file = os.path.join(files_dir, 'galaxy_%s.o' % id_tag) + self.error_file = os.path.join(files_dir, 'galaxy_%s.e' % id_tag) + self.exit_code_file = os.path.join(files_dir, 'galaxy_%s.ec' % id_tag) job_name = 'g%s' % id_tag if self.job_wrapper.tool.old_id: job_name += '_%s' % self.job_wrapper.tool.old_id if self.job_wrapper.user: job_name += '_%s' % self.job_wrapper.user - self.job_name = ''.join( map( lambda x: x if x in ( string.ascii_letters + string.digits + '_' ) else '_', job_name ) ) + self.job_name = ''.join(map(lambda x: x if x in (string.ascii_letters + string.digits + '_') else '_', job_name)) @staticmethod - def default_job_file( files_dir, id_tag ): - return os.path.join( files_dir, 'galaxy_%s.sh' % id_tag ) + def default_job_file(files_dir, id_tag): + return os.path.join(files_dir, 'galaxy_%s.sh' % id_tag) @staticmethod - def default_exit_code_file( files_dir, id_tag ): - return os.path.join( files_dir, 'galaxy_%s.ec' % id_tag ) + def default_exit_code_file(files_dir, id_tag): + return os.path.join(files_dir, 'galaxy_%s.ec' % id_tag) - def cleanup( self ): - for file in [ getattr( self, a ) for a in self.cleanup_file_attributes if hasattr( self, a ) ]: + def cleanup(self): + for file in [getattr(self, a) for a in self.cleanup_file_attributes if hasattr(self, a)]: try: - os.unlink( file ) + os.unlink(file) except Exception as e: # TODO: Move this prefix stuff to a method so we don't have dispatch on attributes we may or may # not have. - if not hasattr( self, "job_id" ): + if not hasattr(self, "job_id"): prefix = "(%s)" % self.job_wrapper.get_id_tag() else: - prefix = "(%s/%s)" % ( self.job_wrapper.get_id_tag(), self.job_id ) - log.debug( "%s Unable to cleanup %s: %s" % (prefix, file, str( e ) ) ) + prefix = "(%s/%s)" % (self.job_wrapper.get_id_tag(), self.job_id) + log.debug("%s Unable to cleanup %s: %s" % (prefix, file, str(e))) -class AsynchronousJobState( JobState ): +class AsynchronousJobState(JobState): """ Encapsulate the state of an asynchronous job, this should be subclassed as needed for various job runners to capture additional information needed to communicate with distributed resource manager. """ - def __init__( self, files_dir=None, job_wrapper=None, job_id=None, job_file=None, output_file=None, error_file=None, exit_code_file=None, job_name=None, job_destination=None ): - super( AsynchronousJobState, self ).__init__( job_wrapper, job_destination ) + def __init__(self, files_dir=None, job_wrapper=None, job_id=None, job_file=None, output_file=None, error_file=None, exit_code_file=None, job_name=None, job_destination=None): + super(AsynchronousJobState, self).__init__(job_wrapper, job_destination) self.old_state = None self._running = False self.check_count = 0 @@ -466,20 +466,20 @@ def __init__( self, files_dir=None, job_wrapper=None, job_id=None, job_file=None self.exit_code_file = exit_code_file self.job_name = job_name - self.set_defaults( files_dir ) + self.set_defaults(files_dir) @property - def running( self ): + def running(self): return self._running @running.setter - def running( self, is_running ): + def running(self, is_running): self._running = is_running # This will be invalid for job recovery if self.start_time is None: self.start_time = datetime.datetime.now() - def check_limits( self, runtime=None ): + def check_limits(self, runtime=None): limit_state = None if self.job_wrapper.has_limits(): self.check_count += 1 @@ -487,7 +487,7 @@ def check_limits( self, runtime=None ): if runtime is None: runtime = datetime.datetime.now() - (self.start_time or datetime.datetime.now()) self.check_count = 0 - limit_state = self.job_wrapper.check_limits( runtime=runtime ) + limit_state = self.job_wrapper.check_limits(runtime=runtime) if limit_state is not None: # Set up the job for failure, but the runner will do the actual work self.runner_state, self.fail_message = limit_state @@ -495,20 +495,20 @@ def check_limits( self, runtime=None ): return True return False - def register_cleanup_file_attribute( self, attribute ): + def register_cleanup_file_attribute(self, attribute): if attribute not in self.cleanup_file_attributes: - self.cleanup_file_attributes.append( attribute ) + self.cleanup_file_attributes.append(attribute) -class AsynchronousJobRunner( BaseJobRunner ): +class AsynchronousJobRunner(BaseJobRunner): """Parent class for any job runner that runs jobs asynchronously (e.g. via a distributed resource manager). Provides general methods for having a thread to monitor the state of asynchronous jobs and submitting those jobs to the correct methods (queue, finish, cleanup) at appropriate times.. """ - def __init__( self, app, nworkers, **kwargs ): - super( AsynchronousJobRunner, self ).__init__( app, nworkers, **kwargs ) + def __init__(self, app, nworkers, **kwargs): + super(AsynchronousJobRunner, self).__init__(app, nworkers, **kwargs) # 'watched' and 'queue' are both used to keep track of jobs to watch. # 'queue' is used to add new watched jobs, and can be called from # any thread (usually by the 'queue_job' method). 'watched' must only @@ -518,15 +518,15 @@ def __init__( self, app, nworkers, **kwargs ): self.monitor_queue = Queue() def _init_monitor_thread(self): - self.monitor_thread = threading.Thread( name="%s.monitor_thread" % self.runner_name, target=self.monitor ) - self.monitor_thread.setDaemon( True ) + self.monitor_thread = threading.Thread(name="%s.monitor_thread" % self.runner_name, target=self.monitor) + self.monitor_thread.setDaemon(True) self.monitor_thread.start() def handle_stop(self): # DRMAA and SGE runners should override this and disconnect. pass - def monitor( self ): + def monitor(self): """ Watches jobs currently in the monitor queue and deals with state changes (queued to running) and job completion. @@ -540,7 +540,7 @@ def monitor( self ): # TODO: This is where any cleanup would occur self.handle_stop() return - self.watched.append( async_job_state ) + self.watched.append(async_job_state) except Empty: pass # Iterate over the list of watched jobs and check state @@ -549,17 +549,17 @@ def monitor( self ): except Exception: log.exception('Unhandled exception checking active jobs') # Sleep a bit before the next state check - time.sleep( 1 ) + time.sleep(1) def monitor_job(self, job_state): - self.monitor_queue.put( job_state ) + self.monitor_queue.put(job_state) - def shutdown( self ): + def shutdown(self): """Attempts to gracefully shut down the monitor thread""" - log.info( "%s: Sending stop signal to monitor thread" % self.runner_name ) - self.monitor_queue.put( STOP_SIGNAL ) + log.info("%s: Sending stop signal to monitor thread" % self.runner_name) + self.monitor_queue.put(STOP_SIGNAL) # Call the parent's shutdown method to stop workers - super( AsynchronousJobRunner, self ).shutdown() + super(AsynchronousJobRunner, self).shutdown() def check_watched_items(self): """ @@ -580,7 +580,7 @@ def check_watched_items(self): def check_watched_item(self, job_state): raise NotImplementedError() - def finish_job( self, job_state ): + def finish_job(self, job_state): """ Get the output/error for a finished job, pass to `job_wrapper.finish` and cleanup all the job's temporary files. @@ -595,21 +595,21 @@ def finish_job( self, job_state ): which_try = 0 while which_try < (self.app.config.retry_job_output_collection + 1): try: - stdout = shrink_stream_by_size( open( job_state.output_file, "r" ), DATABASE_MAX_STRING_SIZE, join_by="\n..\n", left_larger=True, beginning_on_size_error=True ) - stderr = shrink_stream_by_size( open( job_state.error_file, "r" ), DATABASE_MAX_STRING_SIZE, join_by="\n..\n", left_larger=True, beginning_on_size_error=True ) + stdout = shrink_stream_by_size(open(job_state.output_file, "r"), DATABASE_MAX_STRING_SIZE, join_by="\n..\n", left_larger=True, beginning_on_size_error=True) + stderr = shrink_stream_by_size(open(job_state.error_file, "r"), DATABASE_MAX_STRING_SIZE, join_by="\n..\n", left_larger=True, beginning_on_size_error=True) which_try = (self.app.config.retry_job_output_collection + 1) except Exception as e: if which_try == self.app.config.retry_job_output_collection: stdout = '' stderr = 'Job output not returned from cluster' - log.error( '(%s/%s) %s: %s' % ( galaxy_id_tag, external_job_id, stderr, str( e ) ) ) + log.error('(%s/%s) %s: %s' % (galaxy_id_tag, external_job_id, stderr, str(e))) else: time.sleep(1) which_try += 1 try: # This should be an 8-bit exit code, but read ahead anyway: - exit_code_str = open( job_state.exit_code_file, "r" ).read(32) + exit_code_str = open(job_state.exit_code_file, "r").read(32) except: # By default, the exit code is 0, which typically indicates success. exit_code_str = "0" @@ -618,22 +618,22 @@ def finish_job( self, job_state ): # Decode the exit code. If it's bogus, then just use 0. exit_code = int(exit_code_str) except: - log.warning( "(%s/%s) Exit code '%s' invalid. Using 0." % ( galaxy_id_tag, external_job_id, exit_code_str ) ) + log.warning("(%s/%s) Exit code '%s' invalid. Using 0." % (galaxy_id_tag, external_job_id, exit_code_str)) exit_code = 0 # clean up the job files cleanup_job = job_state.job_wrapper.cleanup_job - if cleanup_job == "always" or ( not stderr and cleanup_job == "onsuccess" ): + if cleanup_job == "always" or (not stderr and cleanup_job == "onsuccess"): job_state.cleanup() try: - job_state.job_wrapper.finish( stdout, stderr, exit_code ) + job_state.job_wrapper.finish(stdout, stderr, exit_code) except: - log.exception( "(%s/%s) Job wrapper finish method failed" % ( galaxy_id_tag, external_job_id ) ) - job_state.job_wrapper.fail( "Unable to finish job", exception=True ) + log.exception("(%s/%s) Job wrapper finish method failed" % (galaxy_id_tag, external_job_id)) + job_state.job_wrapper.fail("Unable to finish job", exception=True) def mark_as_finished(self, job_state): - self.work_queue.put( ( self.finish_job, job_state ) ) + self.work_queue.put((self.finish_job, job_state)) def mark_as_failed(self, job_state): - self.work_queue.put( ( self.fail_job, job_state ) ) + self.work_queue.put((self.fail_job, job_state)) diff --git a/lib/galaxy/jobs/runners/cli.py b/lib/galaxy/jobs/runners/cli.py index 70e8c3fde7e3..10dd9c2dd7ef 100644 --- a/lib/galaxy/jobs/runners/cli.py +++ b/lib/galaxy/jobs/runners/cli.py @@ -14,50 +14,50 @@ from .util.cli import CliInterface, split_params -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -__all__ = ( 'ShellJobRunner', ) +__all__ = ('ShellJobRunner', ) DEFAULT_EMBED_METADATA_IN_JOB = True -class ShellJobRunner( AsynchronousJobRunner ): +class ShellJobRunner(AsynchronousJobRunner): """ Job runner backed by a finite pool of worker threads. FIFO scheduling """ runner_name = "ShellRunner" - def __init__( self, app, nworkers ): + def __init__(self, app, nworkers): """Start the job runner """ - super( ShellJobRunner, self ).__init__( app, nworkers ) + super(ShellJobRunner, self).__init__(app, nworkers) self.cli_interface = CliInterface() self._init_monitor_thread() self._init_worker_threads() - def get_cli_plugins( self, shell_params, job_params ): - return self.cli_interface.get_plugins( shell_params, job_params ) + def get_cli_plugins(self, shell_params, job_params): + return self.cli_interface.get_plugins(shell_params, job_params) - def url_to_destination( self, url ): + def url_to_destination(self, url): params = {} - shell_params, job_params = url.split( '/' )[ 2:4 ] + shell_params, job_params = url.split('/')[2:4] # split 'foo=bar&baz=quux' into { 'foo' : 'bar', 'baz' : 'quux' } - shell_params = dict( [ ( 'shell_' + k, v ) for k, v in [ kv.split( '=', 1 ) for kv in shell_params.split( '&' ) ] ] ) - job_params = dict( [ ( 'job_' + k, v ) for k, v in [ kv.split( '=', 1 ) for kv in job_params.split( '&' ) ] ] ) - params.update( shell_params ) - params.update( job_params ) - log.debug( "Converted URL '%s' to destination runner=cli, params=%s" % ( url, params ) ) + shell_params = dict([('shell_' + k, v) for k, v in [kv.split('=', 1) for kv in shell_params.split('&')]]) + job_params = dict([('job_' + k, v) for k, v in [kv.split('=', 1) for kv in job_params.split('&')]]) + params.update(shell_params) + params.update(job_params) + log.debug("Converted URL '%s' to destination runner=cli, params=%s" % (url, params)) # Create a dynamic JobDestination - return JobDestination( runner='cli', params=params ) + return JobDestination(runner='cli', params=params) - def parse_destination_params( self, params ): - return split_params( params ) + def parse_destination_params(self, params): + return split_params(params) - def queue_job( self, job_wrapper ): + def queue_job(self, job_wrapper): """Create job script and submit it to the DRM""" # prepare the job - include_metadata = asbool( job_wrapper.job_destination.params.get( "embed_metadata_in_job", DEFAULT_EMBED_METADATA_IN_JOB ) ) - if not self.prepare_job( job_wrapper, include_metadata=include_metadata ): + include_metadata = asbool(job_wrapper.job_destination.params.get("embed_metadata_in_job", DEFAULT_EMBED_METADATA_IN_JOB)) + if not self.prepare_job(job_wrapper, include_metadata=include_metadata): return # Get shell and job execution interface @@ -69,7 +69,7 @@ def queue_job( self, job_wrapper ): galaxy_id_tag = job_wrapper.get_id_tag() # define job attributes - ajs = AsynchronousJobState( files_dir=job_wrapper.working_directory, job_wrapper=job_wrapper ) + ajs = AsynchronousJobState(files_dir=job_wrapper.working_directory, job_wrapper=job_wrapper) job_file_kwargs = job_interface.job_script_kwargs(ajs.output_file, ajs.error_file, ajs.job_name) script = self.get_job_file( @@ -79,20 +79,20 @@ def queue_job( self, job_wrapper ): ) try: - self.write_executable_script( ajs.job_file, script ) + self.write_executable_script(ajs.job_file, script) except: - log.exception("(%s) failure writing job script" % galaxy_id_tag ) + log.exception("(%s) failure writing job script" % galaxy_id_tag) job_wrapper.fail("failure preparing job script", exception=True) return # job was deleted while we were preparing it if job_wrapper.get_state() == model.Job.states.DELETED: - log.info("(%s) Job deleted by user before it entered the queue" % galaxy_id_tag ) + log.info("(%s) Job deleted by user before it entered the queue" % galaxy_id_tag) if job_wrapper.cleanup_job in ("always", "onsuccess"): job_wrapper.cleanup() return - log.debug( "(%s) submitting file: %s" % ( galaxy_id_tag, ajs.job_file ) ) + log.debug("(%s) submitting file: %s" % (galaxy_id_tag, ajs.job_file)) cmd_out = shell.execute(job_interface.submit(ajs.job_file)) if cmd_out.returncode != 0: @@ -108,10 +108,10 @@ def queue_job( self, job_wrapper ): job_wrapper.fail("failure submitting job") return - log.info("(%s) queued with identifier: %s" % ( galaxy_id_tag, external_job_id ) ) + log.info("(%s) queued with identifier: %s" % (galaxy_id_tag, external_job_id)) # store runner information for tracking if Galaxy restarts - job_wrapper.set_job_destination( job_destination, external_job_id ) + job_wrapper.set_job_destination(job_destination, external_job_id) # Store state information for job ajs.job_id = external_job_id @@ -119,9 +119,9 @@ def queue_job( self, job_wrapper ): ajs.job_destination = job_destination # Add to our 'queue' of jobs to monitor - self.monitor_queue.put( ajs ) + self.monitor_queue.put(ajs) - def check_watched_items( self ): + def check_watched_items(self): """ Called by the monitor thread to look at each watched job and deal with state changes. @@ -139,35 +139,30 @@ def check_watched_items( self ): if ajs.job_wrapper.get_state() == model.Job.states.DELETED: continue - external_metadata = not asbool( ajs.job_wrapper.job_destination.params.get( "embed_metadata_in_job", DEFAULT_EMBED_METADATA_IN_JOB ) ) + external_metadata = not asbool(ajs.job_wrapper.job_destination.params.get("embed_metadata_in_job", DEFAULT_EMBED_METADATA_IN_JOB)) if external_metadata: - self._handle_metadata_externally( ajs.job_wrapper, resolve_requirements=True ) + self._handle_metadata_externally(ajs.job_wrapper, resolve_requirements=True) - log.debug("(%s/%s) job not found in batch state check" % ( id_tag, external_job_id ) ) + log.debug("(%s/%s) job not found in batch state check" % (id_tag, external_job_id)) shell_params, job_params = self.parse_destination_params(ajs.job_destination.params) shell, job_interface = self.get_cli_plugins(shell_params, job_params) cmd_out = shell.execute(job_interface.get_single_status(external_job_id)) state = job_interface.parse_single_status(cmd_out.stdout, external_job_id) - if state == model.Job.states.OK: - log.debug('(%s/%s) job execution finished, running job wrapper finish method' % ( id_tag, external_job_id ) ) - self.work_queue.put( ( self.finish_job, ajs ) ) - continue - else: - log.warning('(%s/%s) job not found in batch state check, but found in individual state check' % ( id_tag, external_job_id ) ) - if state != old_state: - ajs.job_wrapper.change_state( state ) - else: - if state != old_state: - log.debug("(%s/%s) state change: from %s to %s" % ( id_tag, external_job_id, old_state, state ) ) - ajs.job_wrapper.change_state( state ) - if state == model.Job.states.RUNNING and not ajs.running: - ajs.running = True - ajs.job_wrapper.change_state( model.Job.states.RUNNING ) + if not state == model.Job.states.OK: + log.warning('(%s/%s) job not found in batch state check, but found in individual state check' % (id_tag, external_job_id)) + if state != old_state: + log.debug("(%s/%s) state change: from %s to %s" % (id_tag, external_job_id, old_state, state)) + if not state == model.Job.states.OK: + # No need to change_state when the state is OK, this will be handled by `self.finish_job` + ajs.job_wrapper.change_state(state) + if state == model.Job.states.RUNNING and not ajs.running: + ajs.running = True ajs.old_state = state if state == model.Job.states.OK: - self.work_queue.put( ( self.finish_job, ajs ) ) + log.debug('(%s/%s) job execution finished, running job wrapper finish method' % (id_tag, external_job_id)) + self.work_queue.put((self.finish_job, ajs)) else: - new_watched.append( ajs ) + new_watched.append(ajs) # Replace the watch list with the updated version self.watched = new_watched @@ -177,9 +172,9 @@ def __get_job_states(self): # unique the list of destinations for ajs in self.watched: if ajs.job_destination.id not in job_destinations: - job_destinations[ajs.job_destination.id] = dict( job_destination=ajs.job_destination, job_ids=[ ajs.job_id ] ) + job_destinations[ajs.job_destination.id] = dict(job_destination=ajs.job_destination, job_ids=[ajs.job_id]) else: - job_destinations[ajs.job_destination.id]['job_ids'].append( ajs.job_id ) + job_destinations[ajs.job_destination.id]['job_ids'].append(ajs.job_id) # check each destination for the listed job ids for job_destination_id, v in job_destinations.items(): job_destination = v['job_destination'] @@ -191,35 +186,35 @@ def __get_job_states(self): job_states.update(job_interface.parse_status(cmd_out.stdout, job_ids)) return job_states - def stop_job( self, job ): + def stop_job(self, job): """Attempts to delete a dispatched job""" try: shell_params, job_params = self.parse_destination_params(job.destination_params) shell, job_interface = self.get_cli_plugins(shell_params, job_params) - cmd_out = shell.execute(job_interface.delete( job.job_runner_external_id )) + cmd_out = shell.execute(job_interface.delete(job.job_runner_external_id)) assert cmd_out.returncode == 0, cmd_out.stderr - log.debug( "(%s/%s) Terminated at user's request" % ( job.id, job.job_runner_external_id ) ) + log.debug("(%s/%s) Terminated at user's request" % (job.id, job.job_runner_external_id)) except Exception as e: - log.debug( "(%s/%s) User killed running job, but error encountered during termination: %s" % ( job.id, job.job_runner_external_id, e ) ) + log.debug("(%s/%s) User killed running job, but error encountered during termination: %s" % (job.id, job.job_runner_external_id, e)) - def recover( self, job, job_wrapper ): + def recover(self, job, job_wrapper): """Recovers jobs stuck in the queued/running state when Galaxy started""" job_id = job.get_job_runner_external_id() if job_id is None: - self.put( job_wrapper ) + self.put(job_wrapper) return - ajs = AsynchronousJobState( files_dir=job_wrapper.working_directory, job_wrapper=job_wrapper ) - ajs.job_id = str( job_id ) + ajs = AsynchronousJobState(files_dir=job_wrapper.working_directory, job_wrapper=job_wrapper) + ajs.job_id = str(job_id) ajs.command_line = job.command_line ajs.job_wrapper = job_wrapper ajs.job_destination = job_wrapper.job_destination if job.state == model.Job.states.RUNNING: - log.debug( "(%s/%s) is still in running state, adding to the runner monitor queue" % ( job.id, job.job_runner_external_id ) ) + log.debug("(%s/%s) is still in running state, adding to the runner monitor queue" % (job.id, job.job_runner_external_id)) ajs.old_state = model.Job.states.RUNNING ajs.running = True - self.monitor_queue.put( ajs ) + self.monitor_queue.put(ajs) elif job.state == model.Job.states.QUEUED: - log.debug( "(%s/%s) is still in queued state, adding to the runner monitor queue" % ( job.id, job.job_runner_external_id ) ) + log.debug("(%s/%s) is still in queued state, adding to the runner monitor queue" % (job.id, job.job_runner_external_id)) ajs.old_state = model.Job.states.QUEUED ajs.running = False - self.monitor_queue.put( ajs ) + self.monitor_queue.put(ajs) diff --git a/lib/galaxy/jobs/runners/condor.py b/lib/galaxy/jobs/runners/condor.py index 97f790c4235c..db3c821398fa 100644 --- a/lib/galaxy/jobs/runners/condor.py +++ b/lib/galaxy/jobs/runners/condor.py @@ -18,41 +18,41 @@ ) from galaxy.util import asbool -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -__all__ = ( 'CondorJobRunner', ) +__all__ = ('CondorJobRunner', ) -class CondorJobState( AsynchronousJobState ): - def __init__( self, **kwargs ): +class CondorJobState(AsynchronousJobState): + def __init__(self, **kwargs): """ Encapsulates state related to a job that is being run via the DRM and that we need to monitor. """ - super( CondorJobState, self ).__init__( **kwargs ) + super(CondorJobState, self).__init__(**kwargs) self.failed = False self.user_log = None self.user_log_size = 0 -class CondorJobRunner( AsynchronousJobRunner ): +class CondorJobRunner(AsynchronousJobRunner): """ Job runner backed by a finite pool of worker threads. FIFO scheduling """ runner_name = "CondorRunner" - def __init__( self, app, nworkers ): + def __init__(self, app, nworkers): """Initialize this job runner and start the monitor thread""" - super( CondorJobRunner, self ).__init__( app, nworkers ) + super(CondorJobRunner, self).__init__(app, nworkers) self._init_monitor_thread() self._init_worker_threads() - def queue_job( self, job_wrapper ): + def queue_job(self, job_wrapper): """Create job script and submit it to the DRM""" # prepare the job - include_metadata = asbool( job_wrapper.job_destination.params.get( "embed_metadata_in_job", True ) ) - if not self.prepare_job( job_wrapper, include_metadata=include_metadata): + include_metadata = asbool(job_wrapper.job_destination.params.get("embed_metadata_in_job", True)) + if not self.prepare_job(job_wrapper, include_metadata=include_metadata): return # get configured job destination @@ -66,7 +66,7 @@ def queue_job( self, job_wrapper ): container = None universe = query_params.get('universe', None) if universe and universe.strip().lower() == 'docker': - container = self._find_container( job_wrapper ) + container = self._find_container(job_wrapper) if container: # HTCondor needs the image as 'docker_image' query_params.update({'docker_image': container.container_id}) @@ -84,9 +84,9 @@ def queue_job( self, job_wrapper ): ) cluster_directory = self.app.config.cluster_files_directory - cjs.user_log = os.path.join( cluster_directory, 'galaxy_%s.condor.log' % galaxy_id_tag ) - cjs.register_cleanup_file_attribute( 'user_log' ) - submit_file = os.path.join( cluster_directory, 'galaxy_%s.condor.desc' % galaxy_id_tag ) + cjs.user_log = os.path.join(cluster_directory, 'galaxy_%s.condor.log' % galaxy_id_tag) + cjs.register_cleanup_file_attribute('user_log') + submit_file = os.path.join(cluster_directory, 'galaxy_%s.condor.desc' % galaxy_id_tag) executable = cjs.job_file build_submit_params = dict( @@ -104,10 +104,10 @@ def queue_job( self, job_wrapper ): slots_statement=galaxy_slots_statement, ) try: - self.write_executable_script( executable, script ) + self.write_executable_script(executable, script) except: - job_wrapper.fail( "failure preparing job script", exception=True ) - log.exception( "(%s) failure preparing job script" % galaxy_id_tag ) + job_wrapper.fail("failure preparing job script", exception=True) + log.exception("(%s) failure preparing job script" % galaxy_id_tag) return cleanup_job = job_wrapper.cleanup_job @@ -117,45 +117,45 @@ def queue_job( self, job_wrapper ): if cleanup_job == "always": cjs.cleanup() # job_wrapper.fail() calls job_wrapper.cleanup() - job_wrapper.fail( "failure preparing submit file", exception=True ) - log.exception( "(%s) failure preparing submit file" % galaxy_id_tag ) + job_wrapper.fail("failure preparing submit file", exception=True) + log.exception("(%s) failure preparing submit file" % galaxy_id_tag) return # job was deleted while we were preparing it if job_wrapper.get_state() == model.Job.states.DELETED: - log.debug( "Job %s deleted by user before it entered the queue" % galaxy_id_tag ) + log.debug("Job %s deleted by user before it entered the queue" % galaxy_id_tag) if cleanup_job in ("always", "onsuccess"): - os.unlink( submit_file ) + os.unlink(submit_file) cjs.cleanup() job_wrapper.cleanup() return - log.debug( "(%s) submitting file %s" % ( galaxy_id_tag, executable ) ) + log.debug("(%s) submitting file %s" % (galaxy_id_tag, executable)) external_job_id, message = condor_submit(submit_file) if external_job_id is None: - log.debug( "condor_submit failed for job %s: %s" % (job_wrapper.get_id_tag(), message) ) + log.debug("condor_submit failed for job %s: %s" % (job_wrapper.get_id_tag(), message)) if self.app.config.cleanup_job == "always": - os.unlink( submit_file ) + os.unlink(submit_file) cjs.cleanup() - job_wrapper.fail( "condor_submit failed", exception=True ) + job_wrapper.fail("condor_submit failed", exception=True) return - os.unlink( submit_file ) + os.unlink(submit_file) - log.info( "(%s) queued as %s" % ( galaxy_id_tag, external_job_id ) ) + log.info("(%s) queued as %s" % (galaxy_id_tag, external_job_id)) # store runner information for tracking if Galaxy restarts - job_wrapper.set_job_destination( job_destination, external_job_id ) + job_wrapper.set_job_destination(job_destination, external_job_id) # Store DRM related state information for job cjs.job_id = external_job_id cjs.job_destination = job_destination # Add to our 'queue' of jobs to monitor - self.monitor_queue.put( cjs ) + self.monitor_queue.put(cjs) - def check_watched_items( self ): + def check_watched_items(self): """ Called by the monitor thread to look at each watched job and deal with state changes. @@ -165,8 +165,8 @@ def check_watched_items( self ): job_id = cjs.job_id galaxy_id_tag = cjs.job_wrapper.get_id_tag() try: - if os.stat( cjs.user_log ).st_size == cjs.user_log_size: - new_watched.append( cjs ) + if os.stat(cjs.user_log).st_size == cjs.user_log_size: + new_watched.append(cjs) continue s1, s4, s7, s5, s9, log_size = summarize_condor_log(cjs.user_log, job_id) job_running = s1 and not (s4 or s7) @@ -175,63 +175,63 @@ def check_watched_items( self ): cjs.user_log_size = log_size except Exception: # so we don't kill the monitor thread - log.exception( "(%s/%s) Unable to check job status" % ( galaxy_id_tag, job_id ) ) - log.warning( "(%s/%s) job will now be errored" % ( galaxy_id_tag, job_id ) ) + log.exception("(%s/%s) Unable to check job status" % (galaxy_id_tag, job_id)) + log.warning("(%s/%s) job will now be errored" % (galaxy_id_tag, job_id)) cjs.fail_message = "Cluster could not complete job" - self.work_queue.put( ( self.fail_job, cjs ) ) + self.work_queue.put((self.fail_job, cjs)) continue if job_running and not cjs.running: - log.debug( "(%s/%s) job is now running" % ( galaxy_id_tag, job_id ) ) - cjs.job_wrapper.change_state( model.Job.states.RUNNING ) + log.debug("(%s/%s) job is now running" % (galaxy_id_tag, job_id)) + cjs.job_wrapper.change_state(model.Job.states.RUNNING) if not job_running and cjs.running: - log.debug( "(%s/%s) job has stopped running" % ( galaxy_id_tag, job_id ) ) + log.debug("(%s/%s) job has stopped running" % (galaxy_id_tag, job_id)) # Will switching from RUNNING to QUEUED confuse Galaxy? # cjs.job_wrapper.change_state( model.Job.states.QUEUED ) if job_complete: if cjs.job_wrapper.get_state() != model.Job.states.DELETED: - external_metadata = not asbool( cjs.job_wrapper.job_destination.params.get( "embed_metadata_in_job", True) ) + external_metadata = not asbool(cjs.job_wrapper.job_destination.params.get("embed_metadata_in_job", True)) if external_metadata: - self._handle_metadata_externally( cjs.job_wrapper, resolve_requirements=True ) - log.debug( "(%s/%s) job has completed" % ( galaxy_id_tag, job_id ) ) - self.work_queue.put( ( self.finish_job, cjs ) ) + self._handle_metadata_externally(cjs.job_wrapper, resolve_requirements=True) + log.debug("(%s/%s) job has completed" % (galaxy_id_tag, job_id)) + self.work_queue.put((self.finish_job, cjs)) continue if job_failed: - log.debug( "(%s/%s) job failed" % ( galaxy_id_tag, job_id ) ) + log.debug("(%s/%s) job failed" % (galaxy_id_tag, job_id)) cjs.failed = True - self.work_queue.put( ( self.finish_job, cjs ) ) + self.work_queue.put((self.finish_job, cjs)) continue cjs.runnning = job_running - new_watched.append( cjs ) + new_watched.append(cjs) # Replace the watch list with the updated version self.watched = new_watched - def stop_job( self, job ): + def stop_job(self, job): """Attempts to delete a job from the DRM queue""" external_id = job.job_runner_external_id failure_message = condor_stop(external_id) if failure_message: log.debug("(%s/%s). Failed to stop condor %s" % (external_id, failure_message)) - def recover( self, job, job_wrapper ): + def recover(self, job, job_wrapper): """Recovers jobs stuck in the queued/running state when Galaxy started""" # TODO Check if we need any changes here job_id = job.get_job_runner_external_id() galaxy_id_tag = job_wrapper.get_id_tag() if job_id is None: - self.put( job_wrapper ) + self.put(job_wrapper) return - cjs = CondorJobState( job_wrapper=job_wrapper, files_dir=self.app.config.cluster_files_directory ) - cjs.job_id = str( job_id ) + cjs = CondorJobState(job_wrapper=job_wrapper, files_dir=self.app.config.cluster_files_directory) + cjs.job_id = str(job_id) cjs.command_line = job.get_command_line() cjs.job_wrapper = job_wrapper cjs.job_destination = job_wrapper.job_destination - cjs.user_log = os.path.join( self.app.config.cluster_files_directory, 'galaxy_%s.condor.log' % galaxy_id_tag ) - cjs.register_cleanup_file_attribute( 'user_log' ) + cjs.user_log = os.path.join(self.app.config.cluster_files_directory, 'galaxy_%s.condor.log' % galaxy_id_tag) + cjs.register_cleanup_file_attribute('user_log') if job.state == model.Job.states.RUNNING: - log.debug( "(%s/%s) is still in running state, adding to the DRM queue" % ( job.id, job.job_runner_external_id ) ) + log.debug("(%s/%s) is still in running state, adding to the DRM queue" % (job.id, job.job_runner_external_id)) cjs.running = True - self.monitor_queue.put( cjs ) + self.monitor_queue.put(cjs) elif job.state == model.Job.states.QUEUED: - log.debug( "(%s/%s) is still in DRM queued state, adding to the DRM queue" % ( job.id, job.job_runner_external_id ) ) + log.debug("(%s/%s) is still in DRM queued state, adding to the DRM queue" % (job.id, job.job_runner_external_id)) cjs.running = False - self.monitor_queue.put( cjs ) + self.monitor_queue.put(cjs) diff --git a/lib/galaxy/jobs/runners/drmaa.py b/lib/galaxy/jobs/runners/drmaa.py index 0e28aff6452d..416c46f15789 100644 --- a/lib/galaxy/jobs/runners/drmaa.py +++ b/lib/galaxy/jobs/runners/drmaa.py @@ -22,45 +22,45 @@ drmaa = None -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -__all__ = ( 'DRMAAJobRunner', ) +__all__ = ('DRMAAJobRunner', ) RETRY_EXCEPTIONS_LOWER = frozenset(['invalidjobexception', 'internalexception']) -class DRMAAJobRunner( AsynchronousJobRunner ): +class DRMAAJobRunner(AsynchronousJobRunner): """ Job runner backed by a finite pool of worker threads. FIFO scheduling """ runner_name = "DRMAARunner" restrict_job_name_length = 15 - def __init__( self, app, nworkers, **kwargs ): + def __init__(self, app, nworkers, **kwargs): """Start the job runner""" global drmaa runner_param_specs = { - 'drmaa_library_path': dict( map=str, default=os.environ.get( 'DRMAA_LIBRARY_PATH', None ) ) } + 'drmaa_library_path': dict(map=str, default=os.environ.get('DRMAA_LIBRARY_PATH', None))} for retry_exception in RETRY_EXCEPTIONS_LOWER: - runner_param_specs[retry_exception + '_state'] = dict( map=str, valid=lambda x: x in ( model.Job.states.OK, model.Job.states.ERROR ), default=model.Job.states.OK ) - runner_param_specs[retry_exception + '_retries'] = dict( map=int, valid=lambda x: int >= 0, default=0 ) + runner_param_specs[retry_exception + '_state'] = dict(map=str, valid=lambda x: x in (model.Job.states.OK, model.Job.states.ERROR), default=model.Job.states.OK) + runner_param_specs[retry_exception + '_retries'] = dict(map=int, valid=lambda x: int >= 0, default=0) if 'runner_param_specs' not in kwargs: - kwargs[ 'runner_param_specs' ] = dict() - kwargs[ 'runner_param_specs' ].update( runner_param_specs ) + kwargs['runner_param_specs'] = dict() + kwargs['runner_param_specs'].update(runner_param_specs) - super( DRMAAJobRunner, self ).__init__( app, nworkers, **kwargs ) + super(DRMAAJobRunner, self).__init__(app, nworkers, **kwargs) # This allows multiple drmaa runners (although only one per handler) in the same job config file if 'drmaa_library_path' in kwargs: - log.info( 'Overriding DRMAA_LIBRARY_PATH due to runner plugin parameter: %s', self.runner_params.drmaa_library_path ) + log.info('Overriding DRMAA_LIBRARY_PATH due to runner plugin parameter: %s', self.runner_params.drmaa_library_path) os.environ['DRMAA_LIBRARY_PATH'] = self.runner_params.drmaa_library_path # Import is delayed until runner initialization to allow for the # drmaa_library_path plugin param to override $DRMAA_LIBRARY_PATH try: - drmaa = __import__( "drmaa" ) + drmaa = __import__("drmaa") except (ImportError, RuntimeError) as exc: raise exc.__class__('The Python drmaa package is required to use this ' 'feature, please install it or correct the ' @@ -99,29 +99,29 @@ def url_to_destination(self, url): return native_spec = url.split('/')[2] if native_spec: - params = dict( nativeSpecification=native_spec ) - log.debug( "Converted URL '%s' to destination runner=drmaa, params=%s" % ( url, params ) ) - return JobDestination( runner='drmaa', params=params ) + params = dict(nativeSpecification=native_spec) + log.debug("Converted URL '%s' to destination runner=drmaa, params=%s" % (url, params)) + return JobDestination(runner='drmaa', params=params) else: - log.debug( "Converted URL '%s' to destination runner=drmaa" % url ) - return JobDestination( runner='drmaa' ) + log.debug("Converted URL '%s' to destination runner=drmaa" % url) + return JobDestination(runner='drmaa') - def get_native_spec( self, url ): + def get_native_spec(self, url): """Get any native DRM arguments specified by the site configuration""" try: return url.split('/')[2] or None except: return None - def queue_job( self, job_wrapper ): + def queue_job(self, job_wrapper): """Create job script and submit it to the DRM""" # prepare the job # external_runJob_script can be None, in which case it's not used. external_runjob_script = job_wrapper.get_destination_configuration("drmaa_external_runjob_script", None) - include_metadata = asbool( job_wrapper.job_destination.params.get( "embed_metadata_in_job", True) ) - if not self.prepare_job( job_wrapper, include_metadata=include_metadata): + include_metadata = asbool(job_wrapper.job_destination.params.get("embed_metadata_in_job", True)) + if not self.prepare_job(job_wrapper, include_metadata=include_metadata): return # get configured job destination @@ -131,7 +131,7 @@ def queue_job( self, job_wrapper ): galaxy_id_tag = job_wrapper.get_id_tag() job_name = self._job_name(job_wrapper) - ajs = AsynchronousJobState( files_dir=job_wrapper.working_directory, job_wrapper=job_wrapper, job_name=job_name ) + ajs = AsynchronousJobState(files_dir=job_wrapper.working_directory, job_wrapper=job_wrapper, job_name=job_name) # set up the drmaa job template jt = dict( @@ -150,22 +150,22 @@ def queue_job( self, job_wrapper ): # fill in the DRM's job run template script = self.get_job_file(job_wrapper, exit_code_path=ajs.exit_code_file) try: - self.write_executable_script( ajs.job_file, script ) + self.write_executable_script(ajs.job_file, script) except: - job_wrapper.fail( "failure preparing job script", exception=True ) - log.exception( "(%s) failure writing job script" % galaxy_id_tag ) + job_wrapper.fail("failure preparing job script", exception=True) + log.exception("(%s) failure writing job script" % galaxy_id_tag) return # job was deleted while we were preparing it if job_wrapper.get_state() == model.Job.states.DELETED: - log.debug( "(%s) Job deleted by user before it entered the queue" % galaxy_id_tag ) - if job_wrapper.cleanup_job in ( "always", "onsuccess" ): + log.debug("(%s) Job deleted by user before it entered the queue" % galaxy_id_tag) + if job_wrapper.cleanup_job in ("always", "onsuccess"): job_wrapper.cleanup() return - log.debug( "(%s) submitting file %s", galaxy_id_tag, ajs.job_file ) + log.debug("(%s) submitting file %s", galaxy_id_tag, ajs.job_file) if native_spec: - log.debug( "(%s) native specification is: %s", galaxy_id_tag, native_spec ) + log.debug("(%s) native specification is: %s", galaxy_id_tag, native_spec) # runJob will raise if there's a submit problem if external_runjob_script is None: @@ -178,39 +178,39 @@ def queue_job( self, job_wrapper ): try: external_job_id = self.ds.run_job(**jt) break - except ( drmaa.InternalException, drmaa.DeniedByDrmException ) as e: + except (drmaa.InternalException, drmaa.DeniedByDrmException) as e: trynum += 1 - log.warning( '(%s) drmaa.Session.runJob() failed, will retry: %s', galaxy_id_tag, e ) + log.warning('(%s) drmaa.Session.runJob() failed, will retry: %s', galaxy_id_tag, e) fail_msg = "Unable to run this job due to a cluster error, please retry it later" - time.sleep( 5 ) + time.sleep(5) except: - log.exception( '(%s) drmaa.Session.runJob() failed unconditionally', galaxy_id_tag ) + log.exception('(%s) drmaa.Session.runJob() failed unconditionally', galaxy_id_tag) trynum = 5 else: - log.error( "(%s) All attempts to submit job failed" % galaxy_id_tag ) + log.error("(%s) All attempts to submit job failed" % galaxy_id_tag) if not fail_msg: fail_msg = DEFAULT_JOB_PUT_FAILURE_MESSAGE - job_wrapper.fail( fail_msg ) + job_wrapper.fail(fail_msg) return else: job_wrapper.change_ownership_for_run() # if user credentials are not available, use galaxy credentials (if permitted) - allow_guests = asbool(job_wrapper.job_destination.params.get( "allow_guests", False) ) + allow_guests = asbool(job_wrapper.job_destination.params.get("allow_guests", False)) pwent = job_wrapper.user_system_pwent if pwent is None: if not allow_guests: fail_msg = "User %s is not mapped to any real user, and not permitted to start jobs." % job_wrapper.user - job_wrapper.fail( fail_msg ) + job_wrapper.fail(fail_msg) return pwent = job_wrapper.galaxy_system_pwent - log.debug( '(%s) submitting with credentials: %s [uid: %s]' % ( galaxy_id_tag, pwent[0], pwent[2] ) ) + log.debug('(%s) submitting with credentials: %s [uid: %s]' % (galaxy_id_tag, pwent[0], pwent[2])) filename = self.store_jobtemplate(job_wrapper, jt) self.userid = pwent[2] external_job_id = self.external_runjob(external_runjob_script, filename, pwent[2]).strip() - log.info( "(%s) queued as %s" % ( galaxy_id_tag, external_job_id ) ) + log.info("(%s) queued as %s" % (galaxy_id_tag, external_job_id)) # store runner information for tracking if Galaxy restarts - job_wrapper.set_job_destination( job_destination, external_job_id ) + job_wrapper.set_job_destination(job_destination, external_job_id) # Store DRM related state information for job ajs.job_id = external_job_id @@ -218,9 +218,9 @@ def queue_job( self, job_wrapper ): ajs.job_destination = job_destination # Add to our 'queue' of jobs to monitor - self.monitor_queue.put( ajs ) + self.monitor_queue.put(ajs) - def _complete_terminal_job( self, ajs, drmaa_state, **kwargs ): + def _complete_terminal_job(self, ajs, drmaa_state, **kwargs): """ Handle a job upon its termination in the DRM. This method is meant to be overridden by subclasses to improve post-mortem and reporting of @@ -231,16 +231,16 @@ def _complete_terminal_job( self, ajs, drmaa_state, **kwargs ): if ajs.job_wrapper.get_state() != model.Job.states.DELETED: ajs.stop_job = False ajs.fail_message = "The cluster DRM system terminated this job" - self.work_queue.put( ( self.fail_job, ajs ) ) + self.work_queue.put((self.fail_job, ajs)) elif drmaa_state == drmaa.JobState.DONE: # External metadata processing for external runjobs - external_metadata = not asbool( ajs.job_wrapper.job_destination.params.get( "embed_metadata_in_job", True) ) + external_metadata = not asbool(ajs.job_wrapper.job_destination.params.get("embed_metadata_in_job", True)) if external_metadata: - self._handle_metadata_externally( ajs.job_wrapper, resolve_requirements=True ) + self._handle_metadata_externally(ajs.job_wrapper, resolve_requirements=True) if ajs.job_wrapper.get_state() != model.Job.states.DELETED: - self.work_queue.put( ( self.finish_job, ajs ) ) + self.work_queue.put((self.finish_job, ajs)) - def check_watched_items( self ): + def check_watched_items(self): """ Called by the monitor thread to look at each watched job and deal with state changes. @@ -251,101 +251,101 @@ def check_watched_items( self ): galaxy_id_tag = ajs.job_wrapper.get_id_tag() old_state = ajs.old_state try: - assert external_job_id not in ( None, 'None' ), '(%s/%s) Invalid job id' % ( galaxy_id_tag, external_job_id ) - state = self.ds.job_status( external_job_id ) + assert external_job_id not in (None, 'None'), '(%s/%s) Invalid job id' % (galaxy_id_tag, external_job_id) + state = self.ds.job_status(external_job_id) # Reset exception retries for retry_exception in RETRY_EXCEPTIONS_LOWER: - setattr( ajs, retry_exception + '_retries', 0) - except ( drmaa.InternalException, drmaa.InvalidJobException ) as e: + setattr(ajs, retry_exception + '_retries', 0) + except (drmaa.InternalException, drmaa.InvalidJobException) as e: ecn = type(e).__name__ retry_param = ecn.lower() + '_retries' state_param = ecn.lower() + '_state' - retries = getattr( ajs, retry_param, 0 ) + retries = getattr(ajs, retry_param, 0) log.warning("(%s/%s) unable to check job status because of %s exception for %d consecutive tries: %s", galaxy_id_tag, external_job_id, ecn, retries + 1, e) - if self.runner_params[ retry_param ] > 0: - if retries < self.runner_params[ retry_param ]: + if self.runner_params[retry_param] > 0: + if retries < self.runner_params[retry_param]: # will retry check on next iteration - setattr( ajs, retry_param, retries + 1 ) - new_watched.append( ajs ) + setattr(ajs, retry_param, retries + 1) + new_watched.append(ajs) continue - if self.runner_params[ state_param ] == model.Job.states.OK: - log.warning( "(%s/%s) job will now be finished OK", galaxy_id_tag, external_job_id ) - self.work_queue.put( ( self.finish_job, ajs ) ) - elif self.runner_params[ state_param ] == model.Job.states.ERROR: - log.warning( "(%s/%s) job will now be errored", galaxy_id_tag, external_job_id ) - self.work_queue.put( ( self.fail_job, ajs ) ) + if self.runner_params[state_param] == model.Job.states.OK: + log.warning("(%s/%s) job will now be finished OK", galaxy_id_tag, external_job_id) + self.work_queue.put((self.finish_job, ajs)) + elif self.runner_params[state_param] == model.Job.states.ERROR: + log.warning("(%s/%s) job will now be errored", galaxy_id_tag, external_job_id) + self.work_queue.put((self.fail_job, ajs)) else: - raise Exception( "%s is set to an invalid value (%s), this should not be possible. See galaxy.jobs.drmaa.__init__()", state_param, self.runner_params[ state_param ] ) + raise Exception("%s is set to an invalid value (%s), this should not be possible. See galaxy.jobs.drmaa.__init__()", state_param, self.runner_params[state_param]) continue except drmaa.DrmCommunicationException as e: - log.warning( "(%s/%s) unable to communicate with DRM: %s", galaxy_id_tag, external_job_id, e ) - new_watched.append( ajs ) + log.warning("(%s/%s) unable to communicate with DRM: %s", galaxy_id_tag, external_job_id, e) + new_watched.append(ajs) continue except Exception as e: # so we don't kill the monitor thread - log.exception( "(%s/%s) unable to check job status: %s" % ( galaxy_id_tag, external_job_id, e ) ) - log.warning( "(%s/%s) job will now be errored" % ( galaxy_id_tag, external_job_id ) ) + log.exception("(%s/%s) unable to check job status: %s" % (galaxy_id_tag, external_job_id, e)) + log.warning("(%s/%s) job will now be errored" % (galaxy_id_tag, external_job_id)) ajs.fail_message = "Cluster could not complete job" - self.work_queue.put( ( self.fail_job, ajs ) ) + self.work_queue.put((self.fail_job, ajs)) continue if state != old_state: - log.debug( "(%s/%s) state change: %s" % ( galaxy_id_tag, external_job_id, self.drmaa_job_state_strings[state] ) ) + log.debug("(%s/%s) state change: %s" % (galaxy_id_tag, external_job_id, self.drmaa_job_state_strings[state])) if state == drmaa.JobState.RUNNING and not ajs.running: ajs.running = True - ajs.job_wrapper.change_state( model.Job.states.RUNNING ) - if state in ( drmaa.JobState.FAILED, drmaa.JobState.DONE ): - if self._complete_terminal_job( ajs, drmaa_state=state ) is not None: + ajs.job_wrapper.change_state(model.Job.states.RUNNING) + if state in (drmaa.JobState.FAILED, drmaa.JobState.DONE): + if self._complete_terminal_job(ajs, drmaa_state=state) is not None: # job was not actually terminal state = ajs.old_state else: continue if ajs.check_limits(): - self.work_queue.put( ( self.fail_job, ajs ) ) + self.work_queue.put((self.fail_job, ajs)) continue ajs.old_state = state - new_watched.append( ajs ) + new_watched.append(ajs) # Replace the watch list with the updated version self.watched = new_watched - def stop_job( self, job ): + def stop_job(self, job): """Attempts to delete a job from the DRM queue""" try: ext_id = job.get_job_runner_external_id() - assert ext_id not in ( None, 'None' ), 'External job id is None' + assert ext_id not in (None, 'None'), 'External job id is None' kill_script = job.get_destination_configuration(self.app.config, "drmaa_external_killjob_script", None) if kill_script is None: - self.ds.kill( ext_id ) + self.ds.kill(ext_id) else: - command = shlex.split( kill_script ) - command.extend( [ str( ext_id ), str( self.userid ) ]) - subprocess.Popen( command, shell=False ) - log.debug( "(%s/%s) Removed from DRM queue at user's request" % ( job.get_id(), ext_id ) ) + command = shlex.split(kill_script) + command.extend([str(ext_id), str(self.userid)]) + subprocess.Popen(command, shell=False) + log.debug("(%s/%s) Removed from DRM queue at user's request" % (job.get_id(), ext_id)) except drmaa.InvalidJobException: - log.debug( "(%s/%s) User killed running job, but it was already dead" % ( job.get_id(), ext_id ) ) + log.debug("(%s/%s) User killed running job, but it was already dead" % (job.get_id(), ext_id)) except Exception as e: - log.debug( "(%s/%s) User killed running job, but error encountered removing from DRM queue: %s" % ( job.get_id(), ext_id, e ) ) + log.debug("(%s/%s) User killed running job, but error encountered removing from DRM queue: %s" % (job.get_id(), ext_id, e)) - def recover( self, job, job_wrapper ): + def recover(self, job, job_wrapper): """Recovers jobs stuck in the queued/running state when Galaxy started""" job_id = job.get_job_runner_external_id() if job_id is None: - self.put( job_wrapper ) + self.put(job_wrapper) return - ajs = AsynchronousJobState( files_dir=job_wrapper.working_directory, job_wrapper=job_wrapper ) - ajs.job_id = str( job_id ) + ajs = AsynchronousJobState(files_dir=job_wrapper.working_directory, job_wrapper=job_wrapper) + ajs.job_id = str(job_id) ajs.command_line = job.get_command_line() ajs.job_wrapper = job_wrapper ajs.job_destination = job_wrapper.job_destination if job.state == model.Job.states.RUNNING: - log.debug( "(%s/%s) is still in running state, adding to the DRM queue" % ( job.get_id(), job.get_job_runner_external_id() ) ) + log.debug("(%s/%s) is still in running state, adding to the DRM queue" % (job.get_id(), job.get_job_runner_external_id())) ajs.old_state = drmaa.JobState.RUNNING ajs.running = True - self.monitor_queue.put( ajs ) + self.monitor_queue.put(ajs) elif job.get_state() == model.Job.states.QUEUED: - log.debug( "(%s/%s) is still in DRM queued state, adding to the DRM queue" % ( job.get_id(), job.get_job_runner_external_id() ) ) + log.debug("(%s/%s) is still in DRM queued state, adding to the DRM queue" % (job.get_id(), job.get_job_runner_external_id())) ajs.old_state = drmaa.JobState.QUEUED_ACTIVE ajs.running = False - self.monitor_queue.put( ajs ) + self.monitor_queue.put(ajs) def store_jobtemplate(self, job_wrapper, jt): """ Stores the content of a DRMAA JobTemplate object in a file as a JSON string. @@ -354,7 +354,7 @@ def store_jobtemplate(self, job_wrapper, jt): filename = "%s/%s.jt_json" % (self.app.config.cluster_files_directory, job_wrapper.get_id_tag()) with open(filename, 'w+') as fp: json.dump(jt, fp) - log.debug( '(%s) Job script for external submission is: %s' % ( job_wrapper.job_id, filename ) ) + log.debug('(%s) Job script for external submission is: %s' % (job_wrapper.job_id, filename)) return filename def external_runjob(self, external_runjob_script, jobtemplate_filename, username): @@ -362,8 +362,8 @@ def external_runjob(self, external_runjob_script, jobtemplate_filename, username The external script needs to be run with sudo, and will setuid() to the specified user. Effectively, will QSUB as a different user (then the one used by Galaxy). """ - command = shlex.split( external_runjob_script ) - command.extend( [ str(username), jobtemplate_filename ] ) + command = shlex.split(external_runjob_script) + command.extend([str(username), jobtemplate_filename]) log.info("Running command %s" % command) p = subprocess.Popen(command, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE) @@ -391,7 +391,7 @@ def _job_name(self, job_wrapper): job_name += '_%s' % job_wrapper.tool.old_id if external_runjob_script is None: job_name += '_%s' % job_wrapper.user - job_name = ''.join( x if x in ( string.ascii_letters + string.digits + '_' ) else '_' for x in job_name ) + job_name = ''.join(x if x in (string.ascii_letters + string.digits + '_') else '_' for x in job_name) if self.restrict_job_name_length: job_name = job_name[:self.restrict_job_name_length] return job_name diff --git a/lib/galaxy/jobs/runners/godocker.py b/lib/galaxy/jobs/runners/godocker.py index aad612cede81..f15774a6dd02 100644 --- a/lib/galaxy/jobs/runners/godocker.py +++ b/lib/galaxy/jobs/runners/godocker.py @@ -21,6 +21,7 @@ class Godocker(object): """ API parameters """ + def __init__(self, server, login, apikey, noCert): self.token = None self.server = server diff --git a/lib/galaxy/jobs/runners/kubernetes.py b/lib/galaxy/jobs/runners/kubernetes.py index beb16c9bfc62..b6027437044b 100644 --- a/lib/galaxy/jobs/runners/kubernetes.py +++ b/lib/galaxy/jobs/runners/kubernetes.py @@ -132,7 +132,7 @@ def __get_supplemental_group(self): try: return int(self.runner_params["k8s_supplemental_group_id"]) except: - log.warn("Supplemental group passed for Kubernetes runner needs to be an integer, value " + log.warning("Supplemental group passed for Kubernetes runner needs to be an integer, value " + self.runner_params["k8s_supplemental_group_id"] + " passed is invalid") return None return None @@ -142,7 +142,7 @@ def __get_fs_group(self): try: return int(self.runner_params["k8s_fs_group_id"]) except: - log.warn("FS group passed for Kubernetes runner needs to be an integer, value " + log.warning("FS group passed for Kubernetes runner needs to be an integer, value " + self.runner_params["k8s_fs_group_id"] + " passed is invalid") return None return None diff --git a/lib/galaxy/jobs/runners/local.py b/lib/galaxy/jobs/runners/local.py index 5ee2741e58b6..aed42306a4ad 100644 --- a/lib/galaxy/jobs/runners/local.py +++ b/lib/galaxy/jobs/runners/local.py @@ -21,9 +21,9 @@ JobState ) -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -__all__ = ( 'LocalJobRunner', ) +__all__ = ('LocalJobRunner', ) DEFAULT_POOL_SLEEP_TIME = 1 # TODO: Set to false and just get rid of this option. It would simplify this @@ -31,91 +31,91 @@ DEFAULT_EMBED_METADATA_IN_JOB = True -class LocalJobRunner( BaseJobRunner ): +class LocalJobRunner(BaseJobRunner): """ Job runner backed by a finite pool of worker threads. FIFO scheduling """ runner_name = "LocalRunner" - def __init__( self, app, nworkers ): + def __init__(self, app, nworkers): """Start the job runner """ # create a local copy of os.environ to use as env for subprocess.Popen self._environ = os.environ.copy() # Set TEMP if a valid temp value is not already set - if not ( 'TMPDIR' in self._environ or 'TEMP' in self._environ or 'TMP' in self._environ ): - self._environ[ 'TEMP' ] = os.path.abspath(tempfile.gettempdir()) + if not ('TMPDIR' in self._environ or 'TEMP' in self._environ or 'TMP' in self._environ): + self._environ['TEMP'] = os.path.abspath(tempfile.gettempdir()) - super( LocalJobRunner, self ).__init__( app, nworkers ) + super(LocalJobRunner, self).__init__(app, nworkers) self._init_worker_threads() - def __command_line( self, job_wrapper ): + def __command_line(self, job_wrapper): """ """ command_line = job_wrapper.runner_command_line # slots would be cleaner name, but don't want deployers to see examples and think it # is going to work with other job runners. - slots = job_wrapper.job_destination.params.get( "local_slots", None ) or os.environ.get("GALAXY_SLOTS", None) + slots = job_wrapper.job_destination.params.get("local_slots", None) or os.environ.get("GALAXY_SLOTS", None) if slots: - slots_statement = 'GALAXY_SLOTS="%d"; export GALAXY_SLOTS; GALAXY_SLOTS_CONFIGURED="1"; export GALAXY_SLOTS_CONFIGURED;' % ( int( slots ) ) + slots_statement = 'GALAXY_SLOTS="%d"; export GALAXY_SLOTS; GALAXY_SLOTS_CONFIGURED="1"; export GALAXY_SLOTS_CONFIGURED;' % (int(slots)) else: slots_statement = 'GALAXY_SLOTS="1"; export GALAXY_SLOTS;' job_id = job_wrapper.get_id_tag() - job_file = JobState.default_job_file( job_wrapper.working_directory, job_id ) - exit_code_path = JobState.default_exit_code_file( job_wrapper.working_directory, job_id ) + job_file = JobState.default_job_file(job_wrapper.working_directory, job_id) + exit_code_path = JobState.default_exit_code_file(job_wrapper.working_directory, job_id) job_script_props = { 'slots_statement': slots_statement, 'command': command_line, 'exit_code_path': exit_code_path, 'working_directory': job_wrapper.working_directory, } - job_file_contents = self.get_job_file( job_wrapper, **job_script_props ) - self.write_executable_script( job_file, job_file_contents ) + job_file_contents = self.get_job_file(job_wrapper, **job_script_props) + self.write_executable_script(job_file, job_file_contents) return job_file, exit_code_path - def queue_job( self, job_wrapper ): - if not self._prepare_job_local( job_wrapper ): + def queue_job(self, job_wrapper): + if not self._prepare_job_local(job_wrapper): return stderr = stdout = '' exit_code = 0 # command line has been added to the wrapper by prepare_job() - command_line, exit_code_path = self.__command_line( job_wrapper ) + command_line, exit_code_path = self.__command_line(job_wrapper) job_id = job_wrapper.get_id_tag() try: - stdout_file = tempfile.NamedTemporaryFile( suffix='_stdout', dir=job_wrapper.working_directory ) - stderr_file = tempfile.NamedTemporaryFile( suffix='_stderr', dir=job_wrapper.working_directory ) - log.debug( '(%s) executing job script: %s' % ( job_id, command_line ) ) - proc = subprocess.Popen( args=command_line, - shell=True, - cwd=job_wrapper.working_directory, - stdout=stdout_file, - stderr=stderr_file, - env=self._environ, - preexec_fn=os.setpgrp ) + stdout_file = tempfile.NamedTemporaryFile(suffix='_stdout', dir=job_wrapper.working_directory) + stderr_file = tempfile.NamedTemporaryFile(suffix='_stderr', dir=job_wrapper.working_directory) + log.debug('(%s) executing job script: %s' % (job_id, command_line)) + proc = subprocess.Popen(args=command_line, + shell=True, + cwd=job_wrapper.working_directory, + stdout=stdout_file, + stderr=stderr_file, + env=self._environ, + preexec_fn=os.setpgrp) job_wrapper.set_job_destination(job_wrapper.job_destination, proc.pid) - job_wrapper.change_state( model.Job.states.RUNNING ) + job_wrapper.change_state(model.Job.states.RUNNING) - terminated = self.__poll_if_needed( proc, job_wrapper, job_id ) + terminated = self.__poll_if_needed(proc, job_wrapper, job_id) if terminated: return # Reap the process and get the exit code. exit_code = proc.wait() try: - exit_code = int( open( exit_code_path, 'r' ).read() ) + exit_code = int(open(exit_code_path, 'r').read()) except Exception: - log.warning( "Failed to read exit code from path %s" % exit_code_path ) + log.warning("Failed to read exit code from path %s" % exit_code_path) pass - stdout_file.seek( 0 ) - stderr_file.seek( 0 ) - stdout = shrink_stream_by_size( stdout_file, DATABASE_MAX_STRING_SIZE, join_by="\n..\n", left_larger=True, beginning_on_size_error=True ) - stderr = shrink_stream_by_size( stderr_file, DATABASE_MAX_STRING_SIZE, join_by="\n..\n", left_larger=True, beginning_on_size_error=True ) + stdout_file.seek(0) + stderr_file.seek(0) + stdout = shrink_stream_by_size(stdout_file, DATABASE_MAX_STRING_SIZE, join_by="\n..\n", left_larger=True, beginning_on_size_error=True) + stderr = shrink_stream_by_size(stderr_file, DATABASE_MAX_STRING_SIZE, join_by="\n..\n", left_larger=True, beginning_on_size_error=True) stdout_file.close() stderr_file.close() log.debug('execution finished: %s' % command_line) @@ -127,45 +127,45 @@ def queue_job( self, job_wrapper ): self._handle_metadata_if_needed(job_wrapper) # Finish the job! try: - job_wrapper.finish( stdout, stderr, exit_code ) + job_wrapper.finish(stdout, stderr, exit_code) except: log.exception("Job wrapper finish method failed") self._fail_job_local(job_wrapper, "Unable to finish job") - def stop_job( self, job ): + def stop_job(self, job): # if our local job has JobExternalOutputMetadata associated, then our primary job has to have already finished job_ext_output_metadata = job.get_external_output_metadata() try: pid = job_ext_output_metadata[0].job_runner_external_pid # every JobExternalOutputMetadata has a pid set, we just need to take from one of them - assert pid not in [ None, '' ] + assert pid not in [None, ''] except Exception: # metadata internal or job not complete yet pid = job.get_job_runner_external_id() - if pid in [ None, '' ]: - log.warning( "stop_job(): %s: no PID in database for job, unable to stop" % job.get_id() ) + if pid in [None, '']: + log.warning("stop_job(): %s: no PID in database for job, unable to stop" % job.get_id()) return - pid = int( pid ) - if not self._check_pid( pid ): - log.warning( "stop_job(): %s: PID %d was already dead or can't be signaled" % ( job.get_id(), pid ) ) + pid = int(pid) + if not self._check_pid(pid): + log.warning("stop_job(): %s: PID %d was already dead or can't be signaled" % (job.get_id(), pid)) return - for sig in [ 15, 9 ]: + for sig in [15, 9]: try: - os.killpg( pid, sig ) + os.killpg(pid, sig) except OSError as e: - log.warning( "stop_job(): %s: Got errno %s when attempting to signal %d to PID %d: %s" % ( job.get_id(), errno.errorcode[e.errno], sig, pid, e.strerror ) ) + log.warning("stop_job(): %s: Got errno %s when attempting to signal %d to PID %d: %s" % (job.get_id(), errno.errorcode[e.errno], sig, pid, e.strerror)) return # give up - sleep( 2 ) - if not self._check_pid( pid ): - log.debug( "stop_job(): %s: PID %d successfully killed with signal %d" % ( job.get_id(), pid, sig ) ) + sleep(2) + if not self._check_pid(pid): + log.debug("stop_job(): %s: PID %d successfully killed with signal %d" % (job.get_id(), pid, sig)) return else: - log.warning( "stop_job(): %s: PID %d refuses to die after signaling TERM/KILL" % ( job.get_id(), pid ) ) + log.warning("stop_job(): %s: PID %d refuses to die after signaling TERM/KILL" % (job.get_id(), pid)) - def recover( self, job, job_wrapper ): + def recover(self, job, job_wrapper): # local jobs can't be recovered - job_wrapper.change_state( model.Job.states.ERROR, info="This job was killed when Galaxy was restarted. Please retry the job." ) + job_wrapper.change_state(model.Job.states.ERROR, info="This job was killed when Galaxy was restarted. Please retry the job.") - def _fail_job_local( self, job_wrapper, message ): + def _fail_job_local(self, job_wrapper, message): job_destination = job_wrapper.job_destination job_state = JobState(job_wrapper, job_destination) job_state.fail_message = message @@ -184,25 +184,25 @@ def _embed_metadata(self, job_wrapper): def _prepare_job_local(self, job_wrapper): return self.prepare_job(job_wrapper, include_metadata=self._embed_metadata(job_wrapper)) - def _check_pid( self, pid ): + def _check_pid(self, pid): try: - os.kill( pid, 0 ) + os.kill(pid, 0) return True except OSError as e: if e.errno == errno.ESRCH: - log.debug( "_check_pid(): PID %d is dead" % pid ) + log.debug("_check_pid(): PID %d is dead" % pid) else: - log.warning( "_check_pid(): Got errno %s when attempting to check PID %d: %s" % ( errno.errorcode[e.errno], pid, e.strerror ) ) + log.warning("_check_pid(): Got errno %s when attempting to check PID %d: %s" % (errno.errorcode[e.errno], pid, e.strerror)) return False - def _terminate( self, proc ): - os.killpg( proc.pid, 15 ) - sleep( 1 ) + def _terminate(self, proc): + os.killpg(proc.pid, 15) + sleep(1) if proc.poll() is None: - os.killpg( proc.pid, 9 ) + os.killpg(proc.pid, 9) return proc.wait() # reap - def __poll_if_needed( self, proc, job_wrapper, job_id ): + def __poll_if_needed(self, proc, job_wrapper, job_id): # Only poll if needed (i.e. job limits are set) if not job_wrapper.has_limits(): return @@ -220,4 +220,4 @@ def __poll_if_needed( self, proc, job_wrapper, job_id ): self._terminate(proc) return True else: - sleep( DEFAULT_POOL_SLEEP_TIME ) + sleep(DEFAULT_POOL_SLEEP_TIME) diff --git a/lib/galaxy/jobs/runners/pbs.py b/lib/galaxy/jobs/runners/pbs.py index 5fe1af748fe0..9961717b660d 100644 --- a/lib/galaxy/jobs/runners/pbs.py +++ b/lib/galaxy/jobs/runners/pbs.py @@ -26,9 +26,9 @@ ) from galaxy.util.bunch import Bunch -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -__all__ = ( 'PBSJobRunner', ) +__all__ = ('PBSJobRunner', ) CLUSTER_ERROR_MESSAGE = "Job cannot be completed due to a cluster error, please retry it later: %s" @@ -85,25 +85,25 @@ } -class PBSJobRunner( AsynchronousJobRunner ): +class PBSJobRunner(AsynchronousJobRunner): """ Job runner backed by a finite pool of worker threads. FIFO scheduling """ runner_name = "PBSRunner" - def __init__( self, app, nworkers ): + def __init__(self, app, nworkers): """Start the job runner """ # Check if PBS was importable, fail if not assert pbs is not None, PBS_IMPORT_MESSAGE if app.config.pbs_application_server and app.config.outputs_to_working_directory: - raise Exception( "pbs_application_server (file staging) and outputs_to_working_directory options are mutually exclusive" ) + raise Exception("pbs_application_server (file staging) and outputs_to_working_directory options are mutually exclusive") # Set the default server during startup self.__default_pbs_server = None self.default_pbs_server # this is a method with a property decorator, so this causes the default server to be set # Proceed with general initialization - super( PBSJobRunner, self ).__init__( app, nworkers ) + super(PBSJobRunner, self).__init__(app, nworkers) self._init_monitor_thread() self._init_worker_threads() @@ -111,7 +111,7 @@ def __init__( self, app, nworkers ): def default_pbs_server(self): if self.__default_pbs_server is None: self.__default_pbs_server = pbs.pbs_default() - log.debug( "Set default PBS server to %s" % self.default_pbs_server ) + log.debug("Set default PBS server to %s" % self.default_pbs_server) return self.__default_pbs_server def url_to_destination(self, url): @@ -126,7 +126,7 @@ def url_to_destination(self, url): if server == '': server = self.default_pbs_server if server is None: - raise Exception( "Could not find TORQUE server" ) + raise Exception("Could not find TORQUE server") # Determine the queue, set the PBS destination (not the same thing as a Galaxy job destination) pbs_destination = '@%s' % server @@ -146,7 +146,7 @@ def url_to_destination(self, url): except: opts = [] for opt in opts: - param, value = opt.split( None, 1 ) + param, value = opt.split(None, 1) params[param] = value log.debug("Converted URL '%s' to destination runner=pbs, params=%s" % (url, params)) @@ -182,11 +182,11 @@ def __args_to_attrs(self, args): for arg, value in args.items(): if arg == 'l': resource_attrs = value.split(',') - for j, ( res, val ) in enumerate( [ a.split('=', 1) for a in resource_attrs ] ): - rval.append( dict( name=pbs.ATTR_l, value=val, resource=res ) ) + for j, (res, val) in enumerate([a.split('=', 1) for a in resource_attrs]): + rval.append(dict(name=pbs.ATTR_l, value=val, resource=res)) else: try: - rval.append( dict( name=getattr( pbs, 'ATTR_' + arg ), value=value ) ) + rval.append(dict(name=getattr(pbs, 'ATTR_' + arg), value=value)) except AttributeError as e: raise Exception("Invalid parameter '%s': %s" % (arg, e)) return rval @@ -196,10 +196,10 @@ def __get_pbs_server(self, job_destination_params): return None return job_destination_params['destination'].split('@')[-1] - def queue_job( self, job_wrapper ): + def queue_job(self, job_wrapper): """Create PBS script for a job and submit it to the PBS queue""" # prepare the job - if not self.prepare_job( job_wrapper, include_metadata=not( self.app.config.pbs_stage_path ) ): + if not self.prepare_job(job_wrapper, include_metadata=not(self.app.config.pbs_stage_path)): return job_destination = job_wrapper.job_destination @@ -228,11 +228,11 @@ def queue_job( self, job_wrapper ): # Explicitly set the determined PBS destination in the persisted job destination for recovery job_destination.params['destination'] = '%s@%s' % (pbs_queue_name or '', pbs_server_name) - c = pbs.pbs_connect( util.smart_str( pbs_server_name ) ) + c = pbs.pbs_connect(util.smart_str(pbs_server_name)) if c <= 0: errno, text = pbs.error() - job_wrapper.fail( "Unable to queue job for execution. Resubmitting the job may succeed." ) - log.error( "Connection to PBS server for submit failed: %s: %s" % ( errno, text ) ) + job_wrapper.fail("Unable to queue job for execution. Resubmitting the job may succeed.") + log.error("Connection to PBS server for submit failed: %s: %s" % (errno, text)) return # define job attributes @@ -246,32 +246,32 @@ def queue_job( self, job_wrapper ): if self.app.config.pbs_application_server: pbs_ofile = self.app.config.pbs_application_server + ':' + ofile pbs_efile = self.app.config.pbs_application_server + ':' + efile - output_files = [ str( o ) for o in output_fnames ] + output_files = [str(o) for o in output_fnames] output_files.append(ecfile) - stagein = self.get_stage_in_out( job_wrapper.get_input_fnames() + output_files, symlink=True ) - stageout = self.get_stage_in_out( output_files ) + stagein = self.get_stage_in_out(job_wrapper.get_input_fnames() + output_files, symlink=True) + stageout = self.get_stage_in_out(output_files) attrs = [ - dict( name=pbs.ATTR_o, value=pbs_ofile ), - dict( name=pbs.ATTR_e, value=pbs_efile ), - dict( name=pbs.ATTR_stagein, value=stagein ), - dict( name=pbs.ATTR_stageout, value=stageout ), + dict(name=pbs.ATTR_o, value=pbs_ofile), + dict(name=pbs.ATTR_e, value=pbs_efile), + dict(name=pbs.ATTR_stagein, value=stagein), + dict(name=pbs.ATTR_stageout, value=stageout), ] # If not, we're using NFS else: attrs = [ - dict( name=pbs.ATTR_o, value=ofile ), - dict( name=pbs.ATTR_e, value=efile ), + dict(name=pbs.ATTR_o, value=ofile), + dict(name=pbs.ATTR_e, value=efile), ] # define PBS job options - attrs.append( dict( name=pbs.ATTR_N, value=str( "%s_%s_%s" % ( job_wrapper.job_id, job_wrapper.tool.id, job_wrapper.user ) ) ) ) - job_attrs = pbs.new_attropl( len( attrs ) + len( pbs_options ) ) - for i, attr in enumerate( attrs + pbs_options ): + attrs.append(dict(name=pbs.ATTR_N, value=str("%s_%s_%s" % (job_wrapper.job_id, job_wrapper.tool.id, job_wrapper.user)))) + job_attrs = pbs.new_attropl(len(attrs) + len(pbs_options)) + for i, attr in enumerate(attrs + pbs_options): job_attrs[i].name = attr['name'] job_attrs[i].value = attr['value'] if 'resource' in attr: job_attrs[i].resource = attr['resource'] - exec_dir = os.path.abspath( job_wrapper.working_directory ) + exec_dir = os.path.abspath(job_wrapper.working_directory) # write the job script if self.app.config.pbs_stage_path != '': @@ -280,23 +280,23 @@ def queue_job( self, job_wrapper ): os.utime(ecfile, None) stage_commands = pbs_symlink_template % ( - " ".join( job_wrapper.get_input_fnames() + output_files ), + " ".join(job_wrapper.get_input_fnames() + output_files), self.app.config.pbs_stage_path, exec_dir, ) else: stage_commands = '' - env_setup_commands = [ stage_commands ] + env_setup_commands = [stage_commands] script = self.get_job_file(job_wrapper, exit_code_path=ecfile, env_setup_commands=env_setup_commands) job_file = "%s/%s.sh" % (self.app.config.cluster_files_directory, job_wrapper.job_id) - self.write_executable_script( job_file, script ) + self.write_executable_script(job_file, script) # job was deleted while we were preparing it if job_wrapper.get_state() == model.Job.states.DELETED: - log.debug( "Job %s deleted by user before it entered the PBS queue" % job_wrapper.job_id ) + log.debug("Job %s deleted by user before it entered the PBS queue" % job_wrapper.job_id) pbs.pbs_disconnect(c) - if job_wrapper.cleanup_job in ( "always", "onsuccess" ): - self.cleanup( ( ofile, efile, ecfile, job_file ) ) + if job_wrapper.cleanup_job in ("always", "onsuccess"): + self.cleanup((ofile, efile, ecfile, job_file)) job_wrapper.cleanup() return @@ -304,7 +304,7 @@ def queue_job( self, job_wrapper ): # The job tag includes the job and the task identifier # (if a TaskWrapper was passed in): galaxy_job_id = job_wrapper.get_id_tag() - log.debug("(%s) submitting file %s" % ( galaxy_job_id, job_file ) ) + log.debug("(%s) submitting file %s" % (galaxy_job_id, job_file)) tries = 0 while tries < 5: @@ -314,20 +314,20 @@ def queue_job( self, job_wrapper ): pbs.pbs_disconnect(c) break errno, text = pbs.error() - log.warning( "(%s) pbs_submit failed (try %d/5), PBS error %d: %s" % (galaxy_job_id, tries, errno, text) ) + log.warning("(%s) pbs_submit failed (try %d/5), PBS error %d: %s" % (galaxy_job_id, tries, errno, text)) time.sleep(2) else: - log.error( "(%s) All attempts to submit job failed" % galaxy_job_id ) - job_wrapper.fail( "Unable to run this job due to a cluster error, please retry it later" ) + log.error("(%s) All attempts to submit job failed" % galaxy_job_id) + job_wrapper.fail("Unable to run this job due to a cluster error, please retry it later") return if pbs_queue_name is None: - log.debug("(%s) queued in default queue as %s" % (galaxy_job_id, job_id) ) + log.debug("(%s) queued in default queue as %s" % (galaxy_job_id, job_id)) else: - log.debug("(%s) queued in %s queue as %s" % (galaxy_job_id, pbs_queue_name, job_id) ) + log.debug("(%s) queued in %s queue as %s" % (galaxy_job_id, pbs_queue_name, job_id)) # persist destination - job_wrapper.set_job_destination( job_destination, job_id ) + job_wrapper.set_job_destination(job_destination, job_id) # Store PBS related state information for job job_state = AsynchronousJobState() @@ -342,24 +342,24 @@ def queue_job( self, job_wrapper ): job_state.job_destination = job_destination # Add to our 'queue' of jobs to monitor - self.monitor_queue.put( job_state ) + self.monitor_queue.put(job_state) - def check_watched_items( self ): + def check_watched_items(self): """ Called by the monitor thread to look at each watched job and deal with state changes. """ new_watched = [] # reduce pbs load by batching status queries - ( failures, statuses ) = self.check_all_jobs() + (failures, statuses) = self.check_all_jobs() for pbs_job_state in self.watched: job_id = pbs_job_state.job_id galaxy_job_id = pbs_job_state.job_wrapper.get_id_tag() old_state = pbs_job_state.old_state pbs_server_name = self.__get_pbs_server(pbs_job_state.job_destination.params) if pbs_server_name in failures: - log.debug( "(%s/%s) Skipping state check because PBS server connection failed" % ( galaxy_job_id, job_id ) ) - new_watched.append( pbs_job_state ) + log.debug("(%s/%s) Skipping state check because PBS server connection failed" % (galaxy_job_id, job_id)) + new_watched.append(pbs_job_state) continue try: status = statuses[job_id] @@ -368,56 +368,56 @@ def check_watched_items( self ): continue try: # Recheck to make sure it wasn't a communication problem - self.check_single_job( pbs_server_name, job_id ) - log.warning( "(%s/%s) PBS job was not in state check list, but was found with individual state check" % ( galaxy_job_id, job_id ) ) - new_watched.append( pbs_job_state ) + self.check_single_job(pbs_server_name, job_id) + log.warning("(%s/%s) PBS job was not in state check list, but was found with individual state check" % (galaxy_job_id, job_id)) + new_watched.append(pbs_job_state) except: errno, text = pbs.error() if errno == 15001: # 15001 == job not in queue - log.debug("(%s/%s) PBS job has left queue" % (galaxy_job_id, job_id) ) - self.work_queue.put( ( self.finish_job, pbs_job_state ) ) + log.debug("(%s/%s) PBS job has left queue" % (galaxy_job_id, job_id)) + self.work_queue.put((self.finish_job, pbs_job_state)) else: # Unhandled error, continue to monitor - log.info("(%s/%s) PBS state check resulted in error (%d): %s" % (galaxy_job_id, job_id, errno, text) ) - new_watched.append( pbs_job_state ) + log.info("(%s/%s) PBS state check resulted in error (%d): %s" % (galaxy_job_id, job_id, errno, text)) + new_watched.append(pbs_job_state) continue if status.job_state != old_state: - log.debug("(%s/%s) PBS job state changed from %s to %s" % ( galaxy_job_id, job_id, old_state, status.job_state ) ) + log.debug("(%s/%s) PBS job state changed from %s to %s" % (galaxy_job_id, job_id, old_state, status.job_state)) if status.job_state == "R" and not pbs_job_state.running: pbs_job_state.running = True - pbs_job_state.job_wrapper.change_state( model.Job.states.RUNNING ) - if status.job_state == "R" and status.get( 'resources_used', False ): + pbs_job_state.job_wrapper.change_state(model.Job.states.RUNNING) + if status.job_state == "R" and status.get('resources_used', False): # resources_used may not be in the status for new jobs - h, m, s = [ int( i ) for i in status.resources_used.walltime.split( ':' ) ] - runtime = timedelta( 0, s, 0, 0, m, h ) - if pbs_job_state.check_limits( runtime=runtime ): - self.work_queue.put( ( self.fail_job, pbs_job_state ) ) + h, m, s = [int(i) for i in status.resources_used.walltime.split(':')] + runtime = timedelta(0, s, 0, 0, m, h) + if pbs_job_state.check_limits(runtime=runtime): + self.work_queue.put((self.fail_job, pbs_job_state)) continue elif status.job_state == "C": # "keep_completed" is enabled in PBS, so try to check exit status try: - assert int( status.exit_status ) == 0 - log.debug("(%s/%s) PBS job has completed successfully" % ( galaxy_job_id, job_id ) ) + assert int(status.exit_status) == 0 + log.debug("(%s/%s) PBS job has completed successfully" % (galaxy_job_id, job_id)) except AssertionError: - exit_status = int( status.exit_status ) - error_message = JOB_EXIT_STATUS.get( exit_status, 'Unknown error: %s' % status.exit_status ) + exit_status = int(status.exit_status) + error_message = JOB_EXIT_STATUS.get(exit_status, 'Unknown error: %s' % status.exit_status) pbs_job_state.fail_message = CLUSTER_ERROR_MESSAGE % error_message - log.error( '(%s/%s) PBS job failed: %s' % ( galaxy_job_id, job_id, error_message ) ) + log.error('(%s/%s) PBS job failed: %s' % (galaxy_job_id, job_id, error_message)) pbs_job_state.stop_job = False - self.work_queue.put( ( self.fail_job, pbs_job_state ) ) + self.work_queue.put((self.fail_job, pbs_job_state)) continue except AttributeError: # No exit_status, can't verify proper completion so we just have to assume success. - log.debug("(%s/%s) PBS job has completed" % ( galaxy_job_id, job_id ) ) - self.work_queue.put( ( self.finish_job, pbs_job_state ) ) + log.debug("(%s/%s) PBS job has completed" % (galaxy_job_id, job_id)) + self.work_queue.put((self.finish_job, pbs_job_state)) continue pbs_job_state.old_state = status.job_state - new_watched.append( pbs_job_state ) + new_watched.append(pbs_job_state) # Replace the watch list with the updated version self.watched = new_watched - def check_all_jobs( self ): + def check_all_jobs(self): """ Returns a list of servers that failed to be contacted and a dict of "job_id : status" pairs (where status is a bunchified version @@ -429,65 +429,65 @@ def check_all_jobs( self ): for pbs_job_state in self.watched: pbs_server_name = self.__get_pbs_server(pbs_job_state.job_destination.params) if pbs_server_name not in servers: - servers.append( pbs_server_name ) + servers.append(pbs_server_name) pbs_job_state.check_count += 1 for pbs_server_name in servers: - c = pbs.pbs_connect( util.smart_str( pbs_server_name ) ) + c = pbs.pbs_connect(util.smart_str(pbs_server_name)) if c <= 0: - log.debug("connection to PBS server %s for state check failed" % pbs_server_name ) - failures.append( pbs_server_name ) + log.debug("connection to PBS server %s for state check failed" % pbs_server_name) + failures.append(pbs_server_name) continue stat_attrl = pbs.new_attrl(3) stat_attrl[0].name = pbs.ATTR_state stat_attrl[1].name = pbs.ATTR_used stat_attrl[2].name = pbs.ATTR_exitstat - jobs = pbs.pbs_statjob( c, None, stat_attrl, None ) - pbs.pbs_disconnect( c ) - statuses.update( self.convert_statjob_to_bunches( jobs ) ) - return( ( failures, statuses ) ) + jobs = pbs.pbs_statjob(c, None, stat_attrl, None) + pbs.pbs_disconnect(c) + statuses.update(self.convert_statjob_to_bunches(jobs)) + return((failures, statuses)) - def convert_statjob_to_bunches( self, statjob_out ): + def convert_statjob_to_bunches(self, statjob_out): statuses = {} for job in statjob_out: status = {} for attrib in job.attribs: if attrib.resource is None: - status[ attrib.name ] = attrib.value + status[attrib.name] = attrib.value else: if attrib.name not in status: - status[ attrib.name ] = Bunch() - status[ attrib.name ][ attrib.resource ] = attrib.value - statuses[ job.name ] = Bunch( **status ) + status[attrib.name] = Bunch() + status[attrib.name][attrib.resource] = attrib.value + statuses[job.name] = Bunch(**status) return statuses - def check_single_job( self, pbs_server_name, job_id ): + def check_single_job(self, pbs_server_name, job_id): """ Returns the state of a single job, used to make sure a job is really dead. """ - c = pbs.pbs_connect( util.smart_str( pbs_server_name ) ) + c = pbs.pbs_connect(util.smart_str(pbs_server_name)) if c <= 0: - log.debug("connection to PBS server %s for state check failed" % pbs_server_name ) + log.debug("connection to PBS server %s for state check failed" % pbs_server_name) return None stat_attrl = pbs.new_attrl(1) stat_attrl[0].name = pbs.ATTR_state - jobs = pbs.pbs_statjob( c, job_id, stat_attrl, None ) - pbs.pbs_disconnect( c ) + jobs = pbs.pbs_statjob(c, job_id, stat_attrl, None) + pbs.pbs_disconnect(c) return jobs[0].attribs[0].value - def fail_job( self, pbs_job_state ): + def fail_job(self, pbs_job_state): """ Separated out so we can use the worker threads for it. """ # NB: The stop_job method was modified to limit exceptions being sent up here, # so the wrapper's fail method will now be called in case of error: if pbs_job_state.stop_job: - self.stop_job( self.sa_session.query( self.app.model.Job ).get( pbs_job_state.job_wrapper.job_id ) ) - pbs_job_state.job_wrapper.fail( pbs_job_state.fail_message ) + self.stop_job(self.sa_session.query(self.app.model.Job).get(pbs_job_state.job_wrapper.job_id)) + pbs_job_state.job_wrapper.fail(pbs_job_state.fail_message) if pbs_job_state.job_wrapper.cleanup_job == "always": - self.cleanup( ( pbs_job_state.output_file, pbs_job_state.error_file, pbs_job_state.exit_code_file, pbs_job_state.job_file ) ) + self.cleanup((pbs_job_state.output_file, pbs_job_state.error_file, pbs_job_state.exit_code_file, pbs_job_state.job_file)) - def get_stage_in_out( self, fnames, symlink=False ): + def get_stage_in_out(self, fnames, symlink=False): """Convenience function to create a stagein/stageout list""" stage = '' for fname in fnames: @@ -502,38 +502,38 @@ def get_stage_in_out( self, fnames, symlink=False ): stage += "%s@%s:%s" % (stage_name, self.app.config.pbs_dataset_server, fname) return stage - def stop_job( self, job ): + def stop_job(self, job): """Attempts to delete a job from the PBS queue""" job_id = job.get_job_runner_external_id().encode('utf-8') - job_tag = "(%s/%s)" % ( job.get_id_tag(), job_id ) - log.debug( "%s Stopping PBS job" % job_tag ) + job_tag = "(%s/%s)" % (job.get_id_tag(), job_id) + log.debug("%s Stopping PBS job" % job_tag) # Declare the connection handle c so that it can be cleaned up: c = None try: - pbs_server_name = self.__get_pbs_server( job.destination_params ) + pbs_server_name = self.__get_pbs_server(job.destination_params) if pbs_server_name is None: log.debug("(%s) Job queued but no destination stored in job params, cannot delete" - % job_tag ) + % job_tag) return - c = pbs.pbs_connect( util.smart_str( pbs_server_name ) ) + c = pbs.pbs_connect(util.smart_str(pbs_server_name)) if c <= 0: log.debug("(%s) Connection to PBS server for job delete failed" - % job_tag ) + % job_tag) return - pbs.pbs_deljob( c, job_id, '' ) - log.debug( "%s Removed from PBS queue before job completion" - % job_tag ) + pbs.pbs_deljob(c, job_id, '') + log.debug("%s Removed from PBS queue before job completion" + % job_tag) except: e = traceback.format_exc() - log.debug( "%s Unable to stop job: %s" % ( job_tag, e ) ) + log.debug("%s Unable to stop job: %s" % (job_tag, e)) finally: # Cleanup: disconnect from the server. - if ( None is not c ): - pbs.pbs_disconnect( c ) + if (None is not c): + pbs.pbs_disconnect(c) - def recover( self, job, job_wrapper ): + def recover(self, job, job_wrapper): """Recovers jobs stuck in the queued/running state when Galaxy started""" job_id = job.get_job_runner_external_id() pbs_job_state = AsynchronousJobState() @@ -541,18 +541,18 @@ def recover( self, job, job_wrapper ): pbs_job_state.error_file = "%s/%s.e" % (self.app.config.cluster_files_directory, job.id) pbs_job_state.exit_code_file = "%s/%s.ec" % (self.app.config.cluster_files_directory, job.id) pbs_job_state.job_file = "%s/%s.sh" % (self.app.config.cluster_files_directory, job.id) - pbs_job_state.job_id = str( job_id ) + pbs_job_state.job_id = str(job_id) pbs_job_state.runner_url = job_wrapper.get_job_runner_url() pbs_job_state.job_destination = job_wrapper.job_destination job_wrapper.command_line = job.command_line pbs_job_state.job_wrapper = job_wrapper if job.state == model.Job.states.RUNNING: - log.debug( "(%s/%s) is still in running state, adding to the PBS queue" % ( job.id, job.get_job_runner_external_id() ) ) + log.debug("(%s/%s) is still in running state, adding to the PBS queue" % (job.id, job.get_job_runner_external_id())) pbs_job_state.old_state = 'R' pbs_job_state.running = True - self.monitor_queue.put( pbs_job_state ) + self.monitor_queue.put(pbs_job_state) elif job.state == model.Job.states.QUEUED: - log.debug( "(%s/%s) is still in PBS queued state, adding to the PBS queue" % ( job.id, job.get_job_runner_external_id() ) ) + log.debug("(%s/%s) is still in PBS queued state, adding to the PBS queue" % (job.id, job.get_job_runner_external_id())) pbs_job_state.old_state = 'Q' pbs_job_state.running = False - self.monitor_queue.put( pbs_job_state ) + self.monitor_queue.put(pbs_job_state) diff --git a/lib/galaxy/jobs/runners/pulsar.py b/lib/galaxy/jobs/runners/pulsar.py index 1a4d7ce7bbed..7c3e96806e10 100644 --- a/lib/galaxy/jobs/runners/pulsar.py +++ b/lib/galaxy/jobs/runners/pulsar.py @@ -42,7 +42,7 @@ ) from galaxy.util.bunch import Bunch -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) __all__ = ( 'PulsarLegacyJobRunner', @@ -166,15 +166,15 @@ PARAMETER_SPECIFICATION_IGNORED = object() -class PulsarJobRunner( AsynchronousJobRunner ): +class PulsarJobRunner(AsynchronousJobRunner): """Base class for pulsar job runners.""" runner_name = "PulsarJobRunner" default_build_pulsar_app = False - def __init__( self, app, nworkers, **kwds ): + def __init__(self, app, nworkers, **kwds): """Start the job runner.""" - super( PulsarJobRunner, self ).__init__( app, nworkers, runner_param_specs=PULSAR_PARAM_SPECS, **kwds ) + super(PulsarJobRunner, self).__init__(app, nworkers, runner_param_specs=PULSAR_PARAM_SPECS, **kwds) self._init_worker_threads() galaxy_url = self.runner_params.galaxy_url if not galaxy_url: @@ -185,29 +185,29 @@ def __init__( self, app, nworkers, **kwds ): self.__init_client_manager() self._monitor() - def _monitor( self ): + def _monitor(self): # Extension point allow MQ variant to setup callback instead self._init_monitor_thread() - def __init_client_manager( self ): + def __init_client_manager(self): pulsar_conf = self.runner_params.get('pulsar_config', None) self.__init_pulsar_app(pulsar_conf) client_manager_kwargs = {} for kwd in 'manager', 'cache', 'transport', 'persistence_directory': - client_manager_kwargs[ kwd ] = self.runner_params[ kwd ] + client_manager_kwargs[kwd] = self.runner_params[kwd] if self.pulsar_app is not None: - client_manager_kwargs[ "pulsar_app" ] = self.pulsar_app + client_manager_kwargs["pulsar_app"] = self.pulsar_app # TODO: Hack remove this following line pulsar lib update # that includes https://github.com/galaxyproject/pulsar/commit/ce0636a5b64fae52d165bcad77b2caa3f0e9c232 - client_manager_kwargs[ "file_cache" ] = None + client_manager_kwargs["file_cache"] = None for kwd in self.runner_params.keys(): - if kwd.startswith( 'amqp_' ) or kwd.startswith( 'transport_' ): - client_manager_kwargs[ kwd ] = self.runner_params[ kwd ] + if kwd.startswith('amqp_') or kwd.startswith('transport_'): + client_manager_kwargs[kwd] = self.runner_params[kwd] self.client_manager = build_client_manager(**client_manager_kwargs) - def __init_pulsar_app( self, pulsar_conf_path ): + def __init_pulsar_app(self, pulsar_conf_path): if pulsar_conf_path is None and not self.default_build_pulsar_app: self.pulsar_app = None return @@ -228,9 +228,9 @@ def __init_pulsar_app( self, pulsar_conf_path ): conf["galaxy_home"] = galaxy_directory() self.pulsar_app = pulsar.core.PulsarApp(**conf) - def url_to_destination( self, url ): + def url_to_destination(self, url): """Convert a legacy URL to a job destination.""" - return JobDestination( runner="pulsar", params=url_to_destination_params( url ) ) + return JobDestination(runner="pulsar", params=url_to_destination_params(url)) def check_watched_item(self, job_state): try: @@ -261,21 +261,21 @@ def _update_job_state_for_status(self, job_state, pulsar_status): return None if pulsar_status == "running" and not job_state.running: job_state.running = True - job_state.job_wrapper.change_state( model.Job.states.RUNNING ) + job_state.job_wrapper.change_state(model.Job.states.RUNNING) return job_state def queue_job(self, job_wrapper): job_destination = job_wrapper.job_destination - self._populate_parameter_defaults( job_destination ) + self._populate_parameter_defaults(job_destination) - command_line, client, remote_job_config, compute_environment = self.__prepare_job( job_wrapper, job_destination ) + command_line, client, remote_job_config, compute_environment = self.__prepare_job(job_wrapper, job_destination) if not command_line: return try: - dependencies_description = PulsarJobRunner.__dependencies_description( client, job_wrapper ) - rewrite_paths = not PulsarJobRunner.__rewrite_parameters( client ) + dependencies_description = PulsarJobRunner.__dependencies_description(client, job_wrapper) + rewrite_paths = not PulsarJobRunner.__rewrite_parameters(client) unstructured_path_rewrites = {} if compute_environment: unstructured_path_rewrites = compute_environment.unstructured_path_rewrites @@ -295,10 +295,10 @@ def queue_job(self, job_wrapper): ) job_id = pulsar_submit_job(client, client_job_description, remote_job_config) log.info("Pulsar job submitted with job_id %s" % job_id) - job_wrapper.set_job_destination( job_destination, job_id ) - job_wrapper.change_state( model.Job.states.QUEUED ) + job_wrapper.set_job_destination(job_destination, job_id) + job_wrapper.change_state(model.Job.states.QUEUED) except Exception: - job_wrapper.fail( "failure running job", exception=True ) + job_wrapper.fail("failure running job", exception=True) log.exception("failure running job %d", job_wrapper.job_id) return @@ -321,15 +321,15 @@ def __prepare_job(self, job_wrapper, job_destination): tool = job_wrapper.tool remote_job_config = client.setup(tool.id, tool.version) PulsarJobRunner.check_job_config(remote_job_config) - rewrite_parameters = PulsarJobRunner.__rewrite_parameters( client ) + rewrite_parameters = PulsarJobRunner.__rewrite_parameters(client) prepare_kwds = {} if rewrite_parameters: - compute_environment = PulsarComputeEnvironment( client, job_wrapper, remote_job_config ) - prepare_kwds[ 'compute_environment' ] = compute_environment - job_wrapper.prepare( **prepare_kwds ) + compute_environment = PulsarComputeEnvironment(client, job_wrapper, remote_job_config) + prepare_kwds['compute_environment'] = compute_environment + job_wrapper.prepare(**prepare_kwds) self.__prepare_input_files_locally(job_wrapper) - remote_metadata = PulsarJobRunner.__remote_metadata( client ) - dependency_resolution = PulsarJobRunner.__dependency_resolution( client ) + remote_metadata = PulsarJobRunner.__remote_metadata(client) + dependency_resolution = PulsarJobRunner.__dependency_resolution(client) metadata_kwds = self.__build_metadata_configuration(client, job_wrapper, remote_metadata, remote_job_config) remote_command_params = dict( working_directory=remote_job_config['metadata_directory'], @@ -365,15 +365,15 @@ def __prepare_job(self, job_wrapper, job_destination): remote_command_params=remote_command_params, ) except UnsupportedPulsarException as e: - job_wrapper.fail( e.message, exception=False ) + job_wrapper.fail(e.message, exception=False) log.exception("failure running job %d", job_wrapper.job_id) except Exception: - job_wrapper.fail( "failure preparing job", exception=True ) + job_wrapper.fail("failure preparing job", exception=True) log.exception("failure running job %d", job_wrapper.job_id) # If we were able to get a command line, run the job if not command_line: - job_wrapper.finish( '', '' ) + job_wrapper.finish('', '') return command_line, client, remote_job_config, compute_environment @@ -386,13 +386,13 @@ def __prepare_input_files_locally(self, job_wrapper): raise Exception('Error running file staging command: %s' % cmd) job_wrapper.prepare_input_files_cmds = None # prevent them from being used in-line - def _populate_parameter_defaults( self, job_destination ): + def _populate_parameter_defaults(self, job_destination): updated = False params = job_destination.params for key, value in self.destination_defaults.items(): if key in params: if value is PARAMETER_SPECIFICATION_IGNORED: - log.warning( "Pulsar runner in selected configuration ignores parameter %s" % key ) + log.warning("Pulsar runner in selected configuration ignores parameter %s" % key) continue # if self.runner_params.get( key, None ): # # Let plugin define defaults for some parameters - @@ -405,19 +405,19 @@ def _populate_parameter_defaults( self, job_destination ): continue if value is PARAMETER_SPECIFICATION_REQUIRED: - raise Exception( "Pulsar destination does not define required parameter %s" % key ) + raise Exception("Pulsar destination does not define required parameter %s" % key) elif value is not PARAMETER_SPECIFICATION_IGNORED: - params[ key ] = value + params[key] = value updated = True return updated def get_output_files(self, job_wrapper): output_paths = job_wrapper.get_output_fnames() - return [ str( o ) for o in output_paths ] # Force job_path from DatasetPath objects. + return [str(o) for o in output_paths] # Force job_path from DatasetPath objects. def get_input_files(self, job_wrapper): input_paths = job_wrapper.get_input_paths() - return [ str( i ) for i in input_paths ] # Force job_path from DatasetPath objects. + return [str(i) for i in input_paths] # Force job_path from DatasetPath objects. def get_client_from_wrapper(self, job_wrapper): job_id = job_wrapper.job_id @@ -426,22 +426,22 @@ def get_client_from_wrapper(self, job_wrapper): params = job_wrapper.job_destination.params.copy() for key, value in params.items(): if value: - params[key] = model.User.expand_user_properties( job_wrapper.get_job().user, value ) + params[key] = model.User.expand_user_properties(job_wrapper.get_job().user, value) - env = getattr( job_wrapper.job_destination, "env", [] ) - return self.get_client( params, job_id, env ) + env = getattr(job_wrapper.job_destination, "env", []) + return self.get_client(params, job_id, env) def get_client_from_state(self, job_state): job_destination_params = job_state.job_destination.params job_id = job_state.job_id - return self.get_client( job_destination_params, job_id ) + return self.get_client(job_destination_params, job_id) - def get_client( self, job_destination_params, job_id, env=[] ): + def get_client(self, job_destination_params, job_id, env=[]): # Cannot use url_for outside of web thread. # files_endpoint = url_for( controller="job_files", job_id=encoded_job_id ) encoded_job_id = self.app.security.encode_id(job_id) - job_key = self.app.security.encode_id( job_id, kind="jobs_files" ) + job_key = self.app.security.encode_id(job_id, kind="jobs_files") endpoint_base = "%s/api/jobs/%s/files?job_key=%s" if self.app.config.nginx_upload_job_files_path: endpoint_base = "%s" + \ @@ -453,13 +453,13 @@ def get_client( self, job_destination_params, job_id, env=[] ): job_key ) get_client_kwds = dict( - job_id=str( job_id ), + job_id=str(job_id), files_endpoint=files_endpoint, env=env ) - return self.client_manager.get_client( job_destination_params, **get_client_kwds ) + return self.client_manager.get_client(job_destination_params, **get_client_kwds) - def finish_job( self, job_state ): + def finish_job(self, job_state): stderr = stdout = '' job_wrapper = job_state.job_wrapper try: @@ -474,24 +474,24 @@ def finish_job( self, job_state ): # Use Pulsar client code to transfer/copy files back # and cleanup job if needed. completed_normally = \ - job_wrapper.get_state() not in [ model.Job.states.ERROR, model.Job.states.DELETED ] + job_wrapper.get_state() not in [model.Job.states.ERROR, model.Job.states.DELETED] cleanup_job = job_wrapper.cleanup_job client_outputs = self.__client_outputs(client, job_wrapper) - finish_args = dict( client=client, - job_completed_normally=completed_normally, - cleanup_job=cleanup_job, - client_outputs=client_outputs, - pulsar_outputs=pulsar_outputs ) - failed = pulsar_finish_job( **finish_args ) + finish_args = dict(client=client, + job_completed_normally=completed_normally, + cleanup_job=cleanup_job, + client_outputs=client_outputs, + pulsar_outputs=pulsar_outputs) + failed = pulsar_finish_job(**finish_args) if failed: job_wrapper.fail("Failed to find or download one or more job outputs from remote server.", exception=True) except Exception: message = GENERIC_REMOTE_ERROR - job_wrapper.fail( message, exception=True ) + job_wrapper.fail(message, exception=True) log.exception("failure finishing job %d", job_wrapper.job_id) return - if not PulsarJobRunner.__remote_metadata( client ): - self._handle_metadata_externally( job_wrapper, resolve_requirements=True ) + if not PulsarJobRunner.__remote_metadata(client): + self._handle_metadata_externally(job_wrapper, resolve_requirements=True) # Finish the job try: job_wrapper.finish( @@ -505,47 +505,47 @@ def finish_job( self, job_state ): log.exception("Job wrapper finish method failed") job_wrapper.fail("Unable to finish job", exception=True) - def fail_job( self, job_state, message=GENERIC_REMOTE_ERROR ): + def fail_job(self, job_state, message=GENERIC_REMOTE_ERROR): """Seperated out so we can use the worker threads for it.""" - self.stop_job( self.sa_session.query( self.app.model.Job ).get( job_state.job_wrapper.job_id ) ) - job_state.job_wrapper.fail( getattr( job_state, "fail_message", message ) ) + self.stop_job(self.sa_session.query(self.app.model.Job).get(job_state.job_wrapper.job_id)) + job_state.job_wrapper.fail(getattr(job_state, "fail_message", message)) - def check_pid( self, pid ): + def check_pid(self, pid): try: - os.kill( pid, 0 ) + os.kill(pid, 0) return True except OSError as e: if e.errno == errno.ESRCH: - log.debug( "check_pid(): PID %d is dead" % pid ) + log.debug("check_pid(): PID %d is dead" % pid) else: - log.warning( "check_pid(): Got errno %s when attempting to check PID %d: %s" % ( errno.errorcode[e.errno], pid, e.strerror ) ) + log.warning("check_pid(): Got errno %s when attempting to check PID %d: %s" % (errno.errorcode[e.errno], pid, e.strerror)) return False - def stop_job( self, job ): + def stop_job(self, job): # if our local job has JobExternalOutputMetadata associated, then our primary job has to have already finished - client = self.get_client( job.destination_params, job.job_runner_external_id ) + client = self.get_client(job.destination_params, job.job_runner_external_id) job_ext_output_metadata = job.get_external_output_metadata() - if not PulsarJobRunner.__remote_metadata( client ) and job_ext_output_metadata: + if not PulsarJobRunner.__remote_metadata(client) and job_ext_output_metadata: pid = job_ext_output_metadata[0].job_runner_external_pid # every JobExternalOutputMetadata has a pid set, we just need to take from one of them - if pid in [ None, '' ]: - log.warning( "stop_job(): %s: no PID in database for job, unable to stop" % job.id ) + if pid in [None, '']: + log.warning("stop_job(): %s: no PID in database for job, unable to stop" % job.id) return - pid = int( pid ) - if not self.check_pid( pid ): - log.warning( "stop_job(): %s: PID %d was already dead or can't be signaled" % ( job.id, pid ) ) + pid = int(pid) + if not self.check_pid(pid): + log.warning("stop_job(): %s: PID %d was already dead or can't be signaled" % (job.id, pid)) return - for sig in [ 15, 9 ]: + for sig in [15, 9]: try: - os.killpg( pid, sig ) + os.killpg(pid, sig) except OSError as e: - log.warning( "stop_job(): %s: Got errno %s when attempting to signal %d to PID %d: %s" % ( job.id, errno.errorcode[e.errno], sig, pid, e.strerror ) ) + log.warning("stop_job(): %s: Got errno %s when attempting to signal %d to PID %d: %s" % (job.id, errno.errorcode[e.errno], sig, pid, e.strerror)) return # give up - sleep( 2 ) - if not self.check_pid( pid ): - log.debug( "stop_job(): %s: PID %d successfully killed with signal %d" % ( job.id, pid, sig ) ) + sleep(2) + if not self.check_pid(pid): + log.debug("stop_job(): %s: PID %d successfully killed with signal %d" % (job.id, pid, sig)) return else: - log.warning( "stop_job(): %s: PID %d refuses to die after signaling TERM/KILL" % ( job.id, pid ) ) + log.warning("stop_job(): %s: PID %d refuses to die after signaling TERM/KILL" % (job.id, pid)) else: # Remote kill pulsar_url = job.job_runner_name @@ -554,35 +554,35 @@ def stop_job( self, job ): client = self.get_client(job.destination_params, job_id) client.kill() - def recover( self, job, job_wrapper ): + def recover(self, job, job_wrapper): """Recover jobs stuck in the queued/running state when Galaxy started.""" - job_state = self._job_state( job, job_wrapper ) + job_state = self._job_state(job, job_wrapper) job_wrapper.command_line = job.get_command_line() state = job.get_state() if state in [model.Job.states.RUNNING, model.Job.states.QUEUED]: - log.debug( "(Pulsar/%s) is still in running state, adding to the Pulsar queue" % ( job.get_id()) ) + log.debug("(Pulsar/%s) is still in running state, adding to the Pulsar queue" % (job.get_id())) job_state.old_state = True job_state.running = state == model.Job.states.RUNNING - self.monitor_queue.put( job_state ) + self.monitor_queue.put(job_state) - def shutdown( self ): - super( PulsarJobRunner, self ).shutdown() + def shutdown(self): + super(PulsarJobRunner, self).shutdown() self.client_manager.shutdown() - def _job_state( self, job, job_wrapper ): + def _job_state(self, job, job_wrapper): job_state = AsynchronousJobState() # TODO: Determine why this is set when using normal message queue updates # but not CLI submitted MQ updates... raw_job_id = job.get_job_runner_external_id() or job_wrapper.job_id - job_state.job_id = str( raw_job_id ) + job_state.job_id = str(raw_job_id) job_state.runner_url = job_wrapper.get_job_runner_url() job_state.job_destination = job_wrapper.job_destination job_state.job_wrapper = job_wrapper return job_state - def __client_outputs( self, client, job_wrapper ): - work_dir_outputs = self.get_work_dir_outputs( job_wrapper ) - output_files = self.get_output_files( job_wrapper ) + def __client_outputs(self, client, job_wrapper): + work_dir_outputs = self.get_work_dir_outputs(job_wrapper) + output_files = self.get_output_files(job_wrapper) client_outputs = ClientOutputs( working_directory=job_wrapper.tool_working_directory, metadata_directory=job_wrapper.working_directory, @@ -601,8 +601,8 @@ def check_job_config(remote_job_config): raise UnsupportedPulsarException() @staticmethod - def __dependencies_description( pulsar_client, job_wrapper ): - dependency_resolution = PulsarJobRunner.__dependency_resolution( pulsar_client ) + def __dependencies_description(pulsar_client, job_wrapper): + dependency_resolution = PulsarJobRunner.__dependency_resolution(pulsar_client) remote_dependency_resolution = dependency_resolution == "remote" if not remote_dependency_resolution: return None @@ -614,19 +614,19 @@ def __dependencies_description( pulsar_client, job_wrapper ): ) @staticmethod - def __dependency_resolution( pulsar_client ): - dependency_resolution = pulsar_client.destination_params.get( "dependency_resolution", "remote" ) + def __dependency_resolution(pulsar_client): + dependency_resolution = pulsar_client.destination_params.get("dependency_resolution", "remote") if dependency_resolution not in ["none", "local", "remote"]: raise Exception("Unknown dependency_resolution value encountered %s" % dependency_resolution) return dependency_resolution @staticmethod - def __remote_metadata( pulsar_client ): - remote_metadata = string_as_bool_or_none( pulsar_client.destination_params.get( "remote_metadata", False ) ) + def __remote_metadata(pulsar_client): + remote_metadata = string_as_bool_or_none(pulsar_client.destination_params.get("remote_metadata", False)) return remote_metadata @staticmethod - def __use_remote_datatypes_conf( pulsar_client ): + def __use_remote_datatypes_conf(pulsar_client): """Use remote metadata datatypes instead of Galaxy's. When setting remote metadata, use integrated datatypes from this @@ -640,12 +640,12 @@ def __use_remote_datatypes_conf( pulsar_client ): there is no guarentee that it will contain all the datatypes available to this Galaxy. """ - use_remote_datatypes = string_as_bool_or_none( pulsar_client.destination_params.get( "use_remote_datatypes", False ) ) + use_remote_datatypes = string_as_bool_or_none(pulsar_client.destination_params.get("use_remote_datatypes", False)) return use_remote_datatypes @staticmethod - def __rewrite_parameters( pulsar_client ): - return string_as_bool_or_none( pulsar_client.destination_params.get( "rewrite_parameters", False ) ) or False + def __rewrite_parameters(pulsar_client): + return string_as_bool_or_none(pulsar_client.destination_params.get("rewrite_parameters", False)) or False def __build_metadata_configuration(self, client, job_wrapper, remote_metadata, remote_job_config): metadata_kwds = {} @@ -680,7 +680,7 @@ def __build_metadata_configuration(self, client, job_wrapper, remote_metadata, r default_config_file = os.path.join(remote_galaxy_home, 'config/galaxy.ini') metadata_kwds['config_file'] = remote_system_properties.get('galaxy_config_file', default_config_file) metadata_kwds['dataset_files_path'] = remote_system_properties.get('galaxy_dataset_files_path', None) - if PulsarJobRunner.__use_remote_datatypes_conf( client ): + if PulsarJobRunner.__use_remote_datatypes_conf(client): remote_datatypes_config = remote_system_properties.get('galaxy_datatypes_config_file', None) if not remote_datatypes_config: log.warning(NO_REMOTE_DATATYPES_CONFIG) @@ -695,7 +695,7 @@ def __build_metadata_configuration(self, client, job_wrapper, remote_metadata, r return metadata_kwds -class PulsarLegacyJobRunner( PulsarJobRunner ): +class PulsarLegacyJobRunner(PulsarJobRunner): """Flavor of Pulsar job runner mimicking behavior of old LWR runner.""" destination_defaults = dict( @@ -704,7 +704,7 @@ class PulsarLegacyJobRunner( PulsarJobRunner ): ) -class PulsarMQJobRunner( PulsarJobRunner ): +class PulsarMQJobRunner(PulsarJobRunner): """Flavor of Pulsar job runner with sensible defaults for message queue communication.""" destination_defaults = dict( @@ -716,26 +716,26 @@ class PulsarMQJobRunner( PulsarJobRunner ): private_token=PARAMETER_SPECIFICATION_IGNORED ) - def _monitor( self ): + def _monitor(self): # This is a message queue driven runner, don't monitor # just setup required callback. self.client_manager.ensure_has_status_update_callback(self.__async_update) self.client_manager.ensure_has_ack_consumers() - def __async_update( self, full_status ): + def __async_update(self, full_status): job_id = None try: - job_id = full_status[ "job_id" ] - job, job_wrapper = self.app.job_manager.job_handler.job_queue.job_pair_for_id( job_id ) - job_state = self._job_state( job, job_wrapper ) - self._update_job_state_for_status(job_state, full_status[ "status" ] ) + job_id = full_status["job_id"] + job, job_wrapper = self.app.job_manager.job_handler.job_queue.job_pair_for_id(job_id) + job_state = self._job_state(job, job_wrapper) + self._update_job_state_for_status(job_state, full_status["status"]) except Exception: - log.exception( "Failed to update Pulsar job status for job_id %s", job_id ) + log.exception("Failed to update Pulsar job status for job_id %s", job_id) raise # Nothing else to do? - Attempt to fail the job? -class PulsarRESTJobRunner( PulsarJobRunner ): +class PulsarRESTJobRunner(PulsarJobRunner): """Flavor of Pulsar job runner with sensible defaults for RESTful usage.""" destination_defaults = dict( @@ -761,9 +761,9 @@ class PulsarEmbeddedJobRunner(PulsarJobRunner): default_build_pulsar_app = True -class PulsarComputeEnvironment( ComputeEnvironment ): +class PulsarComputeEnvironment(ComputeEnvironment): - def __init__( self, pulsar_client, job_wrapper, remote_job_config ): + def __init__(self, pulsar_client, job_wrapper, remote_job_config): self.pulsar_client = pulsar_client self.job_wrapper = job_wrapper self.local_path_config = job_wrapper.default_compute_environment() @@ -773,71 +773,71 @@ def __init__( self, pulsar_client, job_wrapper, remote_job_config ): self._wrapper_input_paths = self.local_path_config.input_paths() self._wrapper_output_paths = self.local_path_config.output_paths() self.path_mapper = PathMapper(pulsar_client, remote_job_config, self.local_path_config.working_directory()) - self._config_directory = remote_job_config[ "configs_directory" ] - self._working_directory = remote_job_config[ "working_directory" ] - self._sep = remote_job_config[ "system_properties" ][ "separator" ] - self._tool_dir = remote_job_config[ "tools_directory" ] + self._config_directory = remote_job_config["configs_directory"] + self._working_directory = remote_job_config["working_directory"] + self._sep = remote_job_config["system_properties"]["separator"] + self._tool_dir = remote_job_config["tools_directory"] version_path = self.local_path_config.version_path() new_version_path = self.path_mapper.remote_version_path_rewrite(version_path) if new_version_path: version_path = new_version_path self._version_path = version_path - def output_paths( self ): + def output_paths(self): local_output_paths = self._wrapper_output_paths results = [] for local_output_path in local_output_paths: - wrapper_path = str( local_output_path ) - remote_path = self.path_mapper.remote_output_path_rewrite( wrapper_path ) - results.append( self._dataset_path( local_output_path, remote_path ) ) + wrapper_path = str(local_output_path) + remote_path = self.path_mapper.remote_output_path_rewrite(wrapper_path) + results.append(self._dataset_path(local_output_path, remote_path)) return results - def input_paths( self ): + def input_paths(self): local_input_paths = self._wrapper_input_paths results = [] for local_input_path in local_input_paths: - wrapper_path = str( local_input_path ) + wrapper_path = str(local_input_path) # This will over-copy in some cases. For instance in the case of task # splitting, this input will be copied even though only the work dir # input will actually be used. - remote_path = self.path_mapper.remote_input_path_rewrite( wrapper_path ) - results.append( self._dataset_path( local_input_path, remote_path ) ) + remote_path = self.path_mapper.remote_input_path_rewrite(wrapper_path) + results.append(self._dataset_path(local_input_path, remote_path)) return results - def _dataset_path( self, local_dataset_path, remote_path ): + def _dataset_path(self, local_dataset_path, remote_path): remote_extra_files_path = None if remote_path: - remote_extra_files_path = "%s_files" % remote_path[ 0:-len( ".dat" ) ] - return local_dataset_path.with_path_for_job( remote_path, remote_extra_files_path ) + remote_extra_files_path = "%s_files" % remote_path[0:-len(".dat")] + return local_dataset_path.with_path_for_job(remote_path, remote_extra_files_path) - def working_directory( self ): + def working_directory(self): return self._working_directory - def config_directory( self ): + def config_directory(self): return self._config_directory - def new_file_path( self ): + def new_file_path(self): return self.working_directory() # Problems with doing this? - def sep( self ): + def sep(self): return self._sep - def version_path( self ): + def version_path(self): return self._version_path - def rewriter( self, parameter_value ): + def rewriter(self, parameter_value): unstructured_path_rewrites = self.unstructured_path_rewrites if parameter_value in unstructured_path_rewrites: # Path previously mapped, use previous mapping. - return unstructured_path_rewrites[ parameter_value ] + return unstructured_path_rewrites[parameter_value] if parameter_value in unstructured_path_rewrites.values(): # Path is a rewritten remote path (this might never occur, # consider dropping check...) return parameter_value - rewrite, new_unstructured_path_rewrites = self.path_mapper.check_for_arbitrary_rewrite( parameter_value ) + rewrite, new_unstructured_path_rewrites = self.path_mapper.check_for_arbitrary_rewrite(parameter_value) if rewrite: unstructured_path_rewrites.update(new_unstructured_path_rewrites) return rewrite @@ -845,10 +845,10 @@ def rewriter( self, parameter_value ): # Did need to rewrite, use original path or value. return parameter_value - def unstructured_path_rewriter( self ): + def unstructured_path_rewriter(self): return self.rewriter - def tool_directory( self ): + def tool_directory(self): return self._tool_dir diff --git a/lib/galaxy/jobs/runners/slurm.py b/lib/galaxy/jobs/runners/slurm.py index 3aa175f1f71b..1b9ca47e59b9 100644 --- a/lib/galaxy/jobs/runners/slurm.py +++ b/lib/galaxy/jobs/runners/slurm.py @@ -9,9 +9,9 @@ from galaxy import model from galaxy.jobs.runners.drmaa import DRMAAJobRunner -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -__all__ = ( 'SlurmJobRunner', ) +__all__ = ('SlurmJobRunner', ) # Error message printed to job stderr when SLURM itself kills a job. # See src/common/slurm_jobacct_gather.c and src/slurmd/slurmd/req.c in @@ -27,24 +27,24 @@ SLURM_MEMORY_LIMIT_SCAN_SIZE = 16 * 1024 * 1024 # 16MB -class SlurmJobRunner( DRMAAJobRunner ): +class SlurmJobRunner(DRMAAJobRunner): runner_name = "SlurmRunner" restrict_job_name_length = False - def _complete_terminal_job( self, ajs, drmaa_state, **kwargs ): + def _complete_terminal_job(self, ajs, drmaa_state, **kwargs): def _get_slurm_state_with_sacct(job_id, cluster): cmd = ['sacct', '-n', '-o state'] if cluster: - cmd.extend( [ '-M', cluster ] ) + cmd.extend(['-M', cluster]) cmd.extend(['-j', job_id]) - p = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = p.communicate() if p.returncode != 0: stderr = stderr.strip() if stderr == 'SLURM accounting storage is disabled': log.warning('SLURM accounting storage is not properly configured, unable to run sacct') return - raise Exception( '`%s` returned %s, stderr: %s' % ( ' '.join( cmd ), p.returncode, stderr ) ) + raise Exception('`%s` returned %s, stderr: %s' % (' '.join(cmd), p.returncode, stderr)) # First line is for 'job_id' # Second line is for 'job_id.batch' (only available after the batch job is complete) # Following lines are for the steps 'job_id.0', 'job_id.1', ... (but Galaxy does not use steps) @@ -53,16 +53,16 @@ def _get_slurm_state_with_sacct(job_id, cluster): return first_line.strip().rstrip('+') def _get_slurm_state(): - cmd = [ 'scontrol', '-o' ] + cmd = ['scontrol', '-o'] if '.' in ajs.job_id: # custom slurm-drmaa-with-cluster-support job id syntax job_id, cluster = ajs.job_id.split('.', 1) - cmd.extend( [ '-M', cluster ] ) + cmd.extend(['-M', cluster]) else: job_id = ajs.job_id cluster = None - cmd.extend( [ 'show', 'job', job_id ] ) - p = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) + cmd.extend(['show', 'job', job_id]) + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = p.communicate() if p.returncode != 0: # Will need to be more clever here if this message is not consistent @@ -72,8 +72,8 @@ def _get_slurm_state(): if job_state: return job_state return 'NOT_FOUND' - raise Exception( '`%s` returned %s, stderr: %s' % ( ' '.join( cmd ), p.returncode, stderr ) ) - job_info_dict = dict( [ out_param.split( '=', 1 ) for out_param in stdout.split() ] ) + raise Exception('`%s` returned %s, stderr: %s' % (' '.join(cmd), p.returncode, stderr)) + job_info_dict = dict([out_param.split('=', 1) for out_param in stdout.split()]) return job_info_dict['JobState'] try: @@ -81,51 +81,51 @@ def _get_slurm_state(): slurm_state = _get_slurm_state() sleep = 1 while slurm_state == 'COMPLETING': - log.debug( '(%s/%s) Waiting %s seconds for failed job to exit COMPLETING state for post-mortem', ajs.job_wrapper.get_id_tag(), ajs.job_id, sleep ) - time.sleep( sleep ) + log.debug('(%s/%s) Waiting %s seconds for failed job to exit COMPLETING state for post-mortem', ajs.job_wrapper.get_id_tag(), ajs.job_id, sleep) + time.sleep(sleep) sleep *= 2 if sleep > 64: ajs.fail_message = "This job failed and the system timed out while trying to determine the cause of the failure." break slurm_state = _get_slurm_state() if slurm_state == 'NOT_FOUND': - log.warning( '(%s/%s) Job not found, assuming job check exceeded MinJobAge and completing as successful', ajs.job_wrapper.get_id_tag(), ajs.job_id ) + log.warning('(%s/%s) Job not found, assuming job check exceeded MinJobAge and completing as successful', ajs.job_wrapper.get_id_tag(), ajs.job_id) drmaa_state = self.drmaa_job_states.DONE elif slurm_state == 'COMPLETED': log.debug("(%s/%s) SLURM reported job success, assuming job check exceeded MinJobAge and completing as successful", ajs.job_wrapper.get_id_tag(), ajs.job_id) drmaa_state = self.drmaa_job_states.DONE elif slurm_state == 'TIMEOUT': - log.info( '(%s/%s) Job hit walltime', ajs.job_wrapper.get_id_tag(), ajs.job_id ) + log.info('(%s/%s) Job hit walltime', ajs.job_wrapper.get_id_tag(), ajs.job_id) ajs.fail_message = "This job was terminated because it ran longer than the maximum allowed job run time." ajs.runner_state = ajs.runner_states.WALLTIME_REACHED elif slurm_state == 'NODE_FAIL': - log.warning( '(%s/%s) Job failed due to node failure, attempting resubmission', ajs.job_wrapper.get_id_tag(), ajs.job_id ) - ajs.job_wrapper.change_state( model.Job.states.QUEUED, info='Job was resubmitted due to node failure' ) + log.warning('(%s/%s) Job failed due to node failure, attempting resubmission', ajs.job_wrapper.get_id_tag(), ajs.job_id) + ajs.job_wrapper.change_state(model.Job.states.QUEUED, info='Job was resubmitted due to node failure') try: - self.queue_job( ajs.job_wrapper ) + self.queue_job(ajs.job_wrapper) return except: ajs.fail_message = "This job failed due to a cluster node failure, and an attempt to resubmit the job failed." elif slurm_state == 'CANCELLED': # Check to see if the job was killed for exceeding memory consumption - check_memory_limit_msg = self.__check_memory_limit( ajs.error_file ) + check_memory_limit_msg = self.__check_memory_limit(ajs.error_file) if check_memory_limit_msg: - log.info( '(%s/%s) Job hit memory limit', ajs.job_wrapper.get_id_tag(), ajs.job_id ) + log.info('(%s/%s) Job hit memory limit', ajs.job_wrapper.get_id_tag(), ajs.job_id) ajs.fail_message = check_memory_limit_msg ajs.runner_state = ajs.runner_states.MEMORY_LIMIT_REACHED else: - log.info( '(%s/%s) Job was cancelled via SLURM (e.g. with scancel(1))', ajs.job_wrapper.get_id_tag(), ajs.job_id ) + log.info('(%s/%s) Job was cancelled via SLURM (e.g. with scancel(1))', ajs.job_wrapper.get_id_tag(), ajs.job_id) ajs.fail_message = "This job failed because it was cancelled by an administrator." elif slurm_state in ('PENDING', 'RUNNING'): - log.warning( '(%s/%s) Job was reported by drmaa as terminal but job state in SLURM is: %s, returning to monitor queue', ajs.job_wrapper.get_id_tag(), ajs.job_id, slurm_state ) + log.warning('(%s/%s) Job was reported by drmaa as terminal but job state in SLURM is: %s, returning to monitor queue', ajs.job_wrapper.get_id_tag(), ajs.job_id, slurm_state) return True else: - log.warning( '(%s/%s) Job failed due to unknown reasons, job state in SLURM was: %s', ajs.job_wrapper.get_id_tag(), ajs.job_id, slurm_state ) + log.warning('(%s/%s) Job failed due to unknown reasons, job state in SLURM was: %s', ajs.job_wrapper.get_id_tag(), ajs.job_id, slurm_state) ajs.fail_message = "This job failed for reasons that could not be determined." if drmaa_state == self.drmaa_job_states.FAILED: ajs.fail_message += '\nPlease click the bug icon to report this problem if you need help.' ajs.stop_job = False - self.work_queue.put( ( self.fail_job, ajs ) ) + self.work_queue.put((self.fail_job, ajs)) return if drmaa_state == self.drmaa_job_states.DONE: with open(ajs.error_file, 'r+') as f: @@ -138,23 +138,23 @@ def _get_slurm_state(): for line in lines: stripped_line = line.strip() if any(_ in stripped_line for _ in SLURM_MEMORY_LIMIT_EXCEEDED_PARTIAL_WARNINGS): - log.debug( '(%s/%s) Job completed, removing SLURM exceeded memory warning: "%s"', ajs.job_wrapper.get_id_tag(), ajs.job_id, stripped_line ) + log.debug('(%s/%s) Job completed, removing SLURM exceeded memory warning: "%s"', ajs.job_wrapper.get_id_tag(), ajs.job_id, stripped_line) else: f.write(line) f.truncate() except Exception: - log.exception( '(%s/%s) Failure in SLURM _complete_terminal_job(), job final state will be: %s', ajs.job_wrapper.get_id_tag(), ajs.job_id, drmaa_state ) + log.exception('(%s/%s) Failure in SLURM _complete_terminal_job(), job final state will be: %s', ajs.job_wrapper.get_id_tag(), ajs.job_id, drmaa_state) # by default, finish the job with the state from drmaa - return super( SlurmJobRunner, self )._complete_terminal_job( ajs, drmaa_state=drmaa_state ) + return super(SlurmJobRunner, self)._complete_terminal_job(ajs, drmaa_state=drmaa_state) - def __check_memory_limit( self, efile_path ): + def __check_memory_limit(self, efile_path): """ A very poor implementation of tail, but it doesn't need to be fancy since we are only searching the last 2K """ try: - log.debug( 'Checking %s for exceeded memory message from SLURM', efile_path ) - with open( efile_path ) as f: + log.debug('Checking %s for exceeded memory message from SLURM', efile_path) + with open(efile_path) as f: if os.path.getsize(efile_path) > 2048: f.seek(-2048, os.SEEK_END) f.readline() diff --git a/lib/galaxy/jobs/runners/state_handlers/_safe_eval.py b/lib/galaxy/jobs/runners/state_handlers/_safe_eval.py index 83480c3581cd..de68e599d355 100644 --- a/lib/galaxy/jobs/runners/state_handlers/_safe_eval.py +++ b/lib/galaxy/jobs/runners/state_handlers/_safe_eval.py @@ -16,7 +16,7 @@ BUILTIN_AND_MATH_FUNCTIONS = 'abs|all|any|bin|chr|cmp|complex|divmod|float|hex|int|len|long|max|min|oct|ord|pow|range|reversed|round|sorted|str|sum|type|unichr|unicode|log|exp|sqrt|ceil|floor'.split('|') -STRING_AND_LIST_METHODS = [ name for name in dir('') + dir([]) if not name.startswith('_') ] +STRING_AND_LIST_METHODS = [name for name in dir('') + dir([]) if not name.startswith('_')] VALID_FUNCTIONS = BUILTIN_AND_MATH_FUNCTIONS + STRING_AND_LIST_METHODS @@ -28,14 +28,14 @@ def _check_name(ast_node, allowed_variables=[]): return name in VALID_FUNCTIONS -def _check_attribute( ast_node ): +def _check_attribute(ast_node): attribute_name = ast_node.attr if attribute_name not in STRING_AND_LIST_METHODS: return False return True -def _check_call( ast_node ): +def _check_call(ast_node): # If we are calling a function or method, it better be a math, # string or list function. ast_func = ast_node.func @@ -44,7 +44,7 @@ def _check_call( ast_node ): if ast_func.id not in BUILTIN_AND_MATH_FUNCTIONS: return False elif ast_func_class == 'Attribute': - if not _check_attribute( ast_func ): + if not _check_attribute(ast_func): return False else: return False diff --git a/lib/galaxy/jobs/runners/state_handlers/resubmit.py b/lib/galaxy/jobs/runners/state_handlers/resubmit.py index 788f13b724c5..6ded2f6eb4fe 100644 --- a/lib/galaxy/jobs/runners/state_handlers/resubmit.py +++ b/lib/galaxy/jobs/runners/state_handlers/resubmit.py @@ -102,7 +102,7 @@ def _handle_resubmit_definitions(resubmit_definitions, app, job_runner, job_stat job_log_prefix, destination, MESSAGES[runner_state], - job_state.job_wrapper.job_destination.id ) + job_state.job_wrapper.job_destination.id) # fetch JobDestination for the id or tag if destination: new_destination = app.job_config.get_destination(destination) @@ -121,7 +121,7 @@ def _handle_resubmit_definitions(resubmit_definitions, app, job_runner, job_stat job_log_prefix, resubmit['handler']) job.set_handler(resubmit['handler']) - job_runner.sa_session.add( job ) + job_runner.sa_session.add(job) # Is this safe to do here? job_runner.sa_session.flush() # Cache the destination to prevent rerunning dynamic after diff --git a/lib/galaxy/jobs/runners/tasks.py b/lib/galaxy/jobs/runners/tasks.py index 400ad1a19ec7..5362e7254f08 100644 --- a/lib/galaxy/jobs/runners/tasks.py +++ b/lib/galaxy/jobs/runners/tasks.py @@ -7,25 +7,25 @@ from galaxy.jobs import TaskWrapper from galaxy.jobs.runners import BaseJobRunner -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -__all__ = ( 'TaskedJobRunner', ) +__all__ = ('TaskedJobRunner', ) -class TaskedJobRunner( BaseJobRunner ): +class TaskedJobRunner(BaseJobRunner): """ Job runner backed by a finite pool of worker threads. FIFO scheduling """ runner_name = "TaskRunner" - def __init__( self, app, nworkers ): + def __init__(self, app, nworkers): """Start the job runner with 'nworkers' worker threads""" - super( TaskedJobRunner, self ).__init__( app, nworkers ) + super(TaskedJobRunner, self).__init__(app, nworkers) self._init_worker_threads() - def queue_job( self, job_wrapper ): + def queue_job(self, job_wrapper): # prepare the job - if not self.prepare_job( job_wrapper ): + if not self.prepare_job(job_wrapper): return # command line has been added to the wrapper by prepare_job() @@ -46,14 +46,14 @@ def queue_job( self, job_wrapper ): job_exit_code = None try: - job_wrapper.change_state( model.Job.states.RUNNING ) + job_wrapper.change_state(model.Job.states.RUNNING) self.sa_session.flush() # Split with the defined method. parallelism = job_wrapper.get_parallelism() try: splitter = getattr(__import__('galaxy.jobs.splitters', globals(), locals(), [parallelism.method]), parallelism.method) except: - job_wrapper.change_state( model.Job.states.ERROR ) + job_wrapper.change_state(model.Job.states.ERROR) job_wrapper.fail("Job Splitting Failed, no match for '%s'" % parallelism) return tasks = splitter.do_split(job_wrapper) @@ -75,9 +75,9 @@ def queue_job( self, job_wrapper ): sleep_time = 1 # sleep/loop until no more progress can be made. That is when # all tasks are one of { OK, ERROR, DELETED }. If a task - completed_states = [ model.Task.states.OK, - model.Task.states.ERROR, - model.Task.states.DELETED ] + completed_states = [model.Task.states.OK, + model.Task.states.ERROR, + model.Task.states.DELETED] # TODO: Should we report an error (and not merge outputs) if # one of the subtasks errored out? Should we prevent any that @@ -94,11 +94,11 @@ def queue_job( self, job_wrapper ): tasks_complete = True for tw in task_wrappers: task_state = tw.get_state() - if ( model.Task.states.ERROR == task_state ): + if (model.Task.states.ERROR == task_state): job_exit_code = tw.get_exit_code() - log.debug( "Canceling job %d: Task %s returned an error" - % ( tw.job_id, tw.task_id ) ) - self._cancel_job( job_wrapper, task_wrappers ) + log.debug("Canceling job %d: Task %s returned an error" + % (tw.job_id, tw.task_id)) + self._cancel_job(job_wrapper, task_wrappers) tasks_complete = True break elif task_state not in completed_states: @@ -107,37 +107,37 @@ def queue_job( self, job_wrapper ): job_exit_code = tw.get_exit_code() count_complete = count_complete + 1 if tasks_complete is False: - sleep( sleep_time ) + sleep(sleep_time) if sleep_time < 8: sleep_time *= 2 job_wrapper.reclaim_ownership() # if running as the actual user, change ownership before merging. log.debug('execution finished - beginning merge: %s' % command_line) stdout, stderr = splitter.do_merge(job_wrapper, task_wrappers) except Exception: - job_wrapper.fail( "failure running job", exception=True ) + job_wrapper.fail("failure running job", exception=True) log.exception("failure running job %d", job_wrapper.job_id) return # run the metadata setting script here # this is terminate-able when output dataset/job is deleted # so that long running set_meta()s can be canceled without having to reboot the server - self._handle_metadata_externally(job_wrapper, resolve_requirements=True ) + self._handle_metadata_externally(job_wrapper, resolve_requirements=True) # Finish the job try: - job_wrapper.finish( stdout, stderr, job_exit_code ) + job_wrapper.finish(stdout, stderr, job_exit_code) except: log.exception("Job wrapper finish method failed") job_wrapper.fail("Unable to finish job", exception=True) - def stop_job( self, job ): + def stop_job(self, job): # We need to stop all subtasks. This is going to stay in the task # runner because the task runner also starts all the tasks. # First, get the list of tasks from job.tasks, which uses SQL # alchemy to retrieve a job's list of tasks. tasks = job.get_tasks() - if ( len( tasks ) > 0 ): + if (len(tasks) > 0): for task in tasks: - log.debug( "Killing task's job " + str(task.get_id()) ) + log.debug("Killing task's job " + str(task.get_id())) self.app.job_manager.job_handler.dispatcher.stop(task) # There were no subtasks, so just kill the job. We'll touch @@ -149,16 +149,16 @@ def stop_job( self, job ): pid = job.external_output_metadata[0].job_runner_external_pid # every JobExternalOutputMetadata has a pid set, we just need to take from one of them else: pid = job.job_runner_external_id - if pid in [ None, '' ]: - log.warning( "stop_job(): %s: no PID in database for job, unable to stop" % job.id ) + if pid in [None, '']: + log.warning("stop_job(): %s: no PID in database for job, unable to stop" % job.id) return - self._stop_pid( pid, job.id ) + self._stop_pid(pid, job.id) - def recover( self, job, job_wrapper ): + def recover(self, job, job_wrapper): # DBTODO Task Recovery, this should be possible. - job_wrapper.change_state( model.Job.states.ERROR, info="This job was killed when Galaxy was restarted. Please retry the job." ) + job_wrapper.change_state(model.Job.states.ERROR, info="This job was killed when Galaxy was restarted. Please retry the job.") - def _cancel_job( self, job_wrapper, task_wrappers ): + def _cancel_job(self, job_wrapper, task_wrappers): """ Cancel the given job. The job's state will be set to ERROR. Any running tasks will be cancelled, and any queued/pending @@ -166,7 +166,7 @@ def _cancel_job( self, job_wrapper, task_wrappers ): to run those tasks. """ job = job_wrapper.get_job() - job.set_state( model.Job.states.ERROR ) + job.set_state(model.Job.states.ERROR) # For every task (except the one that already had an error) # - If the task is queued, then mark it as deleted @@ -186,10 +186,10 @@ def _cancel_job( self, job_wrapper, task_wrappers ): for task_wrapper in task_wrappers: task = task_wrapper.get_task() task_state = task.get_state() - if ( model.Task.states.QUEUED == task_state ): - log.debug( "_cancel_job for job %d: Task %d is not running; setting state to DELETED" - % ( job.get_id(), task.get_id() ) ) - task_wrapper.change_state( task.states.DELETED ) + if (model.Task.states.QUEUED == task_state): + log.debug("_cancel_job for job %d: Task %d is not running; setting state to DELETED" + % (job.get_id(), task.get_id())) + task_wrapper.change_state(task.states.DELETED) # If a task failed, then the caller will have waited a few seconds # before recognizing the failure. In that time, a queued task could # have been picked up by a runner but not marked as running. @@ -197,50 +197,50 @@ def _cancel_job( self, job_wrapper, task_wrappers ): # are running. sleep(5) for task_wrapper in task_wrappers: - if ( model.Task.states.RUNNING == task_wrapper.get_state() ): + if (model.Task.states.RUNNING == task_wrapper.get_state()): task = task_wrapper.get_task() - log.debug( "_cancel_job for job %d: Stopping running task %d" - % ( job.get_id(), task.get_id() ) ) - job_wrapper.app.job_manager.job_handler.dispatcher.stop( task ) + log.debug("_cancel_job for job %d: Stopping running task %d" + % (job.get_id(), task.get_id())) + job_wrapper.app.job_manager.job_handler.dispatcher.stop(task) - def _check_pid( self, pid ): + def _check_pid(self, pid): # DBTODO Need to check all subtask pids and return some sort of cumulative result. return True try: - os.kill( pid, 0 ) + os.kill(pid, 0) return True except OSError as e: if e.errno == errno.ESRCH: - log.debug( "_check_pid(): PID %d is dead" % pid ) + log.debug("_check_pid(): PID %d is dead" % pid) else: - log.warning( "_check_pid(): Got errno %s when attempting to check PID %d: %s" % ( errno.errorcode[e.errno], pid, e.strerror ) ) + log.warning("_check_pid(): Got errno %s when attempting to check PID %d: %s" % (errno.errorcode[e.errno], pid, e.strerror)) return False - def _stop_pid( self, pid, job_id ): + def _stop_pid(self, pid, job_id): """ This method stops the given process id whether it's a task or job. It is meant to be a private helper method, but it is mostly reusable. The first argument is the process id to stop, and the second id is the job's id (which is used for logging messages only right now). """ - pid = int( pid ) - log.debug( "Stopping pid %s" % pid ) - if not self._check_pid( pid ): - log.warning( "_stop_pid(): %s: PID %d was already dead or can't be signaled" % ( job_id, pid ) ) + pid = int(pid) + log.debug("Stopping pid %s" % pid) + if not self._check_pid(pid): + log.warning("_stop_pid(): %s: PID %d was already dead or can't be signaled" % (job_id, pid)) return - for sig in [ 15, 9 ]: + for sig in [15, 9]: try: - os.killpg( pid, sig ) + os.killpg(pid, sig) except OSError as e: # This warning could be bogus; many tasks are stopped with # SIGTERM (signal 15), but ymmv depending on the platform. - log.warning( "_stop_pid(): %s: Got errno %s when attempting to signal %d to PID %d: %s" % ( job_id, errno.errorcode[e.errno], sig, pid, e.strerror ) ) + log.warning("_stop_pid(): %s: Got errno %s when attempting to signal %d to PID %d: %s" % (job_id, errno.errorcode[e.errno], sig, pid, e.strerror)) return # TODO: If we're stopping lots of tasks, then we will want to put this # avoid a two-second overhead using some other asynchronous method. - sleep( 2 ) - if not self._check_pid( pid ): - log.debug( "_stop_pid(): %s: PID %d successfully killed with signal %d" % ( job_id, pid, sig ) ) + sleep(2) + if not self._check_pid(pid): + log.debug("_stop_pid(): %s: PID %d successfully killed with signal %d" % (job_id, pid, sig)) return else: - log.warning( "_stop_pid(): %s: PID %d refuses to die after signaling TERM/KILL" % ( job_id, pid ) ) + log.warning("_stop_pid(): %s: PID %d refuses to die after signaling TERM/KILL" % (job_id, pid)) diff --git a/lib/galaxy/jobs/runners/util/cli/__init__.py b/lib/galaxy/jobs/runners/util/cli/__init__.py index b16ca63b67f1..446c21e6d4ca 100644 --- a/lib/galaxy/jobs/runners/util/cli/__init__.py +++ b/lib/galaxy/jobs/runners/util/cli/__init__.py @@ -39,6 +39,7 @@ def __load(module_path, d): self.cli_shells = {} self.cli_job_interfaces = {} + self.active_cli_shells = {} module_prefix = self.__module__ __load('%s.shell' % module_prefix, self.cli_shells) @@ -55,8 +56,9 @@ def get_plugins(self, shell_params, job_params): def get_shell_plugin(self, shell_params): shell_plugin = shell_params.get('plugin', DEFAULT_SHELL_PLUGIN) - shell = self.cli_shells[shell_plugin](**shell_params) - return shell + if shell_plugin not in self.active_cli_shells: + self.active_cli_shells[shell_plugin] = self.cli_shells[shell_plugin](**shell_params) + return self.active_cli_shells[shell_plugin] def get_job_interface(self, job_params): job_plugin = job_params.get('plugin', None) diff --git a/lib/galaxy/jobs/runners/util/cli/shell/local.py b/lib/galaxy/jobs/runners/util/cli/shell/local.py index 4e610470bdf7..a5b570abaae0 100644 --- a/lib/galaxy/jobs/runners/util/cli/shell/local.py +++ b/lib/galaxy/jobs/runners/util/cli/shell/local.py @@ -48,6 +48,7 @@ def execute(self, cmd, persist=False, timeout=DEFAULT_TIMEOUT, timeout_check_int # poll until timeout for i in range(int(timeout / timeout_check_interval)): + sleep(0.1) # For fast returning commands r = p.poll() if r is not None: break diff --git a/lib/galaxy/jobs/runners/util/cli/shell/rsh.py b/lib/galaxy/jobs/runners/util/cli/shell/rsh.py index ecfffd0f2843..204e44ac38aa 100644 --- a/lib/galaxy/jobs/runners/util/cli/shell/rsh.py +++ b/lib/galaxy/jobs/runners/util/cli/shell/rsh.py @@ -1,10 +1,14 @@ -from logging import getLogger +import logging + +import paramiko from .local import LocalShell +from ....util import Bunch -log = getLogger(__name__) +log = logging.getLogger(__name__) +logging.getLogger("paramiko").setLevel(logging.WARNING) # paramiko logging is very verbose -__all__ = ('RemoteShell', 'SecureShell', 'GlobusSecureShell') +__all__ = ('RemoteShell', 'SecureShell', 'GlobusSecureShell', 'ParamikoShell') class RemoteShell(LocalShell): @@ -29,12 +33,43 @@ def execute(self, cmd, persist=False, timeout=60): class SecureShell(RemoteShell): SSH_NEW_KEY_STRING = 'Are you sure you want to continue connecting' - def __init__(self, rsh='ssh', rcp='scp', **kwargs): - rsh += ' -oStrictHostKeyChecking=yes -oConnectTimeout=60' - rcp += ' -oStrictHostKeyChecking=yes -oConnectTimeout=60' + def __init__(self, rsh='ssh', rcp='scp', private_key=None, port=None, strict_host_key_checking=True, **kwargs): + strict_host_key_checking = "yes" if strict_host_key_checking else "no" + rsh += " -oStrictHostKeyChecking=%s -oConnectTimeout=60" % strict_host_key_checking + rcp += " -oStrictHostKeyChecking=%s -oConnectTimeout=60" % strict_host_key_checking + if private_key: + rsh += " -i %s" % private_key + rcp += " -i %s" % private_key + if port: + rsh += " -p %s" % port + rcp += " -p %s" % port super(SecureShell, self).__init__(rsh=rsh, rcp=rcp, **kwargs) +class ParamikoShell(object): + + def __init__(self, username, hostname, password=None, private_key=None, port=22, timeout=60, **kwargs): + self.username = username + self.hostname = hostname + self.password = password + self.private_key = private_key + self.port = int(port) if port else port + self.timeout = int(timeout) if timeout else timeout + self.ssh = paramiko.SSHClient() + self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + self.ssh.connect(hostname=self.hostname, + port=self.port, + username=self.username, + password=self.password, + key_filename=self.private_key, + timeout=self.timeout) + + def execute(self, cmd, timeout=60): + _, stdout, stderr = self.ssh.exec_command(cmd, timeout=timeout) + return_code = stdout.channel.recv_exit_status() + return Bunch(stdout=stdout.read(), stderr=stderr.read(), returncode=return_code) + + class GlobusSecureShell(SecureShell): def __init__(self, rsh='gsissh', rcp='gsiscp', **kwargs): diff --git a/lib/galaxy/jobs/runners/util/drmaa/__init__.py b/lib/galaxy/jobs/runners/util/drmaa/__init__.py index 0a8da7a0e826..47eb30b39962 100644 --- a/lib/galaxy/jobs/runners/util/drmaa/__init__.py +++ b/lib/galaxy/jobs/runners/util/drmaa/__init__.py @@ -11,6 +11,7 @@ class DrmaaSessionFactory(object): """ Abstraction used to production DrmaaSession wrappers. """ + def __init__(self): self.session_constructor = Session diff --git a/lib/galaxy/jobs/splitters/basic.py b/lib/galaxy/jobs/splitters/basic.py index 7ddd806e1280..9ab2e0674e21 100644 --- a/lib/galaxy/jobs/splitters/basic.py +++ b/lib/galaxy/jobs/splitters/basic.py @@ -2,7 +2,7 @@ from . import multi -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) def set_basic_defaults(job_wrapper): @@ -15,13 +15,13 @@ def set_basic_defaults(job_wrapper): def do_split(job_wrapper): if len(job_wrapper.get_input_fnames()) > 1 or len(job_wrapper.get_output_fnames()) > 1: log.error("The basic splitter is not capable of handling jobs with multiple inputs or outputs.") - raise Exception( "Job Splitting Failed, the basic splitter only handles tools with one input and one output" ) + raise Exception("Job Splitting Failed, the basic splitter only handles tools with one input and one output") # add in the missing information for splitting the one input and merging the one output set_basic_defaults(job_wrapper) return multi.do_split(job_wrapper) -def do_merge( job_wrapper, task_wrappers): +def do_merge(job_wrapper, task_wrappers): # add in the missing information for splitting the one input and merging the one output set_basic_defaults(job_wrapper) return multi.do_merge(job_wrapper, task_wrappers) diff --git a/lib/galaxy/jobs/splitters/multi.py b/lib/galaxy/jobs/splitters/multi.py index 4be3b2d0b25c..3a60663c6672 100644 --- a/lib/galaxy/jobs/splitters/multi.py +++ b/lib/galaxy/jobs/splitters/multi.py @@ -6,7 +6,7 @@ from galaxy import model, util -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) def do_split(job_wrapper): @@ -31,7 +31,7 @@ def do_split(job_wrapper): shared_inputs = [x.strip() for x in shared_inputs.split(",")] illegal_inputs = [x for x in shared_inputs if x in split_inputs] if len(illegal_inputs) > 0: - raise Exception("Inputs have conflicting parallelism attributes: %s" % str( illegal_inputs )) + raise Exception("Inputs have conflicting parallelism attributes: %s" % str(illegal_inputs)) subdir_index = [0] # use a list to get around Python 2.x lame closure support task_dirs = [] @@ -108,7 +108,7 @@ def get_new_working_directory_name(): return tasks -def do_merge( job_wrapper, task_wrappers): +def do_merge(job_wrapper, task_wrappers): parallel_settings = job_wrapper.get_parallelism().attributes # Syntax: merge_outputs="export" pickone_outputs="genomesize" # Designates outputs to be merged, or selected from as a representative @@ -125,7 +125,7 @@ def do_merge( job_wrapper, task_wrappers): illegal_outputs = [x for x in merge_outputs if x in pickone_outputs] if len(illegal_outputs) > 0: - return ('Tool file error', 'Outputs have conflicting parallelism attributes: %s' % str( illegal_outputs )) + return ('Tool file error', 'Outputs have conflicting parallelism attributes: %s' % str(illegal_outputs)) stdout = '' stderr = '' @@ -140,8 +140,8 @@ def do_merge( job_wrapper, task_wrappers): pickone_done = [] task_dirs = [os.path.join(working_directory, x) for x in os.listdir(working_directory) if x.startswith('task_')] task_dirs.sort(key=lambda x: int(x.split('task_')[-1])) - for index, output in enumerate( outputs ): - output_file_name = str( output_paths[ index ] ) # Use false_path if set, else real path. + for index, output in enumerate(outputs): + output_file_name = str(output_paths[index]) # Use false_path if set, else real path. base_output_name = os.path.basename(output_file_name) if output in merge_outputs: output_dataset = outputs[output][0] @@ -149,7 +149,7 @@ def do_merge( job_wrapper, task_wrappers): output_files = [os.path.join(dir, base_output_name) for dir in task_dirs] # Just include those files f in the output list for which the # file f exists; some files may not exist if a task fails. - output_files = [ f for f in output_files if os.path.exists(f) ] + output_files = [f for f in output_files if os.path.exists(f)] if output_files: log.debug('files %s ' % output_files) if len(output_files) < len(task_dirs): @@ -157,7 +157,7 @@ def do_merge( job_wrapper, task_wrappers): % (len(output_files), len(task_dirs), output_file_name)) # First two args to merge always output_files and path of dataset. More # complicated merge methods may require more parameters. Set those up here. - extra_merge_arg_names = inspect.getargspec( output_type.merge ).args[2:] + extra_merge_arg_names = inspect.getargspec(output_type.merge).args[2:] extra_merge_args = {} if "output_dataset" in extra_merge_arg_names: extra_merge_args["output_dataset"] = output_dataset @@ -172,7 +172,7 @@ def do_merge( job_wrapper, task_wrappers): # just pick one of them if output not in pickone_done: task_file_name = os.path.join(task_dirs[0], base_output_name) - shutil.move( task_file_name, output_file_name ) + shutil.move(task_file_name, output_file_name) pickone_done.append(output) else: log_error = "The output '%s' does not define a method for implementing parallelism" % output @@ -180,7 +180,7 @@ def do_merge( job_wrapper, task_wrappers): raise Exception(log_error) except Exception as e: stdout = 'Error merging files' - log.exception( stdout ) + log.exception(stdout) stderr = str(e) for tw in task_wrappers: diff --git a/lib/galaxy/jobs/stock_rules.py b/lib/galaxy/jobs/stock_rules.py index 9d1d79b2643d..0fb09c440a38 100644 --- a/lib/galaxy/jobs/stock_rules.py +++ b/lib/galaxy/jobs/stock_rules.py @@ -7,19 +7,19 @@ from galaxy import util -def choose_one( rule_helper, job, destination_ids, hash_by="job" ): - destination_id_list = util.listify( destination_ids ) - job_hash = rule_helper.job_hash( job, hash_by ) - return rule_helper.choose_one( destination_id_list, hash_value=job_hash ) +def choose_one(rule_helper, job, destination_ids, hash_by="job"): + destination_id_list = util.listify(destination_ids) + job_hash = rule_helper.job_hash(job, hash_by) + return rule_helper.choose_one(destination_id_list, hash_value=job_hash) -def burst( rule_helper, job, from_destination_ids, to_destination_id, num_jobs, job_states=None): - from_destination_ids = util.listify( from_destination_ids ) - if rule_helper.should_burst( from_destination_ids, num_jobs=num_jobs, job_states=job_states ): +def burst(rule_helper, job, from_destination_ids, to_destination_id, num_jobs, job_states=None): + from_destination_ids = util.listify(from_destination_ids) + if rule_helper.should_burst(from_destination_ids, num_jobs=num_jobs, job_states=job_states): return to_destination_id else: - return from_destination_ids[ 0 ] + return from_destination_ids[0] -def docker_dispatch( rule_helper, tool, docker_destination_id, default_destination_id ): - return docker_destination_id if rule_helper.supports_docker( tool ) else default_destination_id +def docker_dispatch(rule_helper, tool, docker_destination_id, default_destination_id): + return docker_destination_id if rule_helper.supports_docker(tool) else default_destination_id diff --git a/lib/galaxy/jobs/transfer_manager.py b/lib/galaxy/jobs/transfer_manager.py index ccd5c88e67fb..01acd6aae8f9 100644 --- a/lib/galaxy/jobs/transfer_manager.py +++ b/lib/galaxy/jobs/transfer_manager.py @@ -12,125 +12,126 @@ from galaxy.util import listify, sleeper from galaxy.util.json import jsonrpc_request, validate_jsonrpc_response -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class TransferManager( object ): +class TransferManager(object): """ Manage simple data transfers from URLs to temporary locations. """ - def __init__( self, app ): + + def __init__(self, app): self.app = app self.sa_session = app.model.context.current - self.command = 'python %s' % os.path.abspath( os.path.join( os.getcwd(), 'scripts', 'transfer.py' ) ) - if app.config.get_bool( 'enable_job_recovery', True ): + self.command = 'python %s' % os.path.abspath(os.path.join(os.getcwd(), 'scripts', 'transfer.py')) + if app.config.get_bool('enable_job_recovery', True): # Only one Galaxy server process should be able to recover jobs! (otherwise you'll have nasty race conditions) self.running = True self.sleeper = sleeper.Sleeper() - self.restarter = threading.Thread( target=self.__restarter ) + self.restarter = threading.Thread(target=self.__restarter) self.restarter.start() - def new( self, path=None, **kwd ): + def new(self, path=None, **kwd): if 'protocol' not in kwd: - raise Exception( 'Missing required parameter "protocol".' ) - protocol = kwd[ 'protocol' ] - if protocol in [ 'http', 'https' ]: + raise Exception('Missing required parameter "protocol".') + protocol = kwd['protocol'] + if protocol in ['http', 'https']: if 'url' not in kwd: - raise Exception( 'Missing required parameter "url".' ) + raise Exception('Missing required parameter "url".') elif protocol == 'scp': # TODO: add more checks here? if 'sample_dataset_id' not in kwd: - raise Exception( 'Missing required parameter "sample_dataset_id".' ) + raise Exception('Missing required parameter "sample_dataset_id".') if 'file_path' not in kwd: - raise Exception( 'Missing required parameter "file_path".' ) - transfer_job = self.app.model.TransferJob( state=self.app.model.TransferJob.states.NEW, params=kwd ) - self.sa_session.add( transfer_job ) + raise Exception('Missing required parameter "file_path".') + transfer_job = self.app.model.TransferJob(state=self.app.model.TransferJob.states.NEW, params=kwd) + self.sa_session.add(transfer_job) self.sa_session.flush() return transfer_job - def run( self, transfer_jobs ): + def run(self, transfer_jobs): """ This method blocks, so if invoking the transfer manager ever starts taking too long, we should move it to a thread. However, the transfer_manager will either daemonize or return after submitting to a running daemon, so it should be fairly quick to return. """ - transfer_jobs = listify( transfer_jobs ) - printable_tj_ids = ', '.join( [ str( tj.id ) for tj in transfer_jobs ] ) - log.debug( 'Initiating transfer job(s): %s' % printable_tj_ids ) + transfer_jobs = listify(transfer_jobs) + printable_tj_ids = ', '.join([str(tj.id) for tj in transfer_jobs]) + log.debug('Initiating transfer job(s): %s' % printable_tj_ids) # Set all jobs running before spawning, or else updating the state may # clobber a state change performed by the worker. - [ tj.__setattr__( 'state', tj.states.RUNNING ) for tj in transfer_jobs ] - self.sa_session.add_all( transfer_jobs ) + [tj.__setattr__('state', tj.states.RUNNING) for tj in transfer_jobs] + self.sa_session.add_all(transfer_jobs) self.sa_session.flush() for tj in transfer_jobs: # The transfer script should daemonize fairly quickly - if this is # not the case, this process will need to be moved to a # non-blocking method. - cmd = '%s %s' % ( self.command, tj.id ) - log.debug( 'Transfer command is: %s' % cmd ) - p = subprocess.Popen( cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT ) + cmd = '%s %s' % (self.command, tj.id) + log.debug('Transfer command is: %s' % cmd) + p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() - output = p.stdout.read( 32768 ) + output = p.stdout.read(32768) if p.returncode != 0: - log.error( 'Spawning transfer job failed: %s: %s' % ( tj.id, output ) ) + log.error('Spawning transfer job failed: %s: %s' % (tj.id, output)) tj.state = tj.states.ERROR tj.info = 'Spawning transfer job failed: %s' % output.splitlines()[-1] - self.sa_session.add( tj ) + self.sa_session.add(tj) self.sa_session.flush() - def get_state( self, transfer_jobs, via_socket=False ): - transfer_jobs = listify( transfer_jobs ) + def get_state(self, transfer_jobs, via_socket=False): + transfer_jobs = listify(transfer_jobs) rval = [] for tj in transfer_jobs: if via_socket and tj.state not in tj.terminal_states and tj.socket: try: - request = jsonrpc_request( method='get_state', id=True ) - sock = socket.socket( socket.AF_INET, socket.SOCK_STREAM ) - sock.settimeout( 5 ) - sock.connect( ( 'localhost', tj.socket ) ) - sock.send( json.dumps( request ) ) - response = sock.recv( 8192 ) - valid, response = validate_jsonrpc_response( response, id=request['id'] ) + request = jsonrpc_request(method='get_state', id=True) + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.settimeout(5) + sock.connect(('localhost', tj.socket)) + sock.send(json.dumps(request)) + response = sock.recv(8192) + valid, response = validate_jsonrpc_response(response, id=request['id']) if not valid: # No valid response received, make some pseudo-json-rpc - raise Exception( dict( code=128, message='Did not receive valid response from transfer daemon for state' ) ) + raise Exception(dict(code=128, message='Did not receive valid response from transfer daemon for state')) if 'error' in response: # Response was valid but Request resulted in an error - raise Exception( response['error']) + raise Exception(response['error']) else: # Request was valid response['result']['transfer_job_id'] = tj.id - rval.append( response['result'] ) + rval.append(response['result']) except Exception as e: # State checking via the transfer daemon failed, just # return the state from the database instead. Callers can # look for the 'error' member of the response to see why # the check failed. - self.sa_session.refresh( tj ) + self.sa_session.refresh(tj) error = e.args - if type( error ) != dict: - error = dict( code=256, message='Error connecting to transfer daemon', data=str( e ) ) - rval.append( dict( transfer_job_id=tj.id, state=tj.state, error=error ) ) + if type(error) != dict: + error = dict(code=256, message='Error connecting to transfer daemon', data=str(e)) + rval.append(dict(transfer_job_id=tj.id, state=tj.state, error=error)) else: - self.sa_session.refresh( tj ) - rval.append( dict( transfer_job_id=tj.id, state=tj.state ) ) + self.sa_session.refresh(tj) + rval.append(dict(transfer_job_id=tj.id, state=tj.state)) for tj_state in rval: if tj_state['state'] in self.app.model.TransferJob.terminal_states: - log.debug( 'Transfer job %s is in terminal state: %s' % ( tj_state['transfer_job_id'], tj_state['state'] ) ) + log.debug('Transfer job %s is in terminal state: %s' % (tj_state['transfer_job_id'], tj_state['state'])) elif tj_state['state'] == self.app.model.TransferJob.states.PROGRESS and 'percent' in tj_state: - log.debug( 'Transfer job %s is %s%% complete' % ( tj_state[ 'transfer_job_id' ], tj_state[ 'percent' ] ) ) - if len( rval ) == 1: + log.debug('Transfer job %s is %s%% complete' % (tj_state['transfer_job_id'], tj_state['percent'])) + if len(rval) == 1: return rval[0] return rval - def __restarter( self ): - log.info( 'Transfer job restarter starting up...' ) + def __restarter(self): + log.info('Transfer job restarter starting up...') while self.running: dead = [] self.sa_session.expunge_all() # our session is threadlocal so this is safe. - for tj in self.sa_session.query( self.app.model.TransferJob ) \ - .filter( self.app.model.TransferJob.state == self.app.model.TransferJob.states.RUNNING ): + for tj in self.sa_session.query(self.app.model.TransferJob) \ + .filter(self.app.model.TransferJob.state == self.app.model.TransferJob.states.RUNNING): if not tj.pid: continue # This will only succeed if the process exists and is owned by the @@ -143,17 +144,17 @@ def __restarter( self ): # it becomes a problem, try to talk to the socket a few times and # restart the transfer if socket communication fails repeatedly. try: - os.kill( tj.pid, 0 ) + os.kill(tj.pid, 0) except: - self.sa_session.refresh( tj ) + self.sa_session.refresh(tj) if tj.state == tj.states.RUNNING: - log.error( 'Transfer job %s is marked as running but pid %s appears to be dead.' % ( tj.id, tj.pid ) ) - dead.append( tj ) + log.error('Transfer job %s is marked as running but pid %s appears to be dead.' % (tj.id, tj.pid)) + dead.append(tj) if dead: - self.run( dead ) - self.sleeper.sleep( 30 ) - log.info( 'Transfer job restarter shutting down...' ) + self.run(dead) + self.sleeper.sleep(30) + log.info('Transfer job restarter shutting down...') - def shutdown( self ): + def shutdown(self): self.running = False self.sleeper.wake() diff --git a/lib/galaxy/managers/annotatable.py b/lib/galaxy/managers/annotatable.py index 7a0aa653c086..6195438e1e73 100644 --- a/lib/galaxy/managers/annotatable.py +++ b/lib/galaxy/managers/annotatable.py @@ -3,13 +3,13 @@ """ import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) # needed to extract this for use in manager *and* serializer, ideally, would use self.manager.annotation # from serializer, but history_contents has no self.manager # TODO: fix -def _match_by_user( item, user ): +def _match_by_user(item, user): if not user: return None for annotation in item.annotations: @@ -18,20 +18,20 @@ def _match_by_user( item, user ): return None -class AnnotatableManagerMixin( object ): +class AnnotatableManagerMixin(object): #: class of AnnotationAssociation (e.g. HistoryAnnotationAssociation) annotation_assoc = None - def annotation( self, item ): + def annotation(self, item): """ Return the annotation string made by the `item`'s owner or `None` if there is no annotation. """ # NOTE: only works with sharable (.user) - return self._user_annotation( item, item.user ) + return self._user_annotation(item, item.user) # TODO: should/do we support multiple, non-owner annotation of items? - def annotate( self, item, annotation, user=None, flush=True ): + def annotate(self, item, annotation, user=None, flush=True): """ Create a new annotation on `item` or delete the existing if annotation is `None`. @@ -39,70 +39,70 @@ def annotate( self, item, annotation, user=None, flush=True ): if not user: return None if annotation is None: - self._delete_annotation( item, user, flush=flush ) + self._delete_annotation(item, user, flush=flush) return None - annotation_obj = item.add_item_annotation( self.session(), user, item, annotation ) + annotation_obj = item.add_item_annotation(self.session(), user, item, annotation) if flush: self.session().flush() return annotation_obj - def _user_annotation( self, item, user ): - return _match_by_user( item, user ) + def _user_annotation(self, item, user): + return _match_by_user(item, user) - def _delete_annotation( self, item, user, flush=True ): - returned = item.delete_item_annotation( self.session(), user, item ) + def _delete_annotation(self, item, user, flush=True): + returned = item.delete_item_annotation(self.session(), user, item) if flush: self.session().flush() return returned -class AnnotatableSerializerMixin( object ): +class AnnotatableSerializerMixin(object): - def add_serializers( self ): - self.serializers[ 'annotation' ] = self.serialize_annotation + def add_serializers(self): + self.serializers['annotation'] = self.serialize_annotation - def serialize_annotation( self, item, key, user=None, **context ): + def serialize_annotation(self, item, key, user=None, **context): """ Get and serialize an `item`'s annotation. """ - annotation = _match_by_user( item, user ) + annotation = _match_by_user(item, user) return annotation.strip() if annotation else None -class AnnotatableDeserializerMixin( object ): +class AnnotatableDeserializerMixin(object): - def add_deserializers( self ): - self.deserializers[ 'annotation' ] = self.deserialize_annotation + def add_deserializers(self): + self.deserializers['annotation'] = self.deserialize_annotation - def deserialize_annotation( self, item, key, val, user=None, **context ): + def deserialize_annotation(self, item, key, val, user=None, **context): """ Make sure `val` is a valid annotation and assign it, deleting any existing if `val` is None. """ - val = self.validate.nullable_basestring( key, val ) - return self.manager.annotate( item, val, user=user, flush=False ) + val = self.validate.nullable_basestring(key, val) + return self.manager.annotate(item, val, user=user, flush=False) # TODO: I'm not entirely convinced this (or tags) are a good idea for filters since they involve a/the user -class AnnotatableFilterMixin( object ): +class AnnotatableFilterMixin(object): - def _owner_annotation( self, item ): + def _owner_annotation(self, item): """ Get the annotation by the item's owner. """ - return _match_by_user( item, item.user ) + return _match_by_user(item, item.user) - def filter_annotation_contains( self, item, val ): + def filter_annotation_contains(self, item, val): """ Test whether `val` is in the owner's annotation. """ - owner_annotation = self._owner_annotation( item ) + owner_annotation = self._owner_annotation(item) if owner_annotation is None: return False return val in owner_annotation - def _add_parsers( self ): + def _add_parsers(self): self.fn_filter_parsers.update({ - 'annotation' : { 'op': { 'has': self.filter_annotation_contains, } }, + 'annotation' : {'op': {'has': self.filter_annotation_contains, }}, }) diff --git a/lib/galaxy/managers/api_keys.py b/lib/galaxy/managers/api_keys.py index 16025bb342f9..506b704146d9 100644 --- a/lib/galaxy/managers/api_keys.py +++ b/lib/galaxy/managers/api_keys.py @@ -1,26 +1,26 @@ -class ApiKeyManager( object ): +class ApiKeyManager(object): - def __init__( self, app ): + def __init__(self, app): self.app = app - def create_api_key( self, user ): + def create_api_key(self, user): guid = self.app.security.get_new_guid() new_key = self.app.model.APIKeys() new_key.user_id = user.id new_key.key = guid sa_session = self.app.model.context - sa_session.add( new_key ) + sa_session.add(new_key) sa_session.flush() return guid - def get_or_create_api_key( self, user ): + def get_or_create_api_key(self, user): # Logic Galaxy has always used - but it would appear to have a race # condition. Worth fixing? Would kind of need a message queue to fix # in multiple process mode. if user.api_keys: key = user.api_keys[0].key else: - key = self.create_api_key( user ) + key = self.create_api_key(user) return key diff --git a/lib/galaxy/managers/base.py b/lib/galaxy/managers/base.py index afe2ce2db76a..cffe8a03a3ab 100644 --- a/lib/galaxy/managers/base.py +++ b/lib/galaxy/managers/base.py @@ -37,11 +37,11 @@ from galaxy import model from galaxy.model import tool_shed_install -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) # ==== accessors from base/controller.py -def security_check( trans, item, check_ownership=False, check_accessible=False ): +def security_check(trans, item, check_ownership=False, check_accessible=False): """ Security checks for an item: checks if (a) user owns item or (b) item is accessible to user. This is a generic method for dealing with objects @@ -57,24 +57,24 @@ def security_check( trans, item, check_ownership=False, check_accessible=False ) # Verify ownership: there is a current user and that user is the same as the item's if check_ownership: if not trans.user: - raise exceptions.ItemOwnershipException( "Must be logged in to manage Galaxy items", type='error' ) + raise exceptions.ItemOwnershipException("Must be logged in to manage Galaxy items", type='error') if item.user != trans.user: - raise exceptions.ItemOwnershipException( "%s is not owned by the current user" % item.__class__.__name__, type='error' ) + raise exceptions.ItemOwnershipException("%s is not owned by the current user" % item.__class__.__name__, type='error') # Verify accessible: # if it's part of a lib - can they access via security # if it's something else (sharable) have they been added to the item's users_shared_with_dot_users if check_accessible: - if type( item ) in ( trans.app.model.LibraryFolder, trans.app.model.LibraryDatasetDatasetAssociation, trans.app.model.LibraryDataset ): - if not trans.app.security_agent.can_access_library_item( trans.get_current_user_roles(), item, trans.user ): - raise exceptions.ItemAccessibilityException( "%s is not accessible to the current user" % item.__class__.__name__, type='error' ) + if type(item) in (trans.app.model.LibraryFolder, trans.app.model.LibraryDatasetDatasetAssociation, trans.app.model.LibraryDataset): + if not trans.app.security_agent.can_access_library_item(trans.get_current_user_roles(), item, trans.user): + raise exceptions.ItemAccessibilityException("%s is not accessible to the current user" % item.__class__.__name__, type='error') else: - if ( item.user != trans.user ) and ( not item.importable ) and ( trans.user not in item.users_shared_with_dot_users ): - raise exceptions.ItemAccessibilityException( "%s is not accessible to the current user" % item.__class__.__name__, type='error' ) + if (item.user != trans.user) and (not item.importable) and (trans.user not in item.users_shared_with_dot_users): + raise exceptions.ItemAccessibilityException("%s is not accessible to the current user" % item.__class__.__name__, type='error') return item -def get_class( class_name ): +def get_class(class_name): """ Returns the class object that a string denotes. Without this method, we'd have to do eval(). @@ -120,13 +120,13 @@ def decode_id(app, id): try: # note: use str - occasionally a fully numeric id will be placed in post body and parsed as int via JSON # resulting in error for valid id - return app.security.decode_id( str( id ) ) - except ( ValueError, TypeError ): - msg = "Malformed id ( %s ) specified, unable to decode" % ( str( id ) ) - raise exceptions.MalformedId( msg, id=str( id ) ) + return app.security.decode_id(str(id)) + except (ValueError, TypeError): + msg = "Malformed id ( %s ) specified, unable to decode" % (str(id)) + raise exceptions.MalformedId(msg, id=str(id)) -def get_object( trans, id, class_name, check_ownership=False, check_accessible=False, deleted=None ): +def get_object(trans, id, class_name, check_ownership=False, check_accessible=False, deleted=None): """ Convenience method to get a model object with the specified checks. This is a generic method for dealing with objects uniformly from the older @@ -135,27 +135,27 @@ def get_object( trans, id, class_name, check_ownership=False, check_accessible=F """ decoded_id = decode_id(trans.app, id) try: - item_class = get_class( class_name ) + item_class = get_class(class_name) assert item_class is not None - item = trans.sa_session.query( item_class ).get( decoded_id ) + item = trans.sa_session.query(item_class).get(decoded_id) assert item is not None except Exception: - log.exception( "Invalid %s id ( %s ) specified." % ( class_name, id ) ) - raise exceptions.MessageException( "Invalid %s id ( %s ) specified" % ( class_name, id ), type="error" ) + log.exception("Invalid %s id ( %s ) specified." % (class_name, id)) + raise exceptions.MessageException("Invalid %s id ( %s ) specified" % (class_name, id), type="error") if check_ownership or check_accessible: - security_check( trans, item, check_ownership, check_accessible ) + security_check(trans, item, check_ownership, check_accessible) if deleted is True and not item.deleted: - raise exceptions.ItemDeletionException( '%s "%s" is not deleted' - % ( class_name, getattr( item, 'name', id ) ), type="warning" ) + raise exceptions.ItemDeletionException('%s "%s" is not deleted' + % (class_name, getattr(item, 'name', id)), type="warning") elif deleted is False and item.deleted: - raise exceptions.ItemDeletionException( '%s "%s" is deleted' - % ( class_name, getattr( item, 'name', id ) ), type="warning" ) + raise exceptions.ItemDeletionException('%s "%s" is deleted' + % (class_name, getattr(item, 'name', id)), type="warning") return item # ============================================================================= -def munge_lists( listA, listB ): +def munge_lists(listA, listB): """ Combine two lists into a single list. @@ -166,15 +166,15 @@ def munge_lists( listA, listB ): return listB if listB is None: return listA - if not isinstance( listA, list ): - listA = [ listA ] - if not isinstance( listB, list ): - listB = [ listB ] + if not isinstance(listA, list): + listA = [listA] + if not isinstance(listB, list): + listB = [listB] return listA + listB # ----------------------------------------------------------------------------- -class ModelManager( object ): +class ModelManager(object): """ Base class for all model/resource managers. @@ -184,105 +184,105 @@ class ModelManager( object ): model_class = object foreign_key_name = None - def __init__( self, app ): + def __init__(self, app): self.app = app - def session( self ): + def session(self): return self.app.model.context - def _session_setattr( self, item, attr, val, fn=None, flush=True ): + def _session_setattr(self, item, attr, val, fn=None, flush=True): if fn: - fn( item, attr, val ) + fn(item, attr, val) else: - setattr( item, attr, val ) + setattr(item, attr, val) - self.session().add( item ) + self.session().add(item) if flush: self.session().flush() return item # .... query foundation wrapper - def query( self, eagerloads=True, **kwargs ): + def query(self, eagerloads=True, **kwargs): """ Return a basic query from model_class, filters, order_by, and limit and offset. Set eagerloads to False to disable them for this query. """ - query = self.session().query( self.model_class ) + query = self.session().query(self.model_class) # joined table loading if eagerloads is False: - query = query.enable_eagerloads( False ) - return self._filter_and_order_query( query, **kwargs ) + query = query.enable_eagerloads(False) + return self._filter_and_order_query(query, **kwargs) - def _filter_and_order_query( self, query, filters=None, order_by=None, limit=None, offset=None, **kwargs ): + def _filter_and_order_query(self, query, filters=None, order_by=None, limit=None, offset=None, **kwargs): # TODO: not a lot of functional cohesion here - query = self._apply_orm_filters( query, filters ) - query = self._apply_order_by( query, order_by ) - query = self._apply_orm_limit_offset( query, limit, offset ) + query = self._apply_orm_filters(query, filters) + query = self._apply_order_by(query, order_by) + query = self._apply_orm_limit_offset(query, limit, offset) return query # .... filters - def _apply_orm_filters( self, query, filters ): + def _apply_orm_filters(self, query, filters): """ Add any filters to the given query. """ if filters is None: return query - if not isinstance( filters, list ): - filters = [ filters ] + if not isinstance(filters, list): + filters = [filters] # note: implicit AND for filter in filters: - query = query.filter( filter ) + query = query.filter(filter) return query - def _munge_filters( self, filtersA, filtersB ): + def _munge_filters(self, filtersA, filtersB): """ Combine two lists into a single list. (While allowing them to be None, non-lists, or lists.) """ - return munge_lists( filtersA, filtersB ) + return munge_lists(filtersA, filtersB) # .... order, limit, and offset - def _apply_order_by( self, query, order_by ): + def _apply_order_by(self, query, order_by): """ Return the query after adding the order_by clauses. Use the manager's default_order_by if order_by is None. """ if order_by is None: - return query.order_by( *self._default_order_by() ) + return query.order_by(*self._default_order_by()) - if isinstance( order_by, ( list, tuple ) ): - return query.order_by( *order_by ) - return query.order_by( order_by ) + if isinstance(order_by, (list, tuple)): + return query.order_by(*order_by) + return query.order_by(order_by) - def _default_order_by( self ): + def _default_order_by(self): """ Returns a tuple of columns for the default order when getting multiple models. """ - return ( self.model_class.create_time, ) + return (self.model_class.create_time, ) - def _apply_orm_limit_offset( self, query, limit, offset ): + def _apply_orm_limit_offset(self, query, limit, offset): """ Return the query after applying the given limit and offset (if not None). """ if limit is not None: - query = query.limit( limit ) + query = query.limit(limit) if offset is not None: - query = query.offset( offset ) + query = query.offset(offset) return query # .... query resolution - def one( self, **kwargs ): + def one(self, **kwargs): """ Sends kwargs to build the query and returns one and only one model. """ - query = self.query( **kwargs ) - return self._one_with_recast_errors( query ) + query = self.query(**kwargs) + return self._one_with_recast_errors(query) - def _one_with_recast_errors( self, query ): + def _one_with_recast_errors(self, query): """ Call sqlalchemy's one and recast errors to serializable errors if any. @@ -293,31 +293,31 @@ def _one_with_recast_errors( self, query ): try: return query.one() except sqlalchemy.orm.exc.NoResultFound: - raise exceptions.ObjectNotFound( self.model_class.__name__ + ' not found' ) + raise exceptions.ObjectNotFound(self.model_class.__name__ + ' not found') except sqlalchemy.orm.exc.MultipleResultsFound: - raise exceptions.InconsistentDatabase( 'found more than one ' + self.model_class.__name__ ) + raise exceptions.InconsistentDatabase('found more than one ' + self.model_class.__name__) - def _one_or_none( self, query ): + def _one_or_none(self, query): """ Return the object if found, None if it's not. :raises exceptions.InconsistentDatabase: if more than one model is found """ try: - return self._one_with_recast_errors( query ) + return self._one_with_recast_errors(query) except exceptions.ObjectNotFound: return None # NOTE: at this layer, all ids are expected to be decoded and in int form - def by_id( self, id, **kwargs ): + def by_id(self, id, **kwargs): """ Gets a model by primary id. """ id_filter = self.model_class.id == id - return self.one( filters=id_filter, **kwargs ) + return self.one(filters=id_filter, **kwargs) # .... multirow queries - def list( self, filters=None, order_by=None, limit=None, offset=None, **kwargs ): + def list(self, filters=None, order_by=None, limit=None, offset=None, **kwargs): """ Returns all objects matching the given filters """ @@ -325,63 +325,63 @@ def list( self, filters=None, order_by=None, limit=None, offset=None, **kwargs ) # and functional filters that aren't currently possible using the orm (such as instance calcluated values # or annotations/tags). List splits those two filters and applies limits/offsets # only after functional filters (if any) using python. - orm_filters, fn_filters = self._split_filters( filters ) + orm_filters, fn_filters = self._split_filters(filters) if not fn_filters: # if no fn_filtering required, we can use the 'all orm' version with limit offset - return self._orm_list( filters=orm_filters, order_by=order_by, - limit=limit, offset=offset, **kwargs ) + return self._orm_list(filters=orm_filters, order_by=order_by, + limit=limit, offset=offset, **kwargs) # fn filters will change the number of items returnable by limit/offset - remove them here from the orm query - query = self.query( filters=orm_filters, order_by=order_by, limit=None, offset=None, **kwargs ) + query = self.query(filters=orm_filters, order_by=order_by, limit=None, offset=None, **kwargs) items = query.all() # apply limit, offset after SQL filtering - items = self._apply_fn_filters_gen( items, fn_filters ) - return list( self._apply_fn_limit_offset_gen( items, limit, offset ) ) + items = self._apply_fn_filters_gen(items, fn_filters) + return list(self._apply_fn_limit_offset_gen(items, limit, offset)) - def _split_filters( self, filters ): + def _split_filters(self, filters): """ Splits `filters` into a tuple of two lists: a list of filters to be added to the SQL query and a list of functional filters to be applied after the SQL query. """ - orm_filters, fn_filters = ( [], [] ) + orm_filters, fn_filters = ([], []) if filters is None: - return ( orm_filters, fn_filters ) - if not isinstance( filters, list ): - filters = [ filters ] + return (orm_filters, fn_filters) + if not isinstance(filters, list): + filters = [filters] for filter_ in filters: - if self._is_fn_filter( filter_ ): - fn_filters.append( filter_ ) + if self._is_fn_filter(filter_): + fn_filters.append(filter_) else: - orm_filters.append( filter_ ) - return ( orm_filters, fn_filters ) + orm_filters.append(filter_) + return (orm_filters, fn_filters) - def _is_fn_filter( self, filter_ ): + def _is_fn_filter(self, filter_): """ Returns True if `filter_` is a functional filter to be applied after the SQL query. """ - return callable( filter_ ) + return callable(filter_) - def _orm_list( self, query=None, **kwargs ): + def _orm_list(self, query=None, **kwargs): """ Sends kwargs to build the query return all models found. """ - query = query or self.query( **kwargs ) + query = query or self.query(**kwargs) return query.all() - def _apply_fn_filters_gen( self, items, filters ): + def _apply_fn_filters_gen(self, items, filters): """ If all the filter functions in `filters` return True for an item in `items`, yield that item. """ # cpu-expensive for item in items: - filter_results = [f( item ) for f in filters] - if all( filter_results ): + filter_results = [f(item) for f in filters] + if all(filter_results): yield item - def _apply_fn_limit_offset_gen( self, items, limit, offset ): + def _apply_fn_limit_offset_gen(self, items, limit, offset): """ Iterate over `items` and begin yielding items after `offset` number of items and stop when we've yielded @@ -394,7 +394,7 @@ def _apply_fn_limit_offset_gen( self, items, limit, offset ): offset = None yielded = 0 - for i, item in enumerate( items ): + for i, item in enumerate(items): if offset is not None and i < offset: continue if limit is not None and yielded >= limit: @@ -402,23 +402,23 @@ def _apply_fn_limit_offset_gen( self, items, limit, offset ): yield item yielded += 1 - def by_ids( self, ids, filters=None, **kwargs ): + def by_ids(self, ids, filters=None, **kwargs): """ Returns an in-order list of models with the matching ids in `ids`. """ if not ids: return [] - ids_filter = self.model_class.id.in_( ids ) - found = self.list( filters=self._munge_filters( ids_filter, filters ), **kwargs ) + ids_filter = self.model_class.id.in_(ids) + found = self.list(filters=self._munge_filters(ids_filter, filters), **kwargs) # TODO: this does not order by the original 'ids' array # ...could use get (supposedly since found are in the session, the db won't be hit twice) # return map( self.session().query( self.model_class ).get, ids ) # ...could implement own version here - slow? - return self._order_items_by_id( ids, found ) + return self._order_items_by_id(ids, found) - def _order_items_by_id( self, ids, items ): + def _order_items_by_id(self, ids, items): """ Given a list of (unique) ids and a list of items having an 'id' attribute, return items that have the given ids in that order. @@ -433,65 +433,65 @@ def _order_items_by_id( self, ids, items ): # move items list into dict by id item_dict = {} for item in items: - item_id = getattr( item, ID_ATTR_NAME, None ) + item_id = getattr(item, ID_ATTR_NAME, None) if item_id: - item_dict[ item_id ] = item + item_dict[item_id] = item # pull from map in order of ids in_order = [] for id in ids: if id in item_dict: - in_order.append( item_dict[ id ] ) + in_order.append(item_dict[id]) return in_order - def create( self, flush=True, *args, **kwargs ): + def create(self, flush=True, *args, **kwargs): """ Generically create a new model. """ # override in subclasses - item = self.model_class( *args, **kwargs ) - self.session().add( item ) + item = self.model_class(*args, **kwargs) + self.session().add(item) if flush: self.session().flush() return item - def copy( self, item, **kwargs ): + def copy(self, item, **kwargs): """ Clone or copy an item. """ - raise exceptions.NotImplemented( 'Abstract method' ) + raise exceptions.NotImplemented('Abstract method') - def update( self, item, new_values, flush=True, **kwargs ): + def update(self, item, new_values, flush=True, **kwargs): """ Given a dictionary of new values, update `item` and return it. ..note: NO validation or deserialization occurs here. """ - self.session().add( item ) + self.session().add(item) for key, value in new_values.items(): - if hasattr( item, key ): - setattr( item, key, value ) + if hasattr(item, key): + setattr(item, key, value) if flush: self.session().flush() return item - def associate( self, associate_with, item, foreign_key_name=None ): + def associate(self, associate_with, item, foreign_key_name=None): """ Generically associate `item` with `associate_with` based on `foreign_key_name`. """ foreign_key_name = foreign_key_name or self.foreign_key_name - setattr( associate_with, foreign_key_name, item ) + setattr(associate_with, foreign_key_name, item) return item - def _foreign_key( self, associated_model_class, foreign_key_name=None ): + def _foreign_key(self, associated_model_class, foreign_key_name=None): foreign_key_name = foreign_key_name or self.foreign_key_name - return getattr( associated_model_class, foreign_key_name ) + return getattr(associated_model_class, foreign_key_name) - def query_associated( self, associated_model_class, item, foreign_key_name=None ): + def query_associated(self, associated_model_class, item, foreign_key_name=None): """ Generically query other items that have been associated with this `item`. """ - foreign_key = self._foreign_key( associated_model_class, foreign_key_name=foreign_key_name ) - return self.session().query( associated_model_class ).filter( foreign_key == item ) + foreign_key = self._foreign_key(associated_model_class, foreign_key_name=foreign_key_name) + return self.session().query(associated_model_class).filter(foreign_key == item) # a rename of sql DELETE to differentiate from the Galaxy notion of mark_as_deleted # def destroy( self, item, **kwargs ): @@ -500,7 +500,7 @@ def query_associated( self, associated_model_class, item, foreign_key_name=None # ---- code for classes that use one *main* model manager # TODO: this may become unecessary if we can access managers some other way (class var, app, etc.) -class HasAModelManager( object ): +class HasAModelManager(object): """ Mixin used where serializers, deserializers, filter parsers, etc. need some functionality around the model they're mainly concerned with @@ -512,34 +512,34 @@ class HasAModelManager( object ): # examples where this doesn't really work are ConfigurationSerializer (no manager) # and contents (2 managers) - def __init__( self, app, manager=None, **kwargs ): + def __init__(self, app, manager=None, **kwargs): self._manager = manager @property - def manager( self ): + def manager(self): """Return an appropriate manager if it exists, instantiate if not.""" # PRECONDITION: assumes self.app is assigned elsewhere if not self._manager: # TODO: pass this serializer to it - self._manager = self.model_manager_class( self.app ) + self._manager = self.model_manager_class(self.app) # this will error for unset model_manager_class'es return self._manager # ==== SERIALIZERS/to_dict,from_dict -class ModelSerializingError( exceptions.InternalServerError ): +class ModelSerializingError(exceptions.InternalServerError): """Thrown when request model values can't be serialized""" pass -class ModelDeserializingError( exceptions.ObjectAttributeInvalidException ): +class ModelDeserializingError(exceptions.ObjectAttributeInvalidException): """Thrown when an incoming value isn't usable by the model (bad type, out of range, etc.) """ pass -class SkipAttribute( Exception ): +class SkipAttribute(Exception): """ Raise this inside a serializer to prevent the returned dictionary from having a the associated key or value for this attribute. @@ -547,7 +547,7 @@ class SkipAttribute( Exception ): pass -class ModelSerializer( HasAModelManager ): +class ModelSerializer(HasAModelManager): """ Turns models into JSONable dicts. @@ -565,13 +565,13 @@ class ModelSerializer( HasAModelManager ): item_dict = MySerializer.serialize( my_item, keys_to_serialize ) """ #: 'service' to use for getting urls - use class var to allow overriding when testing - url_for = staticmethod( routes.url_for ) + url_for = staticmethod(routes.url_for) - def __init__( self, app, **kwargs ): + def __init__(self, app, **kwargs): """ Set up serializer map, any additional serializable keys, and views here. """ - super( ModelSerializer, self ).__init__( app, **kwargs ) + super(ModelSerializer, self).__init__(app, **kwargs) self.app = app # a list of valid serializable keys that can use the default (string) serializer @@ -584,14 +584,14 @@ def __init__( self, app, **kwargs ): # add subclass serializers defined there self.add_serializers() # update the keyset by the serializers (removing the responsibility from subclasses) - self.serializable_keyset.update( self.serializers.keys() ) + self.serializable_keyset.update(self.serializers.keys()) # views are collections of serializable attributes (a named array of keys) # inspired by model.dict_{view}_visible_keys self.views = {} self.default_view = None - def add_serializers( self ): + def add_serializers(self): """ Register a map of attribute keys -> serializing functions that will serialize the attribute. @@ -602,7 +602,7 @@ def add_serializers( self ): 'update_time' : self.serialize_date, }) - def add_view( self, view_name, key_list, include_keys_from=None ): + def add_view(self, view_name, key_list, include_keys_from=None): """ Add the list of serializable attributes `key_list` to the serializer's view dictionary under the key `view_name`. @@ -610,12 +610,12 @@ def add_view( self, view_name, key_list, include_keys_from=None ): If `include_keys_from` is a proper view name, extend `key_list` by the list in that view. """ - key_list = list( set( key_list + self.views.get( include_keys_from, [] ) ) ) - self.views[ view_name ] = key_list - self.serializable_keyset.update( key_list ) + key_list = list(set(key_list + self.views.get(include_keys_from, []))) + self.views[view_name] = key_list + self.serializable_keyset.update(key_list) return key_list - def serialize( self, item, keys, **context ): + def serialize(self, item, keys, **context): """ Serialize the model `item` to a dictionary. @@ -629,67 +629,67 @@ def serialize( self, item, keys, **context ): # check both serializers and serializable keys if key in self.serializers: try: - returned[ key ] = self.serializers[ key ]( item, key, **context ) + returned[key] = self.serializers[key](item, key, **context) except SkipAttribute: # dont add this key if the deserializer threw this pass elif key in self.serializable_keyset: - returned[ key ] = self.default_serializer( item, key, **context ) + returned[key] = self.default_serializer(item, key, **context) # ignore bad/unreg keys return returned - def skip( self, msg='skipped' ): + def skip(self, msg='skipped'): """ To be called from inside a serializer to skip it. Handy for config checks, information hiding, etc. """ - raise SkipAttribute( msg ) + raise SkipAttribute(msg) - def _remap_from( self, original_key ): + def _remap_from(self, original_key): if original_key in self.serializers: - return self.serializers[ original_key ] + return self.serializers[original_key] if original_key in self.serializable_keyset: - return lambda i, k, **c: self.default_serializer( i, original_key, **c ) - raise KeyError( 'serializer not found for remap: ' + original_key ) + return lambda i, k, **c: self.default_serializer(i, original_key, **c) + raise KeyError('serializer not found for remap: ' + original_key) - def default_serializer( self, item, key, **context ): + def default_serializer(self, item, key, **context): """ Serialize the `item`'s attribute named `key`. """ # TODO:?? point of change but not really necessary? - return getattr( item, key ) + return getattr(item, key) # serializers for common galaxy objects - def serialize_date( self, item, key, **context ): + def serialize_date(self, item, key, **context): """ Serialize a date attribute of `item`. """ - date = getattr( item, key ) + date = getattr(item, key) return date.isoformat() if date is not None else None - def serialize_id( self, item, key, **context ): + def serialize_id(self, item, key, **context): """ Serialize an id attribute of `item`. """ - id = getattr( item, key ) + id = getattr(item, key) # Note: it may not be best to encode the id at this layer - return self.app.security.encode_id( id ) if id is not None else None + return self.app.security.encode_id(id) if id is not None else None - def serialize_type_id( self, item, key, **context ): + def serialize_type_id(self, item, key, **context): """ Serialize an type-id for `item`. """ TYPE_ID_SEP = '-' - type_id = getattr( item, key ) + type_id = getattr(item, key) if type_id is None: return None - split = type_id.split( TYPE_ID_SEP, 1 ) + split = type_id.split(TYPE_ID_SEP, 1) # Note: it may not be best to encode the id at this layer - return TYPE_ID_SEP.join([ split[0], self.app.security.encode_id( split[1] )]) + return TYPE_ID_SEP.join([split[0], self.app.security.encode_id(split[1])]) # serializing to a view where a view is a predefied list of keys to serialize - def serialize_to_view( self, item, view=None, keys=None, default_view=None, **context ): + def serialize_to_view(self, item, view=None, keys=None, default_view=None, **context): """ Use a predefined list of keys (the string `view`) and any additional keys listed in `keys`. @@ -707,18 +707,18 @@ def serialize_to_view( self, item, view=None, keys=None, default_view=None, **co # chose explicit over concise here if view: if keys: - all_keys = self._view_to_keys( view ) + keys + all_keys = self._view_to_keys(view) + keys else: - all_keys = self._view_to_keys( view ) + all_keys = self._view_to_keys(view) else: if keys: all_keys = keys elif default_view: - all_keys = self._view_to_keys( default_view ) + all_keys = self._view_to_keys(default_view) - return self.serialize( item, all_keys, **context ) + return self.serialize(item, all_keys, **context) - def _view_to_keys( self, view=None ): + def _view_to_keys(self, view=None): """ Converts a known view into a list of keys. @@ -727,31 +727,31 @@ def _view_to_keys( self, view=None ): if view is None: view = self.default_view if view not in self.views: - raise ModelSerializingError( 'unknown view', view=view, available_views=self.views ) - return self.views[ view ][:] + raise ModelSerializingError('unknown view', view=view, available_views=self.views) + return self.views[view][:] -class ModelDeserializer( HasAModelManager ): +class ModelDeserializer(HasAModelManager): """ An object that converts an incoming serialized dict into values that can be directly assigned to an item's attributes and assigns them. """ # TODO:?? a larger question is: which should be first? Deserialize then validate - or - validate then deserialize? - def __init__( self, app, validator=None, **kwargs ): + def __init__(self, app, validator=None, **kwargs): """ Set up deserializers and validator. """ - super( ModelDeserializer, self ).__init__( app, **kwargs ) + super(ModelDeserializer, self).__init__(app, **kwargs) self.app = app self.deserializers = {} self.deserializable_keyset = set([]) self.add_deserializers() # a sub object that can validate incoming values - self.validate = validator or ModelValidator( self.app ) + self.validate = validator or ModelValidator(self.app) - def add_deserializers( self ): + def add_deserializers(self): """ Register a map of attribute keys -> functions that will deserialize data into attributes to be assigned to the item. @@ -759,7 +759,7 @@ def add_deserializers( self ): # to be overridden in subclasses pass - def deserialize( self, item, data, flush=True, **context ): + def deserialize(self, item, data, flush=True, **context): """ Convert an incoming serialized dict into values that can be directly assigned to an item's attributes and assign them @@ -769,18 +769,18 @@ def deserialize( self, item, data, flush=True, **context ): new_dict = {} for key, val in data.items(): if key in self.deserializers: - new_dict[ key ] = self.deserializers[ key ]( item, key, val, **context ) + new_dict[key] = self.deserializers[key](item, key, val, **context) # !important: don't error on unreg. keys -- many clients will add weird ass keys onto the model # TODO:?? add and flush here or in manager? - if flush and len( new_dict ): - sa_session.add( item ) + if flush and len(new_dict): + sa_session.add(item) sa_session.flush() return new_dict # ... common deserializers for primitives - def default_deserializer( self, item, key, val, **context ): + def default_deserializer(self, item, key, val, **context): """ If the incoming `val` is different than the `item` value change it and, in either case, return the value. @@ -788,94 +788,94 @@ def default_deserializer( self, item, key, val, **context ): # TODO: sets the item attribute to value (this may not work in all instances) # only do the following if val == getattr( item, key ) - if hasattr( item, key ) and getattr( item, key ) != val: - setattr( item, key, val ) + if hasattr(item, key) and getattr(item, key) != val: + setattr(item, key, val) return val - def deserialize_basestring( self, item, key, val, convert_none_to_empty=False, **context ): - val = '' if ( convert_none_to_empty and val is None ) else self.validate.basestring( key, val ) - return self.default_deserializer( item, key, val, **context ) + def deserialize_basestring(self, item, key, val, convert_none_to_empty=False, **context): + val = '' if (convert_none_to_empty and val is None) else self.validate.basestring(key, val) + return self.default_deserializer(item, key, val, **context) - def deserialize_bool( self, item, key, val, **context ): - val = self.validate.bool( key, val ) - return self.default_deserializer( item, key, val, **context ) + def deserialize_bool(self, item, key, val, **context): + val = self.validate.bool(key, val) + return self.default_deserializer(item, key, val, **context) - def deserialize_int( self, item, key, val, min=None, max=None, **context ): - val = self.validate.int_range( key, val, min, max ) - return self.default_deserializer( item, key, val, **context ) + def deserialize_int(self, item, key, val, min=None, max=None, **context): + val = self.validate.int_range(key, val, min, max) + return self.default_deserializer(item, key, val, **context) # def deserialize_date( self, item, key, val ): # #TODO: parse isoformat date into date object # ... common deserializers for Galaxy - def deserialize_genome_build( self, item, key, val, **context ): + def deserialize_genome_build(self, item, key, val, **context): """ Make sure `val` is a valid dbkey and assign it. """ - val = self.validate.genome_build( key, val ) - return self.default_deserializer( item, key, val, **context ) + val = self.validate.genome_build(key, val) + return self.default_deserializer(item, key, val, **context) -class ModelValidator( HasAModelManager ): +class ModelValidator(HasAModelManager): """ An object that inspects a dictionary (generally meant to be a set of new/updated values for the model) and raises an error if a value is not acceptable. """ - def __init__( self, app, *args, **kwargs ): - super( ModelValidator, self ).__init__( app, **kwargs ) + def __init__(self, app, *args, **kwargs): + super(ModelValidator, self).__init__(app, **kwargs) self.app = app - def type( self, key, val, types ): + def type(self, key, val, types): """ Check `val` against the type (or tuple of types) in `types`. :raises exceptions.RequestParameterInvalidException: if not an instance. """ - if not isinstance( val, types ): - msg = 'must be a type: %s' % ( str( types ) ) - raise exceptions.RequestParameterInvalidException( msg, key=key, val=val ) + if not isinstance(val, types): + msg = 'must be a type: %s' % (str(types)) + raise exceptions.RequestParameterInvalidException(msg, key=key, val=val) return val # validators for primitives and compounds of primitives - def basestring( self, key, val ): - return self.type( key, val, string_types ) + def basestring(self, key, val): + return self.type(key, val, string_types) - def bool( self, key, val ): - return self.type( key, val, bool ) + def bool(self, key, val): + return self.type(key, val, bool) - def int( self, key, val ): - return self.type( key, val, int ) + def int(self, key, val): + return self.type(key, val, int) - def nullable_basestring( self, key, val ): + def nullable_basestring(self, key, val): """ Must be a basestring or None. """ - return self.type( key, val, ( string_types, type( None ) ) ) + return self.type(key, val, (string_types, type(None))) - def int_range( self, key, val, min=None, max=None ): + def int_range(self, key, val, min=None, max=None): """ Must be a int between min and max. """ - val = self.type( key, val, int ) + val = self.type(key, val, int) if min is not None and val < min: - raise exceptions.RequestParameterInvalidException( "less than minimum", key=key, val=val, min=min ) + raise exceptions.RequestParameterInvalidException("less than minimum", key=key, val=val, min=min) if max is not None and val > max: - raise exceptions.RequestParameterInvalidException( "greater than maximum", key=key, val=val, max=max ) + raise exceptions.RequestParameterInvalidException("greater than maximum", key=key, val=val, max=max) return val - def basestring_list( self, key, val ): + def basestring_list(self, key, val): """ Must be a list of basestrings. """ # TODO: Here's where compound types start becoming a nightmare. Any more or more complex # and should find a different way. - val = self.type( key, val, list ) - return [ self.basestring( key, elem ) for elem in val ] + val = self.type(key, val, list) + return [self.basestring(key, elem) for elem in val] # validators for Galaxy - def genome_build( self, key, val ): + def genome_build(self, key, val): """ Must be a valid base_string. @@ -892,7 +892,7 @@ def genome_build( self, key, val ): # return val # raise exceptions.RequestParameterInvalidException( "invalid reference", key=key, val=val ) # IOW: fallback to string validation - return self.basestring( key, val ) + return self.basestring(key, val) # def slug( self, item, key, val ): # """validate slug""" @@ -900,7 +900,7 @@ def genome_build( self, key, val ): # ==== Building query filters based on model data -class ModelFilterParser( HasAModelManager ): +class ModelFilterParser(HasAModelManager): """ Converts string tuples (partially converted query string params) of attr, op, val into either: @@ -928,15 +928,15 @@ class ModelFilterParser( HasAModelManager ): #: model class model_class = None - def __init__( self, app, **kwargs ): + def __init__(self, app, **kwargs): """ Set up serializer map, any additional serializable keys, and views here. """ - super( ModelFilterParser, self ).__init__( app, **kwargs ) + super(ModelFilterParser, self).__init__(app, **kwargs) self.app = app #: regex for testing/dicing iso8601 date strings, with optional time and ms, but allowing only UTC timezone - self.date_string_re = re.compile( r'^(\d{4}\-\d{2}\-\d{2})[T| ]{0,1}(\d{2}:\d{2}:\d{2}(?:\.\d{1,6}){0,1}){0,1}Z{0,1}$' ) + self.date_string_re = re.compile(r'^(\d{4}\-\d{2}\-\d{2})[T| ]{0,1}(\d{2}:\d{2}:\d{2}(?:\.\d{1,6}){0,1}){0,1}Z{0,1}$') # dictionary containing parsing data for ORM/SQLAlchemy-based filters # ..note: although kind of a pain in the ass and verbose, opt-in/whitelisting allows more control @@ -949,32 +949,32 @@ def __init__( self, app, **kwargs ): # set up both of the above self._add_parsers() - def _add_parsers( self ): + def _add_parsers(self): """ Set up, extend, or alter `orm_filter_parsers` and `fn_filter_parsers`. """ # note: these are the default filters for all models self.orm_filter_parsers.update({ # (prob.) applicable to all models - 'id' : { 'op': ( 'in' ) }, - 'encoded_id' : { 'column' : 'id', 'op': ( 'in' ), 'val': self.parse_id_list }, + 'id' : {'op': ('in')}, + 'encoded_id' : {'column' : 'id', 'op': ('in'), 'val': self.parse_id_list}, # dates can be directly passed through the orm into a filter (no need to parse into datetime object) - 'create_time' : { 'op': ( 'le', 'ge' ), 'val': self.parse_date }, - 'update_time' : { 'op': ( 'le', 'ge' ), 'val': self.parse_date }, + 'create_time' : {'op': ('le', 'ge'), 'val': self.parse_date}, + 'update_time' : {'op': ('le', 'ge'), 'val': self.parse_date}, }) - def parse_filters( self, filter_tuple_list ): + def parse_filters(self, filter_tuple_list): """ Parse string 3-tuples (attr, op, val) into orm or functional filters. """ # TODO: allow defining the default filter op in this class (and not 'eq' in base/controller.py) parsed = [] - for ( attr, op, val ) in filter_tuple_list: - filter_ = self.parse_filter( attr, op, val ) - parsed.append( filter_ ) + for (attr, op, val) in filter_tuple_list: + filter_ = self.parse_filter(attr, op, val) + parsed.append(filter_) return parsed - def parse_filter( self, attr, op, val ): + def parse_filter(self, attr, op, val): """ Attempt to parse filter as a custom/fn filter, then an orm filter, and if neither work - raise an error. @@ -984,93 +984,93 @@ def parse_filter( self, attr, op, val ): """ try: # check for a custom filter - fn_filter = self._parse_fn_filter( attr, op, val ) + fn_filter = self._parse_fn_filter(attr, op, val) if fn_filter is not None: return fn_filter # if no custom filter found, try to make an ORM filter # note: have to use explicit is None here, bool( sqlalx.filter ) == False - orm_filter = self._parse_orm_filter( attr, op, val ) + orm_filter = self._parse_orm_filter(attr, op, val) if orm_filter is not None: return orm_filter # by convention, assume most val parsers raise ValueError except ValueError as val_err: - raise exceptions.RequestParameterInvalidException( 'unparsable value for filter', - column=attr, operation=op, value=val, ValueError=str( val_err ) ) + raise exceptions.RequestParameterInvalidException('unparsable value for filter', + column=attr, operation=op, value=val, ValueError=str(val_err)) # if neither of the above work, raise an error with how-to info # TODO: send back all valid filter keys in exception for added user help - raise exceptions.RequestParameterInvalidException( 'bad filter', column=attr, operation=op ) + raise exceptions.RequestParameterInvalidException('bad filter', column=attr, operation=op) # ---- fn filters - def _parse_fn_filter( self, attr, op, val ): + def _parse_fn_filter(self, attr, op, val): """ Attempt to parse a non-ORM filter function. """ # fn_filter_list is a dict: fn_filter_list[ attr ] = { 'opname1' : opfn1, 'opname2' : opfn2, etc. } # attr, op is a nested dictionary pointing to the filter fn - attr_map = self.fn_filter_parsers.get( attr, None ) + attr_map = self.fn_filter_parsers.get(attr, None) if not attr_map: return None - allowed_ops = attr_map.get( 'op' ) + allowed_ops = attr_map.get('op') # allowed ops is a map here, op => fn - filter_fn = allowed_ops.get( op, None ) + filter_fn = allowed_ops.get(op, None) if not filter_fn: return None # parse the val from string using the 'val' parser if present (otherwise, leave as string) - val_parser = attr_map.get( 'val', None ) + val_parser = attr_map.get('val', None) if val_parser: - val = val_parser( val ) + val = val_parser(val) # curry/partial and fold the val in there now - return lambda i: filter_fn( i, val ) + return lambda i: filter_fn(i, val) # ---- ORM filters - def _parse_orm_filter( self, attr, op, val ): + def _parse_orm_filter(self, attr, op, val): """ Attempt to parse a ORM-based filter. Using SQLAlchemy, this would yield a sql.elements.BinaryExpression. """ # orm_filter_list is a dict: orm_filter_list[ attr ] = - column_map = self.orm_filter_parsers.get( attr, None ) + column_map = self.orm_filter_parsers.get(attr, None) if not column_map: # no column mapping (not whitelisted) return None # attr must be a whitelisted column by attr name or by key passed in column_map # note: column_map[ 'column' ] takes precedence if 'column' in column_map: - attr = column_map[ 'column' ] - column = self.model_class.table.columns.get( attr ) + attr = column_map['column'] + column = self.model_class.table.columns.get(attr) if column is None: # could be a property (hybrid_property, etc.) - assume we can make a filter from it - column = getattr( self.model_class, attr ) + column = getattr(self.model_class, attr) if column is None: # no orm column return None # op must be whitelisted: contained in the list orm_filter_list[ attr ][ 'op' ] - allowed_ops = column_map.get( 'op' ) + allowed_ops = column_map.get('op') if op not in allowed_ops: return None - op = self._convert_op_string_to_fn( column, op ) + op = self._convert_op_string_to_fn(column, op) if not op: return None # parse the val from string using the 'val' parser if present (otherwise, leave as string) - val_parser = column_map.get( 'val', None ) + val_parser = column_map.get('val', None) if val_parser: - val = val_parser( val ) + val = val_parser(val) - orm_filter = op( val ) + orm_filter = op(val) return orm_filter #: these are the easier/shorter string equivalents to the python operator fn names that need '__' around them - UNDERSCORED_OPS = ( 'lt', 'le', 'eq', 'ne', 'ge', 'gt' ) + UNDERSCORED_OPS = ('lt', 'le', 'eq', 'ne', 'ge', 'gt') - def _convert_op_string_to_fn( self, column, op_string ): + def _convert_op_string_to_fn(self, column, op_string): """ Convert the query string filter op shorthand into actual ORM usable function names, then return the ORM function. @@ -1084,50 +1084,50 @@ def _convert_op_string_to_fn( self, column, op_string ): # get the column fn using the op_string and error if not a callable attr # TODO: special case 'not in' - or disallow? - op_fn = getattr( column, fn_name, None ) - if not op_fn or not callable( op_fn ): + op_fn = getattr(column, fn_name, None) + if not op_fn or not callable(op_fn): return None return op_fn # ---- preset fn_filters: dictionaries of standard filter ops for standard datatypes - def string_standard_ops( self, key ): + def string_standard_ops(self, key): return { 'op' : { - 'eq' : lambda i, v: v == getattr( i, key ), - 'contains' : lambda i, v: v in getattr( i, key ), + 'eq' : lambda i, v: v == getattr(i, key), + 'contains' : lambda i, v: v in getattr(i, key), } } # --- more parsers! yay! # TODO: These should go somewhere central - we've got ~6 parser modules/sections now - def parse_bool( self, bool_string ): + def parse_bool(self, bool_string): """ Parse a boolean from a string. """ # Be strict here to remove complexity of options (but allow already parsed). - if bool_string in ( 'True', True ): + if bool_string in ('True', True): return True - if bool_string in ( 'False', False ): + if bool_string in ('False', False): return False - raise ValueError( 'invalid boolean: ' + str( bool_string ) ) + raise ValueError('invalid boolean: ' + str(bool_string)) - def parse_id_list( self, id_list_string, sep=',' ): + def parse_id_list(self, id_list_string, sep=','): """ Split `id_list_string` at `sep`. """ # TODO: move id decoding out - id_list = [ self.app.security.decode_id( id_ ) for id_ in id_list_string.split( sep ) ] + id_list = [self.app.security.decode_id(id_) for id_ in id_list_string.split(sep)] return id_list - def parse_int_list( self, int_list_string, sep=',' ): + def parse_int_list(self, int_list_string, sep=','): """ Split `int_list_string` at `sep` and parse as ints. """ # TODO: move id decoding out - int_list = [ int( v ) for v in int_list_string.split( sep ) ] + int_list = [int(v) for v in int_list_string.split(sep)] return int_list - def parse_date( self, date_string ): + def parse_date(self, date_string): """ Reformats a string containing either seconds from epoch or an iso8601 formated date string into a new date string usable within a filter query. @@ -1136,14 +1136,14 @@ def parse_date( self, date_string ): """ # assume it's epoch if no date separator is present try: - epoch = float( date_string ) - datetime_obj = datetime.datetime.fromtimestamp( epoch ) - return datetime_obj.isoformat( sep=' ' ) + epoch = float(date_string) + datetime_obj = datetime.datetime.fromtimestamp(epoch) + return datetime_obj.isoformat(sep=' ') except ValueError: pass - match = self.date_string_re.match( date_string ) + match = self.date_string_re.match(date_string) if match: - date_string = ' '.join([ group for group in match.groups() if group ]) + date_string = ' '.join([group for group in match.groups() if group]) return date_string - raise ValueError( 'datetime strings must be in the ISO 8601 format and in the UTC' ) + raise ValueError('datetime strings must be in the ISO 8601 format and in the UTC') diff --git a/lib/galaxy/managers/citations.py b/lib/galaxy/managers/citations.py index d1c5b3a14e3e..7b88b2a37280 100644 --- a/lib/galaxy/managers/citations.py +++ b/lib/galaxy/managers/citations.py @@ -6,45 +6,45 @@ from beaker.util import parse_cache_config_options import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class CitationsManager( object ): +class CitationsManager(object): - def __init__( self, app ): + def __init__(self, app): self.app = app - self.doi_cache = DoiCache( app.config ) + self.doi_cache = DoiCache(app.config) - def citations_for_tool( self, tool ): + def citations_for_tool(self, tool): return tool.citations - def citations_for_tool_ids( self, tool_ids ): + def citations_for_tool_ids(self, tool_ids): citation_collection = CitationCollection() for tool_id in tool_ids: - tool = self._get_tool( tool_id ) - for citation in self.citations_for_tool( tool ): - citation_collection.add( citation ) + tool = self._get_tool(tool_id) + for citation in self.citations_for_tool(tool): + citation_collection.add(citation) return citation_collection.citations - def parse_citation( self, citation_elem, tool_directory ): - return parse_citation( citation_elem, tool_directory, self ) + def parse_citation(self, citation_elem, tool_directory): + return parse_citation(citation_elem, tool_directory, self) - def _get_tool( self, tool_id ): - tool = self.app.toolbox.get_tool( tool_id ) + def _get_tool(self, tool_id): + tool = self.app.toolbox.get_tool(tool_id) return tool -class DoiCache( object ): +class DoiCache(object): - def __init__( self, config ): + def __init__(self, config): cache_opts = { - 'cache.type': getattr( config, 'citation_cache_type', 'file'), - 'cache.data_dir': getattr( config, 'citation_cache_data_dir', None), - 'cache.lock_dir': getattr( config, 'citation_cache_lock_dir', None), + 'cache.type': getattr(config, 'citation_cache_type', 'file'), + 'cache.data_dir': getattr(config, 'citation_cache_data_dir', None), + 'cache.lock_dir': getattr(config, 'citation_cache_lock_dir', None), } self._cache = CacheManager(**parse_cache_config_options(cache_opts)).get_cache('doi') - def _raw_get_bibtex( self, doi ): + def _raw_get_bibtex(self, doi): dx_url = "http://dx.doi.org/" + doi headers = {'Accept': 'text/bibliography; style=bibtex, application/x-bibtex'} req = urllib2.Request(dx_url, data="", headers=headers) @@ -52,49 +52,49 @@ def _raw_get_bibtex( self, doi ): bibtex = response.read() return bibtex - def get_bibtex( self, doi ): + def get_bibtex(self, doi): createfunc = functools.partial(self._raw_get_bibtex, doi) return self._cache.get(key=doi, createfunc=createfunc) -def parse_citation( elem, directory, citation_manager ): +def parse_citation(elem, directory, citation_manager): """ Parse an abstract citation entry from the specified XML element. The directory parameter should be used to find external files for this citation. """ - citation_type = elem.attrib.get( 'type', None ) - citation_class = CITATION_CLASSES.get( citation_type, None ) + citation_type = elem.attrib.get('type', None) + citation_class = CITATION_CLASSES.get(citation_type, None) if not citation_class: log.warning("Unknown or unspecified citation type: %s" % citation_type) return None - return citation_class( elem, directory, citation_manager ) + return citation_class(elem, directory, citation_manager) -class CitationCollection( object ): +class CitationCollection(object): - def __init__( self ): + def __init__(self): self.citations = [] - def __iter__( self ): + def __iter__(self): return self.citations.__iter__() - def __len__( self ): - return len( self.citations ) + def __len__(self): + return len(self.citations) - def add( self, new_citation ): + def add(self, new_citation): for citation in self.citations: - if citation.equals( new_citation ): + if citation.equals(new_citation): # TODO: We have two equivalent citations, pick the more # informative/complete/correct. return False - self.citations.append( new_citation ) + self.citations.append(new_citation) return True -class BaseCitation( object ): +class BaseCitation(object): - def to_dict( self, citation_format ): + def to_dict(self, citation_format): if citation_format == "bibtex": return dict( format="bibtex", @@ -103,49 +103,49 @@ def to_dict( self, citation_format ): else: raise Exception("Unknown citation format %s" % citation_format) - def equals( self, other_citation ): + def equals(self, other_citation): if self.has_doi() and other_citation.has_doi(): return self.doi() == other_citation.doi() else: # TODO: Do a better job figuring out if this is the same citation. return self.to_bibtex() == other_citation.to_bibtex() - def has_doi( self ): + def has_doi(self): return False -class BibtexCitation( BaseCitation ): +class BibtexCitation(BaseCitation): - def __init__( self, elem, directory, citation_manager ): + def __init__(self, elem, directory, citation_manager): bibtex_file = elem.attrib.get("file", None) if bibtex_file: raw_bibtex = open(os.path.join(directory, bibtex_file), "r").read() else: raw_bibtex = elem.text.strip() - self._set_raw_bibtex( raw_bibtex ) + self._set_raw_bibtex(raw_bibtex) - def _set_raw_bibtex( self, raw_bibtex ): + def _set_raw_bibtex(self, raw_bibtex): self.raw_bibtex = raw_bibtex - def to_bibtex( self ): + def to_bibtex(self): return self.raw_bibtex -class DoiCitation( BaseCitation ): +class DoiCitation(BaseCitation): BIBTEX_UNSET = object() - def __init__( self, elem, directory, citation_manager ): + def __init__(self, elem, directory, citation_manager): self.__doi = elem.text.strip() self.doi_cache = citation_manager.doi_cache self.raw_bibtex = DoiCitation.BIBTEX_UNSET - def has_doi( self ): + def has_doi(self): return True - def doi( self ): + def doi(self): return self.__doi - def to_bibtex( self ): + def to_bibtex(self): if self.raw_bibtex is DoiCitation.BIBTEX_UNSET: try: self.raw_bibtex = self.doi_cache.get_bibtex(self.__doi) diff --git a/lib/galaxy/managers/collections.py b/lib/galaxy/managers/collections.py index c4b96365d143..286aa4e8f70f 100644 --- a/lib/galaxy/managers/collections.py +++ b/lib/galaxy/managers/collections.py @@ -14,34 +14,34 @@ from galaxy.util import odict from galaxy.util import validation import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) ERROR_INVALID_ELEMENTS_SPECIFICATION = "Create called with invalid parameters, must specify element identifiers." ERROR_NO_COLLECTION_TYPE = "Create called without specifing a collection type." -class DatasetCollectionManager( object ): +class DatasetCollectionManager(object): """ Abstraction for interfacing with dataset collections instance - ideally abstarcts out model and plugin details. """ ELEMENTS_UNINITIALIZED = object() - def __init__( self, app ): - self.type_registry = DatasetCollectionTypesRegistry( app ) - self.collection_type_descriptions = CollectionTypeDescriptionFactory( self.type_registry ) + def __init__(self, app): + self.type_registry = DatasetCollectionTypesRegistry(app) + self.collection_type_descriptions = CollectionTypeDescriptionFactory(self.type_registry) self.model = app.model self.security = app.security - self.hda_manager = hdas.HDAManager( app ) - self.history_manager = histories.HistoryManager( app ) - self.tag_manager = tags.GalaxyTagManager( app.model.context ) - self.ldda_manager = lddas.LDDAManager( app ) + self.hda_manager = hdas.HDAManager(app) + self.history_manager = histories.HistoryManager(app) + self.tag_manager = tags.GalaxyTagManager(app.model.context) + self.ldda_manager = lddas.LDDAManager(app) - def create( self, trans, parent, name, collection_type, element_identifiers=None, - elements=None, implicit_collection_info=None, trusted_identifiers=None, - hide_source_items=False, tags=None): + def create(self, trans, parent, name, collection_type, element_identifiers=None, + elements=None, implicit_collection_info=None, trusted_identifiers=None, + hide_source_items=False, tags=None): """ PRECONDITION: security checks on ability to add to parent occurred during load. @@ -51,7 +51,7 @@ def create( self, trans, parent, name, collection_type, element_identifiers=None trusted_identifiers = implicit_collection_info is not None if element_identifiers and not trusted_identifiers: - validate_input_element_identifiers( element_identifiers ) + validate_input_element_identifiers(element_identifiers) dataset_collection = self.create_dataset_collection( trans=trans, @@ -61,33 +61,33 @@ def create( self, trans, parent, name, collection_type, element_identifiers=None hide_source_items=hide_source_items, ) - if isinstance( parent, model.History ): + if isinstance(parent, model.History): dataset_collection_instance = self.model.HistoryDatasetCollectionAssociation( collection=dataset_collection, name=name, ) if implicit_collection_info: - for input_name, input_collection in implicit_collection_info[ "implicit_inputs" ]: - dataset_collection_instance.add_implicit_input_collection( input_name, input_collection ) - for output_dataset in implicit_collection_info.get( "outputs" ): + for input_name, input_collection in implicit_collection_info["implicit_inputs"]: + dataset_collection_instance.add_implicit_input_collection(input_name, input_collection) + for output_dataset in implicit_collection_info.get("outputs"): if output_dataset not in trans.sa_session: - output_dataset = trans.sa_session.query( type( output_dataset ) ).get( output_dataset.id ) - if isinstance( output_dataset, model.HistoryDatasetAssociation ): + output_dataset = trans.sa_session.query(type(output_dataset)).get(output_dataset.id) + if isinstance(output_dataset, model.HistoryDatasetAssociation): output_dataset.hidden_beneath_collection_instance = dataset_collection_instance - elif isinstance( output_dataset, model.HistoryDatasetCollectionAssociation ): - dataset_collection_instance.add_implicit_input_collection( input_name, input_collection ) + elif isinstance(output_dataset, model.HistoryDatasetCollectionAssociation): + dataset_collection_instance.add_implicit_input_collection(input_name, input_collection) else: # dataset collection, don't need to do anything... pass - trans.sa_session.add( output_dataset ) + trans.sa_session.add(output_dataset) - dataset_collection_instance.implicit_output_name = implicit_collection_info[ "implicit_output_name" ] + dataset_collection_instance.implicit_output_name = implicit_collection_info["implicit_output_name"] - log.debug("Created collection with %d elements" % ( len( dataset_collection_instance.collection.elements ) ) ) + log.debug("Created collection with %d elements" % (len(dataset_collection_instance.collection.elements))) # Handle setting hid - parent.add_dataset_collection( dataset_collection_instance ) + parent.add_dataset_collection(dataset_collection_instance) - elif isinstance( parent, model.LibraryFolder ): + elif isinstance(parent, model.LibraryFolder): dataset_collection_instance = self.model.LibraryDatasetCollectionAssociation( collection=dataset_collection, folder=parent, @@ -95,9 +95,9 @@ def create( self, trans, parent, name, collection_type, element_identifiers=None ) else: - message = "Internal logic error - create called with unknown parent type %s" % type( parent ) - log.exception( message ) - raise MessageException( message ) + message = "Internal logic error - create called with unknown parent type %s" % type(parent) + log.exception(message) + raise MessageException(message) tags = tags or {} if implicit_collection_info: for k, v in implicit_collection_info.get('implicit_inputs', []): @@ -106,140 +106,140 @@ def create( self, trans, parent, name, collection_type, element_identifiers=None for _, tag in tags.items(): dataset_collection_instance.tags.append(tag.copy()) - return self.__persist( dataset_collection_instance ) + return self.__persist(dataset_collection_instance) - def create_dataset_collection( self, trans, collection_type, element_identifiers=None, elements=None, - hide_source_items=None ): + def create_dataset_collection(self, trans, collection_type, element_identifiers=None, elements=None, + hide_source_items=None): if element_identifiers is None and elements is None: - raise RequestParameterInvalidException( ERROR_INVALID_ELEMENTS_SPECIFICATION ) + raise RequestParameterInvalidException(ERROR_INVALID_ELEMENTS_SPECIFICATION) if not collection_type: - raise RequestParameterInvalidException( ERROR_NO_COLLECTION_TYPE ) - collection_type_description = self.collection_type_descriptions.for_collection_type( collection_type ) + raise RequestParameterInvalidException(ERROR_NO_COLLECTION_TYPE) + collection_type_description = self.collection_type_descriptions.for_collection_type(collection_type) # If we have elements, this is an internal request, don't need to load # objects from identifiers. if elements is None: - if collection_type_description.has_subcollections( ): + if collection_type_description.has_subcollections(): # Nested collection - recursively create collections and update identifiers. - self.__recursively_create_collections( trans, element_identifiers ) + self.__recursively_create_collections(trans, element_identifiers) new_collection = False for element_identifier in element_identifiers: if element_identifier.get("src") == "new_collection" and element_identifier.get('collection_type') == '': new_collection = True elements = self.__load_elements(trans, element_identifier['element_identifiers']) if not new_collection: - elements = self.__load_elements( trans, element_identifiers ) + elements = self.__load_elements(trans, element_identifiers) # else if elements is set, it better be an ordered dict! if elements is not self.ELEMENTS_UNINITIALIZED: type_plugin = collection_type_description.rank_type_plugin() - dataset_collection = builder.build_collection( type_plugin, elements ) + dataset_collection = builder.build_collection(type_plugin, elements) if hide_source_items: log.debug("Hiding source items during dataset collection creation") for dataset in dataset_collection.dataset_instances: dataset.visible = False else: - dataset_collection = model.DatasetCollection( populated=False ) + dataset_collection = model.DatasetCollection(populated=False) dataset_collection.collection_type = collection_type return dataset_collection - def set_collection_elements( self, dataset_collection, dataset_instances ): + def set_collection_elements(self, dataset_collection, dataset_instances): if dataset_collection.populated: raise Exception("Cannot reset elements of an already populated dataset collection.") collection_type = dataset_collection.collection_type - collection_type_description = self.collection_type_descriptions.for_collection_type( collection_type ) + collection_type_description = self.collection_type_descriptions.for_collection_type(collection_type) type_plugin = collection_type_description.rank_type_plugin() - builder.set_collection_elements( dataset_collection, type_plugin, dataset_instances ) + builder.set_collection_elements(dataset_collection, type_plugin, dataset_instances) dataset_collection.mark_as_populated() return dataset_collection - def collection_builder_for( self, dataset_collection ): + def collection_builder_for(self, dataset_collection): collection_type = dataset_collection.collection_type - collection_type_description = self.collection_type_descriptions.for_collection_type( collection_type ) - return builder.BoundCollectionBuilder( dataset_collection, collection_type_description ) + collection_type_description = self.collection_type_descriptions.for_collection_type(collection_type) + return builder.BoundCollectionBuilder(dataset_collection, collection_type_description) - def delete( self, trans, instance_type, id ): - dataset_collection_instance = self.get_dataset_collection_instance( trans, instance_type, id, check_ownership=True ) + def delete(self, trans, instance_type, id): + dataset_collection_instance = self.get_dataset_collection_instance(trans, instance_type, id, check_ownership=True) dataset_collection_instance.deleted = True - trans.sa_session.add( dataset_collection_instance ) - trans.sa_session.flush( ) + trans.sa_session.add(dataset_collection_instance) + trans.sa_session.flush() - def update( self, trans, instance_type, id, payload ): - dataset_collection_instance = self.get_dataset_collection_instance( trans, instance_type, id, check_ownership=True ) + def update(self, trans, instance_type, id, payload): + dataset_collection_instance = self.get_dataset_collection_instance(trans, instance_type, id, check_ownership=True) if trans.user is None: anon_allowed_payload = {} if 'deleted' in payload: - anon_allowed_payload[ 'deleted' ] = payload[ 'deleted' ] + anon_allowed_payload['deleted'] = payload['deleted'] if 'visible' in payload: - anon_allowed_payload[ 'visible' ] = payload[ 'visible' ] - payload = self._validate_and_parse_update_payload( anon_allowed_payload ) + anon_allowed_payload['visible'] = payload['visible'] + payload = self._validate_and_parse_update_payload(anon_allowed_payload) else: - payload = self._validate_and_parse_update_payload( payload ) - changed = self._set_from_dict( trans, dataset_collection_instance, payload ) + payload = self._validate_and_parse_update_payload(payload) + changed = self._set_from_dict(trans, dataset_collection_instance, payload) return changed - def copy( self, trans, parent, source, encoded_source_id ): + def copy(self, trans, parent, source, encoded_source_id): """ PRECONDITION: security checks on ability to add to parent occurred during load. """ assert source == "hdca" # for now - source_hdca = self.__get_history_collection_instance( trans, encoded_source_id ) + source_hdca = self.__get_history_collection_instance(trans, encoded_source_id) new_hdca = source_hdca.copy() tags_str = self.tag_manager.get_tags_str(source_hdca.tags) self.tag_manager.apply_item_tags(trans.get_user(), new_hdca, tags_str) - parent.add_dataset_collection( new_hdca ) - trans.sa_session.add( new_hdca ) + parent.add_dataset_collection(new_hdca) + trans.sa_session.add(new_hdca) trans.sa_session.flush() return new_hdca - def _set_from_dict( self, trans, dataset_collection_instance, new_data ): + def _set_from_dict(self, trans, dataset_collection_instance, new_data): # send what we can down into the model - changed = dataset_collection_instance.set_from_dict( new_data ) + changed = dataset_collection_instance.set_from_dict(new_data) # the rest (often involving the trans) - do here if 'annotation' in new_data.keys() and trans.get_user(): - dataset_collection_instance.add_item_annotation( trans.sa_session, trans.get_user(), dataset_collection_instance, new_data[ 'annotation' ] ) - changed[ 'annotation' ] = new_data[ 'annotation' ] + dataset_collection_instance.add_item_annotation(trans.sa_session, trans.get_user(), dataset_collection_instance, new_data['annotation']) + changed['annotation'] = new_data['annotation'] if 'tags' in new_data.keys() and trans.get_user(): # set_tags_from_list will flush on its own, no need to add to 'changed' here and incur a second flush. - self.tag_manager.set_tags_from_list( trans.get_user(), dataset_collection_instance, new_data[ 'tags' ] ) + self.tag_manager.set_tags_from_list(trans.get_user(), dataset_collection_instance, new_data['tags']) if changed.keys(): trans.sa_session.flush() return changed - def _validate_and_parse_update_payload( self, payload ): + def _validate_and_parse_update_payload(self, payload): validated_payload = {} for key, val in payload.items(): if val is None: continue - if key in ( 'name' ): - val = validation.validate_and_sanitize_basestring( key, val ) - validated_payload[ key ] = val - if key in ( 'deleted', 'visible' ): - validated_payload[ key ] = validation.validate_boolean( key, val ) + if key in ('name'): + val = validation.validate_and_sanitize_basestring(key, val) + validated_payload[key] = val + if key in ('deleted', 'visible'): + validated_payload[key] = validation.validate_boolean(key, val) elif key == 'tags': - validated_payload[ key ] = validation.validate_and_sanitize_basestring_list( key, val ) + validated_payload[key] = validation.validate_and_sanitize_basestring_list(key, val) return validated_payload def history_dataset_collections(self, history, query): collections = history.active_dataset_collections - collections = filter( query.direct_match, collections ) + collections = filter(query.direct_match, collections) return collections - def __persist( self, dataset_collection_instance ): + def __persist(self, dataset_collection_instance): context = self.model.context - context.add( dataset_collection_instance ) + context.add(dataset_collection_instance) context.flush() return dataset_collection_instance - def __recursively_create_collections( self, trans, element_identifiers ): - for index, element_identifier in enumerate( element_identifiers ): + def __recursively_create_collections(self, trans, element_identifiers): + for index, element_identifier in enumerate(element_identifiers): try: - if not element_identifier[ "src" ] == "new_collection": + if not element_identifier["src"] == "new_collection": # not a new collection, keep moving... continue except KeyError: @@ -247,23 +247,23 @@ def __recursively_create_collections( self, trans, element_identifiers ): continue # element identifier is a dict with src new_collection... - collection_type = element_identifier.get( "collection_type", None ) + collection_type = element_identifier.get("collection_type", None) collection = self.create_dataset_collection( trans=trans, collection_type=collection_type, - element_identifiers=element_identifier[ "element_identifiers" ], + element_identifiers=element_identifier["element_identifiers"], ) - element_identifier[ "__object__" ] = collection + element_identifier["__object__"] = collection return element_identifiers - def __load_elements( self, trans, element_identifiers ): + def __load_elements(self, trans, element_identifiers): elements = odict.odict() for element_identifier in element_identifiers: - elements[ element_identifier[ "name" ] ] = self.__load_element( trans, element_identifier ) + elements[element_identifier["name"]] = self.__load_element(trans, element_identifier) return elements - def __load_element( self, trans, element_identifier ): + def __load_element(self, trans, element_identifier): # if not isinstance( element_identifier, dict ): # # Is allowing this to just be the id of an hda too clever? Somewhat # # consistent with other API methods though. @@ -272,72 +272,72 @@ def __load_element( self, trans, element_identifier ): # Previously created collection already found in request, just pass # through as is. if "__object__" in element_identifier: - the_object = element_identifier[ "__object__" ] + the_object = element_identifier["__object__"] if the_object is not None and the_object.id: context = self.model.context if the_object not in context: - the_object = context.query( type(the_object) ).get(the_object.id) + the_object = context.query(type(the_object)).get(the_object.id) return the_object # dateset_identifier is dict {src=hda|ldda|hdca|new_collection, id=} try: - src_type = element_identifier.get( 'src', 'hda' ) + src_type = element_identifier.get('src', 'hda') except AttributeError: - raise MessageException( "Dataset collection element definition (%s) not dictionary-like." % element_identifier ) - encoded_id = element_identifier.get( 'id', None ) + raise MessageException("Dataset collection element definition (%s) not dictionary-like." % element_identifier) + encoded_id = element_identifier.get('id', None) if not src_type or not encoded_id: message_template = "Problem decoding element identifier %s - must contain a 'src' and a 'id'." message = message_template % element_identifier - raise RequestParameterInvalidException( message ) + raise RequestParameterInvalidException(message) if src_type == 'hda': - decoded_id = int( trans.app.security.decode_id( encoded_id ) ) - element = self.hda_manager.get_accessible( decoded_id, trans.user ) + decoded_id = int(trans.app.security.decode_id(encoded_id)) + element = self.hda_manager.get_accessible(decoded_id, trans.user) elif src_type == 'ldda': - element = self.ldda_manager.get( trans, encoded_id ) + element = self.ldda_manager.get(trans, encoded_id) elif src_type == 'hdca': # TODO: Option to copy? Force copy? Copy or allow if not owned? - element = self.__get_history_collection_instance( trans, encoded_id ).collection + element = self.__get_history_collection_instance(trans, encoded_id).collection # TODO: ldca. else: - raise RequestParameterInvalidException( "Unknown src_type parameter supplied '%s'." % src_type ) + raise RequestParameterInvalidException("Unknown src_type parameter supplied '%s'." % src_type) return element - def match_collections( self, collections_to_match ): + def match_collections(self, collections_to_match): """ May seem odd to place it here, but planning to grow sophistication and get plugin types involved so it will likely make sense in the future. """ - return MatchingCollections.for_collections( collections_to_match, self.collection_type_descriptions ) + return MatchingCollections.for_collections(collections_to_match, self.collection_type_descriptions) - def get_dataset_collection_instance( self, trans, instance_type, id, **kwds ): + def get_dataset_collection_instance(self, trans, instance_type, id, **kwds): """ """ if instance_type == "history": - return self.__get_history_collection_instance( trans, id, **kwds ) + return self.__get_history_collection_instance(trans, id, **kwds) elif instance_type == "library": - return self.__get_library_collection_instance( trans, id, **kwds ) + return self.__get_library_collection_instance(trans, id, **kwds) - def get_dataset_collection( self, trans, encoded_id ): - collection_id = int( trans.app.security.decode_id( encoded_id ) ) - collection = trans.sa_session.query( trans.app.model.DatasetCollection ).get( collection_id ) + def get_dataset_collection(self, trans, encoded_id): + collection_id = int(trans.app.security.decode_id(encoded_id)) + collection = trans.sa_session.query(trans.app.model.DatasetCollection).get(collection_id) return collection - def __get_history_collection_instance( self, trans, id, check_ownership=False, check_accessible=True ): - instance_id = int( trans.app.security.decode_id( id ) ) - collection_instance = trans.sa_session.query( trans.app.model.HistoryDatasetCollectionAssociation ).get( instance_id ) + def __get_history_collection_instance(self, trans, id, check_ownership=False, check_accessible=True): + instance_id = int(trans.app.security.decode_id(id)) + collection_instance = trans.sa_session.query(trans.app.model.HistoryDatasetCollectionAssociation).get(instance_id) if check_ownership: - self.history_manager.error_unless_owner( collection_instance.history, trans.user, current_history=trans.history ) + self.history_manager.error_unless_owner(collection_instance.history, trans.user, current_history=trans.history) if check_accessible: - self.history_manager.error_unless_accessible( collection_instance.history, trans.user, current_history=trans.history ) + self.history_manager.error_unless_accessible(collection_instance.history, trans.user, current_history=trans.history) return collection_instance - def __get_library_collection_instance( self, trans, id, check_ownership=False, check_accessible=True ): + def __get_library_collection_instance(self, trans, id, check_ownership=False, check_accessible=True): if check_ownership: - raise NotImplemented( "Functionality (getting library dataset collection with ownership check) unimplemented." ) - instance_id = int( trans.security.decode_id( id ) ) - collection_instance = trans.sa_session.query( trans.app.model.LibraryDatasetCollectionAssociation ).get( instance_id ) + raise NotImplemented("Functionality (getting library dataset collection with ownership check) unimplemented.") + instance_id = int(trans.security.decode_id(id)) + collection_instance = trans.sa_session.query(trans.app.model.LibraryDatasetCollectionAssociation).get(instance_id) if check_accessible: - if not trans.app.security_agent.can_access_library_item( trans.get_current_user_roles(), collection_instance, trans.user ): - raise ItemAccessibilityException( "LibraryDatasetCollectionAssociation is not accessible to the current user", type='error' ) + if not trans.app.security_agent.can_access_library_item(trans.get_current_user_roles(), collection_instance, trans.user): + raise ItemAccessibilityException("LibraryDatasetCollectionAssociation is not accessible to the current user", type='error') return collection_instance diff --git a/lib/galaxy/managers/collections_util.py b/lib/galaxy/managers/collections_util.py index a3e19c160fa5..6c6f8067d73a 100644 --- a/lib/galaxy/managers/collections_util.py +++ b/lib/galaxy/managers/collections_util.py @@ -3,7 +3,7 @@ from galaxy import exceptions, model, web from galaxy.util import string_as_bool -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) ERROR_MESSAGE_UNKNOWN_SRC = "Unknown dataset source (src) %s." ERROR_MESSAGE_NO_NESTED_IDENTIFIERS = "Dataset source new_collection requires nested element_identifiers for new collection." @@ -13,56 +13,56 @@ ERROR_MESSAGE_DUPLICATED_IDENTIFIER_FOUND = "Found duplicated element identifier name %s." -def api_payload_to_create_params( payload ): +def api_payload_to_create_params(payload): """ Cleanup API payload to pass into dataset_collections. """ - required_parameters = [ "collection_type", "element_identifiers" ] - missing_parameters = [ p for p in required_parameters if p not in payload ] + required_parameters = ["collection_type", "element_identifiers"] + missing_parameters = [p for p in required_parameters if p not in payload] if missing_parameters: message = "Missing required parameters %s" % missing_parameters - raise exceptions.ObjectAttributeMissingException( message ) + raise exceptions.ObjectAttributeMissingException(message) params = dict( - collection_type=payload.get( "collection_type" ), - element_identifiers=payload.get( "element_identifiers" ), - name=payload.get( "name", None ), - hide_source_items=string_as_bool( payload.get( "hide_source_items", False ) ) + collection_type=payload.get("collection_type"), + element_identifiers=payload.get("element_identifiers"), + name=payload.get("name", None), + hide_source_items=string_as_bool(payload.get("hide_source_items", False)) ) return params -def validate_input_element_identifiers( element_identifiers ): +def validate_input_element_identifiers(element_identifiers): """ Scan through the list of element identifiers supplied by the API consumer and verify the structure is valid. """ - log.debug( "Validating %d element identifiers for collection creation." % len( element_identifiers ) ) + log.debug("Validating %d element identifiers for collection creation." % len(element_identifiers)) identifier_names = set() for element_identifier in element_identifiers: if "__object__" in element_identifier: - message = ERROR_MESSAGE_INVALID_PARAMETER_FOUND % ( "__object__", element_identifier ) - raise exceptions.RequestParameterInvalidException( message ) + message = ERROR_MESSAGE_INVALID_PARAMETER_FOUND % ("__object__", element_identifier) + raise exceptions.RequestParameterInvalidException(message) if "name" not in element_identifier: message = ERROR_MESSAGE_NO_NAME % element_identifier - raise exceptions.RequestParameterInvalidException( message ) - name = element_identifier[ "name" ] + raise exceptions.RequestParameterInvalidException(message) + name = element_identifier["name"] if name in identifier_names: message = ERROR_MESSAGE_DUPLICATED_IDENTIFIER_FOUND % name - raise exceptions.RequestParameterInvalidException( message ) + raise exceptions.RequestParameterInvalidException(message) else: - identifier_names.add( name ) - src = element_identifier.get( "src", "hda" ) - if src not in [ "hda", "hdca", "ldda", "new_collection" ]: + identifier_names.add(name) + src = element_identifier.get("src", "hda") + if src not in ["hda", "hdca", "ldda", "new_collection"]: message = ERROR_MESSAGE_UNKNOWN_SRC % src - raise exceptions.RequestParameterInvalidException( message ) + raise exceptions.RequestParameterInvalidException(message) if src == "new_collection": if "element_identifiers" not in element_identifier: message = ERROR_MESSAGE_NO_NESTED_IDENTIFIERS - raise exceptions.RequestParameterInvalidException( ERROR_MESSAGE_NO_NESTED_IDENTIFIERS ) + raise exceptions.RequestParameterInvalidException(ERROR_MESSAGE_NO_NESTED_IDENTIFIERS) if "collection_type" not in element_identifier: message = ERROR_MESSAGE_NO_COLLECTION_TYPE % element_identifier - raise exceptions.RequestParameterInvalidException( message ) - validate_input_element_identifiers( element_identifier[ "element_identifiers" ] ) + raise exceptions.RequestParameterInvalidException(message) + validate_input_element_identifiers(element_identifier["element_identifiers"]) def get_hda_and_element_identifiers(dataset_collection_instance): @@ -91,36 +91,36 @@ def get_subcollections(collection, name=""): return names, hdas -def dictify_dataset_collection_instance( dataset_collection_instance, parent, security, view="element" ): - dict_value = dataset_collection_instance.to_dict( view=view ) - encoded_id = security.encode_id( dataset_collection_instance.id ) - if isinstance( parent, model.History ): - encoded_history_id = security.encode_id( parent.id ) - dict_value[ 'url' ] = web.url_for( 'history_content_typed', history_id=encoded_history_id, id=encoded_id, type="dataset_collection" ) - elif isinstance( parent, model.LibraryFolder ): - encoded_library_id = security.encode_id( parent.library.id ) - encoded_folder_id = security.encode_id( parent.id ) +def dictify_dataset_collection_instance(dataset_collection_instance, parent, security, view="element"): + dict_value = dataset_collection_instance.to_dict(view=view) + encoded_id = security.encode_id(dataset_collection_instance.id) + if isinstance(parent, model.History): + encoded_history_id = security.encode_id(parent.id) + dict_value['url'] = web.url_for('history_content_typed', history_id=encoded_history_id, id=encoded_id, type="dataset_collection") + elif isinstance(parent, model.LibraryFolder): + encoded_library_id = security.encode_id(parent.library.id) + encoded_folder_id = security.encode_id(parent.id) # TODO: Work in progress - this end-point is not right yet... - dict_value[ 'url' ] = web.url_for( 'library_content', library_id=encoded_library_id, id=encoded_id, folder_id=encoded_folder_id ) + dict_value['url'] = web.url_for('library_content', library_id=encoded_library_id, id=encoded_id, folder_id=encoded_folder_id) if view == "element": collection = dataset_collection_instance.collection - dict_value[ 'elements' ] = [ dictify_element(_) for _ in collection.elements ] - dict_value[ 'populated' ] = collection.populated - security.encode_all_ids( dict_value, recursive=True ) # TODO: Use Kyle's recursive formulation of this. + dict_value['elements'] = [dictify_element(_) for _ in collection.elements] + dict_value['populated'] = collection.populated + security.encode_all_ids(dict_value, recursive=True) # TODO: Use Kyle's recursive formulation of this. return dict_value -def dictify_element( element ): - dictified = element.to_dict( view="element" ) +def dictify_element(element): + dictified = element.to_dict(view="element") object_detials = element.element_object.to_dict() if element.child_collection: # Recursively yield elements for each nested collection... child_collection = element.child_collection - object_detials[ "elements" ] = [ dictify_element(_) for _ in child_collection.elements ] - object_detials[ "populated" ] = child_collection.populated + object_detials["elements"] = [dictify_element(_) for _ in child_collection.elements] + object_detials["populated"] = child_collection.populated - dictified[ "object" ] = object_detials + dictified["object"] = object_detials return dictified -__all__ = ( 'api_payload_to_create_params', 'dictify_dataset_collection_instance' ) +__all__ = ('api_payload_to_create_params', 'dictify_dataset_collection_instance') diff --git a/lib/galaxy/managers/configuration.py b/lib/galaxy/managers/configuration.py index f12109ba504c..727a3c3683b0 100644 --- a/lib/galaxy/managers/configuration.py +++ b/lib/galaxy/managers/configuration.py @@ -12,93 +12,94 @@ from galaxy.managers import base import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class ConfigSerializer( base.ModelSerializer ): +class ConfigSerializer(base.ModelSerializer): """Configuration (galaxy.ini) settings viewable by all users""" - def __init__( self, app ): - super( ConfigSerializer, self ).__init__( app ) + def __init__(self, app): + super(ConfigSerializer, self).__init__(app) self.default_view = 'all' - self.add_view( 'all', self.serializers.keys() ) + self.add_view('all', self.serializers.keys()) - def default_serializer( self, config, key ): - return getattr( config, key, None ) + def default_serializer(self, config, key): + return getattr(config, key, None) - def add_serializers( self ): - def _defaults_to( default ): - return lambda i, k, **c: getattr( i, k, default ) + def add_serializers(self): + def _defaults_to(default): + return lambda i, k, **c: getattr(i, k, default) self.serializers = { # TODO: this is available from user data, remove 'is_admin_user' : lambda *a, **c: False, - 'brand' : _defaults_to( '' ), + 'brand' : _defaults_to(''), # TODO: this doesn't seem right - 'logo_url' : lambda i, k, **c: self.url_for( i.get( k, '/' ) ), - 'logo_src' : lambda i, k, **c: self.url_for( '/static/images/galaxyIcon_noText.png' ), - 'terms_url' : _defaults_to( '' ), - - 'wiki_url' : _defaults_to( self.app.config.wiki_url ), - 'search_url' : _defaults_to( self.app.config.wiki_url.rstrip("/") + "/search/" ), - 'mailing_lists' : _defaults_to( self.app.config.wiki_url.rstrip("/") + "/mailing-lists/"), - 'screencasts_url' : _defaults_to( "https://vimeo.com/galaxyproject" ), - 'citation_url' : _defaults_to( self.app.config.citation_url ), - 'support_url' : _defaults_to( self.app.config.support_url ), - 'lims_doc_url' : _defaults_to( "https://usegalaxy.org/u/rkchak/p/sts" ), - 'biostar_url' : _defaults_to( '' ), - 'biostar_url_redirect' : lambda *a, **c: self.url_for( controller='biostar', action='biostar_redirect', qualified=True ), - - 'enable_beta_ts_api_install' : _defaults_to( False ), - 'enable_communication_server' : _defaults_to( False ), - 'communication_server_port' : _defaults_to( None ), - 'communication_server_host' : _defaults_to( None ), - 'persistent_communication_rooms' : _defaults_to( None ), - 'allow_user_impersonation' : _defaults_to( False ), - 'allow_user_creation' : _defaults_to( False ), - 'use_remote_user' : _defaults_to( None ), - 'enable_openid' : _defaults_to( False ), - 'enable_quotas' : _defaults_to( False ), - 'remote_user_logout_href' : _defaults_to( '' ), - 'datatypes_disable_auto' : _defaults_to( False ), - 'allow_user_dataset_purge' : _defaults_to( False ), - 'ga_code' : _defaults_to( None ), - 'enable_unique_workflow_defaults' : _defaults_to( False ), - 'has_user_tool_filters' : _defaults_to( False ), + 'logo_url' : lambda i, k, **c: self.url_for(i.get(k, '/')), + 'logo_src' : lambda i, k, **c: self.url_for('/static/images/galaxyIcon_noText.png'), + 'terms_url' : _defaults_to(''), + + 'wiki_url' : _defaults_to(self.app.config.wiki_url), + 'search_url' : _defaults_to(self.app.config.wiki_url.rstrip("/") + "/search/"), + 'mailing_lists' : _defaults_to(self.app.config.wiki_url.rstrip("/") + "/mailing-lists/"), + 'screencasts_url' : _defaults_to("https://vimeo.com/galaxyproject"), + 'genomespace_ui_url' : _defaults_to(None), + 'citation_url' : _defaults_to(self.app.config.citation_url), + 'support_url' : _defaults_to(self.app.config.support_url), + 'lims_doc_url' : _defaults_to("https://usegalaxy.org/u/rkchak/p/sts"), + 'biostar_url' : _defaults_to(''), + 'biostar_url_redirect' : lambda *a, **c: self.url_for(controller='biostar', action='biostar_redirect', qualified=True), + + 'enable_beta_ts_api_install' : _defaults_to(False), + 'enable_communication_server' : _defaults_to(False), + 'communication_server_port' : _defaults_to(None), + 'communication_server_host' : _defaults_to(None), + 'persistent_communication_rooms' : _defaults_to(None), + 'allow_user_impersonation' : _defaults_to(False), + 'allow_user_creation' : _defaults_to(False), + 'use_remote_user' : _defaults_to(None), + 'enable_openid' : _defaults_to(False), + 'enable_quotas' : _defaults_to(False), + 'remote_user_logout_href' : _defaults_to(''), + 'datatypes_disable_auto' : _defaults_to(False), + 'allow_user_dataset_purge' : _defaults_to(False), + 'ga_code' : _defaults_to(None), + 'enable_unique_workflow_defaults' : _defaults_to(False), + 'has_user_tool_filters' : _defaults_to(False), # TODO: is there no 'correct' way to get an api url? controller='api', action='tools' is a hack # at any rate: the following works with path_prefix but is still brittle # TODO: change this to (more generic) upload_path and incorporate config.nginx_upload_path into building it - 'nginx_upload_path' : lambda i, k, **c: getattr( i, k, False ) or self.url_for( '/api/tools' ), - 'ftp_upload_dir' : _defaults_to( None ), - 'ftp_upload_site' : _defaults_to( None ), - 'version_major' : _defaults_to( None ), - 'require_login' : _defaults_to( None ), - 'inactivity_box_content' : _defaults_to( None ), - 'message_box_content' : _defaults_to( None ), - 'message_box_visible' : _defaults_to( False ), - 'message_box_class' : _defaults_to( 'info' ), + 'nginx_upload_path' : lambda i, k, **c: getattr(i, k, False) or self.url_for('/api/tools'), + 'ftp_upload_dir' : _defaults_to(None), + 'ftp_upload_site' : _defaults_to(None), + 'version_major' : _defaults_to(None), + 'require_login' : _defaults_to(None), + 'inactivity_box_content' : _defaults_to(None), + 'message_box_content' : _defaults_to(None), + 'message_box_visible' : _defaults_to(False), + 'message_box_class' : _defaults_to('info'), 'server_startttime' : lambda i, k, **c: server_starttime, } -class AdminConfigSerializer( ConfigSerializer ): +class AdminConfigSerializer(ConfigSerializer): """Configuration attributes viewable only by admin users""" - def add_serializers( self ): - super( AdminConfigSerializer, self ).add_serializers() + def add_serializers(self): + super(AdminConfigSerializer, self).add_serializers() - def _defaults_to( default ): - return lambda i, k, **c: getattr( i, k, default ) + def _defaults_to(default): + return lambda i, k, **c: getattr(i, k, default) self.serializers.update({ # TODO: this is available from user serialization: remove 'is_admin_user' : lambda *a: True, - 'library_import_dir' : _defaults_to( None ), - 'user_library_import_dir' : _defaults_to( None ), - 'allow_library_path_paste' : _defaults_to( False ), - 'allow_user_deletion' : _defaults_to( False ), + 'library_import_dir' : _defaults_to(None), + 'user_library_import_dir' : _defaults_to(None), + 'allow_library_path_paste' : _defaults_to(False), + 'allow_user_deletion' : _defaults_to(False), }) diff --git a/lib/galaxy/managers/containers.py b/lib/galaxy/managers/containers.py index cac49647b999..a2da3c9982d1 100644 --- a/lib/galaxy/managers/containers.py +++ b/lib/galaxy/managers/containers.py @@ -14,11 +14,11 @@ import galaxy.util import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) # ==== -class ContainerManagerMixin( object ): +class ContainerManagerMixin(object): """ A class that tracks/contains two types of items: 1) some non-container object (such as datasets) @@ -37,81 +37,81 @@ class ContainerManagerMixin( object ): default_order_by = None # ---- interface - def contents( self, container ): + def contents(self, container): """ Returns both types of contents: filtered and in some order. """ iters = [] - iters.append( self.contained( container ) ) - iters.append( self.subcontainers( container ) ) - return galaxy.util.merge_sorted_iterables( self.order_contents_on, *iters ) + iters.append(self.contained(container)) + iters.append(self.subcontainers(container)) + return galaxy.util.merge_sorted_iterables(self.order_contents_on, *iters) - def contained( self, container, **kwargs ): + def contained(self, container, **kwargs): """ Returns non-container objects. """ - return self._filter_contents( container, self.contained_class, **kwargs ) + return self._filter_contents(container, self.contained_class, **kwargs) - def subcontainers( self, container, **kwargs ): + def subcontainers(self, container, **kwargs): """ Returns only the containers within this one. """ - return self._filter_contents( container, self.subcontainer_class, **kwargs ) + return self._filter_contents(container, self.subcontainer_class, **kwargs) # ---- private - def _filter_contents( self, container, content_class, **kwargs ): + def _filter_contents(self, container, content_class, **kwargs): # TODO: use list (or by_history etc.) - container_filter = self._filter_to_contained( container, content_class ) - query = self.session().query( content_class ).filter( container_filter ) + container_filter = self._filter_to_contained(container, content_class) + query = self.session().query(content_class).filter(container_filter) return query - def _get_filter_for_contained( self, container, content_class ): - raise galaxy.exceptions.NotImplemented( 'Abstract class' ) + def _get_filter_for_contained(self, container, content_class): + raise galaxy.exceptions.NotImplemented('Abstract class') - def _content_manager( self, content ): - raise galaxy.exceptions.NotImplemented( 'Abstract class' ) + def _content_manager(self, content): + raise galaxy.exceptions.NotImplemented('Abstract class') -class LibraryFolderAsContainerManagerMixin( ContainerManagerMixin ): +class LibraryFolderAsContainerManagerMixin(ContainerManagerMixin): # can contain two types of subcontainer: LibraryFolder, LibraryDatasetCollectionAssociation # has as the top level container: Library contained_class = model.LibraryDataset subcontainer_class = model.LibraryFolder # subcontainer_class = model.LibraryDatasetCollectionAssociation - order_contents_on = operator.attrgetter( 'create_time' ) + order_contents_on = operator.attrgetter('create_time') - def _get_filter_for_contained( self, container, content_class ): + def _get_filter_for_contained(self, container, content_class): if content_class == self.subcontainer_class: return self.subcontainer_class.parent == container return self.contained_class.folder == container - def _content_manager( self, content ): + def _content_manager(self, content): # type snifffing is inevitable - if isinstance( content, model.LibraryDataset ): + if isinstance(content, model.LibraryDataset): return self.lda_manager - elif isinstance( content, model.LibraryFolder ): + elif isinstance(content, model.LibraryFolder): return self.folder_manager - raise TypeError( 'Unknown contents class: ' + str( content ) ) + raise TypeError('Unknown contents class: ' + str(content)) -class DatasetCollectionAsContainerManagerMixin( ContainerManagerMixin ): +class DatasetCollectionAsContainerManagerMixin(ContainerManagerMixin): # (note: unlike the other collections, dc's wrap both contained and subcontainers in this class) contained_class = model.DatasetCollectionElement subcontainer_class = model.DatasetCollection - order_contents_on = operator.attrgetter( 'element_index' ) + order_contents_on = operator.attrgetter('element_index') - def _get_filter_for_contained( self, container, content_class ): + def _get_filter_for_contained(self, container, content_class): return content_class.collection == container - def _content_manager( self, content ): + def _content_manager(self, content): # type snifffing is inevitable - if isinstance( content, model.DatasetCollectionElement ): + if isinstance(content, model.DatasetCollectionElement): return self.collection_manager - elif isinstance( content, model.DatasetCollection ): + elif isinstance(content, model.DatasetCollection): return self.collection_manager - raise TypeError( 'Unknown contents class: ' + str( content ) ) + raise TypeError('Unknown contents class: ' + str(content)) # ==== @@ -121,11 +121,11 @@ class ContainableModelMixin: """ # ---- interface - def parent_container( self, containable ): + def parent_container(self, containable): """ Return this item's parent container or None if unrecorded. """ - raise galaxy.exceptions.NotImplemented( 'Abstract class' ) + raise galaxy.exceptions.NotImplemented('Abstract class') - def set_parent_container( self, containable, new_parent_container ): - raise galaxy.exceptions.NotImplemented( 'Abstract class' ) + def set_parent_container(self, containable, new_parent_container): + raise galaxy.exceptions.NotImplemented('Abstract class') diff --git a/lib/galaxy/managers/context.py b/lib/galaxy/managers/context.py index aaa192cb4855..291cddc5b915 100644 --- a/lib/galaxy/managers/context.py +++ b/lib/galaxy/managers/context.py @@ -9,19 +9,19 @@ from galaxy.util import bunch -class ProvidesAppContext( object ): +class ProvidesAppContext(object): """ For transaction-like objects to provide Galaxy convience layer for database and event handling. Mixed in class must provide `app` property. """ - def log_action( self, user=None, action=None, context=None, params=None): + def log_action(self, user=None, action=None, context=None, params=None): """ Application-level logging of user actions. """ if self.app.config.log_actions: - action = self.app.model.UserAction(action=action, context=context, params=text_type( dumps( params ) ) ) + action = self.app.model.UserAction(action=action, context=context, params=text_type(dumps(params))) try: if user: action.user = user @@ -33,10 +33,10 @@ def log_action( self, user=None, action=None, context=None, params=None): action.session_id = self.galaxy_session.id except: action.session_id = None - self.sa_session.add( action ) + self.sa_session.add(action) self.sa_session.flush() - def log_event( self, message, tool_id=None, **kwargs ): + def log_event(self, message, tool_id=None, **kwargs): """ Application level logging. Still needs fleshing out (log levels and such) Logging events is a config setting - if False, do not log. @@ -64,11 +64,11 @@ def log_event( self, message, tool_id=None, **kwargs ): event.session_id = self.galaxy_session.id except: event.session_id = None - self.sa_session.add( event ) + self.sa_session.add(event) self.sa_session.flush() @property - def sa_session( self ): + def sa_session(self): """ Returns a SQLAlchemy session -- currently just gets the current session from the threadlocal session context, but this is provided @@ -76,7 +76,7 @@ def sa_session( self ): """ return self.app.model.context.current - def expunge_all( self ): + def expunge_all(self): app = self.app context = app.model.context context.expunge_all() @@ -91,20 +91,20 @@ def get_toolbox(self): return self.app.toolbox @property - def model( self ): + def model(self): return self.app.model @property - def install_model( self ): + def install_model(self): return self.app.install_model def request_types(self): - if self.sa_session.query( self.app.model.RequestType ).filter_by( deleted=False ).count() > 0: + if self.sa_session.query(self.app.model.RequestType).filter_by(deleted=False).count() > 0: return True return False -class ProvidesUserContext( object ): +class ProvidesUserContext(object): """ For transaction-like objects to provide Galaxy convience layer for reasoning about users. @@ -113,10 +113,10 @@ class ProvidesUserContext( object ): """ @property - def anonymous( self ): + def anonymous(self): return self.user is None and not self.api_inherit_admin - def get_current_user_roles( self ): + def get_current_user_roles(self): user = self.user if user: roles = user.all_roles() @@ -124,13 +124,13 @@ def get_current_user_roles( self ): roles = [] return roles - def user_is_admin( self ): + def user_is_admin(self): if self.api_inherit_admin: return True return self.user and self.user.email in self.app.config.admin_users_list - def user_can_do_run_as( self ): - run_as_users = [ user for user in self.app.config.get( "api_allow_run_as", "" ).split( "," ) if user ] + def user_can_do_run_as(self): + run_as_users = [user for user in self.app.config.get("api_allow_run_as", "").split(",") if user] if not run_as_users: return False user_in_run_as_users = self.user and self.user.email in run_as_users @@ -139,7 +139,7 @@ def user_can_do_run_as( self ): return can_do_run_as @property - def user_ftp_dir( self ): + def user_ftp_dir(self): base_dir = self.app.config.ftp_upload_dir if base_dir is None: return None @@ -155,7 +155,7 @@ def user_ftp_dir( self ): return path -class ProvidesHistoryContext( object ): +class ProvidesHistoryContext(object): """ For transaction-like objects to provide Galaxy convience layer for reasoning about histories. @@ -163,7 +163,7 @@ class ProvidesHistoryContext( object ): properties. """ - def db_dataset_for( self, dbkey ): + def db_dataset_for(self, dbkey): """ Returns the db_file dataset associated/needed by `dataset`, or `None`. """ @@ -171,22 +171,22 @@ def db_dataset_for( self, dbkey ): if self.history is None: return None # TODO: when does this happen? is it Bunch or util.bunch.Bunch? - if isinstance( self.history, bunch.Bunch ): + if isinstance(self.history, bunch.Bunch): # The API presents a Bunch for a history. Until the API is # more fully featured for handling this, also return None. return None - datasets = self.sa_session.query( self.app.model.HistoryDatasetAssociation ) \ - .filter_by( deleted=False, history_id=self.history.id, extension="len" ) + datasets = self.sa_session.query(self.app.model.HistoryDatasetAssociation) \ + .filter_by(deleted=False, history_id=self.history.id, extension="len") for ds in datasets: if dbkey == ds.dbkey: return ds return None @property - def db_builds( self ): + def db_builds(self): """ Returns the builds defined by galaxy and the builds defined by the user (chromInfo in history). """ # FIXME: This method should be removed - return self.app.genome_builds.get_genome_build_names( trans=self ) + return self.app.genome_builds.get_genome_build_names(trans=self) diff --git a/lib/galaxy/managers/datasets.py b/lib/galaxy/managers/datasets.py index db95c1f0aa1d..82b5d831f4e3 100644 --- a/lib/galaxy/managers/datasets.py +++ b/lib/galaxy/managers/datasets.py @@ -17,10 +17,10 @@ from galaxy.managers import users import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class DatasetManager( base.ModelManager, secured.AccessibleManagerMixin, deletable.PurgableManagerMixin ): +class DatasetManager(base.ModelManager, secured.AccessibleManagerMixin, deletable.PurgableManagerMixin): """ Manipulate datasets: the components contained in DatasetAssociations/DatasetInstances/HDAs/LDDAs """ @@ -29,72 +29,72 @@ class DatasetManager( base.ModelManager, secured.AccessibleManagerMixin, deletab # TODO:?? get + error_if_uploading is common pattern, should upload check be worked into access/owed? - def __init__( self, app ): - super( DatasetManager, self ).__init__( app ) - self.permissions = DatasetRBACPermissions( app ) + def __init__(self, app): + super(DatasetManager, self).__init__(app) + self.permissions = DatasetRBACPermissions(app) # needed for admin test - self.user_manager = users.UserManager( app ) + self.user_manager = users.UserManager(app) - def create( self, manage_roles=None, access_roles=None, flush=True, **kwargs ): + def create(self, manage_roles=None, access_roles=None, flush=True, **kwargs): """ Create and return a new Dataset object. """ # default to NEW state on new datasets - kwargs.update( dict( state=( kwargs.get( 'state', model.Dataset.states.NEW ) ) ) ) - dataset = model.Dataset( **kwargs ) - self.session().add( dataset ) + kwargs.update(dict(state=(kwargs.get('state', model.Dataset.states.NEW)))) + dataset = model.Dataset(**kwargs) + self.session().add(dataset) - self.permissions.set( dataset, manage_roles, access_roles, flush=False ) + self.permissions.set(dataset, manage_roles, access_roles, flush=False) if flush: self.session().flush() return dataset - def copy( self, dataset, **kwargs ): - raise galaxy.exceptions.NotImplemented( 'Datasets cannot be copied' ) + def copy(self, dataset, **kwargs): + raise galaxy.exceptions.NotImplemented('Datasets cannot be copied') - def purge( self, dataset, flush=True ): + def purge(self, dataset, flush=True): """ Remove the object_store/file for this dataset from storage and mark as purged. :raises exceptions.ConfigDoesNotAllowException: if the instance doesn't allow """ - self.error_unless_dataset_purge_allowed( dataset ) + self.error_unless_dataset_purge_allowed(dataset) # the following also marks dataset as purged and deleted dataset.full_delete() - self.session().add( dataset ) + self.session().add(dataset) if flush: self.session().flush() return dataset # TODO: this may be more conv. somewhere else # TODO: how to allow admin bypass? - def error_unless_dataset_purge_allowed( self, msg=None ): + def error_unless_dataset_purge_allowed(self, msg=None): if not self.app.config.allow_user_dataset_purge: msg = msg or 'This instance does not allow user dataset purging' - raise exceptions.ConfigDoesNotAllowException( msg ) + raise exceptions.ConfigDoesNotAllowException(msg) # .... accessibility # datasets can implement the accessible interface, but accessibility is checked in an entirely different way # than those resources that have a user attribute (histories, pages, etc.) - def is_accessible( self, dataset, user, **kwargs ): + def is_accessible(self, dataset, user, **kwargs): """ Is this dataset readable/viewable to user? """ - if self.user_manager.is_admin( user ): + if self.user_manager.is_admin(user): return True - if self.has_access_permission( dataset, user ): + if self.has_access_permission(dataset, user): return True return False - def has_access_permission( self, dataset, user ): + def has_access_permission(self, dataset, user): """ Return T/F if the user has role-based access to the dataset. """ roles = user.all_roles_exploiting_cache() if user else [] - return self.app.security_agent.can_access_dataset( roles, dataset ) + return self.app.security_agent.can_access_dataset(roles, dataset) # TODO: implement above for groups # TODO: datatypes? @@ -103,50 +103,50 @@ def has_access_permission( self, dataset, user ): # TODO: SecurityAgentDatasetRBACPermissions( object ): -class DatasetRBACPermissions( object ): +class DatasetRBACPermissions(object): - def __init__( self, app ): + def __init__(self, app): self.app = app - self.access = rbac_secured.AccessDatasetRBACPermission( app ) - self.manage = rbac_secured.ManageDatasetRBACPermission( app ) + self.access = rbac_secured.AccessDatasetRBACPermission(app) + self.manage = rbac_secured.ManageDatasetRBACPermission(app) # TODO: temporary facade over security_agent - def available_roles( self, trans, dataset, controller='root' ): - return self.app.security_agent.get_legitimate_roles( trans, dataset, controller ) + def available_roles(self, trans, dataset, controller='root'): + return self.app.security_agent.get_legitimate_roles(trans, dataset, controller) - def get( self, dataset, flush=True ): - manage = self.manage.by_dataset( dataset ) - access = self.access.by_dataset( dataset ) - return ( manage, access ) + def get(self, dataset, flush=True): + manage = self.manage.by_dataset(dataset) + access = self.access.by_dataset(dataset) + return (manage, access) - def set( self, dataset, manage_roles, access_roles, flush=True ): - manage = self.manage.set( dataset, manage_roles or [], flush=False ) - access = self.access.set( dataset, access_roles or [], flush=flush ) - return ( manage, access ) + def set(self, dataset, manage_roles, access_roles, flush=True): + manage = self.manage.set(dataset, manage_roles or [], flush=False) + access = self.access.set(dataset, access_roles or [], flush=flush) + return (manage, access) # ---- conv. settings - def set_public_with_single_manager( self, dataset, user, flush=True ): - manage = self.manage.grant( dataset, user, flush=flush ) - self.access.clear( dataset, flush=False ) - return ( [ manage ], [] ) + def set_public_with_single_manager(self, dataset, user, flush=True): + manage = self.manage.grant(dataset, user, flush=flush) + self.access.clear(dataset, flush=False) + return ([manage], []) - def set_private_to_one_user( self, dataset, user, flush=True ): - manage = self.manage.grant( dataset, user, flush=False ) - access = self.access.set_private( dataset, user, flush=flush ) - return ( [ manage ], access ) + def set_private_to_one_user(self, dataset, user, flush=True): + manage = self.manage.grant(dataset, user, flush=False) + access = self.access.set_private(dataset, user, flush=flush) + return ([manage], access) -class DatasetSerializer( base.ModelSerializer, deletable.PurgableSerializerMixin ): +class DatasetSerializer(base.ModelSerializer, deletable.PurgableSerializerMixin): model_manager_class = DatasetManager - def __init__( self, app ): - super( DatasetSerializer, self ).__init__( app ) + def __init__(self, app): + super(DatasetSerializer, self).__init__(app) self.dataset_manager = self.manager # needed for admin test - self.user_manager = users.UserManager( app ) + self.user_manager = users.UserManager(app) self.default_view = 'summary' - self.add_view( 'summary', [ + self.add_view('summary', [ 'id', 'create_time', 'update_time', @@ -163,116 +163,116 @@ def __init__( self, app ): ]) # could do visualizations and/or display_apps - def add_serializers( self ): - super( DatasetSerializer, self ).add_serializers() - deletable.PurgableSerializerMixin.add_serializers( self ) + def add_serializers(self): + super(DatasetSerializer, self).add_serializers() + deletable.PurgableSerializerMixin.add_serializers(self) self.serializers.update({ 'create_time' : self.serialize_date, 'update_time' : self.serialize_date, - 'uuid' : lambda i, k, **c: str( i.uuid ) if i.uuid else None, + 'uuid' : lambda i, k, **c: str(i.uuid) if i.uuid else None, 'file_name' : self.serialize_file_name, 'extra_files_path' : self.serialize_extra_files_path, 'permissions' : self.serialize_permissions, - 'total_size' : lambda i, k, **c: int( i.get_total_size() ), - 'file_size' : lambda i, k, **c: int( i.get_size() ) + 'total_size' : lambda i, k, **c: int(i.get_total_size()), + 'file_size' : lambda i, k, **c: int(i.get_size()) }) - def serialize_file_name( self, dataset, key, user=None, **context ): + def serialize_file_name(self, dataset, key, user=None, **context): """ If the config allows or the user is admin, return the file name of the file that contains this dataset's data. """ - is_admin = self.user_manager.is_admin( user ) + is_admin = self.user_manager.is_admin(user) # expensive: allow config option due to cost of operation if is_admin or self.app.config.expose_dataset_path: if not dataset.purged: return dataset.file_name self.skip() - def serialize_extra_files_path( self, dataset, key, user=None, **context ): + def serialize_extra_files_path(self, dataset, key, user=None, **context): """ If the config allows or the user is admin, return the file path. """ - is_admin = self.user_manager.is_admin( user ) + is_admin = self.user_manager.is_admin(user) # expensive: allow config option due to cost of operation if is_admin or self.app.config.expose_dataset_path: if not dataset.purged: return dataset.extra_files_path self.skip() - def serialize_permissions( self, dataset, key, user=None, **context ): + def serialize_permissions(self, dataset, key, user=None, **context): """ """ - if not self.dataset_manager.permissions.manage.is_permitted( dataset, user ): + if not self.dataset_manager.permissions.manage.is_permitted(dataset, user): self.skip() - management_permissions = self.dataset_manager.permissions.manage.by_dataset( dataset ) - access_permissions = self.dataset_manager.permissions.access.by_dataset( dataset ) + management_permissions = self.dataset_manager.permissions.manage.by_dataset(dataset) + access_permissions = self.dataset_manager.permissions.access.by_dataset(dataset) permissions = { - 'manage' : [ self.app.security.encode_id( perm.role.id ) for perm in management_permissions ], - 'access' : [ self.app.security.encode_id( perm.role.id ) for perm in access_permissions ], + 'manage' : [self.app.security.encode_id(perm.role.id) for perm in management_permissions], + 'access' : [self.app.security.encode_id(perm.role.id) for perm in access_permissions], } return permissions -class DatasetDeserializer( base.ModelDeserializer, deletable.PurgableDeserializerMixin ): +class DatasetDeserializer(base.ModelDeserializer, deletable.PurgableDeserializerMixin): model_manager_class = DatasetManager - def __init__( self, app ): - super( DatasetDeserializer, self ).__init__( app ) + def __init__(self, app): + super(DatasetDeserializer, self).__init__(app) # TODO: this manager may make more sense inside rbac_secured - self.role_manager = roles.RoleManager( app ) + self.role_manager = roles.RoleManager(app) - def add_deserializers( self ): - super( DatasetDeserializer, self ).add_deserializers() + def add_deserializers(self): + super(DatasetDeserializer, self).add_deserializers() # not much to set here besides permissions and purged/deleted - deletable.PurgableDeserializerMixin.add_deserializers( self ) + deletable.PurgableDeserializerMixin.add_deserializers(self) self.deserializers.update({ 'permissions' : self.deserialize_permissions, }) - def deserialize_permissions( self, dataset, key, permissions, user=None, **context ): + def deserialize_permissions(self, dataset, key, permissions, user=None, **context): """ Create permissions for each list of encoded role ids in the (validated) `permissions` dictionary, where `permissions` is in the form:: { 'manage': [ , ... ], 'access': [ , ... ] } """ - self.manager.permissions.manage.error_unless_permitted( dataset, user ) - self._validate_permissions( permissions, **context ) - manage = self._list_of_roles_from_ids( permissions[ 'manage' ] ) - access = self._list_of_roles_from_ids( permissions[ 'access' ] ) - self.manager.permissions.set( dataset, manage, access, flush=False ) + self.manager.permissions.manage.error_unless_permitted(dataset, user) + self._validate_permissions(permissions, **context) + manage = self._list_of_roles_from_ids(permissions['manage']) + access = self._list_of_roles_from_ids(permissions['access']) + self.manager.permissions.set(dataset, manage, access, flush=False) return permissions - def _validate_permissions( self, permissions, **context ): - self.validate.type( 'permissions', permissions, dict ) - for permission_key in ( 'manage', 'access' ): - if( not isinstance( permissions.get( permission_key, None ), list ) ): - msg = 'permissions requires "{0}" as a list of role ids'.format( permission_key ) - raise exceptions.RequestParameterInvalidException( msg ) + def _validate_permissions(self, permissions, **context): + self.validate.type('permissions', permissions, dict) + for permission_key in ('manage', 'access'): + if(not isinstance(permissions.get(permission_key, None), list)): + msg = 'permissions requires "{0}" as a list of role ids'.format(permission_key) + raise exceptions.RequestParameterInvalidException(msg) # TODO: push down into permissions? - manage_permissions = permissions[ 'manage' ] - if len( manage_permissions ) < 1: - raise exceptions.RequestParameterInvalidException( 'At least one managing role is required' ) + manage_permissions = permissions['manage'] + if len(manage_permissions) < 1: + raise exceptions.RequestParameterInvalidException('At least one managing role is required') return permissions - def _list_of_roles_from_ids( self, id_list ): + def _list_of_roles_from_ids(self, id_list): # TODO: this may make more sense inside rbac_secured # note: no checking of valid roles is made - return self.role_manager.by_ids( [ self.app.security.decode_id( id_ ) for id_ in id_list ] ) + return self.role_manager.by_ids([self.app.security.decode_id(id_) for id_ in id_list]) # ============================================================================= AKA DatasetInstanceManager -class DatasetAssociationManager( base.ModelManager, - secured.AccessibleManagerMixin, - deletable.PurgableManagerMixin ): +class DatasetAssociationManager(base.ModelManager, + secured.AccessibleManagerMixin, + deletable.PurgableManagerMixin): """ DatasetAssociation/DatasetInstances are intended to be working proxies to a Dataset, associated with either a library or a @@ -284,18 +284,18 @@ class DatasetAssociationManager( base.ModelManager, # NOTE: model_manager_class should be set in HDA/LDA subclasses - def __init__( self, app ): - super( DatasetAssociationManager, self ).__init__( app ) - self.dataset_manager = DatasetManager( app ) + def __init__(self, app): + super(DatasetAssociationManager, self).__init__(app) + self.dataset_manager = DatasetManager(app) - def is_accessible( self, dataset_assoc, user, **kwargs ): + def is_accessible(self, dataset_assoc, user, **kwargs): """ Is this DA accessible to `user`? """ # defer to the dataset - return self.dataset_manager.is_accessible( dataset_assoc.dataset, user ) + return self.dataset_manager.is_accessible(dataset_assoc.dataset, user) - def purge( self, dataset_assoc, flush=True ): + def purge(self, dataset_assoc, flush=True): """ Purge this DatasetInstance and the dataset underlying it. """ @@ -306,21 +306,21 @@ def purge( self, dataset_assoc, flush=True ): # We need to ignore a potential flush=False here and force the flush # so that job cleanup associated with stop_creating_job will see # the dataset as purged. - super( DatasetAssociationManager, self ).purge( dataset_assoc, flush=True ) + super(DatasetAssociationManager, self).purge(dataset_assoc, flush=True) # stop any jobs outputing the dataset_assoc - self.stop_creating_job( dataset_assoc ) + self.stop_creating_job(dataset_assoc) # more importantly, purge underlying dataset as well if dataset_assoc.dataset.user_can_purge: - self.dataset_manager.purge( dataset_assoc.dataset ) + self.dataset_manager.purge(dataset_assoc.dataset) return dataset_assoc - def by_user( self, user ): - raise galaxy.exceptions.NotImplemented( 'Abstract Method' ) + def by_user(self, user): + raise galaxy.exceptions.NotImplemented('Abstract Method') # .... associated job - def creating_job( self, dataset_assoc ): + def creating_job(self, dataset_assoc): """ Return the `Job` that created this dataset or None if not found. """ @@ -332,22 +332,22 @@ def creating_job( self, dataset_assoc ): break return job - def stop_creating_job( self, dataset_assoc ): + def stop_creating_job(self, dataset_assoc): """ Stops an dataset_assoc's creating job if all the job's other outputs are deleted. """ - if dataset_assoc.parent_id is None and len( dataset_assoc.creating_job_associations ) > 0: + if dataset_assoc.parent_id is None and len(dataset_assoc.creating_job_associations) > 0: # Mark associated job for deletion job = dataset_assoc.creating_job_associations[0].job if not job.finished: # Are *all* of the job's other output datasets deleted? if job.check_if_output_datasets_deleted(): - job.mark_deleted( self.app.config.track_jobs_in_database ) - self.app.job_manager.job_stop_queue.put( job.id ) + job.mark_deleted(self.app.config.track_jobs_in_database) + self.app.job_manager.job_stop_queue.put(job.id) return True return False - def is_composite( self, dataset_assoc ): + def is_composite(self, dataset_assoc): """ Return True if this hda/ldda is a composite type dataset. @@ -355,46 +355,46 @@ def is_composite( self, dataset_assoc ): """ return dataset_assoc.extension in self.app.datatypes_registry.get_composite_extensions() - def extra_files( self, dataset_assoc ): + def extra_files(self, dataset_assoc): """Return a list of file paths for composite files, an empty list otherwise.""" - if not self.is_composite( dataset_assoc ): + if not self.is_composite(dataset_assoc): return [] - return glob.glob( os.path.join( dataset_assoc.dataset.extra_files_path, '*' ) ) + return glob.glob(os.path.join(dataset_assoc.dataset.extra_files_path, '*')) -class _UnflattenedMetadataDatasetAssociationSerializer( base.ModelSerializer, - deletable.PurgableSerializerMixin ): +class _UnflattenedMetadataDatasetAssociationSerializer(base.ModelSerializer, + deletable.PurgableSerializerMixin): - def __init__( self, app ): - self.dataset_serializer = DatasetSerializer( app ) - super( _UnflattenedMetadataDatasetAssociationSerializer, self ).__init__( app ) + def __init__(self, app): + self.dataset_serializer = DatasetSerializer(app) + super(_UnflattenedMetadataDatasetAssociationSerializer, self).__init__(app) - def add_serializers( self ): - super( _UnflattenedMetadataDatasetAssociationSerializer, self ).add_serializers() - deletable.PurgableSerializerMixin.add_serializers( self ) + def add_serializers(self): + super(_UnflattenedMetadataDatasetAssociationSerializer, self).add_serializers() + deletable.PurgableSerializerMixin.add_serializers(self) self.serializers.update({ 'create_time' : self.serialize_date, 'update_time' : self.serialize_date, # underlying dataset - 'dataset' : lambda i, k, **c: self.dataset_serializer.serialize_to_view( i.dataset, view='summary', **c ), - 'dataset_id' : self._proxy_to_dataset( key='id' ), + 'dataset' : lambda i, k, **c: self.dataset_serializer.serialize_to_view(i.dataset, view='summary', **c), + 'dataset_id' : self._proxy_to_dataset(key='id'), # TODO: why is this named uuid!? The da doesn't have a uuid - it's the underlying dataset's uuid! - 'uuid' : self._proxy_to_dataset( key='uuid' ), + 'uuid' : self._proxy_to_dataset(key='uuid'), # 'dataset_uuid' : self._proxy_to_dataset( key='uuid' ), - 'file_name' : self._proxy_to_dataset( serializer=self.dataset_serializer.serialize_file_name ), - 'extra_files_path' : self._proxy_to_dataset( serializer=self.dataset_serializer.serialize_extra_files_path ), - 'permissions' : self._proxy_to_dataset( serializer=self.dataset_serializer.serialize_permissions ), + 'file_name' : self._proxy_to_dataset(serializer=self.dataset_serializer.serialize_file_name), + 'extra_files_path' : self._proxy_to_dataset(serializer=self.dataset_serializer.serialize_extra_files_path), + 'permissions' : self._proxy_to_dataset(serializer=self.dataset_serializer.serialize_permissions), # TODO: do the sizes proxy accurately/in the same way? - 'size' : lambda i, k, **c: int( i.get_size() ), - 'file_size' : lambda i, k, **c: self.serializers[ 'size' ]( i, k, **c ), - 'nice_size' : lambda i, k, **c: i.get_size( nice_size=True ), + 'size' : lambda i, k, **c: int(i.get_size()), + 'file_size' : lambda i, k, **c: self.serializers['size'](i, k, **c), + 'nice_size' : lambda i, k, **c: i.get_size(nice_size=True), # common to lddas and hdas - from mapping.py 'copied_from_history_dataset_association_id' : self.serialize_id, 'copied_from_library_dataset_dataset_association_id': self.serialize_id, - 'info' : lambda i, k, **c: i.info.strip() if isinstance( i.info, string_types ) else i.info, + 'info' : lambda i, k, **c: i.info.strip() if isinstance(i.info, string_types) else i.info, 'blurb' : lambda i, k, **c: i.blurb, 'peek' : lambda i, k, **c: i.display_peek() if i.peek and i.peek != 'no peek' else None, @@ -421,34 +421,34 @@ def add_serializers( self ): }) # this an abstract superclass, so no views created # because of that: we need to add a few keys that will use the default serializer - self.serializable_keyset.update([ 'name', 'state', 'tool_version', 'extension', 'visible', 'dbkey' ]) + self.serializable_keyset.update(['name', 'state', 'tool_version', 'extension', 'visible', 'dbkey']) - def _proxy_to_dataset( self, serializer=None, key=None ): + def _proxy_to_dataset(self, serializer=None, key=None): # dataset associations are (rough) proxies to datasets - access their serializer using this remapping fn # remapping done by either kwarg key: IOW dataset attr key (e.g. uuid) # or by kwarg serializer: a function that's passed in (e.g. permissions) if key: - serializer = self.dataset_serializer.serializers.get( key ) + serializer = self.dataset_serializer.serializers.get(key) if serializer: - return lambda i, k, **c: serializer( i.dataset, key or k, **c ) - raise TypeError( 'kwarg serializer or key needed') + return lambda i, k, **c: serializer(i.dataset, key or k, **c) + raise TypeError('kwarg serializer or key needed') - def serialize_meta_files( self, dataset_assoc, key, **context ): + def serialize_meta_files(self, dataset_assoc, key, **context): """ Cycle through meta files and return them as a list of dictionaries. """ meta_files = [] for meta_type in dataset_assoc.metadata.spec.keys(): - if isinstance( dataset_assoc.metadata.spec[ meta_type ].param, galaxy.datatypes.metadata.FileParameter ): + if isinstance(dataset_assoc.metadata.spec[meta_type].param, galaxy.datatypes.metadata.FileParameter): meta_files.append( - dict( file_type=meta_type, - download_url=self.url_for( 'history_contents_metadata_file', - history_id=self.app.security.encode_id(dataset_assoc.history_id), - history_content_id=self.app.security.encode_id(dataset_assoc.id), - metadata_file=meta_type) ) ) + dict(file_type=meta_type, + download_url=self.url_for('history_contents_metadata_file', + history_id=self.app.security.encode_id(dataset_assoc.history_id), + history_content_id=self.app.security.encode_id(dataset_assoc.id), + metadata_file=meta_type))) return meta_files - def serialize_metadata( self, dataset_assoc, key, excluded=None, **context ): + def serialize_metadata(self, dataset_assoc, key, excluded=None, **context): """ Cycle through metadata and return as dictionary. """ @@ -460,43 +460,43 @@ def serialize_metadata( self, dataset_assoc, key, excluded=None, **context ): for name, spec in dataset_assoc.metadata.spec.items(): if name in excluded: continue - val = dataset_assoc.metadata.get( name ) + val = dataset_assoc.metadata.get(name) # NOTE: no files - if isinstance( val, model.MetadataFile ): + if isinstance(val, model.MetadataFile): # only when explicitly set: fetching filepaths can be expensive if not self.app.config.expose_dataset_path: continue val = val.file_name # TODO:? possibly split this off? # If no value for metadata, look in datatype for metadata. - elif val is None and hasattr( dataset_assoc.datatype, name ): - val = getattr( dataset_assoc.datatype, name ) - metadata[ name ] = val + elif val is None and hasattr(dataset_assoc.datatype, name): + val = getattr(dataset_assoc.datatype, name) + metadata[name] = val return metadata - def serialize_creating_job( self, dataset, key, **context ): + def serialize_creating_job(self, dataset, key, **context): """ Return the id of the Job that created this dataset (or its original) or None if no `creating_job` is found. """ if dataset.creating_job: - return self.serialize_id( dataset.creating_job, 'id' ) + return self.serialize_id(dataset.creating_job, 'id') else: return None - def serialize_rerunnable( self, dataset, key, **context ): + def serialize_rerunnable(self, dataset, key, **context): """ Return False if this tool that created this dataset can't be re-run (e.g. upload). """ if dataset.creating_job: - tool = self.app.toolbox.get_tool( dataset.creating_job.tool_id, dataset.creating_job.tool_version ) + tool = self.app.toolbox.get_tool(dataset.creating_job.tool_id, dataset.creating_job.tool_version) if tool and tool.is_workflow_compatible: return True return False - def serialize_converted_datasets( self, dataset_assoc, key, **context ): + def serialize_converted_datasets(self, dataset_assoc, key, **context): """ Return a file extension -> converted dataset encoded id map with all the existing converted datasets associated with this instance. @@ -506,113 +506,113 @@ def serialize_converted_datasets( self, dataset_assoc, key, **context ): id_map = {} for converted in dataset_assoc.implicitly_converted_datasets: if not converted.deleted and converted.dataset: - id_map[ converted.type ] = self.serialize_id( converted.dataset, 'id' ) + id_map[converted.type] = self.serialize_id(converted.dataset, 'id') return id_map -class DatasetAssociationSerializer( _UnflattenedMetadataDatasetAssociationSerializer ): +class DatasetAssociationSerializer(_UnflattenedMetadataDatasetAssociationSerializer): # TODO: remove this class - metadata should be a sub-object instead as in the superclass - def add_serializers( self ): - super( DatasetAssociationSerializer, self ).add_serializers() + def add_serializers(self): + super(DatasetAssociationSerializer, self).add_serializers() # remove the single nesting key here - del self.serializers[ 'metadata' ] + del self.serializers['metadata'] - def serialize( self, dataset_assoc, keys, **context ): + def serialize(self, dataset_assoc, keys, **context): """ Override to add metadata as flattened keys on the serialized DatasetInstance. """ # if 'metadata' isn't removed from keys here serialize will retrieve the un-serializable MetadataCollection # TODO: remove these when metadata is sub-object - KEYS_HANDLED_SEPARATELY = ( 'metadata', ) - left_to_handle = self._pluck_from_list( keys, KEYS_HANDLED_SEPARATELY ) - serialized = super( DatasetAssociationSerializer, self ).serialize( dataset_assoc, keys, **context ) + KEYS_HANDLED_SEPARATELY = ('metadata', ) + left_to_handle = self._pluck_from_list(keys, KEYS_HANDLED_SEPARATELY) + serialized = super(DatasetAssociationSerializer, self).serialize(dataset_assoc, keys, **context) # add metadata directly to the dict instead of as a sub-object if 'metadata' in left_to_handle: - metadata = self._prefixed_metadata( dataset_assoc ) - serialized.update( metadata ) + metadata = self._prefixed_metadata(dataset_assoc) + serialized.update(metadata) return serialized # TODO: this is more util/gen. use - def _pluck_from_list( self, l, elems ): + def _pluck_from_list(self, l, elems): """ Removes found elems from list l and returns list of found elems if found. """ found = [] for elem in elems: try: - index = l.index( elem ) - found.append( l.pop( index ) ) + index = l.index(elem) + found.append(l.pop(index)) except ValueError: pass return found - def _prefixed_metadata( self, dataset_assoc ): + def _prefixed_metadata(self, dataset_assoc): """ Adds (a prefixed version of) the DatasetInstance metadata to the dict, prefixing each key with 'metadata_'. """ # build the original, nested dictionary - metadata = self.serialize_metadata( dataset_assoc, 'metadata' ) + metadata = self.serialize_metadata(dataset_assoc, 'metadata') # prefix each key within and return prefixed = {} for key, val in metadata.items(): prefixed_key = 'metadata_' + key - prefixed[ prefixed_key ] = val + prefixed[prefixed_key] = val return prefixed -class DatasetAssociationDeserializer( base.ModelDeserializer, deletable.PurgableDeserializerMixin ): +class DatasetAssociationDeserializer(base.ModelDeserializer, deletable.PurgableDeserializerMixin): - def add_deserializers( self ): - super( DatasetAssociationDeserializer, self ).add_deserializers() - deletable.PurgableDeserializerMixin.add_deserializers( self ) + def add_deserializers(self): + super(DatasetAssociationDeserializer, self).add_deserializers() + deletable.PurgableDeserializerMixin.add_deserializers(self) self.deserializers.update({ 'name' : self.deserialize_basestring, 'info' : self.deserialize_basestring, }) - self.deserializable_keyset.update( self.deserializers.keys() ) + self.deserializable_keyset.update(self.deserializers.keys()) # TODO: untested - def deserialize_metadata( self, dataset_assoc, metadata_key, metadata_dict, **context ): + def deserialize_metadata(self, dataset_assoc, metadata_key, metadata_dict, **context): """ """ - self.validate.type( metadata_key, metadata_dict, dict ) + self.validate.type(metadata_key, metadata_dict, dict) returned = {} for key, val in metadata_dict.items(): - returned[ key ] = self.deserialize_metadatum( dataset_assoc, key, val, **context ) + returned[key] = self.deserialize_metadatum(dataset_assoc, key, val, **context) return returned - def deserialize_metadatum( self, dataset_assoc, key, val, **context ): + def deserialize_metadatum(self, dataset_assoc, key, val, **context): """ """ if key not in dataset_assoc.datatype.metadata_spec: return - metadata_specification = dataset_assoc.datatype.metadata_spec[ key ] - if metadata_specification.get( 'readonly' ): + metadata_specification = dataset_assoc.datatype.metadata_spec[key] + if metadata_specification.get('readonly'): return - unwrapped_val = metadata_specification.unwrap( val ) - setattr( dataset_assoc.metadata, key, unwrapped_val ) + unwrapped_val = metadata_specification.unwrap(val) + setattr(dataset_assoc.metadata, key, unwrapped_val) # ...? return unwrapped_val -class DatasetAssociationFilterParser( base.ModelFilterParser, deletable.PurgableFiltersMixin ): +class DatasetAssociationFilterParser(base.ModelFilterParser, deletable.PurgableFiltersMixin): - def _add_parsers( self ): - super( DatasetAssociationFilterParser, self )._add_parsers() - deletable.PurgableFiltersMixin._add_parsers( self ) + def _add_parsers(self): + super(DatasetAssociationFilterParser, self)._add_parsers() + deletable.PurgableFiltersMixin._add_parsers(self) self.orm_filter_parsers.update({ - 'name' : { 'op': ( 'eq', 'contains', 'like' ) }, - 'state' : { 'column' : '_state', 'op': ( 'eq', 'in' ) }, - 'visible' : { 'op': ( 'eq' ), 'val': self.parse_bool }, + 'name' : {'op': ('eq', 'contains', 'like')}, + 'state' : {'column' : '_state', 'op': ('eq', 'in')}, + 'visible' : {'op': ('eq'), 'val': self.parse_bool}, }) self.fn_filter_parsers.update({ - 'genome_build' : self.string_standard_ops( 'dbkey' ), + 'genome_build' : self.string_standard_ops('dbkey'), 'data_type' : { 'op': { 'eq' : self.eq_datatype, @@ -621,24 +621,24 @@ def _add_parsers( self ): } }) - def eq_datatype( self, dataset_assoc, class_str ): + def eq_datatype(self, dataset_assoc, class_str): """ Is the `dataset_assoc` datatype equal to the registered datatype `class_str`? """ - comparison_class = self.app.datatypes_registry.get_datatype_class_by_name( class_str ) - return ( comparison_class and - dataset_assoc.datatype.__class__ == comparison_class ) + comparison_class = self.app.datatypes_registry.get_datatype_class_by_name(class_str) + return (comparison_class and + dataset_assoc.datatype.__class__ == comparison_class) - def isinstance_datatype( self, dataset_assoc, class_strs ): + def isinstance_datatype(self, dataset_assoc, class_strs): """ Is the `dataset_assoc` datatype derived from any of the registered datatypes in the comma separated string `class_strs`? """ parse_datatype_fn = self.app.datatypes_registry.get_datatype_class_by_name comparison_classes = [] - for class_str in class_strs.split( ',' ): - datatype_class = parse_datatype_fn( class_str ) + for class_str in class_strs.split(','): + datatype_class = parse_datatype_fn(class_str) if datatype_class: - comparison_classes.append( datatype_class ) - return ( comparison_classes and - isinstance( dataset_assoc.datatype, comparison_classes ) ) + comparison_classes.append(datatype_class) + return (comparison_classes and + isinstance(dataset_assoc.datatype, comparison_classes)) diff --git a/lib/galaxy/managers/deletable.py b/lib/galaxy/managers/deletable.py index 0f2c4f2e3464..33ee6c023ed6 100644 --- a/lib/galaxy/managers/deletable.py +++ b/lib/galaxy/managers/deletable.py @@ -10,7 +10,7 @@ """ -class DeletableManagerMixin( object ): +class DeletableManagerMixin(object): """ A mixin/interface for a model that is deletable (i.e. has a 'deleted' attr). @@ -18,100 +18,102 @@ class DeletableManagerMixin( object ): that they are no longer needed, should not be displayed, or may be actually removed by an admin/script. """ - def delete( self, item, flush=True, **kwargs ): + + def delete(self, item, flush=True, **kwargs): """ Mark as deleted and return. """ - return self._session_setattr( item, 'deleted', True, flush=flush ) + return self._session_setattr(item, 'deleted', True, flush=flush) - def undelete( self, item, flush=True, **kwargs ): + def undelete(self, item, flush=True, **kwargs): """ Mark as not deleted and return. """ - return self._session_setattr( item, 'deleted', False, flush=flush ) + return self._session_setattr(item, 'deleted', False, flush=flush) -class DeletableSerializerMixin( object ): +class DeletableSerializerMixin(object): - def add_serializers( self ): - self.serializable_keyset.add( 'deleted' ) + def add_serializers(self): + self.serializable_keyset.add('deleted') # TODO: these are of questionable value if we don't want to enable users to delete/purge via update -class DeletableDeserializerMixin( object ): +class DeletableDeserializerMixin(object): - def add_deserializers( self ): - self.deserializers[ 'deleted' ] = self.deserialize_deleted + def add_deserializers(self): + self.deserializers['deleted'] = self.deserialize_deleted - def deserialize_deleted( self, item, key, val, **context ): + def deserialize_deleted(self, item, key, val, **context): """ Delete or undelete `item` based on `val` then return `item.deleted`. """ - new_deleted = self.validate.bool( key, val ) + new_deleted = self.validate.bool(key, val) if new_deleted == item.deleted: return item.deleted # TODO:?? flush=False? if new_deleted: - self.manager.delete( item, flush=False ) + self.manager.delete(item, flush=False) else: - self.manager.undelete( item, flush=False ) + self.manager.undelete(item, flush=False) return item.deleted -class DeletableFiltersMixin( object ): +class DeletableFiltersMixin(object): - def _add_parsers( self ): + def _add_parsers(self): self.orm_filter_parsers.update({ - 'deleted': { 'op': ( 'eq' ), 'val': self.parse_bool } + 'deleted': {'op': ('eq'), 'val': self.parse_bool} }) -class PurgableManagerMixin( DeletableManagerMixin ): +class PurgableManagerMixin(DeletableManagerMixin): """ A manager interface/mixin for a resource that allows deleting and purging where purging is often removal of some additional, non-db resource (e.g. a dataset's file). """ - def purge( self, item, flush=True, **kwargs ): + + def purge(self, item, flush=True, **kwargs): """ Mark as purged and return. Override this in subclasses to do the additional resource removal. """ self.delete(item, flush=False) - return self._session_setattr( item, 'purged', True, flush=flush ) + return self._session_setattr(item, 'purged', True, flush=flush) -class PurgableSerializerMixin( DeletableSerializerMixin ): +class PurgableSerializerMixin(DeletableSerializerMixin): - def add_serializers( self ): - DeletableSerializerMixin.add_serializers( self ) - self.serializable_keyset.add( 'purged' ) + def add_serializers(self): + DeletableSerializerMixin.add_serializers(self) + self.serializable_keyset.add('purged') -class PurgableDeserializerMixin( DeletableDeserializerMixin ): +class PurgableDeserializerMixin(DeletableDeserializerMixin): - def add_deserializers( self ): - DeletableDeserializerMixin.add_deserializers( self ) - self.deserializers[ 'purged' ] = self.deserialize_purged + def add_deserializers(self): + DeletableDeserializerMixin.add_deserializers(self) + self.deserializers['purged'] = self.deserialize_purged - def deserialize_purged( self, item, key, val, **context ): + def deserialize_purged(self, item, key, val, **context): """ If `val` is True, purge `item` and return `item.purged`. """ - new_purged = self.validate.bool( key, val ) + new_purged = self.validate.bool(key, val) if new_purged == item.purged: return item.purged # do we want to error if something attempts to 'unpurge'? if new_purged: - self.manager.purge( item, flush=False ) + self.manager.purge(item, flush=False) return item.purged -class PurgableFiltersMixin( DeletableFiltersMixin ): +class PurgableFiltersMixin(DeletableFiltersMixin): - def _add_parsers( self ): - DeletableFiltersMixin._add_parsers( self ) + def _add_parsers(self): + DeletableFiltersMixin._add_parsers(self) self.orm_filter_parsers.update({ - 'purged': { 'op': ( 'eq' ), 'val': self.parse_bool } + 'purged': {'op': ('eq'), 'val': self.parse_bool} }) diff --git a/lib/galaxy/managers/folders.py b/lib/galaxy/managers/folders.py index cbb4ad3e9b75..d681e6a78da3 100644 --- a/lib/galaxy/managers/folders.py +++ b/lib/galaxy/managers/folders.py @@ -12,16 +12,16 @@ from sqlalchemy.orm.exc import MultipleResultsFound from sqlalchemy.orm.exc import NoResultFound import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) # ============================================================================= -class FolderManager( object ): +class FolderManager(object): """ Interface/service object for interacting with folders. """ - def get( self, trans, decoded_folder_id, check_manageable=False, check_accessible=True): + def get(self, trans, decoded_folder_id, check_manageable=False, check_accessible=True): """ Get the folder from the DB. @@ -38,17 +38,17 @@ def get( self, trans, decoded_folder_id, check_manageable=False, check_accessibl :raises: InconsistentDatabase, RequestParameterInvalidException, InternalServerError """ try: - folder = trans.sa_session.query( trans.app.model.LibraryFolder ).filter( trans.app.model.LibraryFolder.table.c.id == decoded_folder_id ).one() + folder = trans.sa_session.query(trans.app.model.LibraryFolder).filter(trans.app.model.LibraryFolder.table.c.id == decoded_folder_id).one() except MultipleResultsFound: - raise InconsistentDatabase( 'Multiple folders found with the same id.' ) + raise InconsistentDatabase('Multiple folders found with the same id.') except NoResultFound: - raise RequestParameterInvalidException( 'No folder found with the id provided.' ) + raise RequestParameterInvalidException('No folder found with the id provided.') except Exception as e: - raise InternalServerError( 'Error loading from the database.' + str( e ) ) - folder = self.secure( trans, folder, check_manageable, check_accessible ) + raise InternalServerError('Error loading from the database.' + str(e)) + folder = self.secure(trans, folder, check_manageable, check_accessible) return folder - def secure( self, trans, folder, check_manageable=True, check_accessible=True ): + def secure(self, trans, folder, check_manageable=True, check_accessible=True): """ Check if (a) user can manage folder or (b) folder is accessible to user. @@ -66,12 +66,12 @@ def secure( self, trans, folder, check_manageable=True, check_accessible=True ): if trans.user_is_admin(): return folder if check_manageable: - folder = self.check_manageable( trans, folder ) + folder = self.check_manageable(trans, folder) if check_accessible: - folder = self.check_accessible( trans, folder ) + folder = self.check_accessible(trans, folder) return folder - def check_manageable( self, trans, folder ): + def check_manageable(self, trans, folder): """ Check whether the user can manage the folder. @@ -81,21 +81,21 @@ def check_manageable( self, trans, folder ): :raises: AuthenticationRequired, InsufficientPermissionsException """ if not trans.user: - raise AuthenticationRequired( "Must be logged in to manage Galaxy items.", type='error' ) + raise AuthenticationRequired("Must be logged in to manage Galaxy items.", type='error') current_user_roles = trans.get_current_user_roles() - if not trans.app.security_agent.can_manage_library_item( current_user_roles, folder ): - raise InsufficientPermissionsException( "You don't have permissions to manage this folder.", type='error' ) + if not trans.app.security_agent.can_manage_library_item(current_user_roles, folder): + raise InsufficientPermissionsException("You don't have permissions to manage this folder.", type='error') else: return folder - def check_accessible( self, trans, folder ): + def check_accessible(self, trans, folder): """ Check whether the folder is accessible to current user. By default every folder is accessible (contents have their own permissions). """ return folder - def get_folder_dict( self, trans, folder ): + def get_folder_dict(self, trans, folder): """ Return folder data in the form of a dictionary. @@ -106,15 +106,15 @@ def get_folder_dict( self, trans, folder ): :rtype: dictionary """ - folder_dict = folder.to_dict( view='element' ) - folder_dict = trans.security.encode_all_ids( folder_dict, True ) - folder_dict[ 'id' ] = 'F' + folder_dict[ 'id' ] - if folder_dict[ 'parent_id' ] is not None: - folder_dict[ 'parent_id' ] = 'F' + folder_dict[ 'parent_id' ] - folder_dict['update_time'] = folder.update_time.strftime( "%Y-%m-%d %I:%M %p" ) + folder_dict = folder.to_dict(view='element') + folder_dict = trans.security.encode_all_ids(folder_dict, True) + folder_dict['id'] = 'F' + folder_dict['id'] + if folder_dict['parent_id'] is not None: + folder_dict['parent_id'] = 'F' + folder_dict['parent_id'] + folder_dict['update_time'] = folder.update_time.strftime("%Y-%m-%d %I:%M %p") return folder_dict - def create( self, trans, parent_folder_id, new_folder_name, new_folder_description='' ): + def create(self, trans, parent_folder_id, new_folder_name, new_folder_description=''): """ Create a new folder under the given folder. @@ -130,23 +130,23 @@ def create( self, trans, parent_folder_id, new_folder_name, new_folder_descripti :raises: InsufficientPermissionsException """ - parent_folder = self.get( trans, parent_folder_id ) + parent_folder = self.get(trans, parent_folder_id) current_user_roles = trans.get_current_user_roles() - if not ( trans.user_is_admin or trans.app.security_agent.can_add_library_item( current_user_roles, parent_folder ) ): - raise InsufficientPermissionsException( 'You do not have proper permission to create folders under given folder.' ) - new_folder = trans.app.model.LibraryFolder( name=new_folder_name, description=new_folder_description ) + if not (trans.user_is_admin or trans.app.security_agent.can_add_library_item(current_user_roles, parent_folder)): + raise InsufficientPermissionsException('You do not have proper permission to create folders under given folder.') + new_folder = trans.app.model.LibraryFolder(name=new_folder_name, description=new_folder_description) # We are associating the last used genome build with folders, so we will always # initialize a new folder with the first dbkey in genome builds list which is currently # ? unspecified (?) new_folder.genome_build = trans.app.genome_builds.default_value - parent_folder.add_folder( new_folder ) - trans.sa_session.add( new_folder ) + parent_folder.add_folder(new_folder) + trans.sa_session.add(new_folder) trans.sa_session.flush() # New folders default to having the same permissions as their parent folder - trans.app.security_agent.copy_library_permissions( trans, parent_folder, new_folder ) + trans.app.security_agent.copy_library_permissions(trans, parent_folder, new_folder) return new_folder - def update( self, trans, folder, name=None, description=None): + def update(self, trans, folder, name=None, description=None): """ Update the given folder's name or description. @@ -164,10 +164,10 @@ def update( self, trans, folder, name=None, description=None): """ changed = False if not trans.user_is_admin(): - if not self.check_manageable( trans, folder ): - raise InsufficientPermissionsException( "You do not have proper permission to update the library folder." ) + if not self.check_manageable(trans, folder): + raise InsufficientPermissionsException("You do not have proper permission to update the library folder.") if folder.deleted is True: - raise ItemAccessibilityException( "You cannot update a deleted library folder. Undelete it first." ) + raise ItemAccessibilityException("You cannot update a deleted library folder. Undelete it first.") if name is not None and name != folder.name: folder.name = name changed = True @@ -175,11 +175,11 @@ def update( self, trans, folder, name=None, description=None): folder.description = description changed = True if changed: - trans.sa_session.add( folder ) + trans.sa_session.add(folder) trans.sa_session.flush() return folder - def delete( self, trans, folder, undelete=False ): + def delete(self, trans, folder, undelete=False): """ Mark given folder deleted/undeleted based on the flag. @@ -194,16 +194,16 @@ def delete( self, trans, folder, undelete=False ): :raises: ItemAccessibilityException """ if not trans.user_is_admin(): - folder = self.check_manageable( trans, folder ) + folder = self.check_manageable(trans, folder) if undelete: folder.deleted = False else: folder.deleted = True - trans.sa_session.add( folder ) + trans.sa_session.add(folder) trans.sa_session.flush() return folder - def get_current_roles( self, trans, folder ): + def get_current_roles(self, trans, folder): """ Find all roles currently connected to relevant permissions on the folder. @@ -215,31 +215,31 @@ def get_current_roles( self, trans, folder ): :rtype: dictionary """ # Omit duplicated roles by converting to set - modify_roles = set( trans.app.security_agent.get_roles_for_action( folder, trans.app.security_agent.permitted_actions.LIBRARY_MODIFY ) ) - manage_roles = set( trans.app.security_agent.get_roles_for_action( folder, trans.app.security_agent.permitted_actions.LIBRARY_MANAGE ) ) - add_roles = set( trans.app.security_agent.get_roles_for_action( folder, trans.app.security_agent.permitted_actions.LIBRARY_ADD ) ) - - modify_folder_role_list = [ ( modify_role.name, trans.security.encode_id( modify_role.id ) ) for modify_role in modify_roles ] - manage_folder_role_list = [ ( manage_role.name, trans.security.encode_id( manage_role.id ) ) for manage_role in manage_roles ] - add_library_item_role_list = [ ( add_role.name, trans.security.encode_id( add_role.id ) ) for add_role in add_roles ] - return dict( modify_folder_role_list=modify_folder_role_list, - manage_folder_role_list=manage_folder_role_list, - add_library_item_role_list=add_library_item_role_list ) - - def can_add_item( self, trans, folder ): + modify_roles = set(trans.app.security_agent.get_roles_for_action(folder, trans.app.security_agent.permitted_actions.LIBRARY_MODIFY)) + manage_roles = set(trans.app.security_agent.get_roles_for_action(folder, trans.app.security_agent.permitted_actions.LIBRARY_MANAGE)) + add_roles = set(trans.app.security_agent.get_roles_for_action(folder, trans.app.security_agent.permitted_actions.LIBRARY_ADD)) + + modify_folder_role_list = [(modify_role.name, trans.security.encode_id(modify_role.id)) for modify_role in modify_roles] + manage_folder_role_list = [(manage_role.name, trans.security.encode_id(manage_role.id)) for manage_role in manage_roles] + add_library_item_role_list = [(add_role.name, trans.security.encode_id(add_role.id)) for add_role in add_roles] + return dict(modify_folder_role_list=modify_folder_role_list, + manage_folder_role_list=manage_folder_role_list, + add_library_item_role_list=add_library_item_role_list) + + def can_add_item(self, trans, folder): """ Return true if the user has permissions to add item to the given folder. """ if trans.user_is_admin: return True current_user_roles = trans.get_current_user_roles() - add_roles = set( trans.app.security_agent.get_roles_for_action( folder, trans.app.security_agent.permitted_actions.LIBRARY_ADD ) ) + add_roles = set(trans.app.security_agent.get_roles_for_action(folder, trans.app.security_agent.permitted_actions.LIBRARY_ADD)) for role in current_user_roles: if role in add_roles: return True return False - def cut_the_prefix( self, encoded_folder_id ): + def cut_the_prefix(self, encoded_folder_id): """ Remove the prefix from the encoded folder id. @@ -251,13 +251,13 @@ def cut_the_prefix( self, encoded_folder_id ): :raises: MalformedId """ - if ( ( len( encoded_folder_id ) % 16 == 1 ) and encoded_folder_id.startswith( 'F' ) ): - cut_id = encoded_folder_id[ 1: ] + if ((len(encoded_folder_id) % 16 == 1) and encoded_folder_id.startswith('F')): + cut_id = encoded_folder_id[1:] else: - raise MalformedId( 'Malformed folder id ( %s ) specified, unable to decode.' % str( encoded_folder_id ) ) + raise MalformedId('Malformed folder id ( %s ) specified, unable to decode.' % str(encoded_folder_id)) return cut_id - def decode_folder_id( self, trans, encoded_folder_id ): + def decode_folder_id(self, trans, encoded_folder_id): """ Decode the folder id given that it has already lost the prefixed 'F'. @@ -270,12 +270,12 @@ def decode_folder_id( self, trans, encoded_folder_id ): :raises: MalformedId """ try: - decoded_id = trans.security.decode_id( encoded_folder_id ) + decoded_id = trans.security.decode_id(encoded_folder_id) except ValueError: - raise MalformedId( "Malformed folder id ( %s ) specified, unable to decode" % ( str( encoded_folder_id ) ) ) + raise MalformedId("Malformed folder id ( %s ) specified, unable to decode" % (str(encoded_folder_id))) return decoded_id - def cut_and_decode( self, trans, encoded_folder_id ): + def cut_and_decode(self, trans, encoded_folder_id): """ Cuts the folder prefix (the prepended 'F') and returns the decoded id. @@ -285,4 +285,4 @@ def cut_and_decode( self, trans, encoded_folder_id ): :returns: decoded Folder id :rtype: int """ - return self.decode_folder_id( trans, self.cut_the_prefix( encoded_folder_id ) ) + return self.decode_folder_id(trans, self.cut_the_prefix(encoded_folder_id)) diff --git a/lib/galaxy/managers/hdas.py b/lib/galaxy/managers/hdas.py index 61dbb3361f21..feb132d3f761 100644 --- a/lib/galaxy/managers/hdas.py +++ b/lib/galaxy/managers/hdas.py @@ -18,13 +18,13 @@ from galaxy.managers import users import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class HDAManager( datasets.DatasetAssociationManager, - secured.OwnableManagerMixin, - taggable.TaggableManagerMixin, - annotatable.AnnotatableManagerMixin ): +class HDAManager(datasets.DatasetAssociationManager, + secured.OwnableManagerMixin, + taggable.TaggableManagerMixin, + annotatable.AnnotatableManagerMixin): """ Interface/service object for interacting with HDAs. """ @@ -37,15 +37,15 @@ class HDAManager( datasets.DatasetAssociationManager, # TODO: move what makes sense into DatasetManager # TODO: which of these are common with LDDAs and can be pushed down into DatasetAssociationManager? - def __init__( self, app ): + def __init__(self, app): """ Set up and initialize other managers needed by hdas. """ - super( HDAManager, self ).__init__( app ) - self.user_manager = users.UserManager( app ) + super(HDAManager, self).__init__(app) + self.user_manager = users.UserManager(app) # .... security and permissions - def is_accessible( self, hda, user, **kwargs ): + def is_accessible(self, hda, user, **kwargs): """ Override to allow owners (those that own the associated history). """ @@ -54,26 +54,26 @@ def is_accessible( self, hda, user, **kwargs ): # I can not access that dataset even if it's in my history # if self.is_owner( hda, user, **kwargs ): # return True - return super( HDAManager, self ).is_accessible( hda, user ) + return super(HDAManager, self).is_accessible(hda, user) - def is_owner( self, hda, user, current_history=None, **kwargs ): + def is_owner(self, hda, user, current_history=None, **kwargs): """ Use history to see if current user owns HDA. """ history = hda.history - if self.user_manager.is_admin( user ): + if self.user_manager.is_admin(user): return True # allow anonymous user to access current history # TODO: some dup here with historyManager.is_owner but prevents circ import # TODO: awkward kwarg (which is my new band name); this may not belong here - move to controller? - if self.user_manager.is_anonymous( user ): + if self.user_manager.is_anonymous(user): if current_history and history == current_history: return True return False return history.user == user # .... create and copy - def create( self, history=None, dataset=None, flush=True, **kwargs ): + def create(self, history=None, dataset=None, flush=True, **kwargs): """ Create a new hda optionally passing in it's history and dataset. @@ -81,20 +81,20 @@ def create( self, history=None, dataset=None, flush=True, **kwargs ): it will be automatically set. """ if not dataset: - kwargs[ 'create_dataset' ] = True - hda = model.HistoryDatasetAssociation( history=history, dataset=dataset, - sa_session=self.app.model.context, **kwargs ) + kwargs['create_dataset'] = True + hda = model.HistoryDatasetAssociation(history=history, dataset=dataset, + sa_session=self.app.model.context, **kwargs) if history: - history.add_dataset( hda, set_hid=( 'hid' not in kwargs ) ) + history.add_dataset(hda, set_hid=('hid' not in kwargs)) # TODO:?? some internal sanity check here (or maybe in add_dataset) to make sure hids are not duped? - self.session().add( hda ) + self.session().add(hda) if flush: self.session().flush() return hda - def copy( self, hda, history=None, **kwargs ): + def copy(self, hda, history=None, **kwargs): """ Copy and return the given HDA. """ @@ -111,22 +111,22 @@ def copy( self, hda, history=None, **kwargs ): dataset=hda.dataset, visible=hda.visible, deleted=hda.deleted, - parent_id=kwargs.get( 'parent_id', None ), + parent_id=kwargs.get('parent_id', None), ) # add_dataset will update the hid to the next avail. in history if history: - history.add_dataset( copy ) + history.add_dataset(copy) copy.copied_from_history_dataset_association = hda copy.set_size() - original_annotation = self.annotation( hda ) - self.annotate( copy, original_annotation, user=history.user ) + original_annotation = self.annotation(hda) + self.annotate(copy, original_annotation, user=history.user) # TODO: update from kwargs? # Need to set after flushed, as MetadataFiles require dataset.id - self.session().add( copy ) + self.session().add(copy) self.session().flush() copy.metadata = hda.metadata @@ -137,52 +137,52 @@ def copy( self, hda, history=None, **kwargs ): self.session().flush() # these use a second session flush and need to be after the first - original_tags = self.get_tags( hda ) - self.set_tags( copy, original_tags, user=history.user ) + original_tags = self.get_tags(hda) + self.set_tags(copy, original_tags, user=history.user) return copy - def copy_ldda( self, history, ldda, **kwargs ): + def copy_ldda(self, history, ldda, **kwargs): """ Copy this HDA as a LDDA and return. """ - return ldda.to_history_dataset_association( history, add_to_history=True ) + return ldda.to_history_dataset_association(history, add_to_history=True) # .... deletion and purging - def purge( self, hda, flush=True): + def purge(self, hda, flush=True): """ Purge this HDA and the dataset underlying it. """ user = hda.history.user or None quota_amount_reduction = 0 if user: - quota_amount_reduction = hda.quota_amount( user ) - super( HDAManager, self ).purge( hda, flush=flush ) + quota_amount_reduction = hda.quota_amount(user) + super(HDAManager, self).purge(hda, flush=flush) # decrease the user's space used if quota_amount_reduction: user.adjust_total_disk_usage(-quota_amount_reduction) return hda # .... states - def error_if_uploading( self, hda ): + def error_if_uploading(self, hda): """ Raise error if HDA is still uploading. """ # TODO: may be better added to an overridden get_accessible if hda.state == model.Dataset.states.UPLOAD: - raise exceptions.Conflict( "Please wait until this dataset finishes uploading" ) + raise exceptions.Conflict("Please wait until this dataset finishes uploading") return hda - def has_been_resubmitted( self, hda ): + def has_been_resubmitted(self, hda): """ Return True if the hda's job was resubmitted at any point. """ job_states = model.Job.states - query = ( self._job_state_history_query( hda ) - .filter( model.JobStateHistory.state == job_states.RESUBMITTED ) ) - return self.app.model.context.query( query.exists() ).scalar() + query = (self._job_state_history_query(hda) + .filter(model.JobStateHistory.state == job_states.RESUBMITTED)) + return self.app.model.context.query(query.exists()).scalar() - def _job_state_history_query( self, hda ): + def _job_state_history_query(self, hda): """ Return a query of the job's state history for the job that created this hda. """ @@ -193,13 +193,13 @@ def _job_state_history_query( self, hda ): # TODO: this does not play well with copied hdas # NOTE: don't eagerload (JODA will load the hda were using!) hda_id = hda.id - query = ( session.query( JobToOutputDatasetAssociation, JobStateHistory ) - .filter( JobToOutputDatasetAssociation.dataset_id == hda_id ) - .filter( JobStateHistory.job_id == JobToOutputDatasetAssociation.job_id ) - .enable_eagerloads( False ) ) + query = (session.query(JobToOutputDatasetAssociation, JobStateHistory) + .filter(JobToOutputDatasetAssociation.dataset_id == hda_id) + .filter(JobStateHistory.job_id == JobToOutputDatasetAssociation.job_id) + .enable_eagerloads(False)) return query - def data_conversion_status( self, hda ): + def data_conversion_status(self, hda): """ Returns a message if an hda is not ready to be used in visualization. """ @@ -214,7 +214,7 @@ def data_conversion_status( self, hda ): # .... data # TODO: to data provider or Text datatype directly - def text_data( self, hda, preview=True ): + def text_data(self, hda, preview=True): """ Get data from text file, truncating if necessary. """ @@ -224,33 +224,33 @@ def text_data( self, hda, preview=True ): truncated = False hda_data = None # For now, cannot get data from non-text datasets. - if not isinstance( hda.datatype, datatypes.data.Text ): + if not isinstance(hda.datatype, datatypes.data.Text): return truncated, hda_data - if not os.path.exists( hda.file_name ): + if not os.path.exists(hda.file_name): return truncated, hda_data - truncated = preview and os.stat( hda.file_name ).st_size > MAX_PEEK_SIZE - hda_data = open( hda.file_name ).read( MAX_PEEK_SIZE ) + truncated = preview and os.stat(hda.file_name).st_size > MAX_PEEK_SIZE + hda_data = open(hda.file_name).read(MAX_PEEK_SIZE) return truncated, hda_data # .... annotatable - def annotation( self, hda ): + def annotation(self, hda): # override to scope to history owner - return self._user_annotation( hda, hda.history.user ) + return self._user_annotation(hda, hda.history.user) class HDASerializer( # datasets._UnflattenedMetadataDatasetAssociationSerializer, datasets.DatasetAssociationSerializer, taggable.TaggableSerializerMixin, - annotatable.AnnotatableSerializerMixin ): + annotatable.AnnotatableSerializerMixin): model_manager_class = HDAManager - def __init__( self, app ): - super( HDASerializer, self ).__init__( app ) + def __init__(self, app): + super(HDASerializer, self).__init__(app) self.hda_manager = self.manager self.default_view = 'summary' - self.add_view( 'summary', [ + self.add_view('summary', [ 'id', 'type_id', 'name', @@ -267,7 +267,7 @@ def __init__( self, app ): 'create_time', 'update_time', ]) - self.add_view( 'detailed', [ + self.add_view('detailed', [ 'model_class', 'history_id', 'hid', # why include if model_class is there? @@ -300,23 +300,23 @@ def __init__( self, app ): 'annotation', 'api_type' - ], include_keys_from='summary' ) + ], include_keys_from='summary') - self.add_view( 'extended', [ + self.add_view('extended', [ 'tool_version', 'parent_id', 'designation', - ], include_keys_from='detailed' ) + ], include_keys_from='detailed') # keyset returned to create show a dataset where the owner has no access - self.add_view( 'inaccessible', [ + self.add_view('inaccessible', [ 'accessible', 'id', 'name', 'history_id', 'hid', 'history_content_type', 'state', 'deleted', 'visible' ]) - def add_serializers( self ): - super( HDASerializer, self ).add_serializers() - taggable.TaggableSerializerMixin.add_serializers( self ) - annotatable.AnnotatableSerializerMixin.add_serializers( self ) + def add_serializers(self): + super(HDASerializer, self).add_serializers() + taggable.TaggableSerializerMixin.add_serializers(self) + annotatable.AnnotatableSerializerMixin.add_serializers(self) self.serializers.update({ 'model_class' : lambda *a, **c: 'HistoryDatasetAssociation', @@ -327,11 +327,11 @@ def add_serializers( self ): 'history_id' : self.serialize_id, # remapped - 'misc_info' : self._remap_from( 'info' ), - 'misc_blurb' : self._remap_from( 'blurb' ), - 'file_ext' : self._remap_from( 'extension' ), - 'file_path' : self._remap_from( 'file_name' ), - 'resubmitted' : lambda i, k, **c: self.hda_manager.has_been_resubmitted( i ), + 'misc_info' : self._remap_from('info'), + 'misc_blurb' : self._remap_from('blurb'), + 'file_ext' : self._remap_from('extension'), + 'file_path' : self._remap_from('file_name'), + 'resubmitted' : lambda i, k, **c: self.hda_manager.has_been_resubmitted(i), 'display_apps' : self.serialize_display_apps, 'display_types' : self.serialize_old_display_applications, 'visualizations': self.serialize_visualization_links, @@ -340,51 +340,51 @@ def add_serializers( self ): # TODO: this intermittently causes a routes.GenerationException - temp use the legacy route to prevent this # see also: https://trello.com/c/5d6j4X5y # see also: https://sentry.galaxyproject.org/galaxy/galaxy-main/group/20769/events/9352883/ - 'url' : lambda i, k, **c: self.url_for( 'history_content', - history_id=self.app.security.encode_id( i.history_id ), - id=self.app.security.encode_id( i.id ) ), + 'url' : lambda i, k, **c: self.url_for('history_content', + history_id=self.app.security.encode_id(i.history_id), + id=self.app.security.encode_id(i.id)), 'urls' : self.serialize_urls, # TODO: backwards compat: need to go away - 'download_url' : lambda i, k, **c: self.url_for( 'history_contents_display', - history_id=self.app.security.encode_id( i.history.id ), - history_content_id=self.app.security.encode_id( i.id ) ), + 'download_url' : lambda i, k, **c: self.url_for('history_contents_display', + history_id=self.app.security.encode_id(i.history.id), + history_content_id=self.app.security.encode_id(i.id)), 'parent_id' : self.serialize_id, # TODO: to DatasetAssociationSerializer - 'accessible' : lambda i, k, user=None, **c: self.manager.is_accessible( i, user ), + 'accessible' : lambda i, k, user=None, **c: self.manager.is_accessible(i, user), 'api_type' : lambda *a, **c: 'file', 'type' : lambda *a, **c: 'file' }) - def serialize( self, hda, keys, user=None, **context ): + def serialize(self, hda, keys, user=None, **context): """ Override to hide information to users not able to access. """ # TODO: to DatasetAssociationSerializer - if not self.manager.is_accessible( hda, user, **context ): - keys = self._view_to_keys( 'inaccessible' ) - return super( HDASerializer, self ).serialize( hda, keys, user=user, **context ) + if not self.manager.is_accessible(hda, user, **context): + keys = self._view_to_keys('inaccessible') + return super(HDASerializer, self).serialize(hda, keys, user=user, **context) - def serialize_display_apps( self, hda, key, trans=None, **context ): + def serialize_display_apps(self, hda, key, trans=None, **context): """ Return dictionary containing new-style display app urls. """ display_apps = [] - for display_app in hda.get_display_applications( trans ).itervalues(): + for display_app in hda.get_display_applications(trans).itervalues(): app_links = [] for link_app in display_app.links.itervalues(): app_links.append({ - 'target': link_app.url.get( 'target_frame', '_blank' ), - 'href': link_app.get_display_url( hda, trans ), - 'text': gettext.gettext( link_app.name ) + 'target': link_app.url.get('target_frame', '_blank'), + 'href': link_app.get_display_url(hda, trans), + 'text': gettext.gettext(link_app.name) }) if app_links: - display_apps.append( dict( label=display_app.name, links=app_links ) ) + display_apps.append(dict(label=display_app.name, links=app_links)) return display_apps - def serialize_old_display_applications( self, hda, key, trans=None, **context ): + def serialize_old_display_applications(self, hda, key, trans=None, **context): """ Return dictionary containing old-style display app urls. """ @@ -394,24 +394,24 @@ def serialize_old_display_applications( self, hda, key, trans=None, **context ): display_link_fn = hda.datatype.get_display_links for display_app in hda.datatype.get_display_types(): - target_frame, display_links = display_link_fn( hda, display_app, self.app, trans.request.base ) + target_frame, display_links = display_link_fn(hda, display_app, self.app, trans.request.base) - if len( display_links ) > 0: - display_label = hda.datatype.get_display_label( display_app ) + if len(display_links) > 0: + display_label = hda.datatype.get_display_label(display_app) app_links = [] for display_name, display_link in display_links: app_links.append({ 'target': target_frame, 'href': display_link, - 'text': gettext.gettext( display_name ) + 'text': gettext.gettext(display_name) }) if app_links: - display_apps.append( dict( label=display_label, links=app_links ) ) + display_apps.append(dict(label=display_label, links=app_links)) return display_apps - def serialize_visualization_links( self, hda, key, trans=None, **context ): + def serialize_visualization_links(self, hda, key, trans=None, **context): """ Return a list of dictionaries with links to visualization pages for those visualizations that apply to this hda. @@ -419,65 +419,65 @@ def serialize_visualization_links( self, hda, key, trans=None, **context ): # use older system if registry is off in the config if not self.app.visualizations_registry: return hda.get_visualizations() - return self.app.visualizations_registry.get_visualizations( trans, hda ) + return self.app.visualizations_registry.get_visualizations(trans, hda) - def serialize_urls( self, hda, key, **context ): + def serialize_urls(self, hda, key, **context): """ Return web controller urls useful for this HDA. """ url_for = self.url_for - encoded_id = self.app.security.encode_id( hda.id ) + encoded_id = self.app.security.encode_id(hda.id) urls = { - 'purge' : url_for( controller='dataset', action='purge_async', dataset_id=encoded_id ), - 'display' : url_for( controller='dataset', action='display', dataset_id=encoded_id, preview=True ), - 'edit' : url_for( controller='dataset', action='edit', dataset_id=encoded_id ), - 'download' : url_for( controller='dataset', action='display', - dataset_id=encoded_id, to_ext=hda.extension ), - 'report_error' : url_for( controller='dataset', action='errors', id=encoded_id ), - 'rerun' : url_for( controller='tool_runner', action='rerun', id=encoded_id ), - 'show_params' : url_for( controller='dataset', action='show_params', dataset_id=encoded_id ), - 'visualization' : url_for( controller='visualization', action='index', - id=encoded_id, model='HistoryDatasetAssociation' ), - 'meta_download' : url_for( controller='dataset', action='get_metadata_file', - hda_id=encoded_id, metadata_name='' ), + 'purge' : url_for(controller='dataset', action='purge_async', dataset_id=encoded_id), + 'display' : url_for(controller='dataset', action='display', dataset_id=encoded_id, preview=True), + 'edit' : url_for(controller='dataset', action='edit', dataset_id=encoded_id), + 'download' : url_for(controller='dataset', action='display', + dataset_id=encoded_id, to_ext=hda.extension), + 'report_error' : url_for(controller='dataset', action='errors', id=encoded_id), + 'rerun' : url_for(controller='tool_runner', action='rerun', id=encoded_id), + 'show_params' : url_for(controller='dataset', action='show_params', dataset_id=encoded_id), + 'visualization' : url_for(controller='visualization', action='index', + id=encoded_id, model='HistoryDatasetAssociation'), + 'meta_download' : url_for(controller='dataset', action='get_metadata_file', + hda_id=encoded_id, metadata_name=''), } return urls -class HDADeserializer( datasets.DatasetAssociationDeserializer, - taggable.TaggableDeserializerMixin, - annotatable.AnnotatableDeserializerMixin ): +class HDADeserializer(datasets.DatasetAssociationDeserializer, + taggable.TaggableDeserializerMixin, + annotatable.AnnotatableDeserializerMixin): """ Interface/service object for validating and deserializing dictionaries into histories. """ model_manager_class = HDAManager - def __init__( self, app ): - super( HDADeserializer, self ).__init__( app ) + def __init__(self, app): + super(HDADeserializer, self).__init__(app) self.hda_manager = self.manager - def add_deserializers( self ): - super( HDADeserializer, self ).add_deserializers() - taggable.TaggableDeserializerMixin.add_deserializers( self ) - annotatable.AnnotatableDeserializerMixin.add_deserializers( self ) + def add_deserializers(self): + super(HDADeserializer, self).add_deserializers() + taggable.TaggableDeserializerMixin.add_deserializers(self) + annotatable.AnnotatableDeserializerMixin.add_deserializers(self) self.deserializers.update({ 'visible' : self.deserialize_bool, # remapped - 'genome_build' : lambda i, k, v, **c: self.deserialize_genome_build( i, 'dbkey', v ), - 'misc_info' : lambda i, k, v, **c: self.deserialize_basestring( i, 'info', v, - convert_none_to_empty=True ), + 'genome_build' : lambda i, k, v, **c: self.deserialize_genome_build(i, 'dbkey', v), + 'misc_info' : lambda i, k, v, **c: self.deserialize_basestring(i, 'info', v, + convert_none_to_empty=True), }) - self.deserializable_keyset.update( self.deserializers.keys() ) + self.deserializable_keyset.update(self.deserializers.keys()) -class HDAFilterParser( datasets.DatasetAssociationFilterParser, - taggable.TaggableFilterMixin, - annotatable.AnnotatableFilterMixin ): +class HDAFilterParser(datasets.DatasetAssociationFilterParser, + taggable.TaggableFilterMixin, + annotatable.AnnotatableFilterMixin): model_manager_class = HDAManager model_class = model.HistoryDatasetAssociation - def _add_parsers( self ): - super( HDAFilterParser, self )._add_parsers() - taggable.TaggableFilterMixin._add_parsers( self ) - annotatable.AnnotatableFilterMixin._add_parsers( self ) + def _add_parsers(self): + super(HDAFilterParser, self)._add_parsers() + taggable.TaggableFilterMixin._add_parsers(self) + annotatable.AnnotatableFilterMixin._add_parsers(self) diff --git a/lib/galaxy/managers/hdcas.py b/lib/galaxy/managers/hdcas.py index 33f59dcb7604..a6f1ac8159be 100644 --- a/lib/galaxy/managers/hdcas.py +++ b/lib/galaxy/managers/hdcas.py @@ -16,7 +16,7 @@ from galaxy.managers import hdas import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) # TODO: to DatasetCollectionInstanceManager @@ -26,7 +26,7 @@ class HDCAManager( secured.OwnableManagerMixin, deletable.PurgableManagerMixin, taggable.TaggableManagerMixin, - annotatable.AnnotatableManagerMixin ): + annotatable.AnnotatableManagerMixin): """ Interface/service object for interacting with HDCAs. """ @@ -36,13 +36,13 @@ class HDCAManager( tag_assoc = model.HistoryDatasetCollectionTagAssociation annotation_assoc = model.HistoryDatasetCollectionAnnotationAssociation - def __init__( self, app ): + def __init__(self, app): """ Set up and initialize other managers needed by hdcas. """ - super( HDCAManager, self ).__init__( app ) + super(HDCAManager, self).__init__(app) - def map_datasets( self, content, fn, *parents ): + def map_datasets(self, content, fn, *parents): """ Iterate over the datasets of a given collection, recursing into collections, and calling fn on each dataset. @@ -51,16 +51,16 @@ def map_datasets( self, content, fn, *parents ): """ returned = [] # lots of nesting going on within the nesting - collection = content.collection if hasattr( content, 'collection' ) else content - this_parents = ( content, ) + parents + collection = content.collection if hasattr(content, 'collection') else content + this_parents = (content, ) + parents for element in collection.elements: - next_parents = ( element, ) + this_parents + next_parents = (element, ) + this_parents if element.is_collection: - processed_list = self.map_datasets( element.child_collection, fn, *next_parents ) - returned.extend( processed_list ) + processed_list = self.map_datasets(element.child_collection, fn, *next_parents) + returned.extend(processed_list) else: - processed = fn( element.dataset_instance, *next_parents ) - returned.append( processed ) + processed = fn(element.dataset_instance, *next_parents) + returned.append(processed) return returned # TODO: un-stub @@ -68,18 +68,18 @@ def map_datasets( self, content, fn, *parents ): # serializers # ----------------------------------------------------------------------------- -class DCESerializer( base.ModelSerializer ): +class DCESerializer(base.ModelSerializer): """ Serializer for DatasetCollectionElements. """ - def __init__( self, app ): - super( DCESerializer, self ).__init__( app ) - self.hda_serializer = hdas.HDASerializer( app ) - self.dc_serializer = DCSerializer( app, dce_serializer=self ) + def __init__(self, app): + super(DCESerializer, self).__init__(app) + self.hda_serializer = hdas.HDASerializer(app) + self.dc_serializer = DCSerializer(app, dce_serializer=self) self.default_view = 'summary' - self.add_view( 'summary', [ + self.add_view('summary', [ 'id', 'model_class', 'element_index', 'element_identifier', @@ -87,32 +87,32 @@ def __init__( self, app ): 'object' ]) - def add_serializers( self ): - super( DCESerializer, self ).add_serializers() + def add_serializers(self): + super(DCESerializer, self).add_serializers() self.serializers.update({ 'model_class' : lambda *a, **c: 'DatasetCollectionElement', 'object' : self.serialize_object }) - def serialize_object( self, item, key, **context ): + def serialize_object(self, item, key, **context): if item.hda: - return self.hda_serializer.serialize_to_view( item.hda, view='summary', **context ) + return self.hda_serializer.serialize_to_view(item.hda, view='summary', **context) if item.child_collection: - return self.dc_serializer.serialize_to_view( item.child_collection, view='detailed', **context ) + return self.dc_serializer.serialize_to_view(item.child_collection, view='detailed', **context) return 'object' -class DCSerializer( base.ModelSerializer ): +class DCSerializer(base.ModelSerializer): """ Serializer for DatasetCollections. """ - def __init__( self, app, dce_serializer=None ): - super( DCSerializer, self ).__init__( app ) - self.dce_serializer = dce_serializer or DCESerializer( app ) + def __init__(self, app, dce_serializer=None): + super(DCSerializer, self).__init__(app) + self.dce_serializer = dce_serializer or DCESerializer(app) self.default_view = 'summary' - self.add_view( 'summary', [ + self.add_view('summary', [ 'id', 'create_time', 'update_time', @@ -121,45 +121,45 @@ def __init__( self, app, dce_serializer=None ): 'populated_state', 'populated_state_message', ]) - self.add_view( 'detailed', [ + self.add_view('detailed', [ 'elements' - ], include_keys_from='summary' ) + ], include_keys_from='summary') - def add_serializers( self ): - super( DCSerializer, self ).add_serializers() + def add_serializers(self): + super(DCSerializer, self).add_serializers() self.serializers.update({ 'model_class' : lambda *a, **c: 'DatasetCollection', 'elements' : self.serialize_elements, 'element_count' : self.serialize_element_count }) - def serialize_elements( self, item, key, **context ): + def serialize_elements(self, item, key, **context): returned = [] for element in item.elements: - serialized = self.dce_serializer.serialize_to_view( element, view='summary', **context ) - returned.append( serialized ) + serialized = self.dce_serializer.serialize_to_view(element, view='summary', **context) + returned.append(serialized) return returned - def serialize_element_count( self, item, key, **context ): + def serialize_element_count(self, item, key, **context): """Return the count of elements for this collection.""" # TODO: app.model.context -> session # TODO: to the container interface (dataset_collection_contents) - return ( self.app.model.context.query( model.DatasetCollectionElement ) - .filter( model.DatasetCollectionElement.dataset_collection_id == item.id ) - .count() ) + return (self.app.model.context.query(model.DatasetCollectionElement) + .filter(model.DatasetCollectionElement.dataset_collection_id == item.id) + .count()) -class DCASerializer( base.ModelSerializer ): +class DCASerializer(base.ModelSerializer): """ Base (abstract) Serializer class for HDCAs and LDCAs. """ - def __init__( self, app, dce_serializer=None ): - super( DCASerializer, self ).__init__( app ) - self.dce_serializer = dce_serializer or DCESerializer( app ) + def __init__(self, app, dce_serializer=None): + super(DCASerializer, self).__init__(app) + self.dce_serializer = dce_serializer or DCESerializer(app) self.default_view = 'summary' - self.add_view( 'summary', [ + self.add_view('summary', [ 'id', 'create_time', 'update_time', 'collection_type', @@ -167,14 +167,14 @@ def __init__( self, app, dce_serializer=None ): 'populated_state', 'populated_state_message', ]) - self.add_view( 'detailed', [ + self.add_view('detailed', [ 'elements' - ], include_keys_from='summary' ) + ], include_keys_from='summary') - def add_serializers( self ): - super( DCASerializer, self ).add_serializers() + def add_serializers(self): + super(DCASerializer, self).add_serializers() # most attributes are (kinda) proxied from DCs - we need a serializer to proxy to - self.dc_serializer = DCSerializer( self.app ) + self.dc_serializer = DCSerializer(self.app) # then set the serializers to point to it for those attrs collection_keys = [ 'create_time', @@ -187,33 +187,33 @@ def add_serializers( self ): 'element_count' ] for key in collection_keys: - self.serializers[ key ] = self._proxy_to_dataset_collection( key=key ) + self.serializers[key] = self._proxy_to_dataset_collection(key=key) - def _proxy_to_dataset_collection( self, serializer=None, key=None ): + def _proxy_to_dataset_collection(self, serializer=None, key=None): # dataset_collection associations are (rough) proxies to datasets - access their serializer using this remapping fn # remapping done by either kwarg key: IOW dataset attr key (e.g. populated_state) # or by kwarg serializer: a function that's passed in (e.g. elements) if key: - return lambda i, k, **c: self.dc_serializer.serialize( i.collection, [ k ], **c )[ k ] + return lambda i, k, **c: self.dc_serializer.serialize(i.collection, [k], **c)[k] if serializer: - return lambda i, k, **c: serializer( i.collection, key or k, **c ) - raise TypeError( 'kwarg serializer or key needed') + return lambda i, k, **c: serializer(i.collection, key or k, **c) + raise TypeError('kwarg serializer or key needed') class HDCASerializer( DCASerializer, taggable.TaggableSerializerMixin, - annotatable.AnnotatableSerializerMixin ): + annotatable.AnnotatableSerializerMixin): """ Serializer for HistoryDatasetCollectionAssociations. """ - def __init__( self, app ): - super( HDCASerializer, self ).__init__( app ) - self.hdca_manager = HDCAManager( app ) + def __init__(self, app): + super(HDCASerializer, self).__init__(app) + self.hdca_manager = HDCAManager(app) self.default_view = 'summary' - self.add_view( 'summary', [ + self.add_view('summary', [ 'id', 'type_id', 'name', @@ -237,14 +237,14 @@ def __init__( self, app ): 'create_time', 'update_time', 'tags', # TODO: detail view only (maybe) ]) - self.add_view( 'detailed', [ + self.add_view('detailed', [ 'elements' - ], include_keys_from='summary' ) + ], include_keys_from='summary') - def add_serializers( self ): - super( HDCASerializer, self ).add_serializers() - taggable.TaggableSerializerMixin.add_serializers( self ) - annotatable.AnnotatableSerializerMixin.add_serializers( self ) + def add_serializers(self): + super(HDCASerializer, self).add_serializers() + taggable.TaggableSerializerMixin.add_serializers(self) + annotatable.AnnotatableSerializerMixin.add_serializers(self) self.serializers.update({ 'model_class' : lambda *a, **c: self.hdca_manager.model_class.__class__.__name__, @@ -255,8 +255,8 @@ def add_serializers( self ): 'history_content_type' : lambda *a, **c: self.hdca_manager.model_class.content_type, 'type_id' : self.serialize_type_id, - 'url' : lambda i, k, **c: self.url_for( 'history_content_typed', - history_id=self.app.security.encode_id( i.history_id ), - id=self.app.security.encode_id( i.id ), - type=self.hdca_manager.model_class.content_type ), + 'url' : lambda i, k, **c: self.url_for('history_content_typed', + history_id=self.app.security.encode_id(i.history_id), + id=self.app.security.encode_id(i.id), + type=self.hdca_manager.model_class.content_type), }) diff --git a/lib/galaxy/managers/histories.py b/lib/galaxy/managers/histories.py index 9eafb9cd1ff4..ec2b92d07f43 100644 --- a/lib/galaxy/managers/histories.py +++ b/lib/galaxy/managers/histories.py @@ -17,10 +17,10 @@ import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class HistoryManager( sharable.SharableModelManager, deletable.PurgableManagerMixin ): +class HistoryManager(sharable.SharableModelManager, deletable.PurgableManagerMixin): model_class = model.History foreign_key_name = 'history' @@ -32,58 +32,58 @@ class HistoryManager( sharable.SharableModelManager, deletable.PurgableManagerMi # TODO: incorporate imp/exp (or alias to) - def __init__( self, app, *args, **kwargs ): - super( HistoryManager, self ).__init__( app, *args, **kwargs ) - self.hda_manager = hdas.HDAManager( app ) - self.contents_manager = history_contents.HistoryContentsManager( app ) - self.contents_filters = history_contents.HistoryContentsFilters( app ) + def __init__(self, app, *args, **kwargs): + super(HistoryManager, self).__init__(app, *args, **kwargs) + self.hda_manager = hdas.HDAManager(app) + self.contents_manager = history_contents.HistoryContentsManager(app) + self.contents_filters = history_contents.HistoryContentsFilters(app) - def copy( self, history, user, **kwargs ): + def copy(self, history, user, **kwargs): """ Copy and return the given `history`. """ - return history.copy( target_user=user, **kwargs ) + return history.copy(target_user=user, **kwargs) # .... sharable # overriding to handle anonymous users' current histories in both cases - def by_user( self, user, current_history=None, **kwargs ): + def by_user(self, user, current_history=None, **kwargs): """ Get all the histories for a given user (allowing anon users' theirs) ordered by update time. """ # handle default and/or anonymous user (which still may not have a history yet) - if self.user_manager.is_anonymous( user ): - return [ current_history ] if current_history else [] - return super( HistoryManager, self ).by_user( user, **kwargs ) + if self.user_manager.is_anonymous(user): + return [current_history] if current_history else [] + return super(HistoryManager, self).by_user(user, **kwargs) - def is_owner( self, history, user, current_history=None, **kwargs ): + def is_owner(self, history, user, current_history=None, **kwargs): """ True if the current user is the owner of the given history. """ # anon users are only allowed to view their current history - if self.user_manager.is_anonymous( user ): + if self.user_manager.is_anonymous(user): if current_history and history == current_history: return True return False - return super( HistoryManager, self ).is_owner( history, user ) + return super(HistoryManager, self).is_owner(history, user) # TODO: possibly to sharable or base - def most_recent( self, user, filters=None, current_history=None, **kwargs ): + def most_recent(self, user, filters=None, current_history=None, **kwargs): """ Return the most recently update history for the user. If user is anonymous, return the current history. If the user is anonymous and the current history is deleted, return None. """ - if self.user_manager.is_anonymous( user ): - return None if ( not current_history or current_history.deleted ) else current_history - desc_update_time = desc( self.model_class.table.c.update_time ) - filters = self._munge_filters( filters, self.model_class.user_id == user.id ) + if self.user_manager.is_anonymous(user): + return None if (not current_history or current_history.deleted) else current_history + desc_update_time = desc(self.model_class.table.c.update_time) + filters = self._munge_filters(filters, self.model_class.user_id == user.id) # TODO: normalize this return value - return self.query( filters=filters, order_by=desc_update_time, limit=1, **kwargs ).first() + return self.query(filters=filters, order_by=desc_update_time, limit=1, **kwargs).first() # .... purgable - def purge( self, history, flush=True, **kwargs ): + def purge(self, history, flush=True, **kwargs): """ Purge this history and all HDAs, Collections, and Datasets inside this history. """ @@ -91,66 +91,66 @@ def purge( self, history, flush=True, **kwargs ): # First purge all the datasets for hda in history.datasets: if not hda.purged: - self.hda_manager.purge( hda, flush=True ) + self.hda_manager.purge(hda, flush=True) # Now mark the history as purged - super( HistoryManager, self ).purge( history, flush=flush, **kwargs ) + super(HistoryManager, self).purge(history, flush=flush, **kwargs) # .... current # TODO: make something to bypass the anon user + current history permissions issue # def is_current_users_current_history( self, history, trans ): # pass - def get_current( self, trans ): + def get_current(self, trans): """ Return the current history. """ # TODO: trans return trans.get_history() - def set_current( self, trans, history ): + def set_current(self, trans, history): """ Set the current history. """ # TODO: trans - trans.set_history( history ) + trans.set_history(history) return history - def set_current_by_id( self, trans, history_id ): + def set_current_by_id(self, trans, history_id): """ Set the current history by an id. """ - return self.set_current( trans, self.by_id( history_id ) ) + return self.set_current(trans, self.by_id(history_id)) # order_by parsing - similar to FilterParser but not enough yet to warrant a class? - def parse_order_by( self, order_by_string, default=None ): + def parse_order_by(self, order_by_string, default=None): """Return an ORM compatible order_by using the given string""" # TODO: generalize into class # TODO: general (enough) columns - if order_by_string in ( 'create_time', 'create_time-dsc' ): - return desc( self.model_class.create_time ) + if order_by_string in ('create_time', 'create_time-dsc'): + return desc(self.model_class.create_time) if order_by_string == 'create_time-asc': - return asc( self.model_class.create_time ) - if order_by_string in ( 'update_time', 'update_time-dsc' ): - return desc( self.model_class.update_time ) + return asc(self.model_class.create_time) + if order_by_string in ('update_time', 'update_time-dsc'): + return desc(self.model_class.update_time) if order_by_string == 'update_time-asc': - return asc( self.model_class.update_time ) - if order_by_string in ( 'name', 'name-asc' ): - return asc( self.model_class.name ) + return asc(self.model_class.update_time) + if order_by_string in ('name', 'name-asc'): + return asc(self.model_class.name) if order_by_string == 'name-dsc': - return desc( self.model_class.name ) + return desc(self.model_class.name) # TODO: history columns - if order_by_string in ( 'size', 'size-dsc' ): - return desc( self.model_class.disk_size ) + if order_by_string in ('size', 'size-dsc'): + return desc(self.model_class.disk_size) if order_by_string == 'size-asc': - return asc( self.model_class.disk_size ) + return asc(self.model_class.disk_size) # TODO: add functional/non-orm orders (such as rating) if default: - return self.parse_order_by( default ) - raise glx_exceptions.RequestParameterInvalidException( 'Unkown order_by', order_by=order_by_string, - available=[ 'create_time', 'update_time', 'name', 'size' ]) + return self.parse_order_by(default) + raise glx_exceptions.RequestParameterInvalidException('Unkown order_by', order_by=order_by_string, + available=['create_time', 'update_time', 'name', 'size']) - def non_ready_jobs( self, history ): + def non_ready_jobs(self, history): """Return the currently running job objects associated with this history. Where running is defined as new, waiting, queued, running, resubmitted, @@ -158,29 +158,29 @@ def non_ready_jobs( self, history ): """ # TODO: defer to jobModelManager (if there was one) # TODO: genericize the params to allow other filters - jobs = ( self.session().query( model.Job ) - .filter( model.Job.history == history ) - .filter( model.Job.state.in_( model.Job.non_ready_states ) ) ) + jobs = (self.session().query(model.Job) + .filter(model.Job.history == history) + .filter(model.Job.state.in_(model.Job.non_ready_states))) return jobs -class HistorySerializer( sharable.SharableModelSerializer, deletable.PurgableSerializerMixin ): +class HistorySerializer(sharable.SharableModelSerializer, deletable.PurgableSerializerMixin): """ Interface/service object for serializing histories into dictionaries. """ model_manager_class = HistoryManager SINGLE_CHAR_ABBR = 'h' - def __init__( self, app, **kwargs ): - super( HistorySerializer, self ).__init__( app, **kwargs ) + def __init__(self, app, **kwargs): + super(HistorySerializer, self).__init__(app, **kwargs) self.history_manager = self.manager - self.hda_manager = hdas.HDAManager( app ) - self.hda_serializer = hdas.HDASerializer( app ) - self.history_contents_serializer = history_contents.HistoryContentsSerializer( app ) + self.hda_manager = hdas.HDAManager(app) + self.hda_serializer = hdas.HDASerializer(app) + self.history_contents_serializer = history_contents.HistoryContentsSerializer(app) self.default_view = 'summary' - self.add_view( 'summary', [ + self.add_view('summary', [ 'id', 'model_class', 'name', @@ -193,7 +193,7 @@ def __init__( self, app, **kwargs ): 'annotation', 'tags', ]) - self.add_view( 'detailed', [ + self.add_view('detailed', [ 'contents_url', 'empty', 'size', @@ -210,11 +210,11 @@ def __init__( self, app, **kwargs ): 'state_ids', # 'community_rating', # 'user_rating', - ], include_keys_from='summary' ) + ], include_keys_from='summary') # in the Historys' case, each of these views includes the keys from the previous #: ..note: this is a custom view for newer (2016/3) UI and should be considered volatile - self.add_view( 'dev-detailed', [ + self.add_view('dev-detailed', [ 'contents_url', 'size', 'user_id', @@ -227,55 +227,55 @@ def __init__( self, app, **kwargs ): # 'contents_states', 'contents_active', 'hid_counter', - ], include_keys_from='summary' ) + ], include_keys_from='summary') # assumes: outgoing to json.dumps and sanitized - def add_serializers( self ): - super( HistorySerializer, self ).add_serializers() - deletable.PurgableSerializerMixin.add_serializers( self ) + def add_serializers(self): + super(HistorySerializer, self).add_serializers() + deletable.PurgableSerializerMixin.add_serializers(self) self.serializers.update({ 'model_class' : lambda *a, **c: 'History', - 'size' : lambda i, k, **c: int( i.disk_size ), + 'size' : lambda i, k, **c: int(i.disk_size), 'nice_size' : lambda i, k, **c: i.disk_nice_size, 'state' : self.serialize_history_state, - 'url' : lambda i, k, **c: self.url_for( 'history', id=self.app.security.encode_id( i.id ) ), - 'contents_url' : lambda i, k, **c: self.url_for( 'history_contents', - history_id=self.app.security.encode_id( i.id ) ), + 'url' : lambda i, k, **c: self.url_for('history', id=self.app.security.encode_id(i.id)), + 'contents_url' : lambda i, k, **c: self.url_for('history_contents', + history_id=self.app.security.encode_id(i.id)), - 'empty' : lambda i, k, **c: ( len( i.datasets ) + len( i.dataset_collections ) ) <= 0, - 'count' : lambda i, k, **c: len( i.datasets ), - 'hdas' : lambda i, k, **c: [ self.app.security.encode_id( hda.id ) for hda in i.datasets ], + 'empty' : lambda i, k, **c: (len(i.datasets) + len(i.dataset_collections)) <= 0, + 'count' : lambda i, k, **c: len(i.datasets), + 'hdas' : lambda i, k, **c: [self.app.security.encode_id(hda.id) for hda in i.datasets], 'state_details' : self.serialize_state_counts, 'state_ids' : self.serialize_state_ids, 'contents' : self.serialize_contents, - 'non_ready_jobs': lambda i, k, **c: [ self.app.security.encode_id( job.id ) for job - in self.manager.non_ready_jobs( i ) ], + 'non_ready_jobs': lambda i, k, **c: [self.app.security.encode_id(job.id) for job + in self.manager.non_ready_jobs(i)], 'contents_states': self.serialize_contents_states, 'contents_active': self.serialize_contents_active, }) # remove this - def serialize_state_ids( self, history, key, **context ): + def serialize_state_ids(self, history, key, **context): """ Return a dictionary keyed to possible dataset states and valued with lists containing the ids of each HDA in that state. """ state_ids = {} for state in model.Dataset.states.values(): - state_ids[ state ] = [] + state_ids[state] = [] # TODO:?? collections and coll. states? for hda in history.datasets: # TODO: do not encode ids at this layer - encoded_id = self.app.security.encode_id( hda.id ) - state_ids[ hda.state ].append( encoded_id ) + encoded_id = self.app.security.encode_id(hda.id) + state_ids[hda.state].append(encoded_id) return state_ids # remove this - def serialize_state_counts( self, history, key, exclude_deleted=True, exclude_hidden=False, **context ): + def serialize_state_counts(self, history, key, exclude_deleted=True, exclude_hidden=False, **context): """ Return a dictionary keyed to possible dataset states and valued with the number of datasets in this history that have those states. @@ -283,7 +283,7 @@ def serialize_state_counts( self, history, key, exclude_deleted=True, exclude_hi # TODO: the default flags above may not make a lot of sense (T,T?) state_counts = {} for state in model.Dataset.states.values(): - state_counts[ state ] = 0 + state_counts[state] = 0 # TODO:?? collections and coll. states? for hda in history.datasets: @@ -291,11 +291,11 @@ def serialize_state_counts( self, history, key, exclude_deleted=True, exclude_hi continue if exclude_hidden and not hda.visible: continue - state_counts[ hda.state ] = state_counts[ hda.state ] + 1 + state_counts[hda.state] = state_counts[hda.state] + 1 return state_counts # TODO: remove this (is state used/useful?) - def serialize_history_state( self, history, key, **context ): + def serialize_history_state(self, history, key, **context): """ Returns the history state based on the states of the HDAs it contains. """ @@ -304,8 +304,8 @@ def serialize_history_state( self, history, key, **context ): state = states.ERROR # TODO: history_state and state_counts are classically calc'd at the same time # so this is rel. ineff. - if we keep this... - hda_state_counts = self.serialize_state_counts( history, 'counts', exclude_deleted=True, **context ) - num_hdas = sum( hda_state_counts.values() ) + hda_state_counts = self.serialize_state_counts(history, 'counts', exclude_deleted=True, **context) + num_hdas = sum(hda_state_counts.values()) if num_hdas == 0: state = states.NEW @@ -315,35 +315,35 @@ def serialize_history_state( self, history, key, **context ): hda_state_counts[states.UPLOAD] > 0): state = states.RUNNING # TODO: this method may be more useful if we *also* polled the histories jobs here too - elif (hda_state_counts[ states.QUEUED ] > 0 or + elif (hda_state_counts[states.QUEUED] > 0 or hda_state_counts[states.NEW] > 0): state = states.QUEUED elif (hda_state_counts[states.ERROR] > 0 or hda_state_counts[states.FAILED_METADATA] > 0): state = states.ERROR - elif hda_state_counts[ states.OK ] == num_hdas: + elif hda_state_counts[states.OK] == num_hdas: state = states.OK return state - def serialize_contents( self, history, key, trans=None, user=None, **context ): + def serialize_contents(self, history, key, trans=None, user=None, **context): returned = [] - for content in self.manager.contents_manager._union_of_contents_query( history ).all(): - serialized = self.history_contents_serializer.serialize_to_view( content, - view='summary', trans=trans, user=user ) - returned.append( serialized ) + for content in self.manager.contents_manager._union_of_contents_query(history).all(): + serialized = self.history_contents_serializer.serialize_to_view(content, + view='summary', trans=trans, user=user) + returned.append(serialized) return returned - def serialize_contents_states( self, history, key, trans=None, **context ): + def serialize_contents_states(self, history, key, trans=None, **context): """ Return a dictionary containing the counts of all contents in each state keyed by the distinct states. Note: does not include deleted/hidden contents. """ - return self.manager.contents_manager.state_counts( history ) + return self.manager.contents_manager.state_counts(history) - def serialize_contents_active( self, history, key, **context ): + def serialize_contents_active(self, history, key, **context): """ Return a dictionary keyed with 'deleted', 'hidden', and 'active' with values for each representing the count of contents in each state. @@ -351,22 +351,22 @@ def serialize_contents_active( self, history, key, **context ): Note: counts for deleted and hidden overlap; In other words, a dataset that's both deleted and hidden will be added to both totals. """ - return self.manager.contents_manager.active_counts( history ) + return self.manager.contents_manager.active_counts(history) -class HistoryDeserializer( sharable.SharableModelDeserializer, deletable.PurgableDeserializerMixin ): +class HistoryDeserializer(sharable.SharableModelDeserializer, deletable.PurgableDeserializerMixin): """ Interface/service object for validating and deserializing dictionaries into histories. """ model_manager_class = HistoryManager - def __init__( self, app ): - super( HistoryDeserializer, self ).__init__( app ) + def __init__(self, app): + super(HistoryDeserializer, self).__init__(app) self.history_manager = self.manager - def add_deserializers( self ): - super( HistoryDeserializer, self ).add_deserializers() - deletable.PurgableDeserializerMixin.add_deserializers( self ) + def add_deserializers(self): + super(HistoryDeserializer, self).add_deserializers() + deletable.PurgableDeserializerMixin.add_deserializers(self) self.deserializers.update({ 'name' : self.deserialize_basestring, @@ -374,15 +374,15 @@ def add_deserializers( self ): }) -class HistoryFilters( sharable.SharableModelFilters, deletable.PurgableFiltersMixin ): +class HistoryFilters(sharable.SharableModelFilters, deletable.PurgableFiltersMixin): model_class = model.History model_manager_class = HistoryManager - def _add_parsers( self ): - super( HistoryFilters, self )._add_parsers() - deletable.PurgableFiltersMixin._add_parsers( self ) + def _add_parsers(self): + super(HistoryFilters, self)._add_parsers() + deletable.PurgableFiltersMixin._add_parsers(self) self.orm_filter_parsers.update({ # history specific - 'name' : { 'op': ( 'eq', 'contains', 'like' ) }, - 'genome_build' : { 'op': ( 'eq', 'contains', 'like' ) }, + 'name' : {'op': ('eq', 'contains', 'like')}, + 'genome_build' : {'op': ('eq', 'contains', 'like')}, }) diff --git a/lib/galaxy/managers/history_contents.py b/lib/galaxy/managers/history_contents.py index 206c9caeab88..fc38bb98edf8 100644 --- a/lib/galaxy/managers/history_contents.py +++ b/lib/galaxy/managers/history_contents.py @@ -21,12 +21,12 @@ from galaxy.managers import hdcas import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) # into its own class to have it's own filters, etc. # TODO: but can't inherit from model manager (which assumes only one model) -class HistoryContentsManager( containers.ContainerManagerMixin ): +class HistoryContentsManager(containers.ContainerManagerMixin): root_container_class = model.History @@ -59,80 +59,80 @@ class HistoryContentsManager( containers.ContainerManagerMixin ): ) default_order_by = 'hid' - def __init__( self, app ): + def __init__(self, app): self.app = app - self.contained_manager = self.contained_class_manager_class( app ) - self.subcontainer_manager = self.subcontainer_class_manager_class( app ) + self.contained_manager = self.contained_class_manager_class(app) + self.subcontainer_manager = self.subcontainer_class_manager_class(app) # ---- interface - def contained( self, container, filters=None, limit=None, offset=None, order_by=None, **kwargs ): + def contained(self, container, filters=None, limit=None, offset=None, order_by=None, **kwargs): """ Returns non-subcontainer objects within `container`. """ - filter_to_inside_container = self._get_filter_for_contained( container, self.contained_class ) - filters = base.munge_lists( filter_to_inside_container, filters ) - return self.contained_manager.list( filters=filters, limit=limit, offset=offset, order_by=order_by, **kwargs ) + filter_to_inside_container = self._get_filter_for_contained(container, self.contained_class) + filters = base.munge_lists(filter_to_inside_container, filters) + return self.contained_manager.list(filters=filters, limit=limit, offset=offset, order_by=order_by, **kwargs) - def subcontainers( self, container, filters=None, limit=None, offset=None, order_by=None, **kwargs ): + def subcontainers(self, container, filters=None, limit=None, offset=None, order_by=None, **kwargs): """ Returns only the containers within `container`. """ - filter_to_inside_container = self._get_filter_for_contained( container, self.subcontainer_class ) - filters = base.munge_lists( filter_to_inside_container, filters ) + filter_to_inside_container = self._get_filter_for_contained(container, self.subcontainer_class) + filters = base.munge_lists(filter_to_inside_container, filters) # TODO: collections.DatasetCollectionManager doesn't have the list # return self.subcontainer_manager.list( filters=filters, limit=limit, offset=offset, order_by=order_by, **kwargs ) - return self._session().query( self.subcontainer_class ).filter( filters ).all() + return self._session().query(self.subcontainer_class).filter(filters).all() - def contents( self, container, filters=None, limit=None, offset=None, order_by=None, **kwargs ): + def contents(self, container, filters=None, limit=None, offset=None, order_by=None, **kwargs): """ Returns a list of both/all types of contents, filtered and in some order. """ # TODO?: we could branch here based on 'if limit is None and offset is None' - to a simpler (non-union) query # for now, I'm just using this (even for non-limited/offset queries) to reduce code paths - return self._union_of_contents( container, - filters=filters, limit=limit, offset=offset, order_by=order_by, **kwargs ) + return self._union_of_contents(container, + filters=filters, limit=limit, offset=offset, order_by=order_by, **kwargs) - def contents_count( self, container, filters=None, limit=None, offset=None, order_by=None, **kwargs ): + def contents_count(self, container, filters=None, limit=None, offset=None, order_by=None, **kwargs): """ Returns a count of both/all types of contents, based on the given filters. """ - return self.contents_query( container, - filters=filters, limit=limit, offset=offset, order_by=order_by, **kwargs ).count() + return self.contents_query(container, + filters=filters, limit=limit, offset=offset, order_by=order_by, **kwargs).count() - def contents_query( self, container, filters=None, limit=None, offset=None, order_by=None, **kwargs ): + def contents_query(self, container, filters=None, limit=None, offset=None, order_by=None, **kwargs): """ Returns the contents union query for subqueries, etc. """ - return self._union_of_contents_query( container, - filters=filters, limit=limit, offset=offset, order_by=order_by, **kwargs ) + return self._union_of_contents_query(container, + filters=filters, limit=limit, offset=offset, order_by=order_by, **kwargs) # order_by parsing - similar to FilterParser but not enough yet to warrant a class? - def parse_order_by( self, order_by_string, default=None ): + def parse_order_by(self, order_by_string, default=None): """Return an ORM compatible order_by using the given string""" - if order_by_string in ( 'hid', 'hid-dsc' ): - return desc( 'hid' ) + if order_by_string in ('hid', 'hid-dsc'): + return desc('hid') if order_by_string == 'hid-asc': - return asc( 'hid' ) - if order_by_string in ( 'create_time', 'create_time-dsc' ): - return desc( 'create_time' ) + return asc('hid') + if order_by_string in ('create_time', 'create_time-dsc'): + return desc('create_time') if order_by_string == 'create_time-asc': - return asc( 'create_time' ) - if order_by_string in ( 'update_time', 'update_time-dsc' ): - return desc( 'update_time' ) + return asc('create_time') + if order_by_string in ('update_time', 'update_time-dsc'): + return desc('update_time') if order_by_string == 'update_time-asc': - return asc( 'update_time' ) - if order_by_string in ( 'name', 'name-asc' ): - return asc( 'name' ) + return asc('update_time') + if order_by_string in ('name', 'name-asc'): + return asc('name') if order_by_string == 'name-dsc': - return desc( 'name' ) + return desc('name') if default: - return self.parse_order_by( default ) + return self.parse_order_by(default) # TODO: allow order_by None - raise glx_exceptions.RequestParameterInvalidException( 'Unknown order_by', order_by=order_by_string, - available=[ 'create_time', 'update_time', 'name', 'hid' ]) + raise glx_exceptions.RequestParameterInvalidException('Unknown order_by', order_by=order_by_string, + available=['create_time', 'update_time', 'name', 'hid']) # history specific methods - def state_counts( self, history ): + def state_counts(self, history): """ Return a dictionary containing the counts of all contents in each state keyed by the distinct states. @@ -140,17 +140,17 @@ def state_counts( self, history ): Note: does not include deleted/hidden contents. """ filters = [ - sql.column( 'deleted' ) == false(), - sql.column( 'visible' ) == true() + sql.column('deleted') == false(), + sql.column('visible') == true() ] - contents_subquery = self._union_of_contents_query( history, filters=filters ).subquery() - statement = ( sql.select([ sql.column( 'state' ), func.count('*') ]) - .select_from( contents_subquery ) - .group_by( sql.column( 'state' ) ) ) - counts = self.app.model.context.execute( statement ).fetchall() - return dict( counts ) - - def active_counts( self, history ): + contents_subquery = self._union_of_contents_query(history, filters=filters).subquery() + statement = (sql.select([sql.column('state'), func.count('*')]) + .select_from(contents_subquery) + .group_by(sql.column('state'))) + counts = self.app.model.context.execute(statement).fetchall() + return dict(counts) + + def active_counts(self, history): """ Return a dictionary keyed with 'deleted', 'hidden', and 'active' with values for each representing the count of contents in each state. @@ -158,27 +158,27 @@ def active_counts( self, history ): Note: counts for deleted and hidden overlap; In other words, a dataset that's both deleted and hidden will be added to both totals. """ - returned = dict( deleted=0, hidden=0, active=0 ) - contents_subquery = self._union_of_contents_query( history ).subquery() + returned = dict(deleted=0, hidden=0, active=0) + contents_subquery = self._union_of_contents_query(history).subquery() columns = [ - sql.column( 'deleted' ), - sql.column( 'visible' ), - func.count( '*' ) + sql.column('deleted'), + sql.column('visible'), + func.count('*') ] - statement = ( sql.select( columns ) - .select_from( contents_subquery ) - .group_by( sql.column( 'deleted' ), sql.column( 'visible' ) ) ) - groups = self.app.model.context.execute( statement ).fetchall() + statement = (sql.select(columns) + .select_from(contents_subquery) + .group_by(sql.column('deleted'), sql.column('visible'))) + groups = self.app.model.context.execute(statement).fetchall() for deleted, visible, count in groups: if deleted: - returned[ 'deleted' ] += count + returned['deleted'] += count if not visible: - returned[ 'hidden' ] += count + returned['hidden'] += count if not deleted and visible: - returned[ 'active' ] += count + returned['active'] += count return returned - def map_datasets( self, history, fn, **kwargs ): + def map_datasets(self, history, fn, **kwargs): """ Iterate over the datasets of a given history, recursing into collections, and calling fn on each dataset. @@ -186,71 +186,71 @@ def map_datasets( self, history, fn, **kwargs ): Uses the same kwargs as `contents` above. """ returned = [] - contents = self.contents( history, **kwargs ) + contents = self.contents(history, **kwargs) for content in contents: - if isinstance( content, self.subcontainer_class ): - processed_list = self.subcontainer_manager.map_datasets( content, fn ) - returned.extend( processed_list ) + if isinstance(content, self.subcontainer_class): + processed_list = self.subcontainer_manager.map_datasets(content, fn) + returned.extend(processed_list) else: - processed = fn( content ) - returned.append( processed ) + processed = fn(content) + returned.append(processed) return returned # ---- private - def _session( self ): + def _session(self): return self.app.model.context - def _filter_to_contents_query( self, container, content_class, **kwargs ): + def _filter_to_contents_query(self, container, content_class, **kwargs): # TODO: use list (or by_history etc.) - container_filter = self._get_filter_for_contained( container, content_class ) - query = self._session().query( content_class ).filter( container_filter ) + container_filter = self._get_filter_for_contained(container, content_class) + query = self._session().query(content_class).filter(container_filter) return query - def _get_filter_for_contained( self, container, content_class ): + def _get_filter_for_contained(self, container, content_class): return content_class.history == container - def _union_of_contents( self, container, expand_models=True, **kwargs ): + def _union_of_contents(self, container, expand_models=True, **kwargs): """ Returns a limited and offset list of both types of contents, filtered and in some order. """ - contents_results = self._union_of_contents_query( container, **kwargs ).all() + contents_results = self._union_of_contents_query(container, **kwargs).all() if not expand_models: return contents_results # partition ids into a map of { component_class names -> list of ids } from the above union query - id_map = dict( (( self.contained_class_type_name, [] ), ( self.subcontainer_class_type_name, [] )) ) + id_map = dict(((self.contained_class_type_name, []), (self.subcontainer_class_type_name, []))) for result in contents_results: - result_type = self._get_union_type( result ) - contents_id = self._get_union_id( result ) + result_type = self._get_union_type(result) + contents_id = self._get_union_id(result) if result_type in id_map: - id_map[ result_type ].append( contents_id ) + id_map[result_type].append(contents_id) else: - raise TypeError( 'Unknown contents type:', result_type ) + raise TypeError('Unknown contents type:', result_type) # query 2 & 3: use the ids to query each component_class, returning an id->full component model map - contained_ids = id_map[ self.contained_class_type_name ] - id_map[ self.contained_class_type_name ] = self._contained_id_map( contained_ids ) - subcontainer_ids = id_map[ self.subcontainer_class_type_name ] - id_map[ self.subcontainer_class_type_name ] = self._subcontainer_id_map( subcontainer_ids ) + contained_ids = id_map[self.contained_class_type_name] + id_map[self.contained_class_type_name] = self._contained_id_map(contained_ids) + subcontainer_ids = id_map[self.subcontainer_class_type_name] + id_map[self.subcontainer_class_type_name] = self._subcontainer_id_map(subcontainer_ids) # cycle back over the union query to create an ordered list of the objects returned in queries 2 & 3 above contents = [] # TODO: or as generator? for result in contents_results: - result_type = self._get_union_type( result ) - contents_id = self._get_union_id( result ) - content = id_map[ result_type ][ contents_id ] - contents.append( content ) + result_type = self._get_union_type(result) + contents_id = self._get_union_id(result) + content = id_map[result_type][contents_id] + contents.append(content) return contents - def _union_of_contents_query( self, container, filters=None, limit=None, offset=None, order_by=None, **kwargs ): + def _union_of_contents_query(self, container, filters=None, limit=None, offset=None, order_by=None, **kwargs): """ Returns a query for a limited and offset list of both types of contents, filtered and in some order. """ order_by = order_by if order_by is not None else self.default_order_by - order_by = order_by if isinstance( order_by, ( tuple, list ) ) else ( order_by, ) + order_by = order_by if isinstance(order_by, (tuple, list)) else (order_by, ) # TODO: 3 queries and 3 iterations over results - this is undoubtedly better solved in the actual SQL layer # via one common table for contents, Some Yonder Resplendent and Fanciful Join, or ORM functionality @@ -262,115 +262,115 @@ def _union_of_contents_query( self, container, filters=None, limit=None, offset= # note: I'm trying to keep these private functions as generic as possible in order to move them toward base later # query 1: create a union of common columns for which the component_classes can be filtered/limited - contained_query = self._contents_common_query_for_contained( container.id ) - subcontainer_query = self._contents_common_query_for_subcontainer( container.id ) - contents_query = contained_query.union( subcontainer_query ) + contained_query = self._contents_common_query_for_contained(container.id) + subcontainer_query = self._contents_common_query_for_subcontainer(container.id) + contents_query = contained_query.union(subcontainer_query) # TODO: this needs the same fn/orm split that happens in the main query - for orm_filter in ( filters or [] ): - contents_query = contents_query.filter( orm_filter ) - contents_query = contents_query.order_by( *order_by ) + for orm_filter in (filters or []): + contents_query = contents_query.filter(orm_filter) + contents_query = contents_query.order_by(*order_by) if limit is not None: - contents_query = contents_query.limit( limit ) + contents_query = contents_query.limit(limit) if offset is not None: - contents_query = contents_query.offset( offset ) + contents_query = contents_query.offset(offset) return contents_query - def _contents_common_columns( self, component_class, **kwargs ): + def _contents_common_columns(self, component_class, **kwargs): columns = [] # pull column from class by name or override with kwargs if listed there, then label for column_name in self.common_columns: if column_name in kwargs: - column = kwargs.get( column_name, None ) + column = kwargs.get(column_name, None) elif column_name == "model_class": - column = literal( component_class.__name__ ) + column = literal(component_class.__name__) else: - column = getattr( component_class, column_name ) - column = column.label( column_name ) - columns.append( column ) + column = getattr(component_class, column_name) + column = column.label(column_name) + columns.append(column) return columns - def _contents_common_query_for_contained( self, history_id ): + def _contents_common_query_for_contained(self, history_id): component_class = self.contained_class # TODO: and now a join with Dataset - this is getting sad - columns = self._contents_common_columns( component_class, - history_content_type=literal( 'dataset' ), + columns = self._contents_common_columns(component_class, + history_content_type=literal('dataset'), state=model.Dataset.state, # do not have inner collections - collection_id=literal( None ) + collection_id=literal(None) ) - subquery = self._session().query( *columns ) + subquery = self._session().query(*columns) # for the HDA's we need to join the Dataset since it has an actual state column - subquery = subquery.join( model.Dataset, model.Dataset.id == component_class.dataset_id ) - subquery = subquery.filter( component_class.history_id == history_id ) + subquery = subquery.join(model.Dataset, model.Dataset.id == component_class.dataset_id) + subquery = subquery.filter(component_class.history_id == history_id) return subquery - def _contents_common_query_for_subcontainer( self, history_id ): + def _contents_common_query_for_subcontainer(self, history_id): component_class = self.subcontainer_class - columns = self._contents_common_columns( component_class, - history_content_type=literal( 'dataset_collection' ), + columns = self._contents_common_columns(component_class, + history_content_type=literal('dataset_collection'), # do not have datasets - dataset_id=literal( None ), + dataset_id=literal(None), state=model.DatasetCollection.populated_state, # TODO: should be purgable? fix - purged=literal( False ), + purged=literal(False), # these are attached instead to the inner collection joined below create_time=model.DatasetCollection.create_time, update_time=model.DatasetCollection.update_time ) - subquery = self._session().query( *columns ) + subquery = self._session().query(*columns) # for the HDCA's we need to join the DatasetCollection since it has update/create times - subquery = subquery.join( model.DatasetCollection, - model.DatasetCollection.id == component_class.collection_id ) - subquery = subquery.filter( component_class.history_id == history_id ) + subquery = subquery.join(model.DatasetCollection, + model.DatasetCollection.id == component_class.collection_id) + subquery = subquery.filter(component_class.history_id == history_id) return subquery - def _get_union_type( self, union ): + def _get_union_type(self, union): """Return the string name of the class for this row in the union results""" - return str( union[ 1 ] ) + return str(union[1]) - def _get_union_id( self, union ): + def _get_union_id(self, union): """Return the id for this row in the union results""" - return union[ 2 ] + return union[2] - def _contained_id_map( self, id_list ): + def _contained_id_map(self, id_list): """Return an id to model map of all contained-type models in the id_list.""" if not id_list: return [] component_class = self.contained_class - query = ( self._session().query( component_class ) - .filter( component_class.id.in_( id_list ) ) - .options( undefer( '_metadata' ) ) - .options( eagerload( 'dataset.actions' ) ) - .options( eagerload( 'tags' ) ) - .options( eagerload( 'annotations' ) ) ) - return dict( ( row.id, row ) for row in query.all() ) - - def _subcontainer_id_map( self, id_list ): + query = (self._session().query(component_class) + .filter(component_class.id.in_(id_list)) + .options(undefer('_metadata')) + .options(eagerload('dataset.actions')) + .options(eagerload('tags')) + .options(eagerload('annotations'))) + return dict((row.id, row) for row in query.all()) + + def _subcontainer_id_map(self, id_list): """Return an id to model map of all subcontainer-type models in the id_list.""" if not id_list: return [] component_class = self.subcontainer_class - query = ( self._session().query( component_class ) - .filter( component_class.id.in_( id_list ) ) - .options( eagerload( 'collection' ) ) - .options( eagerload( 'tags' ) ) - .options( eagerload( 'annotations' ) ) ) - return dict( ( row.id, row ) for row in query.all() ) + query = (self._session().query(component_class) + .filter(component_class.id.in_(id_list)) + .options(eagerload('collection')) + .options(eagerload('tags')) + .options(eagerload('annotations'))) + return dict((row.id, row) for row in query.all()) -class HistoryContentsSerializer( base.ModelSerializer, deletable.PurgableSerializerMixin ): +class HistoryContentsSerializer(base.ModelSerializer, deletable.PurgableSerializerMixin): """ Interface/service object for serializing histories into dictionaries. """ model_manager_class = HistoryContentsManager - def __init__( self, app, **kwargs ): - super( HistoryContentsSerializer, self ).__init__( app, **kwargs ) + def __init__(self, app, **kwargs): + super(HistoryContentsSerializer, self).__init__(app, **kwargs) self.default_view = 'summary' - self.add_view( 'summary', [ + self.add_view('summary', [ "id", "type_id", "history_id", @@ -388,9 +388,9 @@ def __init__( self, app, **kwargs ): ]) # assumes: outgoing to json.dumps and sanitized - def add_serializers( self ): - super( HistoryContentsSerializer, self ).add_serializers() - deletable.PurgableSerializerMixin.add_serializers( self ) + def add_serializers(self): + super(HistoryContentsSerializer, self).add_serializers() + deletable.PurgableSerializerMixin.add_serializers(self) self.serializers.update({ 'type_id' : self.serialize_type_id, @@ -399,22 +399,22 @@ def add_serializers( self ): 'collection_id' : self.serialize_id_or_skip, }) - def serialize_id_or_skip( self, content, key, **context ): + def serialize_id_or_skip(self, content, key, **context): """Serialize id or skip if attribute with `key` is not present.""" - if not hasattr( content, key ): - raise base.SkipAttribute( 'no such attribute' ) - return self.serialize_id( content, key, **context ) + if not hasattr(content, key): + raise base.SkipAttribute('no such attribute') + return self.serialize_id(content, key, **context) -class HistoryContentsFilters( base.ModelFilterParser, deletable.PurgableFiltersMixin ): +class HistoryContentsFilters(base.ModelFilterParser, deletable.PurgableFiltersMixin): # surprisingly (but ominously), this works for both content classes in the union that's filtered model_class = model.HistoryDatasetAssociation # TODO: history_content_type filter doesn't work with psycopg2: column does not exist (even with hybrid props) - def _parse_orm_filter( self, attr, op, val ): + def _parse_orm_filter(self, attr, op, val): - def raise_filter_err( attr, op, val, msg ): - raise glx_exceptions.RequestParameterInvalidException( msg, column=attr, operation=op, val=val ) + def raise_filter_err(attr, op, val, msg): + raise glx_exceptions.RequestParameterInvalidException(msg, column=attr, operation=op, val=val) # we need to use some manual/text/column fu here since some where clauses on the union don't work # using the model_class defined above - they need to be wrapped in their own .column() @@ -423,64 +423,64 @@ def raise_filter_err( attr, op, val, msg ): # special cases...special cases everywhere if attr == 'history_content_type' and op == 'eq': if val == 'dataset': - return sql.column( 'history_content_type' ) == 'dataset' + return sql.column('history_content_type') == 'dataset' if val == 'dataset_collection': - return sql.column( 'history_content_type' ) == 'dataset_collection' - raise_filter_err( attr, op, val, 'bad op in filter' ) + return sql.column('history_content_type') == 'dataset_collection' + raise_filter_err(attr, op, val, 'bad op in filter') if attr == 'type_id': if op == 'eq': - return sql.column( 'type_id' ) == val + return sql.column('type_id') == val if op == 'in': - return sql.column( 'type_id' ).in_( self.parse_type_id_list( val ) ) - raise_filter_err( attr, op, val, 'bad op in filter' ) + return sql.column('type_id').in_(self.parse_type_id_list(val)) + raise_filter_err(attr, op, val, 'bad op in filter') - if attr in ( 'update_time', 'create_time' ): + if attr in ('update_time', 'create_time'): if op == 'ge': - return sql.column( attr ) >= self.parse_date( val ) + return sql.column(attr) >= self.parse_date(val) if op == 'le': - return sql.column( attr ) <= self.parse_date( val ) - raise_filter_err( attr, op, val, 'bad op in filter' ) + return sql.column(attr) <= self.parse_date(val) + raise_filter_err(attr, op, val, 'bad op in filter') if attr == 'state': valid_states = model.Dataset.states.values() if op == 'eq': if val not in valid_states: - raise_filter_err( attr, op, val, 'invalid state in filter' ) - return sql.column( 'state' ) == val + raise_filter_err(attr, op, val, 'invalid state in filter') + return sql.column('state') == val if op == 'in': - states = [ s for s in val.split( ',' ) if s ] + states = [s for s in val.split(',') if s] for state in states: if state not in valid_states: - raise_filter_err( attr, op, state, 'invalid state in filter' ) - return sql.column( 'state' ).in_( states ) - raise_filter_err( attr, op, val, 'bad op in filter' ) + raise_filter_err(attr, op, state, 'invalid state in filter') + return sql.column('state').in_(states) + raise_filter_err(attr, op, val, 'bad op in filter') - return super( HistoryContentsFilters, self )._parse_orm_filter( attr, op, val ) + return super(HistoryContentsFilters, self)._parse_orm_filter(attr, op, val) - def decode_type_id( self, type_id ): + def decode_type_id(self, type_id): TYPE_ID_SEP = '-' - split = type_id.split( TYPE_ID_SEP, 1 ) - return TYPE_ID_SEP.join([ split[0], str( self.app.security.decode_id( split[1] ) ) ]) + split = type_id.split(TYPE_ID_SEP, 1) + return TYPE_ID_SEP.join([split[0], str(self.app.security.decode_id(split[1]))]) - def parse_type_id_list( self, type_id_list_string, sep=',' ): + def parse_type_id_list(self, type_id_list_string, sep=','): """ Split `type_id_list_string` at `sep`. """ - return [ self.decode_type_id( type_id ) for type_id in type_id_list_string.split( sep ) ] + return [self.decode_type_id(type_id) for type_id in type_id_list_string.split(sep)] - def _add_parsers( self ): - super( HistoryContentsFilters, self )._add_parsers() - deletable.PurgableFiltersMixin._add_parsers( self ) + def _add_parsers(self): + super(HistoryContentsFilters, self)._add_parsers() + deletable.PurgableFiltersMixin._add_parsers(self) self.orm_filter_parsers.update({ - 'history_content_type' : { 'op': ( 'eq' ) }, - 'type_id' : { 'op': ( 'eq', 'in' ), 'val': self.parse_type_id_list }, - 'hid' : { 'op': ( 'eq', 'ge', 'le' ), 'val': int }, + 'history_content_type' : {'op': ('eq')}, + 'type_id' : {'op': ('eq', 'in'), 'val': self.parse_type_id_list}, + 'hid' : {'op': ('eq', 'ge', 'le'), 'val': int}, # TODO: needs a different val parser - but no way to add to the above # 'hid-in' : { 'op': ( 'in' ), 'val': self.parse_int_list }, - 'name' : { 'op': ( 'eq', 'contains', 'like' ) }, - 'state' : { 'op': ( 'eq', 'in' ) }, - 'visible' : { 'op': ( 'eq' ), 'val': self.parse_bool }, - 'create_time' : { 'op': ( 'le', 'ge' ), 'val': self.parse_date }, - 'update_time' : { 'op': ( 'le', 'ge' ), 'val': self.parse_date }, + 'name' : {'op': ('eq', 'contains', 'like')}, + 'state' : {'op': ('eq', 'in')}, + 'visible' : {'op': ('eq'), 'val': self.parse_bool}, + 'create_time' : {'op': ('le', 'ge'), 'val': self.parse_date}, + 'update_time' : {'op': ('le', 'ge'), 'val': self.parse_date}, }) diff --git a/lib/galaxy/managers/lddas.py b/lib/galaxy/managers/lddas.py index 9d9b779e9720..2aa5e384c578 100644 --- a/lib/galaxy/managers/lddas.py +++ b/lib/galaxy/managers/lddas.py @@ -1,19 +1,19 @@ from galaxy.managers import base as manager_base -class LDDAManager( object ): +class LDDAManager(object): """ A fairly sparse manager for LDDAs. """ - def __init__( self, app ): + def __init__(self, app): """ Set up and initialize other managers needed by lddas. """ pass - def get( self, trans, id, check_accessible=True ): - return manager_base.get_object( trans, id, - 'LibraryDatasetDatasetAssociation', - check_ownership=False, - check_accessible=check_accessible ) + def get(self, trans, id, check_accessible=True): + return manager_base.get_object(trans, id, + 'LibraryDatasetDatasetAssociation', + check_ownership=False, + check_accessible=check_accessible) diff --git a/lib/galaxy/managers/libraries.py b/lib/galaxy/managers/libraries.py index 9bcb4d82fd67..37ad6947ff6a 100644 --- a/lib/galaxy/managers/libraries.py +++ b/lib/galaxy/managers/libraries.py @@ -11,19 +11,19 @@ from galaxy.managers import folders from galaxy.util import pretty_print_time_interval -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) # ============================================================================= -class LibraryManager( object ): +class LibraryManager(object): """ Interface/service object for interacting with libraries. """ - def __init__( self, *args, **kwargs ): - super( LibraryManager, self ).__init__( *args, **kwargs ) + def __init__(self, *args, **kwargs): + super(LibraryManager, self).__init__(*args, **kwargs) - def get( self, trans, decoded_library_id, check_accessible=True ): + def get(self, trans, decoded_library_id, check_accessible=True): """ Get the library from the DB. @@ -36,45 +36,45 @@ def get( self, trans, decoded_library_id, check_accessible=True ): :rtype: galaxy.model.Library """ try: - library = trans.sa_session.query( trans.app.model.Library ).filter( trans.app.model.Library.table.c.id == decoded_library_id ).one() + library = trans.sa_session.query(trans.app.model.Library).filter(trans.app.model.Library.table.c.id == decoded_library_id).one() except MultipleResultsFound: - raise exceptions.InconsistentDatabase( 'Multiple libraries found with the same id.' ) + raise exceptions.InconsistentDatabase('Multiple libraries found with the same id.') except NoResultFound: - raise exceptions.RequestParameterInvalidException( 'No library found with the id provided.' ) + raise exceptions.RequestParameterInvalidException('No library found with the id provided.') except Exception as e: - raise exceptions.InternalServerError( 'Error loading from the database.' + str( e ) ) - library = self.secure( trans, library, check_accessible) + raise exceptions.InternalServerError('Error loading from the database.' + str(e)) + library = self.secure(trans, library, check_accessible) return library - def create( self, trans, name, description='', synopsis=''): + def create(self, trans, name, description='', synopsis=''): """ Create a new library. """ if not trans.user_is_admin: - raise exceptions.ItemAccessibilityException( 'Only administrators can create libraries.' ) + raise exceptions.ItemAccessibilityException('Only administrators can create libraries.') else: - library = trans.app.model.Library( name=name, description=description, synopsis=synopsis ) - root_folder = trans.app.model.LibraryFolder( name=name, description='' ) + library = trans.app.model.Library(name=name, description=description, synopsis=synopsis) + root_folder = trans.app.model.LibraryFolder(name=name, description='') library.root_folder = root_folder - trans.sa_session.add_all( ( library, root_folder ) ) + trans.sa_session.add_all((library, root_folder)) trans.sa_session.flush() return library - def update( self, trans, library, name=None, description=None, synopsis=None ): + def update(self, trans, library, name=None, description=None, synopsis=None): """ Update the given library """ changed = False if not trans.user_is_admin(): - raise exceptions.ItemAccessibilityException( 'Only administrators can update libraries.' ) + raise exceptions.ItemAccessibilityException('Only administrators can update libraries.') if library.deleted: - raise exceptions.RequestParameterInvalidException( 'You cannot modify a deleted library. Undelete it first.' ) + raise exceptions.RequestParameterInvalidException('You cannot modify a deleted library. Undelete it first.') if name is not None: library.name = name changed = True # When library is renamed the root folder has to be renamed too. folder_manager = folders.FolderManager() - folder_manager.update( trans, library.root_folder, name=name ) + folder_manager.update(trans, library.root_folder, name=name) if description is not None: library.description = description changed = True @@ -82,25 +82,25 @@ def update( self, trans, library, name=None, description=None, synopsis=None ): library.synopsis = synopsis changed = True if changed: - trans.sa_session.add( library ) + trans.sa_session.add(library) trans.sa_session.flush() return library - def delete( self, trans, library, undelete=False ): + def delete(self, trans, library, undelete=False): """ Mark given library deleted/undeleted based on the flag. """ if not trans.user_is_admin(): - raise exceptions.ItemAccessibilityException( 'Only administrators can delete and undelete libraries.' ) + raise exceptions.ItemAccessibilityException('Only administrators can delete and undelete libraries.') if undelete: library.deleted = False else: library.deleted = True - trans.sa_session.add( library ) + trans.sa_session.add(library) trans.sa_session.flush() return library - def list( self, trans, deleted=False ): + def list(self, trans, deleted=False): """ Return a list of libraries from the DB. @@ -111,37 +111,37 @@ def list( self, trans, deleted=False ): :rtype: sqlalchemy query """ is_admin = trans.user_is_admin() - query = trans.sa_session.query( trans.app.model.Library ) + query = trans.sa_session.query(trans.app.model.Library) if is_admin: if deleted is None: # Flag is not specified, do not filter on it. pass elif deleted: - query = query.filter( trans.app.model.Library.table.c.deleted == true() ) + query = query.filter(trans.app.model.Library.table.c.deleted == true()) else: - query = query.filter( trans.app.model.Library.table.c.deleted == false() ) + query = query.filter(trans.app.model.Library.table.c.deleted == false()) else: # Nonadmins can't see deleted libraries - current_user_role_ids = [ role.id for role in trans.get_current_user_roles() ] + current_user_role_ids = [role.id for role in trans.get_current_user_roles()] library_access_action = trans.app.security_agent.permitted_actions.LIBRARY_ACCESS.action - restricted_library_ids = [ lp.library_id for lp in ( - trans.sa_session.query( trans.model.LibraryPermissions ).filter( + restricted_library_ids = [lp.library_id for lp in ( + trans.sa_session.query(trans.model.LibraryPermissions).filter( trans.model.LibraryPermissions.table.c.action == library_access_action - ).distinct() ) ] - accessible_restricted_library_ids = [ lp.library_id for lp in ( - trans.sa_session.query( trans.model.LibraryPermissions ).filter( + ).distinct())] + accessible_restricted_library_ids = [lp.library_id for lp in ( + trans.sa_session.query(trans.model.LibraryPermissions).filter( and_( trans.model.LibraryPermissions.table.c.action == library_access_action, - trans.model.LibraryPermissions.table.c.role_id.in_( current_user_role_ids ) - ) ) ) ] - query = query.filter( or_( - not_( trans.model.Library.table.c.id.in_( restricted_library_ids ) ), - trans.model.Library.table.c.id.in_( accessible_restricted_library_ids ) - ) ) + trans.model.LibraryPermissions.table.c.role_id.in_(current_user_role_ids) + )))] + query = query.filter(or_( + not_(trans.model.Library.table.c.id.in_(restricted_library_ids)), + trans.model.Library.table.c.id.in_(accessible_restricted_library_ids) + )) return query - def secure( self, trans, library, check_accessible=True ): + def secure(self, trans, library, check_accessible=True): """ Check if library is accessible to user. @@ -157,21 +157,21 @@ def secure( self, trans, library, check_accessible=True ): if trans.user_is_admin(): return library if check_accessible: - library = self.check_accessible( trans, library ) + library = self.check_accessible(trans, library) return library - def check_accessible( self, trans, library ): + def check_accessible(self, trans, library): """ Check whether the library is accessible to current user. """ - if not trans.app.security_agent.can_access_library( trans.get_current_user_roles(), library ): - raise exceptions.ObjectNotFound( 'Library with the id provided was not found.' ) + if not trans.app.security_agent.can_access_library(trans.get_current_user_roles(), library): + raise exceptions.ObjectNotFound('Library with the id provided was not found.') elif library.deleted: - raise exceptions.ObjectNotFound( 'Library with the id provided is deleted.' ) + raise exceptions.ObjectNotFound('Library with the id provided is deleted.') else: return library - def get_library_dict( self, trans, library ): + def get_library_dict(self, trans, library): """ Return library data in the form of a dictionary. @@ -181,22 +181,22 @@ def get_library_dict( self, trans, library ): :returns: dict with data about the library :rtype: dictionary """ - library_dict = library.to_dict( view='element', value_mapper={ 'id': trans.security.encode_id, 'root_folder_id': trans.security.encode_id } ) - if trans.app.security_agent.library_is_public( library, contents=False ): - library_dict[ 'public' ] = True - library_dict[ 'create_time_pretty'] = pretty_print_time_interval( library.create_time, precise=True ) + library_dict = library.to_dict(view='element', value_mapper={'id': trans.security.encode_id, 'root_folder_id': trans.security.encode_id}) + if trans.app.security_agent.library_is_public(library, contents=False): + library_dict['public'] = True + library_dict['create_time_pretty'] = pretty_print_time_interval(library.create_time, precise=True) current_user_roles = trans.get_current_user_roles() if not trans.user_is_admin(): - library_dict[ 'can_user_add' ] = trans.app.security_agent.can_add_library_item( current_user_roles, library ) - library_dict[ 'can_user_modify' ] = trans.app.security_agent.can_modify_library_item( current_user_roles, library ) - library_dict[ 'can_user_manage' ] = trans.app.security_agent.can_manage_library_item( current_user_roles, library ) + library_dict['can_user_add'] = trans.app.security_agent.can_add_library_item(current_user_roles, library) + library_dict['can_user_modify'] = trans.app.security_agent.can_modify_library_item(current_user_roles, library) + library_dict['can_user_manage'] = trans.app.security_agent.can_manage_library_item(current_user_roles, library) else: - library_dict[ 'can_user_add' ] = True - library_dict[ 'can_user_modify' ] = True - library_dict[ 'can_user_manage' ] = True + library_dict['can_user_add'] = True + library_dict['can_user_modify'] = True + library_dict['can_user_manage'] = True return library_dict - def get_current_roles( self, trans, library ): + def get_current_roles(self, trans, library): """ Load all permissions currently related to the given library. @@ -206,53 +206,53 @@ def get_current_roles( self, trans, library ): :rtype: dictionary :returns: dict of current roles for all available permission types """ - access_library_role_list = [ ( access_role.name, trans.security.encode_id( access_role.id ) ) for access_role in self.get_access_roles( trans, library ) ] - modify_library_role_list = [ ( modify_role.name, trans.security.encode_id( modify_role.id ) ) for modify_role in self.get_modify_roles( trans, library ) ] - manage_library_role_list = [ ( manage_role.name, trans.security.encode_id( manage_role.id ) ) for manage_role in self.get_manage_roles( trans, library ) ] - add_library_item_role_list = [ ( add_role.name, trans.security.encode_id( add_role.id ) ) for add_role in self.get_add_roles( trans, library ) ] - return dict( access_library_role_list=access_library_role_list, - modify_library_role_list=modify_library_role_list, - manage_library_role_list=manage_library_role_list, - add_library_item_role_list=add_library_item_role_list ) + access_library_role_list = [(access_role.name, trans.security.encode_id(access_role.id)) for access_role in self.get_access_roles(trans, library)] + modify_library_role_list = [(modify_role.name, trans.security.encode_id(modify_role.id)) for modify_role in self.get_modify_roles(trans, library)] + manage_library_role_list = [(manage_role.name, trans.security.encode_id(manage_role.id)) for manage_role in self.get_manage_roles(trans, library)] + add_library_item_role_list = [(add_role.name, trans.security.encode_id(add_role.id)) for add_role in self.get_add_roles(trans, library)] + return dict(access_library_role_list=access_library_role_list, + modify_library_role_list=modify_library_role_list, + manage_library_role_list=manage_library_role_list, + add_library_item_role_list=add_library_item_role_list) - def get_access_roles( self, trans, library ): + def get_access_roles(self, trans, library): """ Load access roles for all library permissions """ - return set( library.get_access_roles( trans ) ) + return set(library.get_access_roles(trans)) - def get_modify_roles( self, trans, library ): + def get_modify_roles(self, trans, library): """ Load modify roles for all library permissions """ - return set( trans.app.security_agent.get_roles_for_action( library, trans.app.security_agent.permitted_actions.LIBRARY_MODIFY ) ) + return set(trans.app.security_agent.get_roles_for_action(library, trans.app.security_agent.permitted_actions.LIBRARY_MODIFY)) - def get_manage_roles( self, trans, library ): + def get_manage_roles(self, trans, library): """ Load manage roles for all library permissions """ - return set( trans.app.security_agent.get_roles_for_action( library, trans.app.security_agent.permitted_actions.LIBRARY_MANAGE ) ) + return set(trans.app.security_agent.get_roles_for_action(library, trans.app.security_agent.permitted_actions.LIBRARY_MANAGE)) - def get_add_roles( self, trans, library ): + def get_add_roles(self, trans, library): """ Load add roles for all library permissions """ - return set( trans.app.security_agent.get_roles_for_action( library, trans.app.security_agent.permitted_actions.LIBRARY_ADD ) ) + return set(trans.app.security_agent.get_roles_for_action(library, trans.app.security_agent.permitted_actions.LIBRARY_ADD)) - def set_permission_roles( self, trans, library, access_roles, modify_roles, manage_roles, add_roles ): + def set_permission_roles(self, trans, library, access_roles, modify_roles, manage_roles, add_roles): """ Set permissions on the given library. """ - def make_public( self, trans, library ): + def make_public(self, trans, library): """ Makes the given library public (removes all access roles) """ - trans.app.security_agent.make_library_public( library ) - return self.is_public( trans, library ) + trans.app.security_agent.make_library_public(library) + return self.is_public(trans, library) - def is_public( self, trans, library ): + def is_public(self, trans, library): """ Return true if lib is public. """ - return trans.app.security_agent.library_is_public( library ) + return trans.app.security_agent.library_is_public(library) diff --git a/lib/galaxy/managers/pages.py b/lib/galaxy/managers/pages.py index 373007f581eb..3dbee5444831 100644 --- a/lib/galaxy/managers/pages.py +++ b/lib/galaxy/managers/pages.py @@ -10,10 +10,10 @@ from galaxy.managers import sharable import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class PageManager( sharable.SharableModelManager ): +class PageManager(sharable.SharableModelManager): """ """ @@ -25,50 +25,50 @@ class PageManager( sharable.SharableModelManager ): annotation_assoc = model.PageAnnotationAssociation rating_assoc = model.PageRatingAssociation - def __init__( self, app, *args, **kwargs ): + def __init__(self, app, *args, **kwargs): """ """ - super( PageManager, self ).__init__( app, *args, **kwargs ) + super(PageManager, self).__init__(app, *args, **kwargs) - def copy( self, trans, page, user, **kwargs ): + def copy(self, trans, page, user, **kwargs): """ """ pass -class PageSerializer( sharable.SharableModelSerializer ): +class PageSerializer(sharable.SharableModelSerializer): """ Interface/service object for serializing pages into dictionaries. """ SINGLE_CHAR_ABBR = 'p' - def __init__( self, app ): - super( PageSerializer, self ).__init__( app ) - self.page_manager = PageManager( app ) + def __init__(self, app): + super(PageSerializer, self).__init__(app) + self.page_manager = PageManager(app) self.default_view = 'summary' - self.add_view( 'summary', [] ) - self.add_view( 'detailed', [] ) + self.add_view('summary', []) + self.add_view('detailed', []) - def add_serializers( self ): - super( PageSerializer, self ).add_serializers() + def add_serializers(self): + super(PageSerializer, self).add_serializers() self.serializers.update({ }) -class PageDeserializer( sharable.SharableModelDeserializer ): +class PageDeserializer(sharable.SharableModelDeserializer): """ Interface/service object for validating and deserializing dictionaries into pages. """ model_manager_class = PageManager - def __init__( self, app ): - super( PageDeserializer, self ).__init__( app ) + def __init__(self, app): + super(PageDeserializer, self).__init__(app) self.page_manager = self.manager - def add_deserializers( self ): - super( PageDeserializer, self ).add_deserializers() + def add_deserializers(self): + super(PageDeserializer, self).add_deserializers() self.deserializers.update({ }) - self.deserializable_keyset.update( self.deserializers.keys() ) + self.deserializable_keyset.update(self.deserializers.keys()) diff --git a/lib/galaxy/managers/ratable.py b/lib/galaxy/managers/ratable.py index 217029ec5777..1686c44658bf 100644 --- a/lib/galaxy/managers/ratable.py +++ b/lib/galaxy/managers/ratable.py @@ -6,52 +6,52 @@ from . import base import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class RatableManagerMixin( object ): +class RatableManagerMixin(object): #: class of RatingAssociation (e.g. HistoryRatingAssociation) rating_assoc = None - def rating( self, item, user, as_int=True ): + def rating(self, item, user, as_int=True): """Returns the integer rating given to this item by the user. Returns the full rating model if `as_int` is False. """ - rating = self.query_associated( self.rating_assoc, item ).filter_by( user=user ).first() + rating = self.query_associated(self.rating_assoc, item).filter_by(user=user).first() # most common case is assumed to be 'get the number' if not as_int: return rating # get the int value if there's a rating return rating.rating if rating is not None else None - def ratings( self, item ): + def ratings(self, item): """Returns a list of all rating values given to this item.""" - return [ r.rating for r in item.ratings ] + return [r.rating for r in item.ratings] - def ratings_avg( self, item ): + def ratings_avg(self, item): """Returns the average of all ratings given to this item.""" - foreign_key = self._foreign_key( self.rating_assoc ) - avg = self.session().query( func.avg( self.rating_assoc.rating ) ).filter( foreign_key == item ).scalar() + foreign_key = self._foreign_key(self.rating_assoc) + avg = self.session().query(func.avg(self.rating_assoc.rating)).filter(foreign_key == item).scalar() return avg or 0.0 - def ratings_count( self, item ): + def ratings_count(self, item): """Returns the number of ratings given to this item.""" - foreign_key = self._foreign_key( self.rating_assoc ) - return self.session().query( func.count( self.rating_assoc.rating ) ).filter( foreign_key == item ).scalar() + foreign_key = self._foreign_key(self.rating_assoc) + return self.session().query(func.count(self.rating_assoc.rating)).filter(foreign_key == item).scalar() - def rate( self, item, user, value, flush=True ): + def rate(self, item, user, value, flush=True): """Updates or creates a rating for this item and user. Returns the rating""" # TODO?: possible generic update_or_create # TODO?: update and create to RatingsManager (if not overkill) - rating = self.rating( item, user, as_int=False ) + rating = self.rating(item, user, as_int=False) if not rating: - rating = self.rating_assoc( user=user ) - self.associate( rating, item ) + rating = self.rating_assoc(user=user) + self.associate(rating, item) rating.rating = value - self.session().add( rating ) + self.session().add(rating) if flush: self.session().flush() return rating @@ -59,20 +59,20 @@ def rate( self, item, user, value, flush=True ): # TODO?: all ratings for a user -class RatableSerializerMixin( object ): +class RatableSerializerMixin(object): - def add_serializers( self ): - self.serializers[ 'user_rating' ] = self.serialize_user_rating - self.serializers[ 'community_rating' ] = self.serialize_community_rating + def add_serializers(self): + self.serializers['user_rating'] = self.serialize_user_rating + self.serializers['community_rating'] = self.serialize_community_rating - def serialize_user_rating( self, item, key, user=None, **context ): + def serialize_user_rating(self, item, key, user=None, **context): """Returns the integer rating given to this item by the user.""" if not user: - raise base.ModelSerializingError( 'user_rating requires a user', - model_class=self.manager.model_class, id=self.serialize_id( item, 'id' ) ) - return self.manager.rating( item, user ) + raise base.ModelSerializingError('user_rating requires a user', + model_class=self.manager.model_class, id=self.serialize_id(item, 'id')) + return self.manager.rating(item, user) - def serialize_community_rating( self, item, key, **context ): + def serialize_community_rating(self, item, key, **context): """ Returns a dictionary containing: `average` the (float) average of all ratings of this object @@ -82,30 +82,30 @@ def serialize_community_rating( self, item, key, **context ): # than getting the rows and calc'ing both here with one query manager = self.manager return { - 'average' : manager.ratings_avg( item ), - 'count' : manager.ratings_count( item ), + 'average' : manager.ratings_avg(item), + 'count' : manager.ratings_count(item), } -class RatableDeserializerMixin( object ): +class RatableDeserializerMixin(object): - def add_deserializers( self ): - self.deserializers[ 'user_rating' ] = self.deserialize_rating + def add_deserializers(self): + self.deserializers['user_rating'] = self.deserialize_rating - def deserialize_rating( self, item, key, val, user=None, **context ): + def deserialize_rating(self, item, key, val, user=None, **context): if not user: - raise base.ModelDeserializingError( 'user_rating requires a user', - model_class=self.manager.model_class, id=self.serialize_id( item, 'id' ) ) - val = self.validate.int_range( key, val, 0, 5 ) - return self.manager.rate( item, user, val, flush=False ) + raise base.ModelDeserializingError('user_rating requires a user', + model_class=self.manager.model_class, id=self.serialize_id(item, 'id')) + val = self.validate.int_range(key, val, 0, 5) + return self.manager.rate(item, user, val, flush=False) -class RatableFilterMixin( object ): +class RatableFilterMixin(object): - def _ratings_avg_accessor( self, item ): - return self.manager.ratings_avg( item ) + def _ratings_avg_accessor(self, item): + return self.manager.ratings_avg(item) - def _add_parsers( self ): + def _add_parsers(self): """ Adds the following filters: `community_rating`: filter @@ -113,10 +113,10 @@ def _add_parsers( self ): self.fn_filter_parsers.update({ 'community_rating': { 'op': { - 'eq' : lambda i, v: self._ratings_avg_accessor( i ) == v, + 'eq' : lambda i, v: self._ratings_avg_accessor(i) == v, # TODO: default to greater than (currently 'eq' due to base/controller.py) - 'ge' : lambda i, v: self._ratings_avg_accessor( i ) >= v, - 'le' : lambda i, v: self._ratings_avg_accessor( i ) <= v, + 'ge' : lambda i, v: self._ratings_avg_accessor(i) >= v, + 'le' : lambda i, v: self._ratings_avg_accessor(i) <= v, }, 'val' : float } diff --git a/lib/galaxy/managers/rbac_secured.py b/lib/galaxy/managers/rbac_secured.py index 0b2883bba782..45d92ff57c55 100644 --- a/lib/galaxy/managers/rbac_secured.py +++ b/lib/galaxy/managers/rbac_secured.py @@ -4,14 +4,14 @@ from galaxy.managers import users import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class RBACPermissionFailedException( galaxy.exceptions.InsufficientPermissionsException ): +class RBACPermissionFailedException(galaxy.exceptions.InsufficientPermissionsException): pass -class RBACPermission( object ): +class RBACPermission(object): """ Base class for wrangling/controlling the permissions ORM models (\*Permissions, Roles) that control which users can perform certain actions on their associated models @@ -21,45 +21,45 @@ class RBACPermission( object ): permissions_class = None permission_failed_error_class = RBACPermissionFailedException - def __init__( self, app ): + def __init__(self, app): self.app = app - self.user_manager = users.UserManager( app ) + self.user_manager = users.UserManager(app) - def session( self ): + def session(self): return self.app.model.context # TODO: implement group # TODO: how does admin play into this? - def is_permitted( self, item, user ): - raise NotImplementedError( "abstract parent class" ) + def is_permitted(self, item, user): + raise NotImplementedError("abstract parent class") - def error_unless_permitted( self, item, user ): - if not self.is_permitted( item, user ): - error_info = dict( model_class=item.__class__, id=getattr( item, 'id', None ) ) - raise self.permission_failed_error_class( **error_info ) + def error_unless_permitted(self, item, user): + if not self.is_permitted(item, user): + error_info = dict(model_class=item.__class__, id=getattr(item, 'id', None)) + raise self.permission_failed_error_class(**error_info) - def grant( self, item, user, flush=True ): - raise NotImplementedError( "abstract parent class" ) + def grant(self, item, user, flush=True): + raise NotImplementedError("abstract parent class") - def revoke( self, item, user, flush=True ): - raise NotImplementedError( "abstract parent class" ) + def revoke(self, item, user, flush=True): + raise NotImplementedError("abstract parent class") - def _role_is_permitted( self, item, role ): - raise NotImplementedError( "abstract parent class" ) + def _role_is_permitted(self, item, role): + raise NotImplementedError("abstract parent class") - def _error_unless_role_permitted( self, item, role ): - if not self._role_is_permitted( item, role ): - error_info = dict( model_class=item.__class__, id=getattr( item, 'id', None ) ) - raise self.permission_failed_error_class( **error_info ) + def _error_unless_role_permitted(self, item, role): + if not self._role_is_permitted(item, role): + error_info = dict(model_class=item.__class__, id=getattr(item, 'id', None)) + raise self.permission_failed_error_class(**error_info) - def _grant_role( self, item, role, flush=True ): - raise NotImplementedError( "abstract parent class" ) + def _grant_role(self, item, role, flush=True): + raise NotImplementedError("abstract parent class") - def _revoke_role( self, item, role, flush=True ): - raise NotImplementedError( "abstract parent class" ) + def _revoke_role(self, item, role, flush=True): + raise NotImplementedError("abstract parent class") -class DatasetRBACPermission( RBACPermission ): +class DatasetRBACPermission(RBACPermission): """ Base class for the manage and access RBAC permissions used by dataset security. @@ -76,88 +76,88 @@ class DatasetRBACPermission( RBACPermission ): action_name = None # ---- double secrect probation - def __assert_action( self ): + def __assert_action(self): if not self.action_name: - raise NotImplementedError( "abstract parent class" + " needs action_name" ) + raise NotImplementedError("abstract parent class" + " needs action_name") # ---- interface - def by_dataset( self, dataset ): + def by_dataset(self, dataset): self.__assert_action() - all_permissions = self._all_types_by_dataset( dataset ) - return filter( lambda p: p.action == self.action_name, all_permissions ) + all_permissions = self._all_types_by_dataset(dataset) + return filter(lambda p: p.action == self.action_name, all_permissions) # TODO: list? - def by_roles( self, dataset, roles ): - permissions = self.by_dataset( dataset ) - return filter( lambda p: p.role in roles, permissions ) + def by_roles(self, dataset, roles): + permissions = self.by_dataset(dataset) + return filter(lambda p: p.role in roles, permissions) - def by_role( self, dataset, role ): - permissions = self.by_dataset( dataset ) - found = filter( lambda p: p.role == role, permissions ) + def by_role(self, dataset, role): + permissions = self.by_dataset(dataset) + found = filter(lambda p: p.role == role, permissions) if not found: return None - if len( found ) > 1: - raise galaxy.exceptions.InconsistentDatabase( dataset=dataset.id, role=role.id ) + if len(found) > 1: + raise galaxy.exceptions.InconsistentDatabase(dataset=dataset.id, role=role.id) return found[0] - def set( self, dataset, roles, flush=True ): + def set(self, dataset, roles, flush=True): # NOTE: this removes all previous permissions of this type - self.clear( dataset, flush=False ) + self.clear(dataset, flush=False) permissions = [] for role in roles: - permissions.append( self._create( dataset, role, flush=False ) ) + permissions.append(self._create(dataset, role, flush=False)) if flush: self.session().flush() return permissions - def clear( self, dataset, flush=True ): - permissions = self.by_dataset( dataset ) - return self._delete( permissions, flush=flush ) + def clear(self, dataset, flush=True): + permissions = self.by_dataset(dataset) + return self._delete(permissions, flush=flush) # ---- private - def _create( self, dataset, role, flush=True ): - permission = self.permissions_class( self.action_name, dataset, role ) - self.session().add( permission ) + def _create(self, dataset, role, flush=True): + permission = self.permissions_class(self.action_name, dataset, role) + self.session().add(permission) if flush: self.session().flush() return permission - def _roles( self, dataset ): - return [ permission.role for permission in self.by_dataset( dataset ) ] + def _roles(self, dataset): + return [permission.role for permission in self.by_dataset(dataset)] - def _all_types_by_dataset( self, dataset ): + def _all_types_by_dataset(self, dataset): return dataset.actions # as a general rule, DatasetPermissions are considered disposable # and there is no reason to update the models # TODO: list? - def _delete( self, permissions, flush=True ): + def _delete(self, permissions, flush=True): for permission in permissions: if permission in self.session().new: - self.session().expunge( permission ) + self.session().expunge(permission) else: - self.session().delete( permission ) + self.session().delete(permission) if flush: self.session().flush() - def _revoke_role( self, dataset, role, flush=True ): - role_permissions = self.by_roles( dataset, [ role ] ) - return self._delete( role_permissions, flush=flush ) + def _revoke_role(self, dataset, role, flush=True): + role_permissions = self.by_roles(dataset, [role]) + return self._delete(role_permissions, flush=flush) -def iterable_has_all( iterable, has_these ): +def iterable_has_all(iterable, has_these): for item in has_these: if item not in iterable: return False return True -class DatasetManagePermissionFailedException( RBACPermissionFailedException ): +class DatasetManagePermissionFailedException(RBACPermissionFailedException): pass -class ManageDatasetRBACPermission( DatasetRBACPermission ): +class ManageDatasetRBACPermission(DatasetRBACPermission): """ A class that controls the dataset permissions that control who can manage that dataset's permissions. @@ -170,52 +170,52 @@ class ManageDatasetRBACPermission( DatasetRBACPermission ): permission_failed_error_class = DatasetManagePermissionFailedException # ---- interface - def is_permitted( self, dataset, user ): + def is_permitted(self, dataset, user): # anonymous users cannot manage permissions on datasets - if self.user_manager.is_anonymous( user ): + if self.user_manager.is_anonymous(user): return False # admin is always permitted # TODO: could probably move this into RBACPermission and call that first - if self.user_manager.is_admin( user ): + if self.user_manager.is_admin(user): return True for role in user.all_roles(): - if self._role_is_permitted( dataset, role ): + if self._role_is_permitted(dataset, role): return True return False - def grant( self, dataset, user, flush=True ): - private_role = self._user_private_role( user ) - return self._grant_role( dataset, private_role, flush=flush ) + def grant(self, dataset, user, flush=True): + private_role = self._user_private_role(user) + return self._grant_role(dataset, private_role, flush=flush) - def revoke( self, dataset, user, flush=True ): - private_role = self._user_private_role( user ) - return self._revoke_role( dataset, private_role, flush=flush ) + def revoke(self, dataset, user, flush=True): + private_role = self._user_private_role(user) + return self._revoke_role(dataset, private_role, flush=flush) # ---- private - def _role_is_permitted( self, dataset, role ): - return role in self._roles( dataset ) + def _role_is_permitted(self, dataset, role): + return role in self._roles(dataset) - def _user_private_role( self, user ): + def _user_private_role(self, user): # error with 401 if no user - self.user_manager.error_if_anonymous( user ) - return self.user_manager.private_role( user ) + self.user_manager.error_if_anonymous(user) + return self.user_manager.private_role(user) - def _grant_role( self, dataset, role, flush=True ): - existing = self.by_role( dataset, role ) + def _grant_role(self, dataset, role, flush=True): + existing = self.by_role(dataset, role) if existing: return existing - return self._create( dataset, role, flush=flush ) + return self._create(dataset, role, flush=flush) - def _revoke_role( self, dataset, role, flush=True ): - permission = self.by_roles( dataset, [ role ] ) - return self._delete( [ permission ], flush=flush ) + def _revoke_role(self, dataset, role, flush=True): + permission = self.by_roles(dataset, [role]) + return self._delete([permission], flush=flush) -class DatasetAccessPermissionFailedException( RBACPermissionFailedException ): +class DatasetAccessPermissionFailedException(RBACPermissionFailedException): pass -class AccessDatasetRBACPermission( DatasetRBACPermission ): +class AccessDatasetRBACPermission(DatasetRBACPermission): """ A class to manage access permissions on a dataset. @@ -226,46 +226,46 @@ class AccessDatasetRBACPermission( DatasetRBACPermission ): permission_failed_error_class = DatasetAccessPermissionFailedException # ---- interface - def is_permitted( self, dataset, user ): - current_roles = self._roles( dataset ) + def is_permitted(self, dataset, user): + current_roles = self._roles(dataset) # NOTE: that because of short circuiting this allows # anonymous access to public datasets - return ( self._is_public_based_on_roles( current_roles ) or - # admin is always permitted - self.user_manager.is_admin( user ) or - self._user_has_all_roles( user, current_roles ) ) + return (self._is_public_based_on_roles(current_roles) or + # admin is always permitted + self.user_manager.is_admin(user) or + self._user_has_all_roles(user, current_roles)) - def grant( self, item, user ): + def grant(self, item, user): pass # not so easy # need to check for a sharing role # then add the new user to it - def revoke( self, item, user ): + def revoke(self, item, user): pass # not so easy # TODO: these are a lil off message - def is_public( self, dataset ): - current_roles = self._roles( dataset ) - return self._is_public_based_on_roles( current_roles ) + def is_public(self, dataset): + current_roles = self._roles(dataset) + return self._is_public_based_on_roles(current_roles) - def set_private( self, dataset, user, flush=True ): - private_role = self.user_manager.private_role( user ) - return self.set( dataset, [ private_role ], flush=flush ) + def set_private(self, dataset, user, flush=True): + private_role = self.user_manager.private_role(user) + return self.set(dataset, [private_role], flush=flush) # ---- private - def _is_public_based_on_roles( self, roles ): - return len( roles ) == 0 + def _is_public_based_on_roles(self, roles): + return len(roles) == 0 - def _user_has_all_roles( self, user, roles ): + def _user_has_all_roles(self, user, roles): user_roles = [] - if not self.user_manager.is_anonymous( user ): + if not self.user_manager.is_anonymous(user): user_roles = user.all_roles() - return iterable_has_all( user_roles, roles ) + return iterable_has_all(user_roles, roles) - def _role_is_permitted( self, dataset, role ): - current_roles = self._roles( dataset ) - return ( self._is_public_based_on_roles( current_roles ) or - # if there's only one role and this is it, let em in - ( ( len( current_roles ) == 1 ) and ( role == current_roles[0] ) ) ) + def _role_is_permitted(self, dataset, role): + current_roles = self._roles(dataset) + return (self._is_public_based_on_roles(current_roles) or + # if there's only one role and this is it, let em in + ((len(current_roles) == 1) and (role == current_roles[0]))) diff --git a/lib/galaxy/managers/roles.py b/lib/galaxy/managers/roles.py index 085594c58512..5200e9935cf5 100644 --- a/lib/galaxy/managers/roles.py +++ b/lib/galaxy/managers/roles.py @@ -9,10 +9,10 @@ from galaxy.managers import base import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class RoleManager( base.ModelManager ): +class RoleManager(base.ModelManager): """ Business logic for roles. """ @@ -22,10 +22,10 @@ class RoleManager( base.ModelManager ): user_assoc = model.UserRoleAssociation group_assoc = model.GroupRoleAssociation - def __init__( self, app ): - super( RoleManager, self ).__init__( app ) + def __init__(self, app): + super(RoleManager, self).__init__(app) - def get( self, trans, decoded_role_id ): + def get(self, trans, decoded_role_id): """ Method loads the role from the DB based on the given role id. @@ -38,12 +38,12 @@ def get( self, trans, decoded_role_id ): :raises: InconsistentDatabase, RequestParameterInvalidException, InternalServerError """ try: - role = ( self.session().query( self.model_class ) - .filter( self.model_class.id == decoded_role_id ).one() ) + role = (self.session().query(self.model_class) + .filter(self.model_class.id == decoded_role_id).one()) except sqlalchemy_exceptions.MultipleResultsFound: - raise galaxy.exceptions.InconsistentDatabase( 'Multiple roles found with the same id.' ) + raise galaxy.exceptions.InconsistentDatabase('Multiple roles found with the same id.') except sqlalchemy_exceptions.NoResultFound: - raise galaxy.exceptions.RequestParameterInvalidException( 'No role found with the id provided.' ) + raise galaxy.exceptions.RequestParameterInvalidException('No role found with the id provided.') except Exception as e: - raise galaxy.exceptions.InternalServerError( 'Error loading from the database.' + str(e) ) + raise galaxy.exceptions.InternalServerError('Error loading from the database.' + str(e)) return role diff --git a/lib/galaxy/managers/secured.py b/lib/galaxy/managers/secured.py index c08acc9e9712..3aec7a83576e 100644 --- a/lib/galaxy/managers/secured.py +++ b/lib/galaxy/managers/secured.py @@ -7,7 +7,7 @@ from galaxy import exceptions -class AccessibleManagerMixin( object ): +class AccessibleManagerMixin(object): """ A security interface to check if a User can read/view an item's. @@ -15,57 +15,57 @@ class AccessibleManagerMixin( object ): """ # don't want to override by_id since consumers will also want to fetch w/o any security checks - def is_accessible( self, item, user, **kwargs ): + def is_accessible(self, item, user, **kwargs): """ Return True if the item accessible to user. """ # override in subclasses - raise exceptions.NotImplemented( "Abstract interface Method" ) + raise exceptions.NotImplemented("Abstract interface Method") - def get_accessible( self, id, user, **kwargs ): + def get_accessible(self, id, user, **kwargs): """ Return the item with the given id if it's accessible to user, otherwise raise an error. :raises exceptions.ItemAccessibilityException: """ - item = self.by_id( id ) - return self.error_unless_accessible( item, user, **kwargs ) + item = self.by_id(id) + return self.error_unless_accessible(item, user, **kwargs) - def error_unless_accessible( self, item, user, **kwargs ): + def error_unless_accessible(self, item, user, **kwargs): """ Raise an error if the item is NOT accessible to user, otherwise return the item. :raises exceptions.ItemAccessibilityException: """ - if self.is_accessible( item, user, **kwargs ): + if self.is_accessible(item, user, **kwargs): return item - raise exceptions.ItemAccessibilityException( "%s is not accessible by user" % ( self.model_class.__name__ ) ) + raise exceptions.ItemAccessibilityException("%s is not accessible by user" % (self.model_class.__name__)) # TODO:?? are these even useful? - def list_accessible( self, user, **kwargs ): + def list_accessible(self, user, **kwargs): """ Return a list of items accessible to the user, raising an error if ANY are inaccessible. :raises exceptions.ItemAccessibilityException: """ - raise exceptions.NotImplemented( "Abstract interface Method" ) + raise exceptions.NotImplemented("Abstract interface Method") # NOTE: this will be a large, inefficient list if filters are not passed in kwargs # items = ModelManager.list( self, trans, **kwargs ) # return [ self.error_unless_accessible( trans, item, user ) for item in items ] - def filter_accessible( self, user, **kwargs ): + def filter_accessible(self, user, **kwargs): """ Return a list of items accessible to the user. """ - raise exceptions.NotImplemented( "Abstract interface Method" ) + raise exceptions.NotImplemented("Abstract interface Method") # NOTE: this will be a large, inefficient list if filters are not passed in kwargs # items = ModelManager.list( self, trans, **kwargs ) # return filter( lambda item: self.is_accessible( trans, item, user ), items ) -class OwnableManagerMixin( object ): +class OwnableManagerMixin(object): """ A security interface to check if a User is an item's owner. @@ -75,47 +75,47 @@ class OwnableManagerMixin( object ): This can also be thought of as write/edit privileges. """ - def is_owner( self, item, user, **kwargs ): + def is_owner(self, item, user, **kwargs): """ Return True if user owns the item. """ # override in subclasses - raise exceptions.NotImplemented( "Abstract interface Method" ) + raise exceptions.NotImplemented("Abstract interface Method") - def get_owned( self, id, user, **kwargs ): + def get_owned(self, id, user, **kwargs): """ Return the item with the given id if owned by the user, otherwise raise an error. :raises exceptions.ItemOwnershipException: """ - item = self.by_id( id ) - return self.error_unless_owner( item, user, **kwargs ) + item = self.by_id(id) + return self.error_unless_owner(item, user, **kwargs) - def error_unless_owner( self, item, user, **kwargs ): + def error_unless_owner(self, item, user, **kwargs): """ Raise an error if the item is NOT owned by user, otherwise return the item. :raises exceptions.ItemAccessibilityException: """ - if self.is_owner( item, user, **kwargs ): + if self.is_owner(item, user, **kwargs): return item - raise exceptions.ItemOwnershipException( "%s is not owned by user" % ( self.model_class.__name__ ) ) + raise exceptions.ItemOwnershipException("%s is not owned by user" % (self.model_class.__name__)) - def list_owned( self, user, **kwargs ): + def list_owned(self, user, **kwargs): """ Return a list of items owned by the user, raising an error if ANY are not. :raises exceptions.ItemAccessibilityException: """ - raise exceptions.NotImplemented( "Abstract interface Method" ) + raise exceptions.NotImplemented("Abstract interface Method") # just alias to by_user (easier/same thing) # return self.by_user( trans, user, **kwargs ) - def filter_owned( self, user, **kwargs ): + def filter_owned(self, user, **kwargs): """ Return a list of items owned by the user. """ # just alias to list_owned - return self.list_owned( user, **kwargs ) + return self.list_owned(user, **kwargs) diff --git a/lib/galaxy/managers/sharable.py b/lib/galaxy/managers/sharable.py index a091881fcaa2..8a857b761d21 100644 --- a/lib/galaxy/managers/sharable.py +++ b/lib/galaxy/managers/sharable.py @@ -22,11 +22,11 @@ from galaxy.managers import users import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class SharableModelManager( base.ModelManager, secured.OwnableManagerMixin, secured.AccessibleManagerMixin, - taggable.TaggableManagerMixin, annotatable.AnnotatableManagerMixin, ratable.RatableManagerMixin ): +class SharableModelManager(base.ModelManager, secured.OwnableManagerMixin, secured.AccessibleManagerMixin, + taggable.TaggableManagerMixin, annotatable.AnnotatableManagerMixin, ratable.RatableManagerMixin): # e.g. histories, pages, stored workflows, visualizations # base.DeleteableModelMixin? (all four are deletable) @@ -36,32 +36,32 @@ class SharableModelManager( base.ModelManager, secured.OwnableManagerMixin, secu #: the single character abbreviation used in username_and_slug: e.g. 'h' for histories: u/user/h/slug SINGLE_CHAR_ABBR = None - def __init__( self, app ): - super( SharableModelManager, self ).__init__( app ) + def __init__(self, app): + super(SharableModelManager, self).__init__(app) # user manager is needed to check access/ownership/admin - self.user_manager = users.UserManager( app ) + self.user_manager = users.UserManager(app) # .... has a user - def by_user( self, user, filters=None, **kwargs ): + def by_user(self, user, filters=None, **kwargs): """ Return list for all items (of model_class type) associated with the given `user`. """ user_filter = self.model_class.user_id == user.id - filters = self._munge_filters( user_filter, filters ) - return self.list( filters=filters, **kwargs ) + filters = self._munge_filters(user_filter, filters) + return self.list(filters=filters, **kwargs) # .... owned/accessible interfaces - def is_owner( self, item, user, **kwargs ): + def is_owner(self, item, user, **kwargs): """ Return true if this sharable belongs to `user` (or `user` is an admin). """ # ... effectively a good fit to have this here, but not semantically - if self.user_manager.is_admin( user ): + if self.user_manager.is_admin(user): return True return item.user == user - def is_accessible( self, item, user, **kwargs ): + def is_accessible(self, item, user, **kwargs): """ If the item is importable, is owned by `user`, or (the valid) `user` is in 'users shared with' list for the item: return True. @@ -69,25 +69,25 @@ def is_accessible( self, item, user, **kwargs ): if item.importable: return True # note: owners always have access - checking for accessible implicitly checks for ownership - if self.is_owner( item, user, **kwargs ): + if self.is_owner(item, user, **kwargs): return True - if self.user_manager.is_anonymous( user ): + if self.user_manager.is_anonymous(user): return False if user in item.users_shared_with_dot_users: return True return False # .... importable - def make_importable( self, item, flush=True ): + def make_importable(self, item, flush=True): """ Makes item accessible--viewable and importable--and sets item's slug. Does not flush/commit changes, however. Item must have name, user, importable, and slug attributes. """ - self.create_unique_slug( item, flush=False ) - return self._session_setattr( item, 'importable', True, flush=flush ) + self.create_unique_slug(item, flush=False) + return self._session_setattr(item, 'importable', True, flush=flush) - def make_non_importable( self, item, flush=True ): + def make_non_importable(self, item, flush=True): """ Makes item accessible--viewable and importable--and sets item's slug. Does not flush/commit changes, however. Item must have name, user, @@ -95,183 +95,183 @@ def make_non_importable( self, item, flush=True ): """ # item must be unpublished if non-importable if item.published: - self.unpublish( item, flush=False ) - return self._session_setattr( item, 'importable', False, flush=flush ) + self.unpublish(item, flush=False) + return self._session_setattr(item, 'importable', False, flush=flush) # .... published - def publish( self, item, flush=True ): + def publish(self, item, flush=True): """ Set both the importable and published flags on `item` to True. """ # item must be importable to be published if not item.importable: - self.make_importable( item, flush=False ) - return self._session_setattr( item, 'published', True, flush=flush ) + self.make_importable(item, flush=False) + return self._session_setattr(item, 'published', True, flush=flush) - def unpublish( self, item, flush=True ): + def unpublish(self, item, flush=True): """ Set the published flag on `item` to False. """ - return self._session_setattr( item, 'published', False, flush=flush ) + return self._session_setattr(item, 'published', False, flush=flush) - def _query_published( self, filters=None, **kwargs ): + def _query_published(self, filters=None, **kwargs): """ Return a query for all published items. """ published_filter = self.model_class.published == true() - filters = self._munge_filters( published_filter, filters ) - return self.query( filters=filters, **kwargs ) + filters = self._munge_filters(published_filter, filters) + return self.query(filters=filters, **kwargs) - def list_published( self, filters=None, **kwargs ): + def list_published(self, filters=None, **kwargs): """ Return a list of all published items. """ published_filter = self.model_class.published == true() - filters = self._munge_filters( published_filter, filters ) - return self.list( filters=filters, **kwargs ) + filters = self._munge_filters(published_filter, filters) + return self.list(filters=filters, **kwargs) # .... user sharing # sharing is often done via a 3rd table btwn a User and an item -> a UserShareAssociation - def get_share_assocs( self, item, user=None ): + def get_share_assocs(self, item, user=None): """ Get the UserShareAssociations for the `item`. Optionally send in `user` to test for a single match. """ - query = self.query_associated( self.user_share_model, item ) + query = self.query_associated(self.user_share_model, item) if user is not None: - query = query.filter_by( user=user ) + query = query.filter_by(user=user) return query.all() - def share_with( self, item, user, flush=True ): + def share_with(self, item, user, flush=True): """ Get or create a share for the given user (or users if `user` is a list). """ # precondition: user has been validated # allow user to be a list and call recursivly - if isinstance( user, list ): - return map( lambda user: self.share_with( item, user, flush=False ), user ) + if isinstance(user, list): + return map(lambda user: self.share_with(item, user, flush=False), user) # get or create - existing = self.get_share_assocs( item, user=user ) + existing = self.get_share_assocs(item, user=user) if existing: - return existing.pop( 0 ) - return self._create_user_share_assoc( item, user, flush=flush ) + return existing.pop(0) + return self._create_user_share_assoc(item, user, flush=flush) - def _create_user_share_assoc( self, item, user, flush=True ): + def _create_user_share_assoc(self, item, user, flush=True): """ Create a share for the given user. """ user_share_assoc = self.user_share_model() - self.session().add( user_share_assoc ) - self.associate( user_share_assoc, item ) + self.session().add(user_share_assoc) + self.associate(user_share_assoc, item) user_share_assoc.user = user # ensure an item slug so shared users can access if not item.slug: - self.create_unique_slug( item ) + self.create_unique_slug(item) if flush: self.session().flush() return user_share_assoc - def unshare_with( self, item, user, flush=True ): + def unshare_with(self, item, user, flush=True): """ Delete a user share (or list of shares) from the database. """ - if isinstance( user, list ): - return map( lambda user: self.unshare_with( item, user, flush=False ), user ) + if isinstance(user, list): + return map(lambda user: self.unshare_with(item, user, flush=False), user) # Look for and delete sharing relation for user. - user_share_assoc = self.get_share_assocs( item, user=user )[0] - self.session().delete( user_share_assoc ) + user_share_assoc = self.get_share_assocs(item, user=user)[0] + self.session().delete(user_share_assoc) if flush: self.session().flush() return user_share_assoc - def _query_shared_with( self, user, eagerloads=True, **kwargs ): + def _query_shared_with(self, user, eagerloads=True, **kwargs): """ Return a query for this model already filtered to models shared with a particular user. """ - query = self.session().query( self.model_class ).join( 'users_shared_with' ) + query = self.session().query(self.model_class).join('users_shared_with') if eagerloads is False: - query = query.enable_eagerloads( False ) + query = query.enable_eagerloads(False) # TODO: as filter in FilterParser also - query = query.filter( self.user_share_model.user == user ) - return self._filter_and_order_query( query, **kwargs ) + query = query.filter(self.user_share_model.user == user) + return self._filter_and_order_query(query, **kwargs) - def list_shared_with( self, user, filters=None, order_by=None, limit=None, offset=None, **kwargs ): + def list_shared_with(self, user, filters=None, order_by=None, limit=None, offset=None, **kwargs): """ Return a list of those models shared with a particular user. """ # TODO: refactor out dupl-code btwn base.list - orm_filters, fn_filters = self._split_filters( filters ) + orm_filters, fn_filters = self._split_filters(filters) if not fn_filters: # if no fn_filtering required, we can use the 'all orm' version with limit offset - query = self._query_shared_with( user, filters=orm_filters, - order_by=order_by, limit=limit, offset=offset, **kwargs ) - return self._orm_list( query=query, **kwargs ) + query = self._query_shared_with(user, filters=orm_filters, + order_by=order_by, limit=limit, offset=offset, **kwargs) + return self._orm_list(query=query, **kwargs) # fn filters will change the number of items returnable by limit/offset - remove them here from the orm query - query = self._query_shared_with( user, filters=orm_filters, - order_by=order_by, limit=None, offset=None, **kwargs ) + query = self._query_shared_with(user, filters=orm_filters, + order_by=order_by, limit=None, offset=None, **kwargs) # apply limit and offset afterwards - items = self._apply_fn_filters_gen( query.all(), fn_filters ) - return list( self._apply_fn_limit_offset_gen( items, limit, offset ) ) + items = self._apply_fn_filters_gen(query.all(), fn_filters) + return list(self._apply_fn_limit_offset_gen(items, limit, offset)) # .... slugs # slugs are human readable strings often used to link to sharable resources (replacing ids) # TODO: as validator, deserializer, etc. (maybe another object entirely?) - def set_slug( self, item, new_slug, user, flush=True ): + def set_slug(self, item, new_slug, user, flush=True): """ Validate and set the new slug for `item`. """ # precondition: has been validated - if not self.is_valid_slug( new_slug ): - raise exceptions.RequestParameterInvalidException( "Invalid slug", slug=new_slug ) + if not self.is_valid_slug(new_slug): + raise exceptions.RequestParameterInvalidException("Invalid slug", slug=new_slug) # error if slug is already in use - if self._slug_exists( user, new_slug ): - raise exceptions.Conflict( "Slug already exists", slug=new_slug ) + if self._slug_exists(user, new_slug): + raise exceptions.Conflict("Slug already exists", slug=new_slug) item.slug = new_slug if flush: self.session().flush() return item - def is_valid_slug( self, slug ): + def is_valid_slug(self, slug): """ Returns true if `slug` is valid. """ - VALID_SLUG_RE = re.compile( "^[a-z0-9\-]+$" ) - return VALID_SLUG_RE.match( slug ) + VALID_SLUG_RE = re.compile("^[a-z0-9\-]+$") + return VALID_SLUG_RE.match(slug) - def _existing_set_of_slugs( self, user ): - query = ( self.session().query( self.model_class.slug ) - .filter_by( user=user ) ) - return list( set( query.all() ) ) + def _existing_set_of_slugs(self, user): + query = (self.session().query(self.model_class.slug) + .filter_by(user=user)) + return list(set(query.all())) - def _slug_exists( self, user, slug ): - query = ( self.session().query( self.model_class.slug ) - .filter_by( user=user, slug=slug ) ) + def _slug_exists(self, user, slug): + query = (self.session().query(self.model_class.slug) + .filter_by(user=user, slug=slug)) return query.count() != 0 - def _slugify( self, start_with ): + def _slugify(self, start_with): # Replace whitespace with '-' - slug_base = re.sub( "\s+", "-", start_with ) + slug_base = re.sub("\s+", "-", start_with) # Remove all non-alphanumeric characters. - slug_base = re.sub( "[^a-zA-Z0-9\-]", "", slug_base ) + slug_base = re.sub("[^a-zA-Z0-9\-]", "", slug_base) # Remove trailing '-'. if slug_base.endswith('-'): slug_base = slug_base[:-1] return slug_base - def _default_slug_base( self, item ): + def _default_slug_base(self, item): # override in subclasses - if hasattr( item, 'title' ): + if hasattr(item, 'title'): return item.title.lower() return item.name.lower() - def get_unique_slug( self, item ): + def get_unique_slug(self, item): """ Returns a slug that is unique among user's importable items for item's class. @@ -280,7 +280,7 @@ def get_unique_slug( self, item ): # Setup slug base. if cur_slug is None or cur_slug == "": - slug_base = self._slugify( self._default_slug_base( item ) ) + slug_base = self._slugify(self._default_slug_base(item)) else: slug_base = cur_slug @@ -288,22 +288,22 @@ def get_unique_slug( self, item ): # add integer to end. new_slug = slug_base count = 1 - while ( self.session().query( item.__class__ ) - .filter_by( user=item.user, slug=new_slug, importable=True ) - .count() != 0 ): + while (self.session().query(item.__class__) + .filter_by(user=item.user, slug=new_slug, importable=True) + .count() != 0): # Slug taken; choose a new slug based on count. This approach can # handle numerous items with the same name gracefully. - new_slug = '%s-%i' % ( slug_base, count ) + new_slug = '%s-%i' % (slug_base, count) count += 1 return new_slug - def create_unique_slug( self, item, flush=True ): + def create_unique_slug(self, item, flush=True): """ Set a new, unique slug on the item. """ - item.slug = self.get_unique_slug( item ) - self.session().add( item ) + item.slug = self.get_unique_slug(item) + self.session().add(item) if flush: self.session().flush() return item @@ -311,16 +311,16 @@ def create_unique_slug( self, item, flush=True ): # TODO: def by_slug( self, user, **kwargs ): -class SharableModelSerializer( base.ModelSerializer, - taggable.TaggableSerializerMixin, annotatable.AnnotatableSerializerMixin, ratable.RatableSerializerMixin ): +class SharableModelSerializer(base.ModelSerializer, + taggable.TaggableSerializerMixin, annotatable.AnnotatableSerializerMixin, ratable.RatableSerializerMixin): # TODO: stub SINGLE_CHAR_ABBR = None - def add_serializers( self ): - super( SharableModelSerializer, self ).add_serializers() - taggable.TaggableSerializerMixin.add_serializers( self ) - annotatable.AnnotatableSerializerMixin.add_serializers( self ) - ratable.RatableSerializerMixin.add_serializers( self ) + def add_serializers(self): + super(SharableModelSerializer, self).add_serializers() + taggable.TaggableSerializerMixin.add_serializers(self) + annotatable.AnnotatableSerializerMixin.add_serializers(self) + ratable.RatableSerializerMixin.add_serializers(self) self.serializers.update({ 'user_id' : self.serialize_id, @@ -332,16 +332,16 @@ def add_serializers( self ): 'importable', 'published', 'slug' ]) - def serialize_username_and_slug( self, item, key, **context ): - if not ( item.user and item.slug and self.SINGLE_CHAR_ABBR ): + def serialize_username_and_slug(self, item, key, **context): + if not (item.user and item.slug and self.SINGLE_CHAR_ABBR): return None - return ( '/' ).join(( 'u', item.user.username, self.SINGLE_CHAR_ABBR, item.slug ) ) + return ('/').join(('u', item.user.username, self.SINGLE_CHAR_ABBR, item.slug)) # the only ones that needs any fns: # user/user_id # username_and_slug? - def serialize_users_shared_with( self, item, key, user=None, **context ): + def serialize_users_shared_with(self, item, key, user=None, **context): """ Returns a list of encoded ids for users the item has been shared. @@ -349,21 +349,21 @@ def serialize_users_shared_with( self, item, key, user=None, **context ): """ # TODO: still an open question as to whether key removal based on user # should be handled here or at a higher level (even if we didn't have to pass user (via thread context, etc.)) - if not self.manager.is_owner( item, user ): + if not self.manager.is_owner(item, user): self.skip() - share_assocs = self.manager.get_share_assocs( item ) - return [ self.serialize_id( share, 'user_id' ) for share in share_assocs ] + share_assocs = self.manager.get_share_assocs(item) + return [self.serialize_id(share, 'user_id') for share in share_assocs] -class SharableModelDeserializer( base.ModelDeserializer, - taggable.TaggableDeserializerMixin, annotatable.AnnotatableDeserializerMixin, ratable.RatableDeserializerMixin ): +class SharableModelDeserializer(base.ModelDeserializer, + taggable.TaggableDeserializerMixin, annotatable.AnnotatableDeserializerMixin, ratable.RatableDeserializerMixin): - def add_deserializers( self ): - super( SharableModelDeserializer, self ).add_deserializers() - taggable.TaggableDeserializerMixin.add_deserializers( self ) - annotatable.AnnotatableDeserializerMixin.add_deserializers( self ) - ratable.RatableDeserializerMixin.add_deserializers( self ) + def add_deserializers(self): + super(SharableModelDeserializer, self).add_deserializers() + taggable.TaggableDeserializerMixin.add_deserializers(self) + annotatable.AnnotatableDeserializerMixin.add_deserializers(self) + ratable.RatableDeserializerMixin.add_deserializers(self) self.deserializers.update({ 'published' : self.deserialize_published, @@ -371,71 +371,71 @@ def add_deserializers( self ): 'users_shared_with' : self.deserialize_users_shared_with, }) - def deserialize_published( self, item, key, val, **context ): + def deserialize_published(self, item, key, val, **context): """ """ - val = self.validate.bool( key, val ) + val = self.validate.bool(key, val) if item.published == val: return val if val: - self.manager.publish( item, flush=False ) + self.manager.publish(item, flush=False) else: - self.manager.unpublish( item, flush=False ) + self.manager.unpublish(item, flush=False) return item.published - def deserialize_importable( self, item, key, val, **context ): + def deserialize_importable(self, item, key, val, **context): """ """ - val = self.validate.bool( key, val ) + val = self.validate.bool(key, val) if item.importable == val: return val if val: - self.manager.make_importable( item, flush=False ) + self.manager.make_importable(item, flush=False) else: - self.manager.make_non_importable( item, flush=False ) + self.manager.make_non_importable(item, flush=False) return item.importable # TODO: def deserialize_slug( self, item, val, **context ): - def deserialize_users_shared_with( self, item, key, val, **context ): + def deserialize_users_shared_with(self, item, key, val, **context): """ Accept a list of encoded user_ids, validate them as users, and then add or remove user shares in order to update the users_shared_with to match the given list finally returning the new list of shares. """ - unencoded_ids = [ self.app.security.decode_id( id_ ) for id_ in val ] - new_users_shared_with = set( self.manager.user_manager.by_ids( unencoded_ids ) ) - current_shares = self.manager.get_share_assocs( item ) - currently_shared_with = set([ share.user for share in current_shares ]) + unencoded_ids = [self.app.security.decode_id(id_) for id_ in val] + new_users_shared_with = set(self.manager.user_manager.by_ids(unencoded_ids)) + current_shares = self.manager.get_share_assocs(item) + currently_shared_with = set([share.user for share in current_shares]) needs_adding = new_users_shared_with - currently_shared_with for user in needs_adding: - current_shares.append( self.manager.share_with( item, user, flush=False ) ) + current_shares.append(self.manager.share_with(item, user, flush=False)) needs_removing = currently_shared_with - new_users_shared_with for user in needs_removing: - current_shares.remove( self.manager.unshare_with( item, user, flush=False ) ) + current_shares.remove(self.manager.unshare_with(item, user, flush=False)) self.manager.session().flush() # TODO: or should this return the list of ids? return current_shares -class SharableModelFilters( base.ModelFilterParser, - taggable.TaggableFilterMixin, annotatable.AnnotatableFilterMixin, ratable.RatableFilterMixin ): +class SharableModelFilters(base.ModelFilterParser, + taggable.TaggableFilterMixin, annotatable.AnnotatableFilterMixin, ratable.RatableFilterMixin): - def _add_parsers( self ): - super( SharableModelFilters, self )._add_parsers() - taggable.TaggableFilterMixin._add_parsers( self ) - annotatable.AnnotatableFilterMixin._add_parsers( self ) - ratable.RatableFilterMixin._add_parsers( self ) + def _add_parsers(self): + super(SharableModelFilters, self)._add_parsers() + taggable.TaggableFilterMixin._add_parsers(self) + annotatable.AnnotatableFilterMixin._add_parsers(self) + ratable.RatableFilterMixin._add_parsers(self) self.orm_filter_parsers.update({ - 'importable' : { 'op': ( 'eq' ), 'val': self.parse_bool }, - 'published' : { 'op': ( 'eq' ), 'val': self.parse_bool }, - 'slug' : { 'op': ( 'eq', 'contains', 'like' ) }, + 'importable' : {'op': ('eq'), 'val': self.parse_bool}, + 'published' : {'op': ('eq'), 'val': self.parse_bool}, + 'slug' : {'op': ('eq', 'contains', 'like')}, # chose by user should prob. only be available for admin? (most often we'll only need trans.user) # 'user' : { 'op': ( 'eq' ), 'val': self.parse_id_list }, }) diff --git a/lib/galaxy/managers/taggable.py b/lib/galaxy/managers/taggable.py index 4be6f2b6c2c9..edee585460ed 100644 --- a/lib/galaxy/managers/taggable.py +++ b/lib/galaxy/managers/taggable.py @@ -8,11 +8,11 @@ from galaxy.util import unicodify -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) # TODO: work out the relation between serializers and managers and then fold these into the parent of the two -def _tag_str_gen( item ): +def _tag_str_gen(item): # TODO: which user is this? all? for tag in item.tags: tag_str = tag.user_tname @@ -21,13 +21,13 @@ def _tag_str_gen( item ): yield tag_str -def _tags_to_strings( item ): - if not hasattr( item, 'tags' ): +def _tags_to_strings(item): + if not hasattr(item, 'tags'): return None - return list( _tag_str_gen( item ) ) + return list(_tag_str_gen(item)) -def _tags_from_strings( item, tag_handler, new_tags_list, user=None ): +def _tags_from_strings(item, tag_handler, new_tags_list, user=None): # TODO: have to assume trans.user here... if not user: # raise galaxy_exceptions.RequestParameterMissingException( 'User required for tags on ' + str( item ) ) @@ -35,83 +35,83 @@ def _tags_from_strings( item, tag_handler, new_tags_list, user=None ): # I haven't found a better one for anon users copying items with tags return # TODO: duped from tags manager - de-dupe when moved to taggable mixin - tag_handler.delete_item_tags( user, item ) - new_tags_str = ','.join( new_tags_list ) - tag_handler.apply_item_tags( user, item, unicodify( new_tags_str, 'utf-8' ) ) + tag_handler.delete_item_tags(user, item) + new_tags_str = ','.join(new_tags_list) + tag_handler.apply_item_tags(user, item, unicodify(new_tags_str, 'utf-8')) # TODO:!! does the creation of new_tags_list mean there are now more and more unused tag rows in the db? -class TaggableManagerMixin( object ): +class TaggableManagerMixin(object): #: class of TagAssociation (e.g. HistoryTagAssociation) tag_assoc = None # TODO: most of this can be done by delegating to the TagManager? - def get_tags( self, item ): + def get_tags(self, item): """ Return a list of tag strings. """ - return _tags_to_strings( item ) + return _tags_to_strings(item) - def set_tags( self, item, new_tags, user=None ): + def set_tags(self, item, new_tags, user=None): """ Set an `item`'s tags from a list of strings. """ - return _tags_from_strings( item, self.app.tag_handler, new_tags, user=user ) + return _tags_from_strings(item, self.app.tag_handler, new_tags, user=user) # def tags_by_user( self, user, **kwargs ): # TODO: here or TagManager # pass -class TaggableSerializerMixin( object ): +class TaggableSerializerMixin(object): - def add_serializers( self ): - self.serializers[ 'tags' ] = self.serialize_tags + def add_serializers(self): + self.serializers['tags'] = self.serialize_tags - def serialize_tags( self, item, key, **context ): + def serialize_tags(self, item, key, **context): """ Return tags as a list of strings. """ - return _tags_to_strings( item ) + return _tags_to_strings(item) -class TaggableDeserializerMixin( object ): +class TaggableDeserializerMixin(object): - def add_deserializers( self ): - self.deserializers[ 'tags' ] = self.deserialize_tags + def add_deserializers(self): + self.deserializers['tags'] = self.deserialize_tags - def deserialize_tags( self, item, key, val, user=None, **context ): + def deserialize_tags(self, item, key, val, user=None, **context): """ Make sure `val` is a valid list of tag strings and assign them. Note: this will erase any previous tags. """ - new_tags_list = self.validate.basestring_list( key, val ) - _tags_from_strings( item, self.app.tag_handler, new_tags_list, user=user ) + new_tags_list = self.validate.basestring_list(key, val) + _tags_from_strings(item, self.app.tag_handler, new_tags_list, user=user) return item.tags -class TaggableFilterMixin( object ): +class TaggableFilterMixin(object): - def filter_has_partial_tag( self, item, val ): + def filter_has_partial_tag(self, item, val): """ Return True if any tag partially contains `val`. """ - for tag_str in _tag_str_gen( item ): + for tag_str in _tag_str_gen(item): if val in tag_str: return True return False - def filter_has_tag( self, item, val ): + def filter_has_tag(self, item, val): """ Return True if any tag exactly equals `val`. """ - for tag_str in _tag_str_gen( item ): + for tag_str in _tag_str_gen(item): if val == tag_str: return True return False - def _add_parsers( self ): + def _add_parsers(self): self.fn_filter_parsers.update({ 'tag': { 'op': { diff --git a/lib/galaxy/managers/tags.py b/lib/galaxy/managers/tags.py index f59cee26fe86..74cb04f25960 100644 --- a/lib/galaxy/managers/tags.py +++ b/lib/galaxy/managers/tags.py @@ -7,23 +7,23 @@ from sqlalchemy.sql import select from sqlalchemy.sql.expression import func -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) # Item-specific information needed to perform tagging. -class ItemTagAssocInfo( object ): - def __init__( self, item_class, tag_assoc_class, item_id_col ): +class ItemTagAssocInfo(object): + def __init__(self, item_class, tag_assoc_class, item_id_col): self.item_class = item_class self.tag_assoc_class = tag_assoc_class self.item_id_col = item_id_col -class TagManager( object ): +class TagManager(object): """ Manages CRUD operations related to tagging objects. """ - def __init__( self, sa_session ): + def __init__(self, sa_session): self.sa_session = sa_session # Minimum tag length. self.min_tag_len = 2 @@ -38,120 +38,127 @@ def __init__( self, sa_session ): # Initialize with known classes - add to this in subclasses. self.item_tag_assoc_info = {} - def add_tags_from_list( self, user, item, new_tags_list ): - new_tags_set = set( new_tags_list ) + def add_tags_from_list(self, user, item, new_tags_list): + new_tags_set = set(new_tags_list) if item.tags: - new_tags_set.update( self.get_tags_str( item.tags ).split( ',' ) ) - return self.set_tags_from_list( user, item, new_tags_set ) + new_tags_set.update(self.get_tags_str(item.tags).split(',')) + return self.set_tags_from_list(user, item, new_tags_set) - def set_tags_from_list( self, user, item, new_tags_list ): + def remove_tags_from_list(self, user, item, tag_to_remove_list): + tag_to_remove_set = set(tag_to_remove_list) + tags_set = set(self.get_tags_str(item.tags).split(',')) + if item.tags: + tags_set -= tag_to_remove_set + return self.set_tags_from_list(user, item, tags_set) + + def set_tags_from_list(self, user, item, new_tags_list): # precondition: item is already security checked against user # precondition: incoming tags is a list of sanitized/formatted strings - self.delete_item_tags( user, item ) - new_tags_str = ','.join( new_tags_list ) - self.apply_item_tags( user, item, unicodify( new_tags_str, 'utf-8' ) ) + self.delete_item_tags(user, item) + new_tags_str = ','.join(new_tags_list) + self.apply_item_tags(user, item, unicodify(new_tags_str, 'utf-8')) self.sa_session.flush() return item.tags - def get_tag_assoc_class( self, item_class ): + def get_tag_assoc_class(self, item_class): """Returns tag association class for item class.""" return self.item_tag_assoc_info[item_class.__name__].tag_assoc_class - def get_id_col_in_item_tag_assoc_table( self, item_class ): + def get_id_col_in_item_tag_assoc_table(self, item_class): """Returns item id column in class' item-tag association table.""" return self.item_tag_assoc_info[item_class.__name__].item_id_col - def get_community_tags( self, item=None, limit=None ): + def get_community_tags(self, item=None, limit=None): """Returns community tags for an item.""" # Get item-tag association class. item_class = item.__class__ - item_tag_assoc_class = self.get_tag_assoc_class( item_class ) + item_tag_assoc_class = self.get_tag_assoc_class(item_class) if not item_tag_assoc_class: return [] # Build select statement. - cols_to_select = [ item_tag_assoc_class.table.c.tag_id, func.count( '*' ) ] - from_obj = item_tag_assoc_class.table.join( item_class.table ).join( galaxy.model.Tag.table ) - where_clause = ( self.get_id_col_in_item_tag_assoc_table( item_class ) == item.id ) - order_by = [ func.count( "*" ).desc() ] + cols_to_select = [item_tag_assoc_class.table.c.tag_id, func.count('*')] + from_obj = item_tag_assoc_class.table.join(item_class.table).join(galaxy.model.Tag.table) + where_clause = (self.get_id_col_in_item_tag_assoc_table(item_class) == item.id) + order_by = [func.count("*").desc()] group_by = item_tag_assoc_class.table.c.tag_id # Do query and get result set. - query = select( columns=cols_to_select, - from_obj=from_obj, - whereclause=where_clause, - group_by=group_by, - order_by=order_by, - limit=limit ) - result_set = self.sa_session.execute( query ) + query = select(columns=cols_to_select, + from_obj=from_obj, + whereclause=where_clause, + group_by=group_by, + order_by=order_by, + limit=limit) + result_set = self.sa_session.execute(query) # Return community tags. community_tags = [] for row in result_set: tag_id = row[0] - community_tags.append( self.get_tag_by_id( tag_id ) ) + community_tags.append(self.get_tag_by_id(tag_id)) return community_tags - def get_tool_tags( self ): - query = select( columns=[ galaxy.model.ToolTagAssociation.table.c.tag_id ], - from_obj=galaxy.model.ToolTagAssociation.table ).distinct() - result_set = self.sa_session.execute( query ) + def get_tool_tags(self): + query = select(columns=[galaxy.model.ToolTagAssociation.table.c.tag_id], + from_obj=galaxy.model.ToolTagAssociation.table).distinct() + result_set = self.sa_session.execute(query) tags = [] for row in result_set: tag_id = row[0] - tags.append( self.get_tag_by_id( tag_id ) ) + tags.append(self.get_tag_by_id(tag_id)) return tags - def remove_item_tag( self, user, item, tag_name ): + def remove_item_tag(self, user, item, tag_name): """Remove a tag from an item.""" # Get item tag association. - item_tag_assoc = self._get_item_tag_assoc( user, item, tag_name ) + item_tag_assoc = self._get_item_tag_assoc(user, item, tag_name) # Remove association. if item_tag_assoc: # Delete association. - self.sa_session.delete( item_tag_assoc ) - item.tags.remove( item_tag_assoc ) + self.sa_session.delete(item_tag_assoc) + item.tags.remove(item_tag_assoc) return True return False - def delete_item_tags( self, user, item ): + def delete_item_tags(self, user, item): """Delete tags from an item.""" # Delete item-tag associations. for tag in item.tags: - self.sa_session.delete( tag ) + self.sa_session.delete(tag) # Delete tags from item. del item.tags[:] - def item_has_tag( self, user, item, tag ): + def item_has_tag(self, user, item, tag): """Returns true if item is has a given tag.""" # Get tag name. - if isinstance( tag, string_types ): + if isinstance(tag, string_types): tag_name = tag - elif isinstance( tag, galaxy.model.Tag ): + elif isinstance(tag, galaxy.model.Tag): tag_name = tag.name # Check for an item-tag association to see if item has a given tag. - item_tag_assoc = self._get_item_tag_assoc( user, item, tag_name ) + item_tag_assoc = self._get_item_tag_assoc(user, item, tag_name) if item_tag_assoc: return True return False - def apply_item_tag( self, user, item, name, value=None ): + def apply_item_tag(self, user, item, name, value=None): # Use lowercase name for searching/creating tag. lc_name = name.lower() # Get or create item-tag association. - item_tag_assoc = self._get_item_tag_assoc( user, item, lc_name ) + item_tag_assoc = self._get_item_tag_assoc(user, item, lc_name) # If the association does not exist, or if it has a different value, add another. # We do allow multiple associations with different values. if not item_tag_assoc or (item_tag_assoc and item_tag_assoc.value != value): # Create item-tag association. # Create tag; if None, skip the tag (and log error). - tag = self._get_or_create_tag( lc_name ) + tag = self._get_or_create_tag(lc_name) if not tag: - log.warning( "Failed to create tag with name %s" % lc_name ) + log.warning("Failed to create tag with name %s" % lc_name) return # Create tag association based on item class. - item_tag_assoc_class = self.get_tag_assoc_class( item.__class__ ) + item_tag_assoc_class = self.get_tag_assoc_class(item.__class__) item_tag_assoc = item_tag_assoc_class() # Add tag to association. - item.tags.append( item_tag_assoc ) + item.tags.append(item_tag_assoc) item_tag_assoc.tag = tag item_tag_assoc.user = user # Apply attributes to item-tag association. Strip whitespace from user name and tag. @@ -163,15 +170,15 @@ def apply_item_tag( self, user, item, name, value=None ): item_tag_assoc.value = lc_value return item_tag_assoc - def apply_item_tags( self, user, item, tags_str ): + def apply_item_tags(self, user, item, tags_str): """Apply tags to an item.""" # Parse tags. - parsed_tags = self.parse_tags( tags_str ) + parsed_tags = self.parse_tags(tags_str) # Apply each tag. for name, value in parsed_tags: - self.apply_item_tag( user, item, name, value ) + self.apply_item_tag(user, item, name, value) - def get_tags_str( self, tags ): + def get_tags_str(self, tags): """Build a string from an item's tags.""" # Return empty string if there are no tags. if not tags: @@ -182,30 +189,30 @@ def get_tags_str( self, tags ): tag_str = tag.user_tname if tag.value is not None: tag_str += ":" + tag.user_value - tags_str_list.append( tag_str ) - return ", ".join( tags_str_list ) + tags_str_list.append(tag_str) + return ", ".join(tags_str_list) - def get_tag_by_id( self, tag_id ): + def get_tag_by_id(self, tag_id): """Get a Tag object from a tag id.""" - return self.sa_session.query( galaxy.model.Tag ).filter_by( id=tag_id ).first() + return self.sa_session.query(galaxy.model.Tag).filter_by(id=tag_id).first() - def get_tag_by_name( self, tag_name ): + def get_tag_by_name(self, tag_name): """Get a Tag object from a tag name (string).""" if tag_name: - return self.sa_session.query( galaxy.model.Tag ).filter_by( name=tag_name.lower() ).first() + return self.sa_session.query(galaxy.model.Tag).filter_by(name=tag_name.lower()).first() return None - def _create_tag( self, tag_str ): + def _create_tag(self, tag_str): """Create a Tag object from a tag string.""" - tag_hierarchy = tag_str.split( self.hierarchy_separator ) + tag_hierarchy = tag_str.split(self.hierarchy_separator) tag_prefix = "" parent_tag = None for sub_tag in tag_hierarchy: # Get or create subtag. - tag_name = tag_prefix + self._scrub_tag_name( sub_tag ) - tag = self.sa_session.query( galaxy.model.Tag ).filter_by( name=tag_name).first() + tag_name = tag_prefix + self._scrub_tag_name(sub_tag) + tag = self.sa_session.query(galaxy.model.Tag).filter_by(name=tag_name).first() if not tag: - tag = galaxy.model.Tag( type=0, name=tag_name ) + tag = galaxy.model.Tag(type=0, name=tag_name) # Set tag parent. tag.parent = parent_tag # Update parent and tag prefix. @@ -213,31 +220,31 @@ def _create_tag( self, tag_str ): tag_prefix = tag.name + self.hierarchy_separator return tag - def _get_or_create_tag( self, tag_str ): + def _get_or_create_tag(self, tag_str): """Get or create a Tag object from a tag string.""" # Scrub tag; if tag is None after being scrubbed, return None. - scrubbed_tag_str = self._scrub_tag_name( tag_str ) + scrubbed_tag_str = self._scrub_tag_name(tag_str) if not scrubbed_tag_str: return None # Get item tag. - tag = self.get_tag_by_name( scrubbed_tag_str ) + tag = self.get_tag_by_name(scrubbed_tag_str) # Create tag if necessary. if tag is None: - tag = self._create_tag( scrubbed_tag_str ) + tag = self._create_tag(scrubbed_tag_str) return tag - def _get_item_tag_assoc( self, user, item, tag_name ): + def _get_item_tag_assoc(self, user, item, tag_name): """ Return ItemTagAssociation object for a user, item, and tag string; returns None if there is no such association. """ - scrubbed_tag_name = self._scrub_tag_name( tag_name ) + scrubbed_tag_name = self._scrub_tag_name(tag_name) for item_tag_assoc in item.tags: - if ( item_tag_assoc.user == user ) and ( item_tag_assoc.user_tname == scrubbed_tag_name ): + if (item_tag_assoc.user == user) and (item_tag_assoc.user_tname == scrubbed_tag_name): return item_tag_assoc return None - def parse_tags( self, tag_str ): + def parse_tags(self, tag_str): """ Returns a list of raw (tag-name, value) pairs derived from a string; method scrubs tag names and values as well. Return value is a list of (tag_name, tag_value) tuples. @@ -246,88 +253,92 @@ def parse_tags( self, tag_str ): if not tag_str: return dict() # Split tags based on separators. - reg_exp = re.compile( '[' + self.tag_separators + ']' ) - raw_tags = reg_exp.split( tag_str ) + reg_exp = re.compile('[' + self.tag_separators + ']') + raw_tags = reg_exp.split(tag_str) # Extract name-value pairs. name_value_pairs = [] for raw_tag in raw_tags: - nv_pair = self._get_name_value_pair( raw_tag ) - scrubbed_name = self._scrub_tag_name( nv_pair[0] ) - scrubbed_value = self._scrub_tag_value( nv_pair[1] ) + nv_pair = self._get_name_value_pair(raw_tag) + scrubbed_name = self._scrub_tag_name(nv_pair[0]) + scrubbed_value = self._scrub_tag_value(nv_pair[1]) # Append tag_name, tag_value tuple -- TODO use NamedTuple - name_value_pairs.append(( scrubbed_name, scrubbed_value )) + name_value_pairs.append((scrubbed_name, scrubbed_value)) return name_value_pairs - def _scrub_tag_value( self, value ): + def _scrub_tag_value(self, value): """Scrub a tag value.""" # Gracefully handle None: if not value: return None # Remove whitespace from value. - reg_exp = re.compile( '\s' ) - scrubbed_value = re.sub( reg_exp, "", value ) + reg_exp = re.compile('\s') + scrubbed_value = re.sub(reg_exp, "", value) return scrubbed_value - def _scrub_tag_name( self, name ): + def _scrub_tag_name(self, name): """Scrub a tag name.""" # Gracefully handle None: if not name: return None # Remove whitespace from name. - reg_exp = re.compile( '\s' ) - scrubbed_name = re.sub( reg_exp, "", name ) + reg_exp = re.compile('\s') + scrubbed_name = re.sub(reg_exp, "", name) # Ignore starting ':' char. - if scrubbed_name.startswith( self.hierarchy_separator ): + if scrubbed_name.startswith(self.hierarchy_separator): scrubbed_name = scrubbed_name[1:] # If name is too short or too long, return None. - if len( scrubbed_name ) < self.min_tag_len or len( scrubbed_name ) > self.max_tag_len: + if len(scrubbed_name) < self.min_tag_len or len(scrubbed_name) > self.max_tag_len: return None return scrubbed_name - def _scrub_tag_name_list( self, tag_name_list ): + def _scrub_tag_name_list(self, tag_name_list): """Scrub a tag name list.""" scrubbed_tag_list = list() for tag in tag_name_list: - scrubbed_tag_list.append( self._scrub_tag_name( tag ) ) + scrubbed_tag_list.append(self._scrub_tag_name(tag)) return scrubbed_tag_list - def _get_name_value_pair( self, tag_str ): + def _get_name_value_pair(self, tag_str): """Get name, value pair from a tag string.""" # Use regular expression to parse name, value. - reg_exp = re.compile( "[" + self.key_value_separators + "]" ) - name_value_pair = reg_exp.split( tag_str ) + reg_exp = re.compile("[" + self.key_value_separators + "]") + name_value_pair = reg_exp.split(tag_str) # Add empty slot if tag does not have value. - if len( name_value_pair ) < 2: - name_value_pair.append( None ) + if len(name_value_pair) < 2: + name_value_pair.append(None) return name_value_pair -class GalaxyTagManager( TagManager ): - def __init__( self, sa_session ): +class GalaxyTagManager(TagManager): + def __init__(self, sa_session): from galaxy import model - TagManager.__init__( self, sa_session ) - self.item_tag_assoc_info["History"] = ItemTagAssocInfo( model.History, - model.HistoryTagAssociation, - model.HistoryTagAssociation.table.c.history_id ) + TagManager.__init__(self, sa_session) + self.item_tag_assoc_info["History"] = ItemTagAssocInfo(model.History, + model.HistoryTagAssociation, + model.HistoryTagAssociation.table.c.history_id) self.item_tag_assoc_info["HistoryDatasetAssociation"] = \ - ItemTagAssocInfo( model.HistoryDatasetAssociation, - model.HistoryDatasetAssociationTagAssociation, - model.HistoryDatasetAssociationTagAssociation.table.c.history_dataset_association_id ) + ItemTagAssocInfo(model.HistoryDatasetAssociation, + model.HistoryDatasetAssociationTagAssociation, + model.HistoryDatasetAssociationTagAssociation.table.c.history_dataset_association_id) self.item_tag_assoc_info["HistoryDatasetCollectionAssociation"] = \ - ItemTagAssocInfo( model.HistoryDatasetCollectionAssociation, - model.HistoryDatasetCollectionTagAssociation, - model.HistoryDatasetCollectionTagAssociation.table.c.history_dataset_collection_id ) - self.item_tag_assoc_info["Page"] = ItemTagAssocInfo( model.Page, - model.PageTagAssociation, - model.PageTagAssociation.table.c.page_id ) - self.item_tag_assoc_info["StoredWorkflow"] = ItemTagAssocInfo( model.StoredWorkflow, - model.StoredWorkflowTagAssociation, - model.StoredWorkflowTagAssociation.table.c.stored_workflow_id ) - self.item_tag_assoc_info["Visualization"] = ItemTagAssocInfo( model.Visualization, - model.VisualizationTagAssociation, - model.VisualizationTagAssociation.table.c.visualization_id ) - - -class CommunityTagManager( TagManager): - def __init__( self, sa_session ): - TagManager.__init__( self, sa_session ) + ItemTagAssocInfo(model.HistoryDatasetCollectionAssociation, + model.HistoryDatasetCollectionTagAssociation, + model.HistoryDatasetCollectionTagAssociation.table.c.history_dataset_collection_id) + self.item_tag_assoc_info["LibraryDatasetDatasetAssociation"] = \ + ItemTagAssocInfo(model.LibraryDatasetDatasetAssociation, + model.LibraryDatasetDatasetAssociationTagAssociation, + model.LibraryDatasetDatasetAssociationTagAssociation.table.c.library_dataset_dataset_association_id) + self.item_tag_assoc_info["Page"] = ItemTagAssocInfo(model.Page, + model.PageTagAssociation, + model.PageTagAssociation.table.c.page_id) + self.item_tag_assoc_info["StoredWorkflow"] = ItemTagAssocInfo(model.StoredWorkflow, + model.StoredWorkflowTagAssociation, + model.StoredWorkflowTagAssociation.table.c.stored_workflow_id) + self.item_tag_assoc_info["Visualization"] = ItemTagAssocInfo(model.Visualization, + model.VisualizationTagAssociation, + model.VisualizationTagAssociation.table.c.visualization_id) + + +class CommunityTagManager(TagManager): + def __init__(self, sa_session): + TagManager.__init__(self, sa_session) diff --git a/lib/galaxy/managers/users.py b/lib/galaxy/managers/users.py index 12cd7d406fee..149836db8471 100644 --- a/lib/galaxy/managers/users.py +++ b/lib/galaxy/managers/users.py @@ -14,10 +14,10 @@ from galaxy.security import validate_user_input import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class UserManager( base.ModelManager, deletable.PurgableManagerMixin ): +class UserManager(base.ModelManager, deletable.PurgableManagerMixin): model_class = model.User foreign_key_name = 'user' @@ -29,17 +29,17 @@ class UserManager( base.ModelManager, deletable.PurgableManagerMixin ): # TODO: incorp CreatesApiKeysMixin # TODO: incorporate UsesFormDefinitionsMixin? - def create( self, webapp_name=None, **kwargs ): + def create(self, webapp_name=None, **kwargs): """ Create a new user. """ # TODO: deserialize and validate here - email = kwargs[ 'email' ] - username = kwargs[ 'username' ] - password = kwargs[ 'password' ] - self._error_on_duplicate_email( email ) + email = kwargs['email'] + username = kwargs['username'] + password = kwargs['password'] + self._error_on_duplicate_email(email) - user = model.User( email=email, password=password ) + user = model.User(email=email, password=password) user.username = username if self.app.config.user_activation_on: @@ -48,20 +48,20 @@ def create( self, webapp_name=None, **kwargs ): # Activation is off, every new user is active by default. user.active = True - self.session().add( user ) + self.session().add(user) try: self.session().flush() # TODO:?? flush needed for permissions below? If not, make optional except sqlalchemy.exc.IntegrityError as db_err: - raise exceptions.Conflict( db_err.message ) + raise exceptions.Conflict(db_err.message) # can throw an sqlalx.IntegrityError if username not unique - self.app.security_agent.create_private_user_role( user ) + self.app.security_agent.create_private_user_role(user) if webapp_name == 'galaxy': # We set default user permissions, before we log in and set the default history permissions permissions = self.app.config.new_user_dataset_access_role_default_private - self.app.security_agent.user_set_default_permissions( user, default_access_private=permissions ) + self.app.security_agent.user_set_default_permissions(user, default_access_private=permissions) return user def delete(self, user): @@ -69,160 +69,160 @@ def delete(self, user): self.session().add(user) self.session().flush() - def _error_on_duplicate_email( self, email ): + def _error_on_duplicate_email(self, email): """ Check for a duplicate email and raise if found. :raises exceptions.Conflict: if any are found """ # TODO: remove this check when unique=True is added to the email column - if self.by_email( email ) is not None: - raise exceptions.Conflict( 'Email must be unique', email=email ) + if self.by_email(email) is not None: + raise exceptions.Conflict('Email must be unique', email=email) # ---- filters - def by_email( self, email, filters=None, **kwargs ): + def by_email(self, email, filters=None, **kwargs): """ Find a user by their email. """ - filters = self._munge_filters( self.model_class.email == email, filters ) + filters = self._munge_filters(self.model_class.email == email, filters) try: # TODO: use one_or_none - return super( UserManager, self ).one( filters=filters, **kwargs ) + return super(UserManager, self).one(filters=filters, **kwargs) except exceptions.ObjectNotFound: return None - def by_email_like( self, email_with_wildcards, filters=None, order_by=None, **kwargs ): + def by_email_like(self, email_with_wildcards, filters=None, order_by=None, **kwargs): """ Find a user searching with SQL wildcards. """ - filters = self._munge_filters( self.model_class.email.like( email_with_wildcards ), filters ) - order_by = order_by or ( model.User.email, ) - return super( UserManager, self ).list( filters=filters, order_by=order_by, **kwargs ) + filters = self._munge_filters(self.model_class.email.like(email_with_wildcards), filters) + order_by = order_by or (model.User.email, ) + return super(UserManager, self).list(filters=filters, order_by=order_by, **kwargs) # ---- admin - def is_admin( self, user ): + def is_admin(self, user): """ Return True if this user is an admin. """ admin_emails = self._admin_emails() return user and admin_emails and user.email in admin_emails - def _admin_emails( self ): + def _admin_emails(self): """ Return a list of admin email addresses from the config file. """ - return [ email.strip() for email in self.app.config.get( "admin_users", "" ).split( "," ) ] + return [email.strip() for email in self.app.config.get("admin_users", "").split(",")] - def admins( self, filters=None, **kwargs ): + def admins(self, filters=None, **kwargs): """ Return a list of admin Users. """ - filters = self._munge_filters( self.model_class.email.in_( self._admin_emails() ), filters ) - return super( UserManager, self ).list( filters=filters, **kwargs ) + filters = self._munge_filters(self.model_class.email.in_(self._admin_emails()), filters) + return super(UserManager, self).list(filters=filters, **kwargs) - def error_unless_admin( self, user, msg="Administrators only", **kwargs ): + def error_unless_admin(self, user, msg="Administrators only", **kwargs): """ Raise an error if `user` is not an admin. :raises exceptions.AdminRequiredException: if `user` is not an admin. """ # useful in admin only methods - if not self.is_admin( user ): - raise exceptions.AdminRequiredException( msg, **kwargs ) + if not self.is_admin(user): + raise exceptions.AdminRequiredException(msg, **kwargs) return user # ---- anonymous - def is_anonymous( self, user ): + def is_anonymous(self, user): """ Return True if `user` is anonymous. """ # define here for single point of change and make more readable return user is None - def error_if_anonymous( self, user, msg="Log-in required", **kwargs ): + def error_if_anonymous(self, user, msg="Log-in required", **kwargs): """ Raise an error if `user` is anonymous. """ if user is None: # TODO: code is correct (401) but should be named AuthenticationRequired (401 and 403 are flipped) - raise exceptions.AuthenticationFailed( msg, **kwargs ) + raise exceptions.AuthenticationFailed(msg, **kwargs) return user # ---- current - def current_user( self, trans ): + def current_user(self, trans): # define here for single point of change and make more readable # TODO: trans return trans.user # ---- api keys - def create_api_key( self, user ): + def create_api_key(self, user): """ Create and return an API key for `user`. """ # TODO: seems like this should return the model - return api_keys.ApiKeyManager( self.app ).create_api_key( user ) + return api_keys.ApiKeyManager(self.app).create_api_key(user) # TODO: possibly move to ApiKeyManager - def valid_api_key( self, user ): + def valid_api_key(self, user): """ Return this most recent APIKey for this user or None if none have been created. """ - query = ( self.session().query( model.APIKeys ) - .filter_by( user=user ) - .order_by( sqlalchemy.desc( model.APIKeys.create_time ) ) ) + query = (self.session().query(model.APIKeys) + .filter_by(user=user) + .order_by(sqlalchemy.desc(model.APIKeys.create_time))) all = query.all() - if len( all ): + if len(all): return all[0] return None # TODO: possibly move to ApiKeyManager - def get_or_create_valid_api_key( self, user ): + def get_or_create_valid_api_key(self, user): """ Return this most recent APIKey for this user or create one if none have been created. """ - existing = self.valid_api_key( user ) + existing = self.valid_api_key(user) if existing: return existing - return self.create_api_key( self, user ) + return self.create_api_key(self, user) # ---- preferences - def preferences( self, user ): - return dict( (key, value) for key, value in user.preferences.items() ) + def preferences(self, user): + return dict((key, value) for key, value in user.preferences.items()) # ---- roles and permissions - def private_role( self, user ): - return self.app.security_agent.get_private_user_role( user ) + def private_role(self, user): + return self.app.security_agent.get_private_user_role(user) - def sharing_roles( self, user ): - return self.app.security_agent.get_sharing_roles( user ) + def sharing_roles(self, user): + return self.app.security_agent.get_sharing_roles(user) - def default_permissions( self, user ): - return self.app.security_agent.user_get_default_permissions( user ) + def default_permissions(self, user): + return self.app.security_agent.user_get_default_permissions(user) - def quota( self, user, total=False ): + def quota(self, user, total=False): if total: - return self.app.quota_agent.get_quota( user, nice_size=True ) - return self.app.quota_agent.get_percent( user=user ) + return self.app.quota_agent.get_quota(user, nice_size=True) + return self.app.quota_agent.get_percent(user=user) - def tags_used( self, user, tag_models=None ): + def tags_used(self, user, tag_models=None): """ Return a list of distinct 'user_tname:user_value' strings that the given user has used. """ # TODO: simplify and unify with tag manager - if self.is_anonymous( user ): + if self.is_anonymous(user): return [] # get all the taggable model TagAssociations if not tag_models: - tag_models = [ v.tag_assoc_class for v in self.app.tag_handler.item_tag_assoc_info.values() ] + tag_models = [v.tag_assoc_class for v in self.app.tag_handler.item_tag_assoc_info.values()] # create a union of subqueries for each for this user - getting only the tname and user_value all_tags_query = None for tag_model in tag_models: - subq = ( self.session().query( tag_model.user_tname, tag_model.user_value ) - .filter( tag_model.user == user ) ) - all_tags_query = subq if all_tags_query is None else all_tags_query.union( subq ) + subq = (self.session().query(tag_model.user_tname, tag_model.user_value) + .filter(tag_model.user == user)) + all_tags_query = subq if all_tags_query is None else all_tags_query.union(subq) # if nothing init'd the query, bail if all_tags_query is None: @@ -230,33 +230,33 @@ def tags_used( self, user, tag_models=None ): # boil the tag tuples down into a sorted list of DISTINCT name:val strings tags = all_tags_query.distinct().all() - tags = [( ( name + ':' + val ) if val else name ) for name, val in tags ] - return sorted( tags ) + tags = [((name + ':' + val) if val else name) for name, val in tags] + return sorted(tags) - def has_requests( self, user, trans ): + def has_requests(self, user, trans): """ """ - if self.is_anonymous( user ): + if self.is_anonymous(user): return False - request_types = self.app.security_agent.get_accessible_request_types( trans, user ) - return bool( user.requests or request_types ) + request_types = self.app.security_agent.get_accessible_request_types(trans, user) + return bool(user.requests or request_types) -class UserSerializer( base.ModelSerializer, deletable.PurgableSerializerMixin ): +class UserSerializer(base.ModelSerializer, deletable.PurgableSerializerMixin): model_manager_class = UserManager - def __init__( self, app ): + def __init__(self, app): """ Convert a User and associated data to a dictionary representation. """ - super( UserSerializer, self ).__init__( app ) + super(UserSerializer, self).__init__(app) self.user_manager = self.manager self.default_view = 'summary' - self.add_view( 'summary', [ + self.add_view('summary', [ 'id', 'email', 'username' ]) - self.add_view( 'detailed', [ + self.add_view('detailed', [ # 'update_time', # 'create_time', 'is_admin', @@ -273,64 +273,64 @@ def __init__( self, app ): 'tags_used', # all annotations # 'annotations' - ], include_keys_from='summary' ) + ], include_keys_from='summary') - def add_serializers( self ): - super( UserSerializer, self ).add_serializers() - deletable.PurgableSerializerMixin.add_serializers( self ) + def add_serializers(self): + super(UserSerializer, self).add_serializers() + deletable.PurgableSerializerMixin.add_serializers(self) self.serializers.update({ 'id' : self.serialize_id, 'create_time' : self.serialize_date, 'update_time' : self.serialize_date, - 'is_admin' : lambda i, k, **c: self.user_manager.is_admin( i ), + 'is_admin' : lambda i, k, **c: self.user_manager.is_admin(i), - 'preferences' : lambda i, k, **c: self.user_manager.preferences( i ), + 'preferences' : lambda i, k, **c: self.user_manager.preferences(i), - 'total_disk_usage' : lambda i, k, **c: float( i.total_disk_usage ), - 'quota_percent' : lambda i, k, **c: self.user_manager.quota( i ), - 'quota' : lambda i, k, **c: self.user_manager.quota( i, total=True ), + 'total_disk_usage' : lambda i, k, **c: float(i.total_disk_usage), + 'quota_percent' : lambda i, k, **c: self.user_manager.quota(i), + 'quota' : lambda i, k, **c: self.user_manager.quota(i, total=True), - 'tags_used' : lambda i, k, **c: self.user_manager.tags_used( i ), - 'has_requests' : lambda i, k, trans=None, **c: self.user_manager.has_requests( i, trans ) + 'tags_used' : lambda i, k, **c: self.user_manager.tags_used(i), + 'has_requests' : lambda i, k, trans=None, **c: self.user_manager.has_requests(i, trans) }) -class UserDeserializer( base.ModelDeserializer ): +class UserDeserializer(base.ModelDeserializer): """ Service object for validating and deserializing dictionaries that update/alter users. """ model_manager_class = UserManager - def add_deserializers( self ): - super( UserDeserializer, self ).add_deserializers() + def add_deserializers(self): + super(UserDeserializer, self).add_deserializers() self.deserializers.update({ 'username' : self.deserialize_username, }) - def deserialize_username( self, item, key, username, trans=None, **context ): + def deserialize_username(self, item, key, username, trans=None, **context): # TODO: validate_user_input requires trans and should(?) raise exceptions # move validation to UserValidator and use self.app, exceptions instead - validation_error = validate_user_input.validate_publicname( trans, username, user=item ) + validation_error = validate_user_input.validate_publicname(trans, username, user=item) if validation_error: - raise base.ModelDeserializingError( validation_error ) - return self.default_deserializer( item, key, username, trans=trans, **context ) + raise base.ModelDeserializingError(validation_error) + return self.default_deserializer(item, key, username, trans=trans, **context) -class CurrentUserSerializer( UserSerializer ): +class CurrentUserSerializer(UserSerializer): model_manager_class = UserManager - def serialize( self, user, keys, **kwargs ): + def serialize(self, user, keys, **kwargs): """ Override to return at least some usage info if user is anonymous. """ - kwargs[ 'current_user' ] = user - if self.user_manager.is_anonymous( user ): - return self.serialize_current_anonymous_user( user, keys, **kwargs ) - return super( UserSerializer, self ).serialize( user, keys, **kwargs ) + kwargs['current_user'] = user + if self.user_manager.is_anonymous(user): + return self.serialize_current_anonymous_user(user, keys, **kwargs) + return super(UserSerializer, self).serialize(user, keys, **kwargs) - def serialize_current_anonymous_user( self, user, keys, trans=None, **kwargs ): + def serialize_current_anonymous_user(self, user, keys, trans=None, **kwargs): # use the current history if any to get usage stats for trans' anonymous user # TODO: might be better as sep. Serializer class usage = 0 @@ -338,37 +338,37 @@ def serialize_current_anonymous_user( self, user, keys, trans=None, **kwargs ): history = trans.history if history: - usage = self.app.quota_agent.get_usage( trans, history=trans.history ) - percent = self.app.quota_agent.get_percent( trans=trans, usage=usage ) + usage = self.app.quota_agent.get_usage(trans, history=trans.history) + percent = self.app.quota_agent.get_percent(trans=trans, usage=usage) # a very small subset of keys available values = { 'id' : None, - 'total_disk_usage' : float( usage ), - 'nice_total_disk_usage' : util.nice_size( usage ), + 'total_disk_usage' : float(usage), + 'nice_total_disk_usage' : util.nice_size(usage), 'quota_percent' : percent, } serialized = {} for key in keys: if key in values: - serialized[ key ] = values[ key ] + serialized[key] = values[key] return serialized -class AdminUserFilterParser( base.ModelFilterParser, deletable.PurgableFiltersMixin ): +class AdminUserFilterParser(base.ModelFilterParser, deletable.PurgableFiltersMixin): model_manager_class = UserManager model_class = model.User - def _add_parsers( self ): - super( AdminUserFilterParser, self )._add_parsers() - deletable.PurgableFiltersMixin._add_parsers( self ) + def _add_parsers(self): + super(AdminUserFilterParser, self)._add_parsers() + deletable.PurgableFiltersMixin._add_parsers(self) # PRECONDITION: user making the query has been verified as an admin self.orm_filter_parsers.update({ - 'email' : { 'op': ( 'eq', 'contains', 'like' ) }, - 'username' : { 'op': ( 'eq', 'contains', 'like' ) }, - 'active' : { 'op': ( 'eq' ) }, - 'disk_usage' : { 'op': ( 'le', 'ge' ) } + 'email' : {'op': ('eq', 'contains', 'like')}, + 'username' : {'op': ('eq', 'contains', 'like')}, + 'active' : {'op': ('eq')}, + 'disk_usage' : {'op': ('le', 'ge')} }) self.fn_filter_parsers.update({ diff --git a/lib/galaxy/managers/visualizations.py b/lib/galaxy/managers/visualizations.py index 34368fb9186e..cf99f515e146 100644 --- a/lib/galaxy/managers/visualizations.py +++ b/lib/galaxy/managers/visualizations.py @@ -9,10 +9,10 @@ from galaxy.managers import sharable import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class VisualizationManager( sharable.SharableModelManager ): +class VisualizationManager(sharable.SharableModelManager): """ Handle operations outside and between visualizations and other models. """ @@ -27,10 +27,10 @@ class VisualizationManager( sharable.SharableModelManager ): annotation_assoc = model.VisualizationAnnotationAssociation rating_assoc = model.VisualizationRatingAssociation - def __init__( self, app, *args, **kwargs ): + def __init__(self, app, *args, **kwargs): """ """ - super( VisualizationManager, self ).__init__( app, *args, **kwargs ) + super(VisualizationManager, self).__init__(app, *args, **kwargs) # def copy( self, trans, visualization, user, **kwargs ): # """ @@ -38,40 +38,40 @@ def __init__( self, app, *args, **kwargs ): # pass -class VisualizationSerializer( sharable.SharableModelSerializer ): +class VisualizationSerializer(sharable.SharableModelSerializer): """ Interface/service object for serializing visualizations into dictionaries. """ model_manager_class = VisualizationManager SINGLE_CHAR_ABBR = 'v' - def __init__( self, app ): - super( VisualizationSerializer, self ).__init__( app ) + def __init__(self, app): + super(VisualizationSerializer, self).__init__(app) self.visualization_manager = self.manager self.default_view = 'summary' - self.add_view( 'summary', [] ) - self.add_view( 'detailed', [] ) + self.add_view('summary', []) + self.add_view('detailed', []) - def add_serializers( self ): - super( VisualizationSerializer, self ).add_serializers() + def add_serializers(self): + super(VisualizationSerializer, self).add_serializers() self.serializers.update({ }) -class VisualizationDeserializer( sharable.SharableModelDeserializer ): +class VisualizationDeserializer(sharable.SharableModelDeserializer): """ Interface/service object for validating and deserializing dictionaries into visualizations. """ model_manager_class = VisualizationManager - def __init__( self, app ): - super( VisualizationDeserializer, self ).__init__( app ) + def __init__(self, app): + super(VisualizationDeserializer, self).__init__(app) self.visualization_manager = self.manager - def add_deserializers( self ): - super( VisualizationDeserializer, self ).add_deserializers() + def add_deserializers(self): + super(VisualizationDeserializer, self).add_deserializers() self.deserializers.update({ }) - self.deserializable_keyset.update( self.deserializers.keys() ) + self.deserializable_keyset.update(self.deserializers.keys()) diff --git a/lib/galaxy/managers/workflows.py b/lib/galaxy/managers/workflows.py index 0339ec882aec..10349db63d0d 100644 --- a/lib/galaxy/managers/workflows.py +++ b/lib/galaxy/managers/workflows.py @@ -26,77 +26,82 @@ from galaxy.jobs.actions.post import ActionBox from galaxy.web import url_for -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class WorkflowsManager( object ): +class WorkflowsManager(object): """ Handle CRUD type operaitons related to workflows. More interesting stuff regarding workflow execution, step sorting, etc... can be found in the galaxy.workflow module. """ - def __init__( self, app ): + def __init__(self, app): self.app = app - def get_stored_workflow( self, trans, workflow_id ): + def get_stored_workflow(self, trans, workflow_id): """ Use a supplied ID (UUID or encoded stored workflow ID) to find a workflow. """ if util.is_uuid(workflow_id): # see if they have passed in the UUID for a workflow that is attached to a stored workflow workflow_uuid = uuid.UUID(workflow_id) - stored_workflow = trans.sa_session.query(trans.app.model.StoredWorkflow).filter( and_( + stored_workflow = trans.sa_session.query(trans.app.model.StoredWorkflow).filter(and_( trans.app.model.StoredWorkflow.latest_workflow_id == trans.app.model.Workflow.id, trans.app.model.Workflow.uuid == workflow_uuid )).first() if stored_workflow is None: - raise exceptions.ObjectNotFound( "Workflow not found: %s" % workflow_id ) + raise exceptions.ObjectNotFound("Workflow not found: %s" % workflow_id) else: - workflow_id = decode_id( self.app, workflow_id ) - query = trans.sa_session.query( trans.app.model.StoredWorkflow ) - stored_workflow = query.get( workflow_id ) + workflow_id = decode_id(self.app, workflow_id) + query = trans.sa_session.query(trans.app.model.StoredWorkflow) + stored_workflow = query.get(workflow_id) if stored_workflow is None: - raise exceptions.ObjectNotFound( "No such workflow found." ) + raise exceptions.ObjectNotFound("No such workflow found.") return stored_workflow - def get_stored_accessible_workflow( self, trans, workflow_id ): + def get_stored_accessible_workflow(self, trans, workflow_id): """ Get a stored workflow from a encoded stored workflow id and make sure it accessible to the user. """ - stored_workflow = self.get_stored_workflow( trans, workflow_id ) + stored_workflow = self.get_stored_workflow(trans, workflow_id) # check to see if user has permissions to selected workflow - if stored_workflow.user != trans.user and not trans.user_is_admin(): + if stored_workflow.user != trans.user and not trans.user_is_admin() and not stored_workflow.published: if trans.sa_session.query(trans.app.model.StoredWorkflowUserShareAssociation).filter_by(user=trans.user, stored_workflow=stored_workflow).count() == 0: message = "Workflow is not owned by or shared with current user" - raise exceptions.ItemAccessibilityException( message ) + raise exceptions.ItemAccessibilityException(message) return stored_workflow - def get_owned_workflow( self, trans, encoded_workflow_id ): + def get_owned_workflow(self, trans, encoded_workflow_id): """ Get a workflow (non-stored) from a encoded workflow id and make sure it accessible to the user. """ - workflow_id = decode_id( self.app, encoded_workflow_id ) - workflow = trans.sa_session.query( model.Workflow ).get( workflow_id ) - self.check_security( trans, workflow, check_ownership=True ) + workflow_id = decode_id(self.app, encoded_workflow_id) + workflow = trans.sa_session.query(model.Workflow).get(workflow_id) + self.check_security(trans, workflow, check_ownership=True) return workflow - def check_security( self, trans, has_workflow, check_ownership=True, check_accessible=True): + def check_security(self, trans, has_workflow, check_ownership=True, check_accessible=True): """ check accessibility or ownership of workflows, storedworkflows, and workflowinvocations. Throw an exception or returns True if user has needed level of access. """ - if not check_ownership or check_accessible: + if not check_ownership and not check_accessible: return True - # If given an invocation follow to workflow... - if isinstance( has_workflow, model.WorkflowInvocation ): - has_workflow = has_workflow.workflow + # If given an invocation verify ownership of invocation + if isinstance(has_workflow, model.WorkflowInvocation): + # We use the the owner of the history that is associated to the invocation as a proxy + # for the owner of the invocation. + if trans.user != has_workflow.history.user and not trans.user_is_admin(): + raise exceptions.ItemOwnershipException() + else: + return True # stored workflow contains security stuff - follow that workflow to # that unless given a stored workflow. - if isinstance( has_workflow, model.Workflow ): + if isinstance(has_workflow, model.Workflow): stored_workflow = has_workflow.top_level_stored_workflow else: stored_workflow = has_workflow @@ -105,74 +110,78 @@ def check_security( self, trans, has_workflow, check_ownership=True, check_acces if check_ownership: raise exceptions.ItemOwnershipException() # else check_accessible... - if trans.sa_session.query( model.StoredWorkflowUserShareAssociation ).filter_by(user=trans.user, stored_workflow=stored_workflow ).count() == 0: + if trans.sa_session.query(model.StoredWorkflowUserShareAssociation).filter_by(user=trans.user, stored_workflow=stored_workflow).count() == 0: raise exceptions.ItemAccessibilityException() return True - def get_invocation( self, trans, decoded_invocation_id ): - try: - workflow_invocation = trans.sa_session.query( - self.app.model.WorkflowInvocation - ).get( decoded_invocation_id ) - except Exception: - raise exceptions.ObjectNotFound() - self.check_security( trans, workflow_invocation, check_ownership=True, check_accessible=False ) + def get_invocation(self, trans, decoded_invocation_id): + workflow_invocation = trans.sa_session.query( + self.app.model.WorkflowInvocation + ).get(decoded_invocation_id) + if not workflow_invocation: + encoded_wfi_id = trans.security.encode_id(decoded_invocation_id) + message = "'%s' is not a valid workflow invocation id" % encoded_wfi_id + raise exceptions.ObjectNotFound(message) + self.check_security(trans, workflow_invocation, check_ownership=True, check_accessible=False) return workflow_invocation - def cancel_invocation( self, trans, decoded_invocation_id ): - workflow_invocation = self.get_invocation( trans, decoded_invocation_id ) + def cancel_invocation(self, trans, decoded_invocation_id): + workflow_invocation = self.get_invocation(trans, decoded_invocation_id) cancelled = workflow_invocation.cancel() if cancelled: - trans.sa_session.add( workflow_invocation ) + trans.sa_session.add(workflow_invocation) trans.sa_session.flush() else: # TODO: More specific exception? - raise exceptions.MessageException( "Cannot cancel an inactive workflow invocation." ) + raise exceptions.MessageException("Cannot cancel an inactive workflow invocation.") return workflow_invocation - def get_invocation_step( self, trans, decoded_workflow_invocation_step_id ): + def get_invocation_step(self, trans, decoded_workflow_invocation_step_id): try: workflow_invocation_step = trans.sa_session.query( model.WorkflowInvocationStep - ).get( decoded_workflow_invocation_step_id ) + ).get(decoded_workflow_invocation_step_id) except Exception: raise exceptions.ObjectNotFound() - self.check_security( trans, workflow_invocation_step.workflow_invocation, check_ownership=True, check_accessible=False ) + self.check_security(trans, workflow_invocation_step.workflow_invocation, check_ownership=True, check_accessible=False) return workflow_invocation_step - def update_invocation_step( self, trans, decoded_workflow_invocation_step_id, action ): + def update_invocation_step(self, trans, decoded_workflow_invocation_step_id, action): if action is None: - raise exceptions.RequestParameterMissingException( "Updating workflow invocation step requires an action parameter. " ) + raise exceptions.RequestParameterMissingException("Updating workflow invocation step requires an action parameter. ") - workflow_invocation_step = self.get_invocation_step( trans, decoded_workflow_invocation_step_id ) + workflow_invocation_step = self.get_invocation_step(trans, decoded_workflow_invocation_step_id) workflow_invocation = workflow_invocation_step.workflow_invocation if not workflow_invocation.active: - raise exceptions.RequestParameterInvalidException( "Attempting to modify the state of an completed workflow invocation." ) + raise exceptions.RequestParameterInvalidException("Attempting to modify the state of an completed workflow invocation.") step = workflow_invocation_step.workflow_step - module = modules.module_factory.from_workflow_step( trans, step ) - performed_action = module.do_invocation_step_action( step, action ) + module = modules.module_factory.from_workflow_step(trans, step) + performed_action = module.do_invocation_step_action(step, action) workflow_invocation_step.action = performed_action - trans.sa_session.add( workflow_invocation_step ) + trans.sa_session.add(workflow_invocation_step) trans.sa_session.flush() return workflow_invocation_step - def build_invocations_query( self, trans, decoded_stored_workflow_id ): - try: - stored_workflow = trans.sa_session.query( - self.app.model.StoredWorkflow - ).get( decoded_stored_workflow_id ) - except Exception: + def build_invocations_query(self, trans, decoded_stored_workflow_id): + """Get invocations owned by the current user.""" + stored_workflow = trans.sa_session.query( + self.app.model.StoredWorkflow + ).get(decoded_stored_workflow_id) + if not stored_workflow: raise exceptions.ObjectNotFound() - self.check_security( trans, stored_workflow, check_ownership=True, check_accessible=False ) - return trans.sa_session.query( + invocations = trans.sa_session.query( model.WorkflowInvocation ).filter_by( workflow_id=stored_workflow.latest_workflow_id ) + return [inv for inv in invocations if self.check_security(trans, + inv, + check_ownership=True, + check_accessible=False)] CreatedWorkflow = namedtuple("CreatedWorkflow", ["stored_workflow", "workflow", "missing_tools"]) @@ -196,8 +205,8 @@ def build_workflow_from_dict( # Put parameters in workflow mode trans.workflow_building_mode = True # If there's a source, put it in the workflow name. - if source: - name = "%s (imported from %s)" % ( data['name'], source ) + if source and source != 'API': + name = "%s (imported from %s)" % (data['name'], source) else: name = data['name'] workflow, missing_tool_tups = self._workflow_from_dict( @@ -217,24 +226,24 @@ def build_workflow_from_dict( stored.latest_workflow = workflow stored.user = trans.user stored.published = publish - if data[ 'annotation' ]: - annotation = sanitize_html( data[ 'annotation' ], 'utf-8', 'text/html' ) - self.add_item_annotation( trans.sa_session, stored.user, stored, annotation ) + if data['annotation']: + annotation = sanitize_html(data['annotation'], 'utf-8', 'text/html') + self.add_item_annotation(trans.sa_session, stored.user, stored, annotation) # Persist - trans.sa_session.add( stored ) + trans.sa_session.add(stored) if add_to_menu: if trans.user.stored_workflow_menu_entries is None: trans.user.stored_workflow_menu_entries = [] menuEntry = model.StoredWorkflowMenuEntry() menuEntry.stored_workflow = stored - trans.user.stored_workflow_menu_entries.append( menuEntry ) + trans.user.stored_workflow_menu_entries.append(menuEntry) else: stored = None # Persist - trans.sa_session.add( workflow ) + trans.sa_session.add(workflow) trans.sa_session.flush() @@ -268,9 +277,9 @@ def update_workflow_from_dict(self, trans, stored_workflow, workflow_data): # Return something informative errors = [] if workflow.has_errors: - errors.append( "Some steps in this workflow have validation errors" ) + errors.append("Some steps in this workflow have validation errors") if workflow.has_cycles: - errors.append( "This workflow contains cycles" ) + errors.append("This workflow contains cycles") return workflow, errors def _workflow_from_dict(self, trans, data, name, **kwds): @@ -293,25 +302,28 @@ def _workflow_from_dict(self, trans, data, name, **kwds): # the local Galaxy instance. Each tuple in the list of missing_tool_tups # will be ( tool_id, tool_name, tool_version ). missing_tool_tups = [] - for step_dict in self.__walk_step_dicts( data ): - module, step = self.__module_from_dict( trans, steps, steps_by_external_id, step_dict, **kwds ) - is_tool = is_tool_module_type( module.type ) + for step_dict in self.__walk_step_dicts(data): + self.__load_subworkflows(trans, step_dict) + + for step_dict in self.__walk_step_dicts(data): + module, step = self.__module_from_dict(trans, steps, steps_by_external_id, step_dict, **kwds) + is_tool = is_tool_module_type(module.type) if is_tool and module.tool is None: - missing_tool_tup = ( module.tool_id, module.get_name(), module.tool_version, step_dict[ 'id' ] ) + missing_tool_tup = (module.tool_id, module.get_name(), module.tool_version, step_dict['id']) if missing_tool_tup not in missing_tool_tups: - missing_tool_tups.append( missing_tool_tup ) + missing_tool_tups.append(missing_tool_tup) if module.get_errors(): workflow.has_errors = True # Second pass to deal with connections between steps - self.__connect_workflow_steps( steps, steps_by_external_id ) + self.__connect_workflow_steps(steps, steps_by_external_id) # Order the steps if possible - attach_ordered_steps( workflow, steps ) + attach_ordered_steps(workflow, steps) return workflow, missing_tool_tups - def workflow_to_dict( self, trans, stored, style="export" ): + def workflow_to_dict(self, trans, stored, style="export"): """ Export the workflow contents to a dictionary ready for JSON-ification and to be sent out via API for instance. There are three styles of export allowed 'export', 'instance', and 'editor'. The Galaxy team will do it best to preserve the backward compatibility of the @@ -321,91 +333,91 @@ def workflow_to_dict( self, trans, stored, style="export" ): fields like 'url' and 'url' and actual unencoded step ids instead of 'order_index'. """ if style == "editor": - return self._workflow_to_dict_editor( trans, stored ) + return self._workflow_to_dict_editor(trans, stored) elif style == "legacy": - return self._workflow_to_dict_instance( stored, legacy=True ) + return self._workflow_to_dict_instance(stored, legacy=True) elif style == "instance": - return self._workflow_to_dict_instance( stored, legacy=False ) + return self._workflow_to_dict_instance(stored, legacy=False) elif style == "run": - return self._workflow_to_dict_run( trans, stored ) + return self._workflow_to_dict_run(trans, stored) else: - return self._workflow_to_dict_export( trans, stored ) + return self._workflow_to_dict_export(trans, stored) - def _workflow_to_dict_run( self, trans, stored ): + def _workflow_to_dict_run(self, trans, stored): """ Builds workflow dictionary used by run workflow form """ workflow = stored.latest_workflow - if len( workflow.steps ) == 0: - raise exceptions.MessageException( 'Workflow cannot be run because it does not have any steps.' ) - if attach_ordered_steps( workflow, workflow.steps ): - raise exceptions.MessageException( 'Workflow cannot be run because it contains cycles.' ) + if len(workflow.steps) == 0: + raise exceptions.MessageException('Workflow cannot be run because it does not have any steps.') + if attach_ordered_steps(workflow, workflow.steps): + raise exceptions.MessageException('Workflow cannot be run because it contains cycles.') trans.workflow_building_mode = workflow_building_modes.USE_HISTORY - module_injector = WorkflowModuleInjector( trans ) + module_injector = WorkflowModuleInjector(trans) has_upgrade_messages = False step_version_changes = [] missing_tools = [] errors = {} for step in workflow.steps: try: - module_injector.inject( step, steps=workflow.steps ) + module_injector.inject(step, steps=workflow.steps) except exceptions.ToolMissingException: if step.tool_id not in missing_tools: - missing_tools.append( step.tool_id ) + missing_tools.append(step.tool_id) continue if step.upgrade_messages: has_upgrade_messages = True if step.type == 'tool' or step.type is None: if step.module.version_changes: - step_version_changes.extend( step.module.version_changes ) + step_version_changes.extend(step.module.version_changes) step_errors = step.module.get_errors() if step_errors: - errors[ step.id ] = step_errors + errors[step.id] = step_errors if missing_tools: - workflow.annotation = self.get_item_annotation_str( trans.sa_session, trans.user, workflow ) - raise exceptions.MessageException( 'Following tools missing: %s' % missing_tools ) - workflow.annotation = self.get_item_annotation_str( trans.sa_session, trans.user, workflow ) + workflow.annotation = self.get_item_annotation_str(trans.sa_session, trans.user, workflow) + raise exceptions.MessageException('Following tools missing: %s' % missing_tools) + workflow.annotation = self.get_item_annotation_str(trans.sa_session, trans.user, workflow) step_order_indices = {} for step in workflow.steps: - step_order_indices[ step.id ] = step.order_index + step_order_indices[step.id] = step.order_index step_models = [] - for i, step in enumerate( workflow.steps ): + for i, step in enumerate(workflow.steps): step_model = None if step.type == 'tool': incoming = {} - tool = trans.app.toolbox.get_tool( step.tool_id ) - params_to_incoming( incoming, tool.inputs, step.state.inputs, trans.app ) - step_model = tool.to_json( trans, incoming, workflow_building_mode=workflow_building_modes.USE_HISTORY ) - step_model[ 'post_job_actions' ] = [{ - 'short_str' : ActionBox.get_short_str( pja ), + tool = trans.app.toolbox.get_tool(step.tool_id, tool_version=step.tool_version) + params_to_incoming(incoming, tool.inputs, step.state.inputs, trans.app) + step_model = tool.to_json(trans, incoming, workflow_building_mode=workflow_building_modes.USE_HISTORY) + step_model['post_job_actions'] = [{ + 'short_str' : ActionBox.get_short_str(pja), 'action_type' : pja.action_type, 'output_name' : pja.output_name, 'action_arguments' : pja.action_arguments - } for pja in step.post_job_actions ] + } for pja in step.post_job_actions] else: - inputs = step.module.get_runtime_inputs( connections=step.output_connections ) + inputs = step.module.get_runtime_inputs(connections=step.output_connections) step_model = { - 'inputs' : [ input.to_dict( trans ) for input in inputs.itervalues() ] + 'inputs' : [input.to_dict(trans) for input in inputs.itervalues()] } - step_model[ 'step_type' ] = step.type - step_model[ 'step_label' ] = step.label - step_model[ 'step_name' ] = step.module.get_name() - step_model[ 'step_version' ] = step.module.get_version() - step_model[ 'step_index' ] = step.order_index - step_model[ 'output_connections' ] = [ { - 'input_step_index' : step_order_indices.get( oc.input_step_id ), - 'output_step_index' : step_order_indices.get( oc.output_step_id ), + step_model['step_type'] = step.type + step_model['step_label'] = step.label + step_model['step_name'] = step.module.get_name() + step_model['step_version'] = step.module.get_version() + step_model['step_index'] = step.order_index + step_model['output_connections'] = [{ + 'input_step_index' : step_order_indices.get(oc.input_step_id), + 'output_step_index' : step_order_indices.get(oc.output_step_id), 'input_name' : oc.input_name, 'output_name' : oc.output_name - } for oc in step.output_connections ] + } for oc in step.output_connections] if step.annotations: - step_model[ 'annotation' ] = step.annotations[ 0 ].annotation + step_model['annotation'] = step.annotations[0].annotation if step.upgrade_messages: - step_model[ 'messages' ] = step.upgrade_messages - step_models.append( step_model ) + step_model['messages'] = step.upgrade_messages + step_models.append(step_model) return { - 'id' : trans.app.security.encode_id( stored.id ), - 'history_id' : trans.app.security.encode_id( trans.history.id ) if trans.history else None, + 'id' : trans.app.security.encode_id(stored.id), + 'history_id' : trans.app.security.encode_id(trans.history.id) if trans.history else None, 'name' : stored.name, 'steps' : step_models, 'step_version_changes' : step_version_changes, @@ -422,11 +434,11 @@ def _workflow_to_dict_editor(self, trans, stored): # For each step, rebuild the form and encode the state for step in workflow.steps: # Load from database representation - module = module_factory.from_workflow_step( trans, step ) + module = module_factory.from_workflow_step(trans, step) if not module: - raise exceptions.MessageException( 'Unrecognized step type: %s' % step.type ) + raise exceptions.MessageException('Unrecognized step type: %s' % step.type) # Load label from state of data input modules, necessary for backward compatibility - self.__set_default_label( step, module, step.tool_inputs ) + self.__set_default_label(step, module, step.tool_inputs) # Fix any missing parameters upgrade_message = module.check_and_update_state() if upgrade_message: @@ -437,7 +449,7 @@ def _workflow_to_dict_editor(self, trans, stored): else: data['upgrade_messages'][step.order_index] = {module.tool.name: "\n".join(module.version_changes)} # Get user annotation. - step_annotation = self.get_item_annotation_obj( trans.sa_session, trans.user, step ) + step_annotation = self.get_item_annotation_obj(trans.sa_session, trans.user, step) annotation_str = "" if step_annotation: annotation_str = step_annotation.annotation @@ -454,7 +466,7 @@ def _workflow_to_dict_editor(self, trans, stored): 'content_id': module.get_content_id(), 'name': module.get_name(), 'tool_state': module.get_state(), - 'tooltip': module.get_tooltip( static_path=url_for( '/static' ) ), + 'tooltip': module.get_tooltip(static_path=url_for('/static')), 'errors': module.get_errors(), 'data_inputs': module.get_data_inputs(), 'data_outputs': module.get_data_outputs(), @@ -468,22 +480,22 @@ def _workflow_to_dict_editor(self, trans, stored): input_connections = step.input_connections input_connections_type = {} multiple_input = {} # Boolean value indicating if this can be mutliple - if ( step.type is None or step.type == 'tool' ) and module.tool: + if (step.type is None or step.type == 'tool') and module.tool: # Determine full (prefixed) names of valid input datasets data_input_names = {} - def callback( input, prefixed_name, **kwargs ): - if isinstance( input, DataToolParameter ) or isinstance( input, DataCollectionToolParameter ): - data_input_names[ prefixed_name ] = True - multiple_input[ prefixed_name ] = input.multiple - if isinstance( input, DataToolParameter ): - input_connections_type[ input.name ] = "dataset" - if isinstance( input, DataCollectionToolParameter ): - input_connections_type[ input.name ] = "dataset_collection" - visit_input_values( module.tool.inputs, module.state.inputs, callback ) + def callback(input, prefixed_name, **kwargs): + if isinstance(input, DataToolParameter) or isinstance(input, DataCollectionToolParameter): + data_input_names[prefixed_name] = True + multiple_input[prefixed_name] = input.multiple + if isinstance(input, DataToolParameter): + input_connections_type[input.name] = "dataset" + if isinstance(input, DataCollectionToolParameter): + input_connections_type[input.name] = "dataset_collection" + visit_input_values(module.tool.inputs, module.state.inputs, callback) # Filter # FIXME: this removes connection without displaying a message currently! - input_connections = [ conn for conn in input_connections if conn.input_name in data_input_names ] + input_connections = [conn for conn in input_connections if conn.input_name in data_input_names] # post_job_actions pja_dict = {} for pja in step.post_job_actions: @@ -510,15 +522,15 @@ def callback( input, prefixed_name, **kwargs ): for conn in input_connections: input_type = "dataset" if conn.input_name in input_connections_type: - input_type = input_connections_type[ conn.input_name ] - conn_dict = dict( id=conn.output_step.order_index, output_name=conn.output_name, input_type=input_type ) + input_type = input_connections_type[conn.input_name] + conn_dict = dict(id=conn.output_step.order_index, output_name=conn.output_name, input_type=input_type) if conn.input_name in multiple_input: if conn.input_name in input_conn_dict: - input_conn_dict[ conn.input_name ].append( conn_dict ) + input_conn_dict[conn.input_name].append(conn_dict) else: - input_conn_dict[ conn.input_name ] = [ conn_dict ] + input_conn_dict[conn.input_name] = [conn_dict] else: - input_conn_dict[ conn.input_name ] = conn_dict + input_conn_dict[conn.input_name] = conn_dict step_dict['input_connections'] = input_conn_dict # Position @@ -527,7 +539,7 @@ def callback( input, prefixed_name, **kwargs ): data['steps'][step.order_index] = step_dict return data - def _workflow_to_dict_export( self, trans, stored=None, workflow=None ): + def _workflow_to_dict_export(self, trans, stored=None, workflow=None): """ Export the workflow contents to a dictionary ready for JSON-ification and export. """ if workflow is None: @@ -536,7 +548,7 @@ def _workflow_to_dict_export( self, trans, stored=None, workflow=None ): annotation_str = "" if stored is not None: - workflow_annotation = self.get_item_annotation_obj( trans.sa_session, trans.user, stored ) + workflow_annotation = self.get_item_annotation_obj(trans.sa_session, trans.user, stored) if workflow_annotation: annotation_str = workflow_annotation.annotation # Pack workflow data into a dictionary and return @@ -551,18 +563,18 @@ def _workflow_to_dict_export( self, trans, stored=None, workflow=None ): # For each step, rebuild the form and encode the state for step in workflow.steps: # Load from database representation - module = module_factory.from_workflow_step( trans, step ) + module = module_factory.from_workflow_step(trans, step) if not module: return None # Get user annotation. - step_annotation = self.get_item_annotation_obj(trans.sa_session, trans.user, step ) + step_annotation = self.get_item_annotation_obj(trans.sa_session, trans.user, step) annotation_str = "" if step_annotation: annotation_str = step_annotation.annotation content_id = module.get_content_id() # Export differences for backward compatibility if module.type == 'tool': - tool_state = module.get_state( nested=False ) + tool_state = module.get_state(nested=False) else: tool_state = module.state.inputs # Step info @@ -574,7 +586,7 @@ def _workflow_to_dict_export( self, trans, stored=None, workflow=None ): # eliminate after a few years... 'tool_version': step.tool_version, 'name': module.get_name(), - 'tool_state': json.dumps( tool_state ), + 'tool_state': json.dumps(tool_state), 'errors': module.get_errors(), 'uuid': str(step.uuid), 'label': step.label or None, @@ -595,8 +607,8 @@ def _workflow_to_dict_export( self, trans, stored=None, workflow=None ): pja_dict[pja.action_type + pja.output_name] = dict( action_type=pja.action_type, output_name=pja.output_name, - action_arguments=pja.action_arguments ) - step_dict[ 'post_job_actions' ] = pja_dict + action_arguments=pja.action_arguments) + step_dict['post_job_actions'] = pja_dict if module.type == 'subworkflow': del step_dict['content_id'] @@ -615,17 +627,17 @@ def _workflow_to_dict_export( self, trans, stored=None, workflow=None ): input_dicts = [] step_state = module.state.inputs or {} if "name" in step_state and module.type != 'tool': - name = step_state.get( "name" ) - input_dicts.append( { "name": name, "description": annotation_str } ) + name = step_state.get("name") + input_dicts.append({"name": name, "description": annotation_str}) for name, val in step_state.items(): - input_type = type( val ) + input_type = type(val) if input_type == RuntimeValue: - input_dicts.append( { "name": name, "description": "runtime parameter for tool %s" % module.get_name() } ) + input_dicts.append({"name": name, "description": "runtime parameter for tool %s" % module.get_name()}) elif input_type == dict: # Input type is described by a dict, e.g. indexed parameters. for partval in val.values(): - if type( partval ) == RuntimeValue: - input_dicts.append( { "name": name, "description": "runtime parameter for tool %s" % module.get_name() } ) + if type(partval) == RuntimeValue: + input_dicts.append({"name": name, "description": "runtime parameter for tool %s" % module.get_name()}) step_dict['inputs'] = input_dicts # User outputs @@ -641,9 +653,9 @@ def _workflow_to_dict_export( self, trans, stored=None, workflow=None ): # All step outputs step_dict['outputs'] = [] - if type( module ) is ToolModule: + if type(module) is ToolModule: for output in module.get_data_outputs(): - step_dict['outputs'].append( { 'name': output['name'], 'type': output['extensions'][0] } ) + step_dict['outputs'].append({'name': output['name'], 'type': output['extensions'][0]}) # Connections input_connections = step.input_connections @@ -651,19 +663,19 @@ def _workflow_to_dict_export( self, trans, stored=None, workflow=None ): # Determine full (prefixed) names of valid input datasets data_input_names = {} - def callback( input, prefixed_name, **kwargs ): - if isinstance( input, DataToolParameter ) or isinstance( input, DataCollectionToolParameter ): - data_input_names[ prefixed_name ] = True + def callback(input, prefixed_name, **kwargs): + if isinstance(input, DataToolParameter) or isinstance(input, DataCollectionToolParameter): + data_input_names[prefixed_name] = True # FIXME: this updates modules silently right now; messages from updates should be provided. module.check_and_update_state() - visit_input_values( module.tool.inputs, module.state.inputs, callback ) + visit_input_values(module.tool.inputs, module.state.inputs, callback) # Filter # FIXME: this removes connection without displaying a message currently! - input_connections = [ conn for conn in input_connections if (conn.input_name in data_input_names or conn.non_data_connection) ] + input_connections = [conn for conn in input_connections if (conn.input_name in data_input_names or conn.non_data_connection)] # Encode input connections as dictionary input_conn_dict = {} - unique_input_names = set( [conn.input_name for conn in input_connections] ) + unique_input_names = set([conn.input_name for conn in input_connections]) for input_name in unique_input_names: input_conn_dicts = [] for conn in input_connections: @@ -678,7 +690,7 @@ def callback( input, prefixed_name, **kwargs ): input_conn["input_subworkflow_step_id"] = subworkflow_step_id input_conn_dicts.append(input_conn) - input_conn_dict[ input_name ] = input_conn_dicts + input_conn_dict[input_name] = input_conn_dicts # Preserve backward compatability. Previously Galaxy # assumed input connections would be dictionaries not @@ -702,14 +714,14 @@ def callback( input, prefixed_name, **kwargs ): def _workflow_to_dict_instance(self, stored, legacy=True): encode = self.app.security.encode_id sa_session = self.app.model.context - item = stored.to_dict( view='element', value_mapper={ 'id': encode } ) + item = stored.to_dict(view='element', value_mapper={'id': encode}) workflow = stored.latest_workflow item['url'] = url_for('workflow', id=item['id']) item['owner'] = stored.user.username inputs = {} for step in workflow.input_steps: step_type = step.type - step_label = step.label or step.tool_inputs.get( 'name' ) + step_label = step.label or step.tool_inputs.get('name') if step_label: label = step_label elif step_type == "data_input": @@ -717,7 +729,7 @@ def _workflow_to_dict_instance(self, stored, legacy=True): elif step_type == "data_collection_input": label = "Input Dataset Collection" else: - raise ValueError( "Invalid step_type %s" % step_type ) + raise ValueError("Invalid step_type %s" % step_type) if legacy: index = step.id else: @@ -725,7 +737,7 @@ def _workflow_to_dict_instance(self, stored, legacy=True): step_uuid = str(step.uuid) if step.uuid else None inputs[index] = {'label': label, 'value': '', 'uuid': step_uuid} item['inputs'] = inputs - item['annotation'] = self.get_item_annotation_str( sa_session, stored.user, stored ) + item['annotation'] = self.get_item_annotation_str(sa_session, stored.user, stored) steps = {} steps_to_order_index = {} for step in workflow.steps: @@ -738,7 +750,7 @@ def _workflow_to_dict_instance(self, stored, legacy=True): 'type': step_type, 'tool_id': step.tool_id, 'tool_version': step.tool_version, - 'annotation': self.get_item_annotation_str( sa_session, stored.user, step ), + 'annotation': self.get_item_annotation_str(sa_session, stored.user, step), 'tool_inputs': step.tool_inputs, 'input_steps': {}} @@ -760,16 +772,16 @@ def _workflow_to_dict_instance(self, stored, legacy=True): item['steps'] = steps return item - def __walk_step_dicts( self, data ): + def __walk_step_dicts(self, data): """ Walk over the supplid step dictionaries and return them in a way designed to preserve step order when possible. """ - supplied_steps = data[ 'steps' ] + supplied_steps = data['steps'] # Try to iterate through imported workflow in such a way as to # preserve step order. step_indices = supplied_steps.keys() try: - step_indices = sorted( step_indices, key=int ) + step_indices = sorted(step_indices, key=int) except ValueError: # to defensive, were these ever or will they ever not be integers? pass @@ -782,7 +794,7 @@ def __walk_step_dicts( self, data ): # First pass to build step objects and populate basic values for step_index in step_indices: - step_dict = supplied_steps[ step_index ] + step_dict = supplied_steps[step_index] uuid = step_dict.get("uuid", None) if uuid and uuid != "None": if uuid in discovered_uuids: @@ -815,7 +827,15 @@ def __walk_step_dicts( self, data ): yield step_dict - def __module_from_dict( self, trans, steps, steps_by_external_id, step_dict, **kwds ): + def __load_subworkflows(self, trans, step_dict): + step_type = step_dict.get("type", None) + if step_type == "subworkflow": + subworkflow = self.__load_subworkflow_from_step_dict( + trans, step_dict + ) + step_dict["subworkflow"] = subworkflow + + def __module_from_dict(self, trans, steps, steps_by_external_id, step_dict, **kwds): """ Create a WorkflowStep model object and corresponding module representing type-specific functionality from the incoming dictionary. """ @@ -826,28 +846,22 @@ def __module_from_dict( self, trans, steps, steps_by_external_id, step_dict, **k step.uuid = step_dict["uuid"] if "label" in step_dict: step.label = step_dict["label"] - step_type = step_dict.get("type", None) - if step_type == "subworkflow": - subworkflow = self.__load_subworkflow_from_step_dict( - trans, step_dict - ) - step_dict["subworkflow"] = subworkflow - module = module_factory.from_dict( trans, step_dict, **kwds ) - self.__set_default_label( step, module, step_dict.get( 'tool_state' ) ) - module.save_to_step( step ) + module = module_factory.from_dict(trans, step_dict, **kwds) + self.__set_default_label(step, module, step_dict.get('tool_state')) + module.save_to_step(step) - annotation = step_dict[ 'annotation' ] + annotation = step_dict['annotation'] if annotation: - annotation = sanitize_html( annotation, 'utf-8', 'text/html' ) - self.add_item_annotation( trans.sa_session, trans.get_user(), step, annotation ) + annotation = sanitize_html(annotation, 'utf-8', 'text/html') + self.add_item_annotation(trans.sa_session, trans.get_user(), step, annotation) # Stick this in the step temporarily step.temp_input_connections = step_dict['input_connections'] # Create the model class for the step - steps.append( step ) - steps_by_external_id[ step_dict[ 'id' ] ] = step + steps.append(step) + steps_by_external_id[step_dict['id']] = step if 'workflow_outputs' in step_dict: workflow_outputs = step_dict['workflow_outputs'] found_output_names = set([]) @@ -894,7 +908,7 @@ def __load_subworkflow_from_step_dict(self, trans, step_dict): return subworkflow - def __connect_workflow_steps( self, steps, steps_by_external_id ): + def __connect_workflow_steps(self, steps, steps_by_external_id): """ Second pass to deal with connections between steps. Create workflow connection objects using externally specified ids @@ -916,7 +930,7 @@ def __connect_workflow_steps( self, steps, steps_by_external_id ): conn.input_step = step conn.input_name = input_name conn.output_name = conn_dict['output_name'] - conn.output_step = steps_by_external_id[ conn_dict['id'] ] + conn.output_step = steps_by_external_id[conn_dict['id']] input_subworkflow_step_index = conn_dict.get('input_subworkflow_step_id', None) if input_subworkflow_step_index is not None: @@ -924,14 +938,14 @@ def __connect_workflow_steps( self, steps, steps_by_external_id ): del step.temp_input_connections - def __set_default_label( self, step, module, state ): + def __set_default_label(self, step, module, state): """ Previously data input modules had a `name` attribute to rename individual steps. Here, this value is transferred to the actual `label` attribute which is available for all module types, unique, and mapped to its own database column. """ - if not module.label and module.type in [ 'data_input', 'data_collection_input' ]: - new_state = safe_loads( state ) - default_label = new_state.get( 'name' ) - if str( default_label ).lower() not in [ 'input dataset', 'input dataset collection' ]: + if not module.label and module.type in ['data_input', 'data_collection_input']: + new_state = safe_loads(state) + default_label = new_state.get('name') + if str(default_label).lower() not in ['input dataset', 'input dataset collection']: step.label = module.label = default_label diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index be499e3531d0..9b06e090d686 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -28,6 +28,8 @@ import galaxy.model.orm.now import galaxy.security.passwords import galaxy.util + +from galaxy.managers import tags from galaxy.model.item_attrs import UsesAnnotations from galaxy.model.util import pgcalc from galaxy.security import get_permitted_actions @@ -43,7 +45,7 @@ WorkflowMappingField) from galaxy.web.framework.helpers import to_unicode -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) _datatypes_registry = None @@ -76,7 +78,7 @@ def _get_datatypes_registry(): return _datatypes_registry -def set_datatypes_registry( d_registry ): +def set_datatypes_registry(d_registry): """ Set up datatypes_registry """ @@ -84,12 +86,12 @@ def set_datatypes_registry( d_registry ): _datatypes_registry = d_registry -class HasTags( object ): - dict_collection_visible_keys = ( 'tags' ) - dict_element_visible_keys = ( 'tags' ) +class HasTags(object): + dict_collection_visible_keys = ('tags') + dict_element_visible_keys = ('tags') def to_dict(self, *args, **kwargs): - rval = super( HasTags, self ).to_dict(*args, **kwargs) + rval = super(HasTags, self).to_dict(*args, **kwargs) rval['tags'] = self.make_tag_string_list() return rval @@ -100,58 +102,58 @@ def make_tag_string_list(self): tag_str = tag.user_tname if tag.value is not None: tag_str += ":" + tag.user_value - tags_str_list.append( tag_str ) + tags_str_list.append(tag_str) return tags_str_list class HasName: - def get_display_name( self ): + def get_display_name(self): """ These objects have a name attribute can be either a string or a unicode object. If string, convert to unicode object assuming 'utf-8' format. """ name = self.name - name = unicodify( name, 'utf-8' ) + name = unicodify(name, 'utf-8') return name class JobLike: - def _init_metrics( self ): + def _init_metrics(self): self.text_metrics = [] self.numeric_metrics = [] - def add_metric( self, plugin, metric_name, metric_value ): - plugin = unicodify( plugin, 'utf-8' ) - metric_name = unicodify( metric_name, 'utf-8' ) - if isinstance( metric_value, numbers.Number ): - metric = self._numeric_metric( plugin, metric_name, metric_value ) - self.numeric_metrics.append( metric ) + def add_metric(self, plugin, metric_name, metric_value): + plugin = unicodify(plugin, 'utf-8') + metric_name = unicodify(metric_name, 'utf-8') + if isinstance(metric_value, numbers.Number): + metric = self._numeric_metric(plugin, metric_name, metric_value) + self.numeric_metrics.append(metric) else: - metric_value = unicodify( metric_value, 'utf-8' ) - if len( metric_value ) > 1022: + metric_value = unicodify(metric_value, 'utf-8') + if len(metric_value) > 1022: # Truncate these values - not needed with sqlite # but other backends must need it. - metric_value = metric_value[ :1022 ] - metric = self._text_metric( plugin, metric_name, metric_value ) - self.text_metrics.append( metric ) + metric_value = metric_value[:1022] + metric = self._text_metric(plugin, metric_name, metric_value) + self.text_metrics.append(metric) @property - def metrics( self ): + def metrics(self): # TODO: Make iterable, concatenate with chain return self.text_metrics + self.numeric_metrics - def set_streams( self, stdout, stderr ): - stdout = galaxy.util.unicodify( stdout ) - stderr = galaxy.util.unicodify( stderr ) - if ( len( stdout ) > galaxy.util.DATABASE_MAX_STRING_SIZE ): - stdout = galaxy.util.shrink_string_by_size( stdout, galaxy.util.DATABASE_MAX_STRING_SIZE, join_by="\n..\n", left_larger=True, beginning_on_size_error=True ) - log.info( "stdout for %s %d is greater than %s, only a portion will be logged to database", type(self), self.id, galaxy.util.DATABASE_MAX_STRING_SIZE_PRETTY ) + def set_streams(self, stdout, stderr): + stdout = galaxy.util.unicodify(stdout) + stderr = galaxy.util.unicodify(stderr) + if (len(stdout) > galaxy.util.DATABASE_MAX_STRING_SIZE): + stdout = galaxy.util.shrink_string_by_size(stdout, galaxy.util.DATABASE_MAX_STRING_SIZE, join_by="\n..\n", left_larger=True, beginning_on_size_error=True) + log.info("stdout for %s %d is greater than %s, only a portion will be logged to database", type(self), self.id, galaxy.util.DATABASE_MAX_STRING_SIZE_PRETTY) self.stdout = stdout - if ( len( stderr ) > galaxy.util.DATABASE_MAX_STRING_SIZE ): - stderr = galaxy.util.shrink_string_by_size( stderr, galaxy.util.DATABASE_MAX_STRING_SIZE, join_by="\n..\n", left_larger=True, beginning_on_size_error=True ) - log.info( "stderr for %s %d is greater than %s, only a portion will be logged to database", type(self), self.id, galaxy.util.DATABASE_MAX_STRING_SIZE_PRETTY ) + if (len(stderr) > galaxy.util.DATABASE_MAX_STRING_SIZE): + stderr = galaxy.util.shrink_string_by_size(stderr, galaxy.util.DATABASE_MAX_STRING_SIZE, join_by="\n..\n", left_larger=True, beginning_on_size_error=True) + log.info("stderr for %s %d is greater than %s, only a portion will be logged to database", type(self), self.id, galaxy.util.DATABASE_MAX_STRING_SIZE_PRETTY) self.stderr = stderr def log_str(self): @@ -165,18 +167,18 @@ def log_str(self): return "%s[%s,tool_id=%s]" % (self.__class__.__name__, extra, self.tool_id) -class User( object, Dictifiable ): +class User(object, Dictifiable): use_pbkdf2 = True """ Data for a Galaxy user or admin and relations to their histories, credentials, and roles. """ # attributes that will be accessed and returned when calling to_dict( view='collection' ) - dict_collection_visible_keys = ( 'id', 'email', 'username', 'deleted', 'active', 'last_password_change' ) + dict_collection_visible_keys = ('id', 'email', 'username', 'deleted', 'active', 'last_password_change') # attributes that will be accessed and returned when calling to_dict( view='element' ) - dict_element_visible_keys = ( 'id', 'email', 'username', 'total_disk_usage', 'nice_total_disk_usage', 'deleted', 'active', 'last_password_change' ) + dict_element_visible_keys = ('id', 'email', 'username', 'total_disk_usage', 'nice_total_disk_usage', 'deleted', 'active', 'last_password_change') - def __init__( self, email=None, password=None ): + def __init__(self, email=None, password=None): self.email = email self.password = password self.external = False @@ -191,21 +193,21 @@ def __init__( self, email=None, password=None ): self.credentials = [] # ? self.roles = [] - def set_password_cleartext( self, cleartext ): + def set_password_cleartext(self, cleartext): """ Set user password to the digest of `cleartext`. """ if User.use_pbkdf2: - self.password = galaxy.security.passwords.hash_password( cleartext ) + self.password = galaxy.security.passwords.hash_password(cleartext) else: - self.password = new_secure_hash( text_type=cleartext ) + self.password = new_secure_hash(text_type=cleartext) self.last_password_change = datetime.now() - def check_password( self, cleartext ): + def check_password(self, cleartext): """ Check if `cleartext` matches user password when hashed. """ - return galaxy.security.passwords.check_password( cleartext, self.password ) + return galaxy.security.passwords.check_password(cleartext, self.password) def system_user_pwent(self, real_system_username): """ @@ -232,12 +234,12 @@ def system_user_pwent(self, real_system_username): pass return system_user_pwent - def all_roles( self ): + def all_roles(self): """ Return a unique list of Roles associated with this user or any of their groups. """ try: - db_session = object_session( self ) + db_session = object_session(self) user = db_session.query( User ).filter_by( # don't use get, it will use session variant. @@ -255,24 +257,24 @@ def all_roles( self ): # skip optimizations... user = self - roles = [ ura.role for ura in user.roles ] - for group in [ uga.group for uga in user.groups ]: - for role in [ gra.role for gra in group.roles ]: + roles = [ura.role for ura in user.roles] + for group in [uga.group for uga in user.groups]: + for role in [gra.role for gra in group.roles]: if role not in roles: - roles.append( role ) + roles.append(role) return roles - def all_roles_exploiting_cache( self ): + def all_roles_exploiting_cache(self): """ """ - roles = [ ura.role for ura in self.roles ] - for group in [ uga.group for uga in self.groups ]: - for role in [ gra.role for gra in group.roles ]: + roles = [ura.role for ura in self.roles] + for group in [uga.group for uga in self.groups]: + for role in [gra.role for gra in group.roles]: if role not in roles: - roles.append( role ) + roles.append(role) return roles - def get_disk_usage( self, nice_size=False ): + def get_disk_usage(self, nice_size=False): """ Return byte count of disk space used by user or a human-readable string if `nice_size` is `True`. @@ -281,29 +283,29 @@ def get_disk_usage( self, nice_size=False ): if self.disk_usage is not None: rval = self.disk_usage if nice_size: - rval = galaxy.util.nice_size( rval ) + rval = galaxy.util.nice_size(rval) return rval - def set_disk_usage( self, bytes ): + def set_disk_usage(self, bytes): """ Manually set the disk space used by a user to `bytes`. """ self.disk_usage = bytes - total_disk_usage = property( get_disk_usage, set_disk_usage ) + total_disk_usage = property(get_disk_usage, set_disk_usage) - def adjust_total_disk_usage( self, amount ): + def adjust_total_disk_usage(self, amount): if amount != 0: self.disk_usage = func.coalesce(self.table.c.disk_usage, 0) + amount @property - def nice_total_disk_usage( self ): + def nice_total_disk_usage(self): """ Return byte count of disk space used in a human-readable string. """ - return self.get_disk_usage( nice_size=True ) + return self.get_disk_usage(nice_size=True) - def calculate_disk_usage( self ): + def calculate_disk_usage(self): """ Return byte count total of disk space used by all non-purged, non-library HDAs in non-purged histories. @@ -312,28 +314,28 @@ def calculate_disk_usage( self ): dataset_ids = [] total = 0 # this can be a huge number and can run out of memory, so we avoid the mappers - db_session = object_session( self ) - for history in db_session.query( History ).enable_eagerloads( False ).filter_by( user_id=self.id, purged=False ).yield_per( 1000 ): - for hda in db_session.query( HistoryDatasetAssociation ).enable_eagerloads( False ).filter_by( history_id=history.id, purged=False ).yield_per( 1000 ): + db_session = object_session(self) + for history in db_session.query(History).enable_eagerloads(False).filter_by(user_id=self.id, purged=False).yield_per(1000): + for hda in db_session.query(HistoryDatasetAssociation).enable_eagerloads(False).filter_by(history_id=history.id, purged=False).yield_per(1000): # TODO: def hda.counts_toward_disk_usage(): # return ( not self.dataset.purged and not self.dataset.library_associations ) if hda.dataset.id not in dataset_ids and not hda.dataset.purged and not hda.dataset.library_associations: - dataset_ids.append( hda.dataset.id ) + dataset_ids.append(hda.dataset.id) total += hda.dataset.get_total_size() return total - def calculate_and_set_disk_usage( self ): + def calculate_and_set_disk_usage(self): """ Calculates and sets user disk usage. """ new = None db_session = object_session(self) current = self.get_disk_usage() - if db_session.get_bind().dialect.name not in ( 'postgres', 'postgresql' ): + if db_session.get_bind().dialect.name not in ('postgres', 'postgresql'): done = False while not done: new = self.calculate_disk_usage() - db_session.refresh( self ) + db_session.refresh(self) # make sure usage didn't change while calculating # set done if it has not, otherwise reset current and iterate again. if self.get_disk_usage() == current: @@ -343,12 +345,12 @@ def calculate_and_set_disk_usage( self ): else: new = pgcalc(db_session, self.id) if new not in (current, None): - self.set_disk_usage( new ) - db_session.add( self ) + self.set_disk_usage(new) + db_session.add(self) db_session.flush() @staticmethod - def user_template_environment( user ): + def user_template_environment(user): """ >>> env = User.user_template_environment(None) @@ -367,30 +369,30 @@ def user_template_environment( user ): """ if user: user_id = '%d' % user.id - user_email = str( user.email ) - user_name = str( user.username ) + user_email = str(user.email) + user_name = str(user.username) else: user = None user_id = 'Anonymous' user_email = 'Anonymous' user_name = 'Anonymous' environment = {} - environment[ '__user__' ] = user - environment[ '__user_id__' ] = environment[ 'userId' ] = user_id - environment[ '__user_email__' ] = environment[ 'userEmail' ] = user_email - environment[ '__user_name__' ] = user_name + environment['__user__'] = user + environment['__user_id__'] = environment['userId'] = user_id + environment['__user_email__'] = environment['userEmail'] = user_email + environment['__user_name__'] = user_name return environment @staticmethod - def expand_user_properties( user, in_string ): + def expand_user_properties(user, in_string): """ """ - environment = User.user_template_environment( user ) - return Template( in_string ).safe_substitute( environment ) + environment = User.user_template_environment(user) + return Template(in_string).safe_substitute(environment) -class PasswordResetToken( object ): - def __init__( self, user, token=None): +class PasswordResetToken(object): + def __init__(self, user, token=None): if token: self.token = token else: @@ -399,33 +401,33 @@ def __init__( self, user, token=None): self.expiration_time = galaxy.model.orm.now.now() + timedelta(hours=24) -class BaseJobMetric( object ): +class BaseJobMetric(object): - def __init__( self, plugin, metric_name, metric_value ): + def __init__(self, plugin, metric_name, metric_value): self.plugin = plugin self.metric_name = metric_name self.metric_value = metric_value -class JobMetricText( BaseJobMetric ): +class JobMetricText(BaseJobMetric): pass -class JobMetricNumeric( BaseJobMetric ): +class JobMetricNumeric(BaseJobMetric): pass -class TaskMetricText( BaseJobMetric ): +class TaskMetricText(BaseJobMetric): pass -class TaskMetricNumeric( BaseJobMetric ): +class TaskMetricNumeric(BaseJobMetric): pass -class Job( object, JobLike, Dictifiable ): - dict_collection_visible_keys = [ 'id', 'state', 'exit_code', 'update_time', 'create_time' ] - dict_element_visible_keys = [ 'id', 'state', 'exit_code', 'update_time', 'create_time' ] +class Job(object, JobLike, Dictifiable): + dict_collection_visible_keys = ['id', 'state', 'exit_code', 'update_time', 'create_time'] + dict_element_visible_keys = ['id', 'state', 'exit_code', 'update_time', 'create_time'] """ A job represents a request to run a tool given input datasets, tool @@ -434,20 +436,20 @@ class Job( object, JobLike, Dictifiable ): _numeric_metric = JobMetricNumeric _text_metric = JobMetricText - states = Bunch( NEW='new', - RESUBMITTED='resubmitted', - UPLOAD='upload', - WAITING='waiting', - QUEUED='queued', - RUNNING='running', - OK='ok', - ERROR='error', - PAUSED='paused', - DELETED='deleted', - DELETED_NEW='deleted_new' ) - terminal_states = [ states.OK, - states.ERROR, - states.DELETED ] + states = Bunch(NEW='new', + RESUBMITTED='resubmitted', + UPLOAD='upload', + WAITING='waiting', + QUEUED='queued', + RUNNING='running', + OK='ok', + ERROR='error', + PAUSED='paused', + DELETED='deleted', + DELETED_NEW='deleted_new') + terminal_states = [states.OK, + states.ERROR, + states.DELETED] #: job states where the job hasn't finished and the model may still change non_ready_states = [ states.NEW, @@ -459,7 +461,7 @@ class Job( object, JobLike, Dictifiable ): ] # Please include an accessor (get/set pair) for any new columns/members. - def __init__( self ): + def __init__(self): self.session_id = None self.user_id = None self.tool_id = None @@ -487,10 +489,10 @@ def __init__( self ): self.handler = None self.exit_code = None self._init_metrics() - self.state_history.append( JobStateHistory( self ) ) + self.state_history.append(JobStateHistory(self)) @property - def finished( self ): + def finished(self): states = self.states return self.state in [ states.OK, @@ -501,210 +503,210 @@ def finished( self ): # TODO: Add accessors for members defined in SQL Alchemy for the Job table and # for the mapper defined to the Job table. - def get_external_output_metadata( self ): + def get_external_output_metadata(self): """ The external_output_metadata is currently a reference from Job to JobExternalOutputMetadata. It exists for a job but not a task. """ return self.external_output_metadata - def get_session_id( self ): + def get_session_id(self): return self.session_id - def get_user_id( self ): + def get_user_id(self): return self.user_id - def get_tool_id( self ): + def get_tool_id(self): return self.tool_id - def get_tool_version( self ): + def get_tool_version(self): return self.tool_version - def get_command_line( self ): + def get_command_line(self): return self.command_line def get_dependencies(self): return self.dependencies - def get_param_filename( self ): + def get_param_filename(self): return self.param_filename - def get_parameters( self ): + def get_parameters(self): return self.parameters - def get_input_datasets( self ): + def get_input_datasets(self): return self.input_datasets - def get_output_datasets( self ): + def get_output_datasets(self): return self.output_datasets - def get_input_library_datasets( self ): + def get_input_library_datasets(self): return self.input_library_datasets - def get_output_library_datasets( self ): + def get_output_library_datasets(self): return self.output_library_datasets - def get_state( self ): + def get_state(self): return self.state - def get_info( self ): + def get_info(self): return self.info - def get_job_runner_name( self ): + def get_job_runner_name(self): # This differs from the Task class in that job_runner_name is # accessed instead of task_runner_name. Note that the field # runner_name is not the same thing. return self.job_runner_name - def get_job_runner_external_id( self ): + def get_job_runner_external_id(self): # This is different from the Task just in the member accessed: return self.job_runner_external_id - def get_post_job_actions( self ): + def get_post_job_actions(self): return self.post_job_actions - def get_imported( self ): + def get_imported(self): return self.imported - def get_handler( self ): + def get_handler(self): return self.handler - def get_params( self ): + def get_params(self): return self.params - def get_user( self ): + def get_user(self): # This is defined in the SQL Alchemy mapper as a relation to the User. return self.user - def get_id( self ): + def get_id(self): # This is defined in the SQL Alchemy's Job table (and not in the model). return self.id - def get_tasks( self ): + def get_tasks(self): # The tasks member is pert of a reference in the SQL Alchemy schema: return self.tasks - def get_id_tag( self ): + def get_id_tag(self): """ Return a tag that can be useful in identifying a Job. This returns the Job's get_id """ return "%s" % self.id - def set_session_id( self, session_id ): + def set_session_id(self, session_id): self.session_id = session_id - def set_user_id( self, user_id ): + def set_user_id(self, user_id): self.user_id = user_id - def set_tool_id( self, tool_id ): + def set_tool_id(self, tool_id): self.tool_id = tool_id - def set_tool_version( self, tool_version ): + def set_tool_version(self, tool_version): self.tool_version = tool_version - def set_command_line( self, command_line ): + def set_command_line(self, command_line): self.command_line = command_line - def set_dependencies( self, dependencies ): + def set_dependencies(self, dependencies): self.dependencies = dependencies - def set_param_filename( self, param_filename ): + def set_param_filename(self, param_filename): self.param_filename = param_filename - def set_parameters( self, parameters ): + def set_parameters(self, parameters): self.parameters = parameters - def set_input_datasets( self, input_datasets ): + def set_input_datasets(self, input_datasets): self.input_datasets = input_datasets - def set_output_datasets( self, output_datasets ): + def set_output_datasets(self, output_datasets): self.output_datasets = output_datasets - def set_input_library_datasets( self, input_library_datasets ): + def set_input_library_datasets(self, input_library_datasets): self.input_library_datasets = input_library_datasets - def set_output_library_datasets( self, output_library_datasets ): + def set_output_library_datasets(self, output_library_datasets): self.output_library_datasets = output_library_datasets - def set_info( self, info ): + def set_info(self, info): self.info = info - def set_runner_name( self, job_runner_name ): + def set_runner_name(self, job_runner_name): self.job_runner_name = job_runner_name - def get_job( self ): + def get_job(self): # Added so job and task have same interface (.get_job() ) to get at # underlying job object. return self - def set_runner_external_id( self, job_runner_external_id ): + def set_runner_external_id(self, job_runner_external_id): self.job_runner_external_id = job_runner_external_id - def set_post_job_actions( self, post_job_actions ): + def set_post_job_actions(self, post_job_actions): self.post_job_actions = post_job_actions - def set_imported( self, imported ): + def set_imported(self, imported): self.imported = imported - def set_handler( self, handler ): + def set_handler(self, handler): self.handler = handler - def set_params( self, params ): + def set_params(self, params): self.params = params - def add_parameter( self, name, value ): - self.parameters.append( JobParameter( name, value ) ) + def add_parameter(self, name, value): + self.parameters.append(JobParameter(name, value)) - def add_input_dataset( self, name, dataset=None, dataset_id=None ): - assoc = JobToInputDatasetAssociation( name, dataset ) + def add_input_dataset(self, name, dataset=None, dataset_id=None): + assoc = JobToInputDatasetAssociation(name, dataset) if dataset is None and dataset_id is not None: assoc.dataset_id = dataset_id - self.input_datasets.append( assoc ) + self.input_datasets.append(assoc) - def add_output_dataset( self, name, dataset ): - self.output_datasets.append( JobToOutputDatasetAssociation( name, dataset ) ) + def add_output_dataset(self, name, dataset): + self.output_datasets.append(JobToOutputDatasetAssociation(name, dataset)) - def add_input_dataset_collection( self, name, dataset_collection ): - self.input_dataset_collections.append( JobToInputDatasetCollectionAssociation( name, dataset_collection ) ) + def add_input_dataset_collection(self, name, dataset_collection): + self.input_dataset_collections.append(JobToInputDatasetCollectionAssociation(name, dataset_collection)) - def add_output_dataset_collection( self, name, dataset_collection_instance ): - self.output_dataset_collection_instances.append( JobToOutputDatasetCollectionAssociation( name, dataset_collection_instance ) ) + def add_output_dataset_collection(self, name, dataset_collection_instance): + self.output_dataset_collection_instances.append(JobToOutputDatasetCollectionAssociation(name, dataset_collection_instance)) - def add_implicit_output_dataset_collection( self, name, dataset_collection ): - self.output_dataset_collections.append( JobToImplicitOutputDatasetCollectionAssociation( name, dataset_collection ) ) + def add_implicit_output_dataset_collection(self, name, dataset_collection): + self.output_dataset_collections.append(JobToImplicitOutputDatasetCollectionAssociation(name, dataset_collection)) - def add_input_library_dataset( self, name, dataset ): - self.input_library_datasets.append( JobToInputLibraryDatasetAssociation( name, dataset ) ) + def add_input_library_dataset(self, name, dataset): + self.input_library_datasets.append(JobToInputLibraryDatasetAssociation(name, dataset)) - def add_output_library_dataset( self, name, dataset ): - self.output_library_datasets.append( JobToOutputLibraryDatasetAssociation( name, dataset ) ) + def add_output_library_dataset(self, name, dataset): + self.output_library_datasets.append(JobToOutputLibraryDatasetAssociation(name, dataset)) def add_post_job_action(self, pja): - self.post_job_actions.append( PostJobActionAssociation( pja, self ) ) + self.post_job_actions.append(PostJobActionAssociation(pja, self)) - def set_state( self, state ): + def set_state(self, state): """ Save state history """ self.state = state - self.state_history.append( JobStateHistory( self ) ) + self.state_history.append(JobStateHistory(self)) - def get_param_values( self, app, ignore_errors=False ): + def get_param_values(self, app, ignore_errors=False): """ Read encoded parameter values from the database and turn back into a dict of tool parameter values. """ param_dict = self.raw_param_dict() - tool = app.toolbox.get_tool( self.tool_id ) - param_dict = tool.params_from_strings( param_dict, app, ignore_errors=ignore_errors ) + tool = app.toolbox.get_tool(self.tool_id, tool_version=self.tool_version) + param_dict = tool.params_from_strings(param_dict, app, ignore_errors=ignore_errors) return param_dict - def raw_param_dict( self ): - param_dict = dict( [ ( p.name, p.value ) for p in self.parameters ] ) + def raw_param_dict(self): + param_dict = dict([(p.name, p.value) for p in self.parameters]) return param_dict - def check_if_output_datasets_deleted( self ): + def check_if_output_datasets_deleted(self): """ Return true if all of the output datasets associated with this job are in the deleted state @@ -717,7 +719,7 @@ def check_if_output_datasets_deleted( self ): return False return True - def mark_deleted( self, track_jobs_in_database=False ): + def mark_deleted(self, track_jobs_in_database=False): """ Mark this job as deleted, and mark any output datasets as discarded. """ @@ -740,8 +742,8 @@ def mark_deleted( self, track_jobs_in_database=False ): dataset.peek = 'Job deleted' dataset.info = 'Job output deleted by user before job completed' - def to_dict( self, view='collection', system_details=False ): - rval = super( Job, self ).to_dict( view=view ) + def to_dict(self, view='collection', system_details=False): + rval = super(Job, self).to_dict(view=view) rval['tool_id'] = self.tool_id if system_details: # System level details that only admins should have. @@ -749,7 +751,7 @@ def to_dict( self, view='collection', system_details=False ): rval['command_line'] = self.command_line if view == 'element': - param_dict = dict( [ ( p.name, p.value ) for p in self.parameters ] ) + param_dict = dict([(p.name, p.value) for p in self.parameters]) rval['params'] = param_dict input_dict = {} @@ -787,8 +789,8 @@ def to_dict( self, view='collection', system_details=False ): return rval - def set_final_state( self, final_state ): - self.set_state( final_state ) + def set_final_state(self, final_state): + self.set_state(final_state) if self.workflow_invocation_step: self.workflow_invocation_step.update() @@ -805,27 +807,27 @@ def get_destination_configuration(self, config, key, default=None): return config_value @property - def seconds_since_update( self ): + def seconds_since_update(self): return (galaxy.model.orm.now.now() - self.update_time).total_seconds() -class Task( object, JobLike ): +class Task(object, JobLike): """ A task represents a single component of a job. """ _numeric_metric = TaskMetricNumeric _text_metric = TaskMetricText - states = Bunch( NEW='new', - WAITING='waiting', - QUEUED='queued', - RUNNING='running', - OK='ok', - ERROR='error', - DELETED='deleted' ) + states = Bunch(NEW='new', + WAITING='waiting', + QUEUED='queued', + RUNNING='running', + OK='ok', + ERROR='error', + DELETED='deleted') # Please include an accessor (get/set pair) for any new columns/members. - def __init__( self, job, working_directory, prepare_files_cmd ): + def __init__(self, job, working_directory, prepare_files_cmd): self.command_line = None self.parameters = [] self.state = Task.states.NEW @@ -840,58 +842,58 @@ def __init__( self, job, working_directory, prepare_files_cmd ): self.prepare_input_files_cmd = prepare_files_cmd self._init_metrics() - def get_param_values( self, app ): + def get_param_values(self, app): """ Read encoded parameter values from the database and turn back into a dict of tool parameter values. """ - param_dict = dict( [ ( p.name, p.value ) for p in self.parent_job.parameters ] ) - tool = app.toolbox.get_tool( self.tool_id ) - param_dict = tool.params_from_strings( param_dict, app ) + param_dict = dict([(p.name, p.value) for p in self.job.parameters]) + tool = app.toolbox.get_tool(self.job.tool_id, tool_version=self.job.tool_version) + param_dict = tool.params_from_strings(param_dict, app) return param_dict - def get_id( self ): + def get_id(self): # This is defined in the SQL Alchemy schema: return self.id - def get_id_tag( self ): + def get_id_tag(self): """ Return an id tag suitable for identifying the task. This combines the task's job id and the task's own id. """ - return "%s_%s" % ( self.job.get_id(), self.get_id() ) + return "%s_%s" % (self.job.get_id(), self.get_id()) - def get_command_line( self ): + def get_command_line(self): return self.command_line - def get_parameters( self ): + def get_parameters(self): return self.parameters - def get_state( self ): + def get_state(self): return self.state - def get_info( self ): + def get_info(self): return self.info - def get_working_directory( self ): + def get_working_directory(self): return self.working_directory - def get_task_runner_name( self ): + def get_task_runner_name(self): return self.task_runner_name - def get_task_runner_external_id( self ): + def get_task_runner_external_id(self): return self.task_runner_external_id - def get_job( self ): + def get_job(self): return self.job - def get_stdout( self ): + def get_stdout(self): return self.stdout - def get_stderr( self ): + def get_stderr(self): return self.stderr - def get_prepare_input_files_cmd( self ): + def get_prepare_input_files_cmd(self): return self.prepare_input_files_cmd # The following accessors are for members that are in the Job class but @@ -899,7 +901,7 @@ def get_prepare_input_files_cmd( self ): # or return None, depending on whether Tasks need to point to the parent # (e.g., for a session) or never use the member (e.g., external output # metdata). These can be filled in as needed. - def get_external_output_metadata( self ): + def get_external_output_metadata(self): """ The external_output_metadata is currently a backref to JobExternalOutputMetadata. It exists for a job but not a task, @@ -909,14 +911,14 @@ def get_external_output_metadata( self ): """ return None - def get_job_runner_name( self ): + def get_job_runner_name(self): """ Since runners currently access Tasks the same way they access Jobs, this method just refers to *this* instance's runner. """ return self.task_runner_name - def get_job_runner_external_id( self ): + def get_job_runner_external_id(self): """ Runners will use the same methods to get information about the Task class as they will about the Job class, so this method just returns @@ -925,86 +927,86 @@ class as they will about the Job class, so this method just returns # TODO: Merge into get_runner_external_id. return self.task_runner_external_id - def get_session_id( self ): + def get_session_id(self): # The Job's galaxy session is equal to the Job's session, so the # Job's session is the same as the Task's session. return self.get_job().get_session_id() - def set_id( self, id ): + def set_id(self, id): # This is defined in the SQL Alchemy's mapper and not here. # This should never be called. self.id = id - def set_command_line( self, command_line ): + def set_command_line(self, command_line): self.command_line = command_line - def set_parameters( self, parameters ): + def set_parameters(self, parameters): self.parameters = parameters - def set_state( self, state ): + def set_state(self, state): self.state = state - def set_info( self, info ): + def set_info(self, info): self.info = info - def set_working_directory( self, working_directory ): + def set_working_directory(self, working_directory): self.working_directory = working_directory - def set_task_runner_name( self, task_runner_name ): + def set_task_runner_name(self, task_runner_name): self.task_runner_name = task_runner_name - def set_job_runner_external_id( self, task_runner_external_id ): + def set_job_runner_external_id(self, task_runner_external_id): # This method is available for runners that do not want/need to # differentiate between the kinds of Runnable things (Jobs and Tasks) # that they're using. - log.debug( "Task %d: Set external id to %s" - % ( self.id, task_runner_external_id ) ) + log.debug("Task %d: Set external id to %s" + % (self.id, task_runner_external_id)) self.task_runner_external_id = task_runner_external_id - def set_task_runner_external_id( self, task_runner_external_id ): + def set_task_runner_external_id(self, task_runner_external_id): self.task_runner_external_id = task_runner_external_id - def set_job( self, job ): + def set_job(self, job): self.job = job - def set_stdout( self, stdout ): + def set_stdout(self, stdout): self.stdout = stdout - def set_stderr( self, stderr ): + def set_stderr(self, stderr): self.stderr = stderr - def set_prepare_input_files_cmd( self, prepare_input_files_cmd ): + def set_prepare_input_files_cmd(self, prepare_input_files_cmd): self.prepare_input_files_cmd = prepare_input_files_cmd -class JobParameter( object ): - def __init__( self, name, value ): +class JobParameter(object): + def __init__(self, name, value): self.name = name self.value = value -class JobToInputDatasetAssociation( object ): - def __init__( self, name, dataset ): +class JobToInputDatasetAssociation(object): + def __init__(self, name, dataset): self.name = name self.dataset = dataset -class JobToOutputDatasetAssociation( object ): - def __init__( self, name, dataset ): +class JobToOutputDatasetAssociation(object): + def __init__(self, name, dataset): self.name = name self.dataset = dataset -class JobToInputDatasetCollectionAssociation( object ): - def __init__( self, name, dataset_collection ): +class JobToInputDatasetCollectionAssociation(object): + def __init__(self, name, dataset_collection): self.name = name self.dataset_collection = dataset_collection # Many jobs may map to one HistoryDatasetCollection using these for a given # tool output (if mapping over an input collection). -class JobToOutputDatasetCollectionAssociation( object ): - def __init__( self, name, dataset_collection_instance ): +class JobToOutputDatasetCollectionAssociation(object): + def __init__(self, name, dataset_collection_instance): self.name = name self.dataset_collection_instance = dataset_collection_instance @@ -1012,47 +1014,47 @@ def __init__( self, name, dataset_collection_instance ): # A DatasetCollection will be mapped to at most one job per tool output # using these. (You can think of many of these models as going into the # creation of a JobToOutputDatasetCollectionAssociation.) -class JobToImplicitOutputDatasetCollectionAssociation( object ): - def __init__( self, name, dataset_collection ): +class JobToImplicitOutputDatasetCollectionAssociation(object): + def __init__(self, name, dataset_collection): self.name = name self.dataset_collection = dataset_collection -class JobToInputLibraryDatasetAssociation( object ): - def __init__( self, name, dataset ): +class JobToInputLibraryDatasetAssociation(object): + def __init__(self, name, dataset): self.name = name self.dataset = dataset -class JobToOutputLibraryDatasetAssociation( object ): - def __init__( self, name, dataset ): +class JobToOutputLibraryDatasetAssociation(object): + def __init__(self, name, dataset): self.name = name self.dataset = dataset -class JobStateHistory( object ): - def __init__( self, job ): +class JobStateHistory(object): + def __init__(self, job): self.job = job self.state = job.state self.info = job.info -class ImplicitlyCreatedDatasetCollectionInput( object ): - def __init__( self, name, input_dataset_collection ): +class ImplicitlyCreatedDatasetCollectionInput(object): + def __init__(self, name, input_dataset_collection): self.name = name self.input_dataset_collection = input_dataset_collection -class PostJobAction( object ): - def __init__( self, action_type, workflow_step, output_name=None, action_arguments=None): +class PostJobAction(object): + def __init__(self, action_type, workflow_step, output_name=None, action_arguments=None): self.action_type = action_type self.output_name = output_name self.action_arguments = action_arguments self.workflow_step = workflow_step -class PostJobActionAssociation( object ): - def __init__(self, pja, job=None, job_id=None ): +class PostJobActionAssociation(object): + def __init__(self, pja, job=None, job_id=None): if job is not None: self.job = job elif job_id is not None: @@ -1062,16 +1064,16 @@ def __init__(self, pja, job=None, job_id=None ): self.post_job_action = pja -class JobExternalOutputMetadata( object ): - def __init__( self, job=None, dataset=None ): +class JobExternalOutputMetadata(object): + def __init__(self, job=None, dataset=None): self.job = job - if isinstance( dataset, galaxy.model.HistoryDatasetAssociation ): + if isinstance(dataset, galaxy.model.HistoryDatasetAssociation): self.history_dataset_association = dataset - elif isinstance( dataset, galaxy.model.LibraryDatasetDatasetAssociation ): + elif isinstance(dataset, galaxy.model.LibraryDatasetDatasetAssociation): self.library_dataset_dataset_association = dataset @property - def dataset( self ): + def dataset(self): if self.history_dataset_association: return self.history_dataset_association elif self.library_dataset_dataset_association: @@ -1079,10 +1081,10 @@ def dataset( self ): return None -class JobExportHistoryArchive( object ): - def __init__( self, job=None, history=None, dataset=None, compressed=False, - history_attrs_filename=None, datasets_attrs_filename=None, - jobs_attrs_filename=None ): +class JobExportHistoryArchive(object): + def __init__(self, job=None, history=None, dataset=None, compressed=False, + history_attrs_filename=None, datasets_attrs_filename=None, + jobs_attrs_filename=None): self.job = job self.history = history self.dataset = dataset @@ -1092,43 +1094,43 @@ def __init__( self, job=None, history=None, dataset=None, compressed=False, self.jobs_attrs_filename = jobs_attrs_filename @property - def up_to_date( self ): + def up_to_date(self): """ Return False, if a new export should be generated for corresponding history. """ job = self.job - return job.state not in [ Job.states.ERROR, Job.states.DELETED ] \ + return job.state not in [Job.states.ERROR, Job.states.DELETED] \ and job.update_time > self.history.update_time @property - def ready( self ): + def ready(self): return self.job.state == Job.states.OK @property - def preparing( self ): - return self.job.state in [ Job.states.RUNNING, Job.states.QUEUED, Job.states.WAITING ] + def preparing(self): + return self.job.state in [Job.states.RUNNING, Job.states.QUEUED, Job.states.WAITING] @property - def export_name( self ): + def export_name(self): # Stream archive. - hname = ready_name_for_url( self.history.name ) - hname = "Galaxy-History-%s.tar" % ( hname ) + hname = ready_name_for_url(self.history.name) + hname = "Galaxy-History-%s.tar" % (hname) if self.compressed: hname += ".gz" return hname -class JobImportHistoryArchive( object ): - def __init__( self, job=None, history=None, archive_dir=None ): +class JobImportHistoryArchive(object): + def __init__(self, job=None, history=None, archive_dir=None): self.job = job self.history = history self.archive_dir = archive_dir -class GenomeIndexToolData( object ): - def __init__( self, job=None, params=None, dataset=None, deferred_job=None, - transfer_job=None, fasta_path=None, created_time=None, modified_time=None, - dbkey=None, user=None, indexer=None ): +class GenomeIndexToolData(object): + def __init__(self, job=None, params=None, dataset=None, deferred_job=None, + transfer_job=None, fasta_path=None, created_time=None, modified_time=None, + dbkey=None, user=None, indexer=None): self.job = job self.dataset = dataset self.fasta_path = fasta_path @@ -1140,77 +1142,77 @@ def __init__( self, job=None, params=None, dataset=None, deferred_job=None, self.transfer = transfer_job -class DeferredJob( object ): - states = Bunch( NEW='new', - WAITING='waiting', - QUEUED='queued', - RUNNING='running', - OK='ok', - ERROR='error' ) +class DeferredJob(object): + states = Bunch(NEW='new', + WAITING='waiting', + QUEUED='queued', + RUNNING='running', + OK='ok', + ERROR='error') - def __init__( self, state=None, plugin=None, params=None ): + def __init__(self, state=None, plugin=None, params=None): self.state = state self.plugin = plugin self.params = params - def get_check_interval( self ): - if not hasattr( self, '_check_interval' ): + def get_check_interval(self): + if not hasattr(self, '_check_interval'): self._check_interval = None return self._check_interval - def set_check_interval( self, seconds ): + def set_check_interval(self, seconds): self._check_interval = seconds - check_interval = property( get_check_interval, set_check_interval ) + check_interval = property(get_check_interval, set_check_interval) - def get_last_check( self ): - if not hasattr( self, '_last_check' ): + def get_last_check(self): + if not hasattr(self, '_last_check'): self._last_check = 0 return self._last_check - def set_last_check( self, seconds ): + def set_last_check(self, seconds): try: - self._last_check = int( seconds ) + self._last_check = int(seconds) except: self._last_check = time.time() - last_check = property( get_last_check, set_last_check ) + last_check = property(get_last_check, set_last_check) @property - def is_check_time( self ): + def is_check_time(self): if self.check_interval is None: return True - elif ( int( time.time() ) - self.last_check ) > self.check_interval: + elif (int(time.time()) - self.last_check) > self.check_interval: return True else: return False -class Group( object, Dictifiable ): - dict_collection_visible_keys = ( 'id', 'name' ) - dict_element_visible_keys = ( 'id', 'name' ) +class Group(object, Dictifiable): + dict_collection_visible_keys = ('id', 'name') + dict_element_visible_keys = ('id', 'name') - def __init__( self, name=None ): + def __init__(self, name=None): self.name = name self.deleted = False -class UserGroupAssociation( object ): - def __init__( self, user, group ): +class UserGroupAssociation(object): + def __init__(self, user, group): self.user = user self.group = group def is_hda(d): - return isinstance( d, HistoryDatasetAssociation ) + return isinstance(d, HistoryDatasetAssociation) -class History( HasTags, Dictifiable, UsesAnnotations, HasName ): +class History(HasTags, Dictifiable, UsesAnnotations, HasName): - dict_collection_visible_keys = ( 'id', 'name', 'published', 'deleted' ) - dict_element_visible_keys = ( 'id', 'name', 'genome_build', 'deleted', 'purged', 'update_time', - 'published', 'importable', 'slug', 'empty' ) + dict_collection_visible_keys = ('id', 'name', 'published', 'deleted') + dict_element_visible_keys = ('id', 'name', 'genome_build', 'deleted', 'purged', 'update_time', + 'published', 'importable', 'slug', 'empty') default_name = 'Unnamed history' - def __init__( self, id=None, name=None, user=None ): + def __init__(self, id=None, name=None, user=None): self.id = id self.name = name or History.default_name self.deleted = False @@ -1225,12 +1227,12 @@ def __init__( self, id=None, name=None, user=None ): self.tags = [] @property - def empty( self ): + def empty(self): return self.hid_counter == 1 - def _next_hid( self, n=1 ): + def _next_hid(self, n=1): # this is overriden in mapping.py db_next_hid() method - if len( self.datasets ) == 0: + if len(self.datasets) == 0: return n else: last_hid = 0 @@ -1239,20 +1241,20 @@ def _next_hid( self, n=1 ): last_hid = dataset.hid return last_hid + n - def add_galaxy_session( self, galaxy_session, association=None ): + def add_galaxy_session(self, galaxy_session, association=None): if association is None: - self.galaxy_sessions.append( GalaxySessionToHistoryAssociation( galaxy_session, self ) ) + self.galaxy_sessions.append(GalaxySessionToHistoryAssociation(galaxy_session, self)) else: - self.galaxy_sessions.append( association ) + self.galaxy_sessions.append(association) - def add_dataset( self, dataset, parent_id=None, genome_build=None, set_hid=True, quota=True ): - if isinstance( dataset, Dataset ): + def add_dataset(self, dataset, parent_id=None, genome_build=None, set_hid=True, quota=True): + if isinstance(dataset, Dataset): dataset = HistoryDatasetAssociation(dataset=dataset) - object_session( self ).add( dataset ) - object_session( self ).flush() - elif not isinstance( dataset, HistoryDatasetAssociation ): - raise TypeError( "You can only add Dataset and HistoryDatasetAssociation instances to a history" + - " ( you tried to add %s )." % str( dataset ) ) + object_session(self).add(dataset) + object_session(self).flush() + elif not isinstance(dataset, HistoryDatasetAssociation): + raise TypeError("You can only add Dataset and HistoryDatasetAssociation instances to a history" + + " ( you tried to add %s )." % str(dataset)) if parent_id: for data in self.datasets: if data.id == parent_id: @@ -1272,38 +1274,38 @@ def add_dataset( self, dataset, parent_id=None, genome_build=None, set_hid=True, dataset.history_id = self.id return dataset - def add_datasets( self, sa_session, datasets, parent_id=None, genome_build=None, set_hid=True, quota=True, flush=False ): + def add_datasets(self, sa_session, datasets, parent_id=None, genome_build=None, set_hid=True, quota=True, flush=False): """ Optimized version of add_dataset above that minimizes database interactions when adding many datasets to history at once. """ - all_hdas = all( is_hda(_) for _ in datasets ) - optimize = len( datasets) > 1 and parent_id is None and all_hdas and set_hid + all_hdas = all(is_hda(_) for _ in datasets) + optimize = len(datasets) > 1 and parent_id is None and all_hdas and set_hid if optimize: - self.__add_datasets_optimized( datasets, genome_build=genome_build ) + self.__add_datasets_optimized(datasets, genome_build=genome_build) if quota and self.user: disk_usage = sum([d.get_total_size() for d in datasets]) self.user.adjust_total_disk_usage(disk_usage) - sa_session.add_all( datasets ) + sa_session.add_all(datasets) if flush: sa_session.flush() else: for dataset in datasets: - self.add_dataset( dataset, parent_id=parent_id, genome_build=genome_build, set_hid=set_hid, quota=quota ) - sa_session.add( dataset ) + self.add_dataset(dataset, parent_id=parent_id, genome_build=genome_build, set_hid=set_hid, quota=quota) + sa_session.add(dataset) if flush: sa_session.flush() - def __add_datasets_optimized( self, datasets, genome_build=None ): + def __add_datasets_optimized(self, datasets, genome_build=None): """ Optimized version of add_dataset above that minimizes database interactions when adding many datasets to history at once under certain circumstances. """ - n = len( datasets ) + n = len(datasets) - base_hid = self._next_hid( n=n ) + base_hid = self._next_hid(n=n) set_genome = genome_build not in [None, '?'] - for i, dataset in enumerate( datasets ): + for i, dataset in enumerate(datasets): dataset.hid = base_hid + i dataset.history = self if set_genome: @@ -1312,15 +1314,15 @@ def __add_datasets_optimized( self, datasets, genome_build=None ): dataset.history_id = self.id return datasets - def add_dataset_collection( self, history_dataset_collection, set_hid=True ): + def add_dataset_collection(self, history_dataset_collection, set_hid=True): if set_hid: history_dataset_collection.hid = self._next_hid() history_dataset_collection.history = self # TODO: quota? - self.dataset_collections.append( history_dataset_collection ) + self.dataset_collections.append(history_dataset_collection) return history_dataset_collection - def copy( self, name=None, target_user=None, activatable=False, all_datasets=False ): + def copy(self, name=None, target_user=None, activatable=False, all_datasets=False): """ Return a copy of this history using the given `name` and `target_user`. If `activatable`, copy only non-deleted datasets. If `all_datasets`, copy @@ -1330,14 +1332,14 @@ def copy( self, name=None, target_user=None, activatable=False, all_datasets=Fal applies_to_quota = target_user != self.user # Create new history. - new_history = History( name=name, user=target_user ) - db_session = object_session( self ) - db_session.add( new_history ) + new_history = History(name=name, user=target_user) + db_session = object_session(self) + db_session.add(new_history) db_session.flush() # copy history tags and annotations (if copying user is not anonymous) if target_user: - self.copy_item_annotation( db_session, self.user, self, target_user, new_history ) + self.copy_item_annotation(db_session, self.user, self, target_user, new_history) new_history.copy_tags_from(target_user=target_user, source_history=self) # Copy HDAs. @@ -1349,14 +1351,14 @@ def copy( self, name=None, target_user=None, activatable=False, all_datasets=Fal hdas = self.active_datasets for hda in hdas: # Copy HDA. - new_hda = hda.copy( copy_children=True ) - new_history.add_dataset( new_hda, set_hid=False, quota=applies_to_quota ) - db_session.add( new_hda ) + new_hda = hda.copy(copy_children=True) + new_history.add_dataset(new_hda, set_hid=False, quota=applies_to_quota) + db_session.add(new_hda) db_session.flush() if target_user: - new_hda.copy_item_annotation( db_session, self.user, hda, target_user, new_hda ) - new_hda.copy_tags_from( target_user, hda ) + new_hda.copy_item_annotation(db_session, self.user, hda, target_user, new_hda) + new_hda.copy_tags_from(target_user, hda) # Copy history dataset collections if all_datasets: @@ -1365,132 +1367,131 @@ def copy( self, name=None, target_user=None, activatable=False, all_datasets=Fal hdcas = self.active_dataset_collections for hdca in hdcas: new_hdca = hdca.copy() - new_history.add_dataset_collection( new_hdca, set_hid=False ) - db_session.add( new_hdca ) + new_history.add_dataset_collection(new_hdca, set_hid=False) + db_session.add(new_hdca) db_session.flush() if target_user: - new_hdca.copy_item_annotation( db_session, self.user, hdca, target_user, new_hdca ) + new_hdca.copy_item_annotation(db_session, self.user, hdca, target_user, new_hdca) new_history.hid_counter = self.hid_counter - db_session.add( new_history ) + db_session.add(new_history) db_session.flush() return new_history @property - def activatable_datasets( self ): + def activatable_datasets(self): # This needs to be a list - return [ hda for hda in self.datasets if not hda.dataset.deleted ] + return [hda for hda in self.datasets if not hda.dataset.deleted] - def to_dict( self, view='collection', value_mapper=None ): + def to_dict(self, view='collection', value_mapper=None): # Get basic value. - rval = super( History, self ).to_dict( view=view, value_mapper=value_mapper ) + rval = super(History, self).to_dict(view=view, value_mapper=value_mapper) if view == 'element': - rval[ 'size' ] = int( self.disk_size ) + rval['size'] = int(self.disk_size) return rval @property - def latest_export( self ): + def latest_export(self): exports = self.exports - return exports and exports[ 0 ] + return exports and exports[0] - def unhide_datasets( self ): + def unhide_datasets(self): for dataset in self.datasets: dataset.mark_unhidden() - def resume_paused_jobs( self ): + def resume_paused_jobs(self): for dataset in self.datasets: job = dataset.creating_job if job is not None and job.state == Job.states.PAUSED: job.set_state(Job.states.NEW) @hybrid.hybrid_property - def disk_size( self ): + def disk_size(self): """ Return the size in bytes of this history by summing the 'total_size's of all non-purged, unique datasets within it. """ # non-.expression part of hybrid.hybrid_property: called when an instance is the namespace (not the class) - db_session = object_session( self ) + db_session = object_session(self) rval = db_session.query( - func.sum( db_session.query( HistoryDatasetAssociation.dataset_id, Dataset.total_size ).join( Dataset ) - .filter( HistoryDatasetAssociation.table.c.history_id == self.id ) - .filter( HistoryDatasetAssociation.purged != true() ) - .filter( Dataset.purged != true() ) + func.sum(db_session.query(HistoryDatasetAssociation.dataset_id, Dataset.total_size).join(Dataset) + .filter(HistoryDatasetAssociation.table.c.history_id == self.id) + .filter(HistoryDatasetAssociation.purged != true()) + .filter(Dataset.purged != true()) # unique datasets only - .distinct().subquery().c.total_size ) ).first()[0] + .distinct().subquery().c.total_size)).first()[0] if rval is None: rval = 0 return rval @disk_size.expression - def disk_size( cls ): + def disk_size(cls): """ Return a query scalar that will get any history's size in bytes by summing the 'total_size's of all non-purged, unique datasets within it. """ # .expression acts as a column_property and should return a scalar # first, get the distinct datasets within a history that are not purged - hda_to_dataset_join = join( HistoryDatasetAssociation, Dataset, - HistoryDatasetAssociation.table.c.dataset_id == Dataset.table.c.id ) + hda_to_dataset_join = join(HistoryDatasetAssociation, Dataset, + HistoryDatasetAssociation.table.c.dataset_id == Dataset.table.c.id) distinct_datasets = ( select([ # use labels here to better accrss from the query above - HistoryDatasetAssociation.table.c.history_id.label( 'history_id' ), - Dataset.total_size.label( 'dataset_size' ), - Dataset.id.label( 'dataset_id' ) + HistoryDatasetAssociation.table.c.history_id.label('history_id'), + Dataset.total_size.label('dataset_size'), + Dataset.id.label('dataset_id') ]) - .where( HistoryDatasetAssociation.table.c.purged != true() ) - .where( Dataset.table.c.purged != true() ) - .select_from( hda_to_dataset_join ) + .where(HistoryDatasetAssociation.table.c.purged != true()) + .where(Dataset.table.c.purged != true()) + .select_from(hda_to_dataset_join) # TODO: slow (in general) but most probably here - index total_size for easier sorting/distinct? .distinct() ) # postgres needs an alias on FROM - distinct_datasets_alias = aliased( distinct_datasets, name="datasets" ) + distinct_datasets_alias = aliased(distinct_datasets, name="datasets") # then, bind as property of history using the cls.id size_query = ( select([ - func.coalesce( func.sum( distinct_datasets_alias.c.dataset_size ), 0 ) + func.coalesce(func.sum(distinct_datasets_alias.c.dataset_size), 0) ]) - .select_from( distinct_datasets_alias ) - .where( distinct_datasets_alias.c.history_id == cls.id ) + .select_from(distinct_datasets_alias) + .where(distinct_datasets_alias.c.history_id == cls.id) ) # label creates a scalar - return size_query.label( 'disk_size' ) + return size_query.label('disk_size') @property - def disk_nice_size( self ): + def disk_nice_size(self): """Returns human readable size of history on disk.""" - return galaxy.util.nice_size( self.disk_size ) + return galaxy.util.nice_size(self.disk_size) @property - def active_datasets_and_roles( self ): + def active_datasets_and_roles(self): if not hasattr(self, '_active_datasets_and_roles'): - db_session = object_session( self ) - query = ( db_session.query( HistoryDatasetAssociation ) - .filter( HistoryDatasetAssociation.table.c.history_id == self.id ) - .filter( not_( HistoryDatasetAssociation.deleted ) ) - .order_by( HistoryDatasetAssociation.table.c.hid.asc() ) - .options( joinedload("dataset"), - joinedload("dataset.actions"), - joinedload("dataset.actions.role"), - joinedload("tags"), - )) + db_session = object_session(self) + query = (db_session.query(HistoryDatasetAssociation) + .filter(HistoryDatasetAssociation.table.c.history_id == self.id) + .filter(not_(HistoryDatasetAssociation.deleted)) + .order_by(HistoryDatasetAssociation.table.c.hid.asc()) + .options(joinedload("dataset"), + joinedload("dataset.actions"), + joinedload("dataset.actions.role"), + joinedload("tags"))) self._active_datasets_and_roles = query.all() return self._active_datasets_and_roles @property - def active_contents( self ): + def active_contents(self): """ Return all active contents ordered by hid. """ - return self.contents_iter( types=[ "dataset", "dataset_collection" ], deleted=False, visible=True ) + return self.contents_iter(types=["dataset", "dataset_collection"], deleted=False, visible=True) - def contents_iter( self, **kwds ): + def contents_iter(self, **kwds): """ Fetch filtered list of contents of history. """ @@ -1500,36 +1501,36 @@ def contents_iter( self, **kwds ): types = kwds.get('types', default_contents_types) iters = [] if 'dataset' in types: - iters.append( self.__dataset_contents_iter( **kwds ) ) + iters.append(self.__dataset_contents_iter(**kwds)) if 'dataset_collection' in types: - iters.append( self.__collection_contents_iter( **kwds ) ) - return galaxy.util.merge_sorted_iterables( operator.attrgetter( "hid" ), *iters ) + iters.append(self.__collection_contents_iter(**kwds)) + return galaxy.util.merge_sorted_iterables(operator.attrgetter("hid"), *iters) def __dataset_contents_iter(self, **kwds): - return self.__filter_contents( HistoryDatasetAssociation, **kwds ) + return self.__filter_contents(HistoryDatasetAssociation, **kwds) - def __filter_contents( self, content_class, **kwds ): - db_session = object_session( self ) + def __filter_contents(self, content_class, **kwds): + db_session = object_session(self) assert db_session is not None - query = db_session.query( content_class ).filter( content_class.table.c.history_id == self.id ) - query = query.order_by( content_class.table.c.hid.asc() ) - deleted = galaxy.util.string_as_bool_or_none( kwds.get( 'deleted', None ) ) + query = db_session.query(content_class).filter(content_class.table.c.history_id == self.id) + query = query.order_by(content_class.table.c.hid.asc()) + deleted = galaxy.util.string_as_bool_or_none(kwds.get('deleted', None)) if deleted is not None: - query = query.filter( content_class.deleted == deleted ) - visible = galaxy.util.string_as_bool_or_none( kwds.get( 'visible', None ) ) + query = query.filter(content_class.deleted == deleted) + visible = galaxy.util.string_as_bool_or_none(kwds.get('visible', None)) if visible is not None: - query = query.filter( content_class.visible == visible ) + query = query.filter(content_class.visible == visible) if 'ids' in kwds: ids = kwds['ids'] max_in_filter_length = kwds.get('max_in_filter_length', MAX_IN_FILTER_LENGTH) if len(ids) < max_in_filter_length: - query = query.filter( content_class.id.in_(ids) ) + query = query.filter(content_class.id.in_(ids)) else: query = (content for content in query if content.id in ids) return query - def __collection_contents_iter( self, **kwds ): - return self.__filter_contents( HistoryDatasetCollectionAssociation, **kwds ) + def __collection_contents_iter(self, **kwds): + return self.__filter_contents(HistoryDatasetCollectionAssociation, **kwds) def copy_tags_from(self, target_user, source_history): for src_shta in source_history.tags: @@ -1538,27 +1539,27 @@ def copy_tags_from(self, target_user, source_history): self.tags.append(new_shta) -class HistoryUserShareAssociation( object ): - def __init__( self ): +class HistoryUserShareAssociation(object): + def __init__(self): self.history = None self.user = None -class UserRoleAssociation( object ): - def __init__( self, user, role ): +class UserRoleAssociation(object): + def __init__(self, user, role): self.user = user self.role = role -class GroupRoleAssociation( object ): - def __init__( self, group, role ): +class GroupRoleAssociation(object): + def __init__(self, group, role): self.group = group self.role = role -class Role( object, Dictifiable ): - dict_collection_visible_keys = ( 'id', 'name' ) - dict_element_visible_keys = ( 'id', 'name', 'description', 'type' ) +class Role(object, Dictifiable): + dict_collection_visible_keys = ('id', 'name') + dict_element_visible_keys = ('id', 'name', 'description', 'type') private_id = None types = Bunch( PRIVATE='private', @@ -1568,35 +1569,35 @@ class Role( object, Dictifiable ): SHARING='sharing' ) - def __init__( self, name="", description="", type="system", deleted=False ): + def __init__(self, name="", description="", type="system", deleted=False): self.name = name self.description = description self.type = type self.deleted = deleted -class UserQuotaAssociation( object, Dictifiable ): - dict_element_visible_keys = ( 'user', ) +class UserQuotaAssociation(object, Dictifiable): + dict_element_visible_keys = ('user', ) - def __init__( self, user, quota ): + def __init__(self, user, quota): self.user = user self.quota = quota -class GroupQuotaAssociation( object, Dictifiable ): - dict_element_visible_keys = ( 'group', ) +class GroupQuotaAssociation(object, Dictifiable): + dict_element_visible_keys = ('group', ) - def __init__( self, group, quota ): + def __init__(self, group, quota): self.group = group self.quota = quota -class Quota( object, Dictifiable ): - dict_collection_visible_keys = ( 'id', 'name' ) - dict_element_visible_keys = ( 'id', 'name', 'description', 'bytes', 'operation', 'display_amount', 'default', 'users', 'groups' ) - valid_operations = ( '+', '-', '=' ) +class Quota(object, Dictifiable): + dict_collection_visible_keys = ('id', 'name') + dict_element_visible_keys = ('id', 'name', 'description', 'bytes', 'operation', 'display_amount', 'default', 'users', 'groups') + valid_operations = ('+', '-', '=') - def __init__( self, name="", description="", amount=0, operation="=" ): + def __init__(self, name="", description="", amount=0, operation="="): self.name = name self.description = description if amount is None: @@ -1605,41 +1606,41 @@ def __init__( self, name="", description="", amount=0, operation="=" ): self.bytes = amount self.operation = operation - def get_amount( self ): + def get_amount(self): if self.bytes == -1: return None return self.bytes - def set_amount( self, amount ): + def set_amount(self, amount): if amount is None: self.bytes = -1 else: self.bytes = amount - amount = property( get_amount, set_amount ) + amount = property(get_amount, set_amount) @property - def display_amount( self ): + def display_amount(self): if self.bytes == -1: return "unlimited" else: - return galaxy.util.nice_size( self.bytes ) + return galaxy.util.nice_size(self.bytes) -class DefaultQuotaAssociation( Quota, Dictifiable ): - dict_element_visible_keys = ( 'type', ) +class DefaultQuotaAssociation(Quota, Dictifiable): + dict_element_visible_keys = ('type', ) types = Bunch( UNREGISTERED='unregistered', REGISTERED='registered' ) - def __init__( self, type, quota ): + def __init__(self, type, quota): assert type in self.types.__dict__.values(), 'Invalid type' self.type = type self.quota = quota -class DatasetPermissions( object ): - def __init__( self, action, dataset, role=None, role_id=None ): +class DatasetPermissions(object): + def __init__(self, action, dataset, role=None, role_id=None): self.action = action self.dataset = dataset if role is not None: @@ -1648,78 +1649,78 @@ def __init__( self, action, dataset, role=None, role_id=None ): self.role_id = role_id -class LibraryPermissions( object ): - def __init__( self, action, library_item, role ): +class LibraryPermissions(object): + def __init__(self, action, library_item, role): self.action = action - if isinstance( library_item, Library ): + if isinstance(library_item, Library): self.library = library_item else: - raise Exception( "Invalid Library specified: %s" % library_item.__class__.__name__ ) + raise Exception("Invalid Library specified: %s" % library_item.__class__.__name__) self.role = role -class LibraryFolderPermissions( object ): - def __init__( self, action, library_item, role ): +class LibraryFolderPermissions(object): + def __init__(self, action, library_item, role): self.action = action - if isinstance( library_item, LibraryFolder ): + if isinstance(library_item, LibraryFolder): self.folder = library_item else: - raise Exception( "Invalid LibraryFolder specified: %s" % library_item.__class__.__name__ ) + raise Exception("Invalid LibraryFolder specified: %s" % library_item.__class__.__name__) self.role = role -class LibraryDatasetPermissions( object ): - def __init__( self, action, library_item, role ): +class LibraryDatasetPermissions(object): + def __init__(self, action, library_item, role): self.action = action - if isinstance( library_item, LibraryDataset ): + if isinstance(library_item, LibraryDataset): self.library_dataset = library_item else: - raise Exception( "Invalid LibraryDataset specified: %s" % library_item.__class__.__name__ ) + raise Exception("Invalid LibraryDataset specified: %s" % library_item.__class__.__name__) self.role = role -class LibraryDatasetDatasetAssociationPermissions( object ): - def __init__( self, action, library_item, role ): +class LibraryDatasetDatasetAssociationPermissions(object): + def __init__(self, action, library_item, role): self.action = action - if isinstance( library_item, LibraryDatasetDatasetAssociation ): + if isinstance(library_item, LibraryDatasetDatasetAssociation): self.library_dataset_dataset_association = library_item else: - raise Exception( "Invalid LibraryDatasetDatasetAssociation specified: %s" % library_item.__class__.__name__ ) + raise Exception("Invalid LibraryDatasetDatasetAssociation specified: %s" % library_item.__class__.__name__) self.role = role -class DefaultUserPermissions( object ): - def __init__( self, user, action, role ): +class DefaultUserPermissions(object): + def __init__(self, user, action, role): self.user = user self.action = action self.role = role -class DefaultHistoryPermissions( object ): - def __init__( self, history, action, role ): +class DefaultHistoryPermissions(object): + def __init__(self, history, action, role): self.history = history self.action = action self.role = role -class StorableObject( object ): +class StorableObject(object): - def __init__( self, id, **kwargs): + def __init__(self, id, **kwargs): self.id = id -class Dataset( StorableObject ): - states = Bunch( NEW='new', - UPLOAD='upload', - QUEUED='queued', - RUNNING='running', - OK='ok', - EMPTY='empty', - ERROR='error', - DISCARDED='discarded', - PAUSED='paused', - SETTING_METADATA='setting_metadata', - FAILED_METADATA='failed_metadata') +class Dataset(StorableObject): + states = Bunch(NEW='new', + UPLOAD='upload', + QUEUED='queued', + RUNNING='running', + OK='ok', + EMPTY='empty', + ERROR='error', + DISCARDED='discarded', + PAUSED='paused', + SETTING_METADATA='setting_metadata', + FAILED_METADATA='failed_metadata') # failed_metadata is only valid as DatasetInstance state currently non_ready_states = ( @@ -1729,9 +1730,9 @@ class Dataset( StorableObject ): states.RUNNING, states.SETTING_METADATA ) - ready_states = tuple( set( states.__dict__.values() ) - set( non_ready_states ) ) + ready_states = tuple(set(states.__dict__.values()) - set(non_ready_states)) valid_input_states = tuple( - set( states.__dict__.values() ) - set( [states.ERROR, states.DISCARDED] ) + set(states.__dict__.values()) - set([states.ERROR, states.DISCARDED]) ) terminal_states = ( states.OK, @@ -1741,21 +1742,21 @@ class Dataset( StorableObject ): states.FAILED_METADATA, ) - conversion_messages = Bunch( PENDING="pending", - NO_DATA="no data", - NO_CHROMOSOME="no chromosome", - NO_CONVERTER="no converter", - NO_TOOL="no tool", - DATA="data", - ERROR="error", - OK="ok" ) + conversion_messages = Bunch(PENDING="pending", + NO_DATA="no data", + NO_CHROMOSOME="no chromosome", + NO_CONVERTER="no converter", + NO_TOOL="no tool", + DATA="data", + ERROR="error", + OK="ok") - permitted_actions = get_permitted_actions( filter='DATASET' ) + permitted_actions = get_permitted_actions(filter='DATASET') file_path = "/tmp/" object_store = None # This get initialized in mapping.py (method init) by app.py engine = None - def __init__( self, id=None, state=None, external_filename=None, extra_files_path=None, file_size=None, purgable=True, uuid=None ): + def __init__(self, id=None, state=None, external_filename=None, extra_files_path=None, file_size=None, purgable=True, uuid=None): super(Dataset, self).__init__(id=id) self.state = state self.deleted = False @@ -1770,44 +1771,44 @@ def __init__( self, id=None, state=None, external_filename=None, extra_files_pat else: self.uuid = UUID(str(uuid)) - def in_ready_state( self ): + def in_ready_state(self): return self.state in self.ready_states - def get_file_name( self ): + def get_file_name(self): if not self.external_filename: assert self.id is not None, "ID must be set before filename used (commit the object)" assert self.object_store is not None, "Object Store has not been initialized for dataset %s" % self.id - filename = self.object_store.get_filename( self ) + filename = self.object_store.get_filename(self) return filename else: filename = self.external_filename # Make filename absolute - return os.path.abspath( filename ) + return os.path.abspath(filename) - def set_file_name( self, filename ): + def set_file_name(self, filename): if not filename: self.external_filename = None else: self.external_filename = filename - file_name = property( get_file_name, set_file_name ) + file_name = property(get_file_name, set_file_name) - def get_extra_files_path( self ): + def get_extra_files_path(self): # Unlike get_file_name - external_extra_files_path is not backed by an # actual database column so if SA instantiates this object - the # attribute won't exist yet. - if not getattr( self, "external_extra_files_path", None ): - return self.object_store.get_filename( self, dir_only=True, extra_dir=self._extra_files_path or "dataset_%d_files" % self.id ) + if not getattr(self, "external_extra_files_path", None): + return self.object_store.get_filename(self, dir_only=True, extra_dir=self._extra_files_path or "dataset_%d_files" % self.id) else: - return os.path.abspath( self.external_extra_files_path ) + return os.path.abspath(self.external_extra_files_path) - def set_extra_files_path( self, extra_files_path ): + def set_extra_files_path(self, extra_files_path): if not extra_files_path: self.external_extra_files_path = None else: self.external_extra_files_path = extra_files_path - extra_files_path = property( get_extra_files_path, set_extra_files_path) + extra_files_path = property(get_extra_files_path, set_extra_files_path) - def _calculate_size( self ): + def _calculate_size(self): if self.external_filename: try: return os.path.getsize(self.external_filename) @@ -1816,53 +1817,53 @@ def _calculate_size( self ): else: return self.object_store.size(self) - def get_size( self, nice_size=False ): + def get_size(self, nice_size=False): """Returns the size of the data on disk""" if self.file_size: if nice_size: - return galaxy.util.nice_size( self.file_size ) + return galaxy.util.nice_size(self.file_size) else: return self.file_size else: if nice_size: - return galaxy.util.nice_size( self._calculate_size() ) + return galaxy.util.nice_size(self._calculate_size()) else: return self._calculate_size() - def set_size( self ): + def set_size(self): """Returns the size of the data on disk""" if not self.file_size: self.file_size = self._calculate_size() - def get_total_size( self ): + def get_total_size(self): if self.total_size is not None: return self.total_size # for backwards compatibility, set if unset self.set_total_size() - db_session = object_session( self ) + db_session = object_session(self) db_session.flush() return self.total_size - def set_total_size( self ): + def set_total_size(self): if self.file_size is None: self.set_size() self.total_size = self.file_size or 0 if self.object_store.exists(self, extra_dir=self._extra_files_path or "dataset_%d_files" % self.id, dir_only=True): - for root, dirs, files in os.walk( self.extra_files_path ): - self.total_size += sum( [ os.path.getsize( os.path.join( root, file ) ) for file in files if os.path.exists( os.path.join( root, file ) ) ] ) + for root, dirs, files in os.walk(self.extra_files_path): + self.total_size += sum([os.path.getsize(os.path.join(root, file)) for file in files if os.path.exists(os.path.join(root, file))]) - def has_data( self ): + def has_data(self): """Detects whether there is any data""" return self.get_size() > 0 - def mark_deleted( self, include_children=True ): + def mark_deleted(self, include_children=True): self.deleted = True - def is_multi_byte( self ): + def is_multi_byte(self): if not self.has_data(): return False try: - return is_multi_byte( codecs.open( self.file_name, 'r', 'utf-8' ).read( 100 ) ) + return is_multi_byte(codecs.open(self.file_name, 'r', 'utf-8').read(100)) except UnicodeDecodeError: return False # FIXME: sqlalchemy will replace this @@ -1872,12 +1873,12 @@ def _delete(self): self.object_store.delete(self) @property - def user_can_purge( self ): + def user_can_purge(self): return self.purged is False \ - and not bool( self.library_associations ) \ - and len( self.history_associations ) == len( self.purged_history_associations ) + and not bool(self.library_associations) \ + and len(self.history_associations) == len(self.purged_history_associations) - def full_delete( self ): + def full_delete(self): """Remove the file and extra files, marks deleted and purged""" # os.unlink( self.file_name ) self.object_store.delete(self) @@ -1889,37 +1890,37 @@ def full_delete( self ): self.deleted = True self.purged = True - def get_access_roles( self, trans ): + def get_access_roles(self, trans): roles = [] for dp in self.actions: if dp.action == trans.app.security_agent.permitted_actions.DATASET_ACCESS.action: - roles.append( dp.role ) + roles.append(dp.role) return roles - def get_manage_permissions_roles( self, trans ): + def get_manage_permissions_roles(self, trans): roles = [] for dp in self.actions: if dp.action == trans.app.security_agent.permitted_actions.DATASET_MANAGE_PERMISSIONS.action: - roles.append( dp.role ) + roles.append(dp.role) return roles - def has_manage_permissions_roles( self, trans ): + def has_manage_permissions_roles(self, trans): for dp in self.actions: if dp.action == trans.app.security_agent.permitted_actions.DATASET_MANAGE_PERMISSIONS.action: return True return False -class DatasetInstance( object ): +class DatasetInstance(object): """A base class for all 'dataset instances', HDAs, LDAs, etc""" states = Dataset.states conversion_messages = Dataset.conversion_messages permitted_actions = Dataset.permitted_actions - def __init__( self, id=None, hid=None, name=None, info=None, blurb=None, peek=None, tool_version=None, extension=None, - dbkey=None, metadata=None, history=None, dataset=None, deleted=False, designation=None, - parent_id=None, validation_errors=None, visible=True, create_dataset=False, sa_session=None, - extended_metadata=None, flush=True ): + def __init__(self, id=None, hid=None, name=None, info=None, blurb=None, peek=None, tool_version=None, extension=None, + dbkey=None, metadata=None, history=None, dataset=None, deleted=False, designation=None, + parent_id=None, validation_errors=None, visible=True, create_dataset=False, sa_session=None, + extended_metadata=None, flush=True): self.name = name or "Unnamed dataset" self.id = id self.info = info @@ -1939,75 +1940,75 @@ def __init__( self, id=None, hid=None, name=None, info=None, blurb=None, peek=No # Relationships if not dataset and create_dataset: # Had to pass the sqlalchemy session in order to create a new dataset - dataset = Dataset( state=Dataset.states.NEW ) + dataset = Dataset(state=Dataset.states.NEW) if flush: - sa_session.add( dataset ) + sa_session.add(dataset) sa_session.flush() self.dataset = dataset self.parent_id = parent_id self.validation_errors = validation_errors @property - def ext( self ): + def ext(self): return self.extension - def get_dataset_state( self ): + def get_dataset_state(self): # self._state is currently only used when setting metadata externally # leave setting the state as-is, we'll currently handle this specially in the external metadata code if self._state: return self._state return self.dataset.state - def raw_set_dataset_state( self, state ): + def raw_set_dataset_state(self, state): if state != self.dataset.state: self.dataset.state = state return True else: return False - def set_dataset_state( self, state ): - if self.raw_set_dataset_state( state ): - object_session( self ).add( self.dataset ) - object_session( self ).flush() # flush here, because hda.flush() won't flush the Dataset object - state = property( get_dataset_state, set_dataset_state ) + def set_dataset_state(self, state): + if self.raw_set_dataset_state(state): + object_session(self).add(self.dataset) + object_session(self).flush() # flush here, because hda.flush() won't flush the Dataset object + state = property(get_dataset_state, set_dataset_state) - def get_file_name( self ): + def get_file_name(self): return self.dataset.get_file_name() def set_file_name(self, filename): - return self.dataset.set_file_name( filename ) - file_name = property( get_file_name, set_file_name ) + return self.dataset.set_file_name(filename) + file_name = property(get_file_name, set_file_name) @property - def extra_files_path( self ): + def extra_files_path(self): return self.dataset.extra_files_path @property - def datatype( self ): + def datatype(self): extension = self.extension if not extension or extension == 'auto' or extension == '_sniff_': extension = 'data' - ret = _get_datatypes_registry().get_datatype_by_extension( extension ) + ret = _get_datatypes_registry().get_datatype_by_extension(extension) if ret is None: log.warning("Datatype class not found for extension '%s'" % extension) - return _get_datatypes_registry().get_datatype_by_extension( 'data' ) + return _get_datatypes_registry().get_datatype_by_extension('data') return ret - def get_metadata( self ): + def get_metadata(self): # using weakref to store parent (to prevent circ ref), # does a Session.clear() cause parent to be invalidated, while still copying over this non-database attribute? - if not hasattr( self, '_metadata_collection' ) or self._metadata_collection.parent != self: - self._metadata_collection = galaxy.model.metadata.MetadataCollection( self ) + if not hasattr(self, '_metadata_collection') or self._metadata_collection.parent != self: + self._metadata_collection = galaxy.model.metadata.MetadataCollection(self) return self._metadata_collection - def set_metadata( self, bunch ): + def set_metadata(self, bunch): # Needs to accept a MetadataCollection, a bunch, or a dict - self._metadata = self.metadata.make_dict_copy( bunch ) - metadata = property( get_metadata, set_metadata ) + self._metadata = self.metadata.make_dict_copy(bunch) + metadata = property(get_metadata, set_metadata) # This provide backwards compatibility with using the old dbkey # field in the database. That field now maps to "old_dbkey" (see mapping.py). - def get_dbkey( self ): + def get_dbkey(self): dbkey = self.metadata.dbkey if not isinstance(dbkey, list): dbkey = [dbkey] @@ -2015,88 +2016,88 @@ def get_dbkey( self ): return "?" return dbkey[0] - def set_dbkey( self, value ): + def set_dbkey(self, value): if "dbkey" in self.datatype.metadata_spec: if not isinstance(value, list): self.metadata.dbkey = [value] else: self.metadata.dbkey = value - dbkey = property( get_dbkey, set_dbkey ) + dbkey = property(get_dbkey, set_dbkey) - def change_datatype( self, new_ext ): + def change_datatype(self, new_ext): self.clear_associated_files() - _get_datatypes_registry().change_datatype( self, new_ext ) + _get_datatypes_registry().change_datatype(self, new_ext) - def get_size( self, nice_size=False ): + def get_size(self, nice_size=False): """Returns the size of the data on disk""" if nice_size: - return galaxy.util.nice_size( self.dataset.get_size() ) + return galaxy.util.nice_size(self.dataset.get_size()) return self.dataset.get_size() - def set_size( self ): + def set_size(self): """Returns the size of the data on disk""" return self.dataset.set_size() - def get_total_size( self ): + def get_total_size(self): return self.dataset.get_total_size() - def set_total_size( self ): + def set_total_size(self): return self.dataset.set_total_size() - def has_data( self ): + def has_data(self): """Detects whether there is any data""" return self.dataset.has_data() - def get_raw_data( self ): + def get_raw_data(self): """Returns the full data. To stream it open the file_name and read/write as needed""" - return self.datatype.get_raw_data( self ) + return self.datatype.get_raw_data(self) - def write_from_stream( self, stream ): + def write_from_stream(self, stream): """Writes data from a stream""" self.datatype.write_from_stream(self, stream) - def set_raw_data( self, data ): + def set_raw_data(self, data): """Saves the data on the disc""" self.datatype.set_raw_data(self, data) - def get_mime( self ): + def get_mime(self): """Returns the mime type of the data""" try: - return _get_datatypes_registry().get_mimetype_by_extension( self.extension.lower() ) + return _get_datatypes_registry().get_mimetype_by_extension(self.extension.lower()) except AttributeError: # extension is None return 'data' - def is_multi_byte( self ): + def is_multi_byte(self): """Data consists of multi-byte characters""" return self.dataset.is_multi_byte() - def set_peek( self, is_multi_byte=False ): - return self.datatype.set_peek( self, is_multi_byte=is_multi_byte ) + def set_peek(self, is_multi_byte=False): + return self.datatype.set_peek(self, is_multi_byte=is_multi_byte) - def init_meta( self, copy_from=None ): - return self.datatype.init_meta( self, copy_from=copy_from ) + def init_meta(self, copy_from=None): + return self.datatype.init_meta(self, copy_from=copy_from) - def set_meta( self, **kwd ): - self.clear_associated_files( metadata_safe=True ) - return self.datatype.set_meta( self, **kwd ) + def set_meta(self, **kwd): + self.clear_associated_files(metadata_safe=True) + return self.datatype.set_meta(self, **kwd) - def missing_meta( self, **kwd ): - return self.datatype.missing_meta( self, **kwd ) + def missing_meta(self, **kwd): + return self.datatype.missing_meta(self, **kwd) - def as_display_type( self, type, **kwd ): - return self.datatype.as_display_type( self, type, **kwd ) + def as_display_type(self, type, **kwd): + return self.datatype.as_display_type(self, type, **kwd) - def display_peek( self ): - return self.datatype.display_peek( self ) + def display_peek(self): + return self.datatype.display_peek(self) - def display_name( self ): - return self.datatype.display_name( self ) + def display_name(self): + return self.datatype.display_name(self) - def display_info( self ): - return self.datatype.display_info( self ) + def display_info(self): + return self.datatype.display_info(self) - def get_converted_files_by_type( self, file_type ): + def get_converted_files_by_type(self, file_type): for assoc in self.implicitly_converted_datasets: if not assoc.deleted and assoc.type == file_type: if assoc.dataset: @@ -2113,7 +2114,7 @@ def get_converted_dataset_deps(self, trans, target_ext): depends_list = trans.app.datatypes_registry.converter_deps[self.extension][target_ext] except KeyError: depends_list = [] - return dict([ (dep, self.get_converted_dataset(trans, dep)) for dep in depends_list ]) + return dict([(dep, self.get_converted_dataset(trans, dep)) for dep in depends_list]) def get_converted_dataset(self, trans, target_ext, target_context=None, history=None): """ @@ -2122,7 +2123,7 @@ def get_converted_dataset(self, trans, target_ext, target_context=None, history= """ # See if we can convert the dataset if target_ext not in self.get_converter_types(): - raise NoConverterException("Conversion from '%s' to '%s' not possible" % (self.extension, target_ext) ) + raise NoConverterException("Conversion from '%s' to '%s' not possible" % (self.extension, target_ext)) deps = {} # List of string of dependencies try: @@ -2130,10 +2131,10 @@ def get_converted_dataset(self, trans, target_ext, target_context=None, history= except KeyError: depends_list = [] # See if converted dataset already exists, either in metadata in conversions. - converted_dataset = self.get_metadata_dataset( target_ext ) + converted_dataset = self.get_metadata_dataset(target_ext) if converted_dataset: return converted_dataset - converted_dataset = self.get_converted_files_by_type( target_ext ) + converted_dataset = self.get_converted_files_by_type(target_ext) if converted_dataset: return converted_dataset # Conversion is possible but hasn't been done yet, run converter. @@ -2154,23 +2155,23 @@ def get_converted_dataset(self, trans, target_ext, target_context=None, history= raise NoConverterException("A dependency (%s) is missing a converter." % dependency) except KeyError: pass # No deps - new_dataset = next(iter(self.datatype.convert_dataset( trans, self, target_ext, return_output=True, visible=False, deps=deps, target_context=target_context, history=history ).values())) + new_dataset = next(iter(self.datatype.convert_dataset(trans, self, target_ext, return_output=True, visible=False, deps=deps, target_context=target_context, history=history).values())) new_dataset.name = self.name - self.copy_attributes( new_dataset ) - assoc = ImplicitlyConvertedDatasetAssociation( parent=self, file_type=target_ext, dataset=new_dataset, metadata_safe=False ) + self.copy_attributes(new_dataset) + assoc = ImplicitlyConvertedDatasetAssociation(parent=self, file_type=target_ext, dataset=new_dataset, metadata_safe=False) session = trans.sa_session - session.add( new_dataset ) - session.add( assoc ) + session.add(new_dataset) + session.add(assoc) session.flush() return new_dataset - def copy_attributes( self, new_dataset ): + def copy_attributes(self, new_dataset): """ Copies attributes to a new datasets, used for implicit conversions """ pass - def get_metadata_dataset( self, dataset_ext ): + def get_metadata_dataset(self, dataset_ext): """ Returns an HDA that points to a metadata file which contains a converted data with the requested extension. @@ -2178,15 +2179,15 @@ def get_metadata_dataset( self, dataset_ext ): for name, value in self.metadata.items(): # HACK: MetadataFile objects do not have a type/ext, so need to use metadata name # to determine type. - if dataset_ext == 'bai' and name == 'bam_index' and isinstance( value, MetadataFile ): + if dataset_ext == 'bai' and name == 'bam_index' and isinstance(value, MetadataFile): # HACK: MetadataFile objects cannot be used by tools, so return # a fake HDA that points to metadata file. - fake_dataset = Dataset( state=Dataset.states.OK, external_filename=value.file_name ) - fake_hda = HistoryDatasetAssociation( dataset=fake_dataset ) + fake_dataset = Dataset(state=Dataset.states.OK, external_filename=value.file_name) + fake_hda = HistoryDatasetAssociation(dataset=fake_dataset) return fake_hda - def clear_associated_files( self, metadata_safe=False, purge=False ): - raise Exception( "Unimplemented" ) + def clear_associated_files(self, metadata_safe=False, purge=False): + raise Exception("Unimplemented") def get_child_by_designation(self, designation): for child in self.children: @@ -2195,40 +2196,40 @@ def get_child_by_designation(self, designation): return None def get_converter_types(self): - return self.datatype.get_converter_types( self, _get_datatypes_registry() ) + return self.datatype.get_converter_types(self, _get_datatypes_registry()) def can_convert_to(self, format): return format in self.get_converter_types() - def find_conversion_destination( self, accepted_formats, **kwd ): + def find_conversion_destination(self, accepted_formats, **kwd): """Returns ( target_ext, existing converted dataset )""" - return self.datatype.find_conversion_destination( self, accepted_formats, _get_datatypes_registry(), **kwd ) + return self.datatype.find_conversion_destination(self, accepted_formats, _get_datatypes_registry(), **kwd) - def add_validation_error( self, validation_error ): - self.validation_errors.append( validation_error ) + def add_validation_error(self, validation_error): + self.validation_errors.append(validation_error) - def extend_validation_errors( self, validation_errors ): + def extend_validation_errors(self, validation_errors): self.validation_errors.extend(validation_errors) - def mark_deleted( self, include_children=True ): + def mark_deleted(self, include_children=True): self.deleted = True if include_children: for child in self.children: child.mark_deleted() - def mark_undeleted( self, include_children=True ): + def mark_undeleted(self, include_children=True): self.deleted = False if include_children: for child in self.children: child.mark_undeleted() - def mark_unhidden( self, include_children=True ): + def mark_unhidden(self, include_children=True): self.visible = True if include_children: for child in self.children: child.mark_unhidden() - def undeletable( self ): + def undeletable(self): if self.purged: return False return True @@ -2238,53 +2239,53 @@ def is_ok(self): return self.state == self.states.OK @property - def is_pending( self ): + def is_pending(self): """ Return true if the dataset is neither ready nor in error """ - return self.state in ( self.states.NEW, self.states.UPLOAD, - self.states.QUEUED, self.states.RUNNING, - self.states.SETTING_METADATA ) + return self.state in (self.states.NEW, self.states.UPLOAD, + self.states.QUEUED, self.states.RUNNING, + self.states.SETTING_METADATA) @property - def source_library_dataset( self ): - def get_source( dataset ): - if isinstance( dataset, LibraryDatasetDatasetAssociation ): + def source_library_dataset(self): + def get_source(dataset): + if isinstance(dataset, LibraryDatasetDatasetAssociation): if dataset.library_dataset: - return ( dataset, dataset.library_dataset ) + return (dataset, dataset.library_dataset) if dataset.copied_from_library_dataset_dataset_association: - source = get_source( dataset.copied_from_library_dataset_dataset_association ) + source = get_source(dataset.copied_from_library_dataset_dataset_association) if source: return source if dataset.copied_from_history_dataset_association: - source = get_source( dataset.copied_from_history_dataset_association ) + source = get_source(dataset.copied_from_history_dataset_association) if source: return source - return ( None, None ) - return get_source( self ) + return (None, None) + return get_source(self) @property - def source_dataset_chain( self ): - def _source_dataset_chain( dataset, lst ): + def source_dataset_chain(self): + def _source_dataset_chain(dataset, lst): try: cp_from_ldda = dataset.copied_from_library_dataset_dataset_association if cp_from_ldda: - lst.append( (cp_from_ldda, "(Data Library)") ) - return _source_dataset_chain( cp_from_ldda, lst ) + lst.append((cp_from_ldda, "(Data Library)")) + return _source_dataset_chain(cp_from_ldda, lst) except Exception as e: - log.warning( e ) + log.warning(e) try: cp_from_hda = dataset.copied_from_history_dataset_association if cp_from_hda: - lst.append( (cp_from_hda, cp_from_hda.history.name) ) - return _source_dataset_chain( cp_from_hda, lst ) + lst.append((cp_from_hda, cp_from_hda.history.name)) + return _source_dataset_chain(cp_from_hda, lst) except Exception as e: - log.warning( e ) + log.warning(e) return lst - return _source_dataset_chain( self, [] ) + return _source_dataset_chain(self, []) @property - def creating_job( self ): + def creating_job(self): creating_job_associations = None if self.creating_job_associations: creating_job_associations = self.creating_job_associations @@ -2296,13 +2297,13 @@ def creating_job( self ): return creating_job_associations[0].job return None - def get_display_applications( self, trans ): - return self.datatype.get_display_applications_by_dataset( self, trans ) + def get_display_applications(self, trans): + return self.datatype.get_display_applications_by_dataset(self, trans) - def get_visualizations( self ): - return self.datatype.get_visualizations( self ) + def get_visualizations(self): + return self.datatype.get_visualizations(self) - def get_datasources( self, trans ): + def get_datasources(self, trans): """ Returns datasources for dataset; if datasources are not available due to indexing, indexing is started. Return value is a dictionary @@ -2319,12 +2320,12 @@ def get_datasources( self, trans ): data_source = source_list else: # Convert. - if isinstance( source_list, string_types ): - source_list = [ source_list ] + if isinstance(source_list, string_types): + source_list = [source_list] # Loop through sources until viable one is found. for source in source_list: - msg = self.convert_dataset( trans, source ) + msg = self.convert_dataset(trans, source) # No message or PENDING means that source is viable. No # message indicates conversion was done and is successful. if not msg or msg == self.conversion_messages.PENDING: @@ -2332,11 +2333,11 @@ def get_datasources( self, trans ): break # Store msg. - data_sources_dict[ source_type ] = { "name": data_source, "message": msg } + data_sources_dict[source_type] = {"name": data_source, "message": msg} return data_sources_dict - def convert_dataset( self, trans, target_type ): + def convert_dataset(self, trans, target_type): """ Converts a dataset to the target_type and returns a message indicating status of the conversion. None is returned to indicate that dataset @@ -2345,89 +2346,89 @@ def convert_dataset( self, trans, target_type ): # Get converted dataset; this will start the conversion if necessary. try: - converted_dataset = self.get_converted_dataset( trans, target_type ) + converted_dataset = self.get_converted_dataset(trans, target_type) except NoConverterException: return self.conversion_messages.NO_CONVERTER except ConverterDependencyException as dep_error: - return { 'kind': self.conversion_messages.ERROR, 'message': dep_error.value } + return {'kind': self.conversion_messages.ERROR, 'message': dep_error.value} # Check dataset state and return any messages. msg = None if converted_dataset and converted_dataset.state == Dataset.states.ERROR: - job_id = trans.sa_session.query( JobToOutputDatasetAssociation ) \ - .filter_by( dataset_id=converted_dataset.id ).first().job_id - job = trans.sa_session.query( Job ).get( job_id ) - msg = { 'kind': self.conversion_messages.ERROR, 'message': job.stderr } + job_id = trans.sa_session.query(JobToOutputDatasetAssociation) \ + .filter_by(dataset_id=converted_dataset.id).first().job_id + job = trans.sa_session.query(Job).get(job_id) + msg = {'kind': self.conversion_messages.ERROR, 'message': job.stderr} elif not converted_dataset or converted_dataset.state != Dataset.states.OK: msg = self.conversion_messages.PENDING return msg -class HistoryDatasetAssociation( DatasetInstance, HasTags, Dictifiable, UsesAnnotations, - HasName ): +class HistoryDatasetAssociation(DatasetInstance, HasTags, Dictifiable, UsesAnnotations, + HasName): """ Resource class that creates a relation between a dataset and a user history. """ - def __init__( self, - hid=None, - history=None, - copied_from_history_dataset_association=None, - copied_from_library_dataset_dataset_association=None, - sa_session=None, - **kwd ): + def __init__(self, + hid=None, + history=None, + copied_from_history_dataset_association=None, + copied_from_library_dataset_dataset_association=None, + sa_session=None, + **kwd): """ Create a a new HDA and associate it with the given history. """ # FIXME: sa_session is must be passed to DataSetInstance if the create_dataset # parameter is True so that the new object can be flushed. Is there a better way? - DatasetInstance.__init__( self, sa_session=sa_session, **kwd ) + DatasetInstance.__init__(self, sa_session=sa_session, **kwd) self.hid = hid # Relationships self.history = history self.copied_from_history_dataset_association = copied_from_history_dataset_association self.copied_from_library_dataset_dataset_association = copied_from_library_dataset_dataset_association - def copy( self, copy_children=False, parent_id=None ): + def copy(self, copy_children=False, parent_id=None): """ Create a copy of this HDA. """ - hda = HistoryDatasetAssociation( hid=self.hid, - name=self.name, - info=self.info, - blurb=self.blurb, - peek=self.peek, - tool_version=self.tool_version, - extension=self.extension, - dbkey=self.dbkey, - dataset=self.dataset, - visible=self.visible, - deleted=self.deleted, - parent_id=parent_id, - copied_from_history_dataset_association=self ) + hda = HistoryDatasetAssociation(hid=self.hid, + name=self.name, + info=self.info, + blurb=self.blurb, + peek=self.peek, + tool_version=self.tool_version, + extension=self.extension, + dbkey=self.dbkey, + dataset=self.dataset, + visible=self.visible, + deleted=self.deleted, + parent_id=parent_id, + copied_from_history_dataset_association=self) # update init non-keywords as well hda.purged = self.purged - object_session( self ).add( hda ) - object_session( self ).flush() + object_session(self).add(hda) + object_session(self).flush() hda.set_size() # Need to set after flushed, as MetadataFiles require dataset.id hda.metadata = self.metadata if copy_children: for child in self.children: - child.copy( copy_children=copy_children, parent_id=hda.id ) + child.copy(copy_children=copy_children, parent_id=hda.id) if not self.datatype.copy_safe_peek: # In some instances peek relies on dataset_id, i.e. gmaj.zip for viewing MAFs hda.set_peek() - object_session( self ).flush() + object_session(self).flush() return hda - def copy_attributes( self, new_dataset ): + def copy_attributes(self, new_dataset): new_dataset.hid = self.hid - def to_library_dataset_dataset_association( self, trans, target_folder, replace_dataset=None, - parent_id=None, user=None, roles=None, ldda_message='', element_identifier=None ): + def to_library_dataset_dataset_association(self, trans, target_folder, replace_dataset=None, + parent_id=None, user=None, roles=None, ldda_message='', element_identifier=None): """ Copy this HDA to a library optionally replacing an existing LDDA. """ @@ -2439,75 +2440,76 @@ def to_library_dataset_dataset_association( self, trans, target_folder, replace_ # If replace_dataset is None, the Library level permissions will be taken from the folder and # applied to the new LibraryDataset, and the current user's DefaultUserPermissions will be applied # to the associated Dataset. - library_dataset = LibraryDataset( folder=target_folder, name=self.name, info=self.info ) - object_session( self ).add( library_dataset ) - object_session( self ).flush() + library_dataset = LibraryDataset(folder=target_folder, name=self.name, info=self.info) + object_session(self).add(library_dataset) + object_session(self).flush() if not user: # This should never happen since users must be authenticated to upload to a data library user = self.history.user - ldda = LibraryDatasetDatasetAssociation( name=element_identifier or self.name, - info=self.info, - blurb=self.blurb, - peek=self.peek, - tool_version=self.tool_version, - extension=self.extension, - dbkey=self.dbkey, - dataset=self.dataset, - library_dataset=library_dataset, - visible=self.visible, - deleted=self.deleted, - parent_id=parent_id, - copied_from_history_dataset_association=self, - user=user ) - object_session( self ).add( ldda ) - object_session( self ).flush() + ldda = LibraryDatasetDatasetAssociation(name=element_identifier or self.name, + info=self.info, + blurb=self.blurb, + peek=self.peek, + tool_version=self.tool_version, + extension=self.extension, + dbkey=self.dbkey, + dataset=self.dataset, + library_dataset=library_dataset, + visible=self.visible, + deleted=self.deleted, + parent_id=parent_id, + copied_from_history_dataset_association=self, + user=user) + object_session(self).add(ldda) + object_session(self).flush() # If roles were selected on the upload form, restrict access to the Dataset to those roles roles = roles or [] for role in roles: - dp = trans.model.DatasetPermissions( trans.app.security_agent.permitted_actions.DATASET_ACCESS.action, - ldda.dataset, role ) - trans.sa_session.add( dp ) + dp = trans.model.DatasetPermissions(trans.app.security_agent.permitted_actions.DATASET_ACCESS.action, + ldda.dataset, role) + trans.sa_session.add(dp) trans.sa_session.flush() # Must set metadata after ldda flushed, as MetadataFiles require ldda.id ldda.metadata = self.metadata + # TODO: copy #tags from history if ldda_message: ldda.message = ldda_message if not replace_dataset: - target_folder.add_library_dataset( library_dataset, genome_build=ldda.dbkey ) - object_session( self ).add( target_folder ) - object_session( self ).flush() + target_folder.add_library_dataset(library_dataset, genome_build=ldda.dbkey) + object_session(self).add(target_folder) + object_session(self).flush() library_dataset.library_dataset_dataset_association_id = ldda.id - object_session( self ).add( library_dataset ) - object_session( self ).flush() + object_session(self).add(library_dataset) + object_session(self).flush() for child in self.children: - child.to_library_dataset_dataset_association( trans, - target_folder=target_folder, - replace_dataset=replace_dataset, - parent_id=ldda.id, - user=ldda.user ) + child.to_library_dataset_dataset_association(trans, + target_folder=target_folder, + replace_dataset=replace_dataset, + parent_id=ldda.id, + user=ldda.user) if not self.datatype.copy_safe_peek: # In some instances peek relies on dataset_id, i.e. gmaj.zip for viewing MAFs ldda.set_peek() - object_session( self ).flush() + object_session(self).flush() return ldda - def clear_associated_files( self, metadata_safe=False, purge=False ): + def clear_associated_files(self, metadata_safe=False, purge=False): """ """ # metadata_safe = True means to only clear when assoc.metadata_safe == False for assoc in self.implicitly_converted_datasets: - if not assoc.deleted and ( not metadata_safe or not assoc.metadata_safe ): - assoc.clear( purge=purge ) + if not assoc.deleted and (not metadata_safe or not assoc.metadata_safe): + assoc.clear(purge=purge) for assoc in self.implicitly_converted_parent_datasets: - assoc.clear( purge=purge, delete_dataset=False ) + assoc.clear(purge=purge, delete_dataset=False) - def get_access_roles( self, trans ): + def get_access_roles(self, trans): """ Return The access roles associated with this HDA's dataset. """ - return self.dataset.get_access_roles( trans ) + return self.dataset.get_access_roles(trans) - def quota_amount( self, user ): + def quota_amount(self, user): """ Return the disk space used for this HDA relevant to user quotas. @@ -2529,38 +2531,38 @@ def quota_amount( self, user ): else: rval += self.get_total_size() for child in self.children: - rval += child.get_disk_usage( user ) + rval += child.get_disk_usage(user) return rval - def to_dict( self, view='collection', expose_dataset_path=False ): + def to_dict(self, view='collection', expose_dataset_path=False): """ Return attributes of this HDA that are exposed using the API. """ # Since this class is a proxy to rather complex attributes we want to # display in other objects, we can't use the simpler method used by # other model classes. - original_rval = super( HistoryDatasetAssociation, self ).to_dict(view=view) + original_rval = super(HistoryDatasetAssociation, self).to_dict(view=view) hda = self - rval = dict( id=hda.id, - hda_ldda='hda', - uuid=( lambda uuid: str( uuid ) if uuid else None )( hda.dataset.uuid ), - hid=hda.hid, - file_ext=hda.ext, - peek=( lambda hda: hda.display_peek() if hda.peek and hda.peek != 'no peek' else None )( hda ), - model_class=self.__class__.__name__, - name=hda.name, - deleted=hda.deleted, - purged=hda.purged, - visible=hda.visible, - state=hda.state, - history_content_type=hda.history_content_type, - file_size=int( hda.get_size() ), - create_time=hda.create_time.isoformat(), - update_time=hda.update_time.isoformat(), - data_type=hda.datatype.__class__.__module__ + '.' + hda.datatype.__class__.__name__, - genome_build=hda.dbkey, - misc_info=hda.info.strip() if isinstance( hda.info, string_types ) else hda.info, - misc_blurb=hda.blurb ) + rval = dict(id=hda.id, + hda_ldda='hda', + uuid=(lambda uuid: str(uuid) if uuid else None)(hda.dataset.uuid), + hid=hda.hid, + file_ext=hda.ext, + peek=(lambda hda: hda.display_peek() if hda.peek and hda.peek != 'no peek' else None)(hda), + model_class=self.__class__.__name__, + name=hda.name, + deleted=hda.deleted, + purged=hda.purged, + visible=hda.visible, + state=hda.state, + history_content_type=hda.history_content_type, + file_size=int(hda.get_size()), + create_time=hda.create_time.isoformat(), + update_time=hda.update_time.isoformat(), + data_type=hda.datatype.__class__.__module__ + '.' + hda.datatype.__class__.__name__, + genome_build=hda.dbkey, + misc_info=hda.info.strip() if isinstance(hda.info, string_types) else hda.info, + misc_blurb=hda.blurb) rval.update(original_rval) @@ -2573,84 +2575,84 @@ def to_dict( self, view='collection', expose_dataset_path=False ): if hda.extended_metadata is not None: rval['extended_metadata'] = hda.extended_metadata.data - rval[ 'peek' ] = to_unicode( hda.display_peek() ) + rval['peek'] = to_unicode(hda.display_peek()) for name, spec in hda.metadata.spec.items(): - val = hda.metadata.get( name ) - if isinstance( val, MetadataFile ): + val = hda.metadata.get(name) + if isinstance(val, MetadataFile): # only when explicitly set: fetching filepaths can be expensive if not expose_dataset_path: continue val = val.file_name # If no value for metadata, look in datatype for metadata. - elif val is None and hasattr( hda.datatype, name ): - val = getattr( hda.datatype, name ) + elif val is None and hasattr(hda.datatype, name): + val = getattr(hda.datatype, name) rval['metadata_' + name] = val return rval @property - def history_content_type( self ): + def history_content_type(self): return "dataset" # TODO: down into DatasetInstance content_type = u'dataset' @hybrid.hybrid_property - def type_id( self ): - return u'-'.join([ self.content_type, str( self.id ) ]) + def type_id(self): + return u'-'.join([self.content_type, str(self.id)]) @type_id.expression - def type_id( cls ): - return (( type_coerce( cls.content_type, types.Unicode ) + u'-' + - type_coerce( cls.id, types.Unicode ) ).label( 'type_id' )) + def type_id(cls): + return ((type_coerce(cls.content_type, types.Unicode) + u'-' + + type_coerce(cls.id, types.Unicode)).label('type_id')) - def copy_tags_from( self, target_user, source_hda ): + def copy_tags_from(self, target_user, source_hda): """ Copy tags from `source_hda` to this HDA and assign them the user `target_user`. """ for source_tag_assoc in source_hda.tags: new_tag_assoc = source_tag_assoc.copy() new_tag_assoc.user = target_user - self.tags.append( new_tag_assoc ) + self.tags.append(new_tag_assoc) -class HistoryDatasetAssociationDisplayAtAuthorization( object ): - def __init__( self, hda=None, user=None, site=None ): +class HistoryDatasetAssociationDisplayAtAuthorization(object): + def __init__(self, hda=None, user=None, site=None): self.history_dataset_association = hda self.user = user self.site = site -class HistoryDatasetAssociationSubset( object ): +class HistoryDatasetAssociationSubset(object): def __init__(self, hda, subset, location): self.hda = hda self.subset = subset self.location = location -class Library( object, Dictifiable, HasName ): - permitted_actions = get_permitted_actions( filter='LIBRARY' ) - dict_collection_visible_keys = ( 'id', 'name' ) - dict_element_visible_keys = ( 'id', 'deleted', 'name', 'description', 'synopsis', 'root_folder_id', 'create_time' ) +class Library(object, Dictifiable, HasName): + permitted_actions = get_permitted_actions(filter='LIBRARY') + dict_collection_visible_keys = ('id', 'name') + dict_element_visible_keys = ('id', 'deleted', 'name', 'description', 'synopsis', 'root_folder_id', 'create_time') - def __init__( self, name=None, description=None, synopsis=None, root_folder=None ): + def __init__(self, name=None, description=None, synopsis=None, root_folder=None): self.name = name or "Unnamed library" self.description = description self.synopsis = synopsis self.root_folder = root_folder - def to_dict( self, view='collection', value_mapper=None ): + def to_dict(self, view='collection', value_mapper=None): """ We prepend an F to folders. """ - rval = super( Library, self ).to_dict( view=view, value_mapper=value_mapper ) + rval = super(Library, self).to_dict(view=view, value_mapper=value_mapper) if 'root_folder_id' in rval: - rval[ 'root_folder_id' ] = 'F' + str(rval[ 'root_folder_id' ]) + rval['root_folder_id'] = 'F' + str(rval['root_folder_id']) return rval - def get_active_folders( self, folder, folders=None ): + def get_active_folders(self, folder, folders=None): # TODO: should we make sure the library is not deleted? - def sort_by_attr( seq, attr ): + def sort_by_attr(seq, attr): """ Sort the sequence of objects by object's attribute Arguments: @@ -2662,16 +2664,16 @@ def sort_by_attr( seq, attr ): # (seq[i].attr, i, seq[i]) and sort it. The second item of tuple is needed not # only to provide stable sorting, but mainly to eliminate comparison of objects # (which can be expensive or prohibited) in case of equal attribute values. - intermed = map( None, (getattr(_, attr) for _ in seq), range( len( seq ) ), seq ) + intermed = map(None, (getattr(_, attr) for _ in seq), range(len(seq)), seq) intermed.sort() return [_[-1] for _ in intermed] if folders is None: - active_folders = [ folder ] + active_folders = [folder] for active_folder in folder.active_folders: - active_folders.extend( self.get_active_folders( active_folder, folders ) ) - return sort_by_attr( active_folders, 'id' ) + active_folders.extend(self.get_active_folders(active_folder, folders)) + return sort_by_attr(active_folders, 'id') - def get_info_association( self, restrict=False, inherited=False ): + def get_info_association(self, restrict=False, inherited=False): if self.info_association: if not inherited or self.info_association[0].inheritable: return self.info_association[0], inherited @@ -2679,7 +2681,7 @@ def get_info_association( self, restrict=False, inherited=False ): return None, inherited return None, inherited - def get_template_widgets( self, trans, get_contents=True ): + def get_template_widgets(self, trans, get_contents=True): # See if we have any associated templates - the returned value for # inherited is not applicable at the library level. The get_contents # param is passed by callers that are inheriting a template - these @@ -2693,41 +2695,41 @@ def get_template_widgets( self, trans, get_contents=True ): # See if we have any field contents info = info_association.info if info: - return template.get_widgets( trans.user, contents=info.content ) - return template.get_widgets( trans.user ) + return template.get_widgets(trans.user, contents=info.content) + return template.get_widgets(trans.user) return [] - def get_access_roles( self, trans ): + def get_access_roles(self, trans): roles = [] for lp in self.actions: if lp.action == trans.app.security_agent.permitted_actions.LIBRARY_ACCESS.action: - roles.append( lp.role ) + roles.append(lp.role) return roles -class LibraryFolder( object, Dictifiable, HasName ): - dict_element_visible_keys = ( 'id', 'parent_id', 'name', 'description', 'item_count', 'genome_build', 'update_time', 'deleted' ) +class LibraryFolder(object, Dictifiable, HasName): + dict_element_visible_keys = ('id', 'parent_id', 'name', 'description', 'item_count', 'genome_build', 'update_time', 'deleted') - def __init__( self, name=None, description=None, item_count=0, order_id=None ): + def __init__(self, name=None, description=None, item_count=0, order_id=None): self.name = name or "Unnamed folder" self.description = description self.item_count = item_count self.order_id = order_id self.genome_build = None - def add_library_dataset( self, library_dataset, genome_build=None ): + def add_library_dataset(self, library_dataset, genome_build=None): library_dataset.folder_id = self.id library_dataset.order_id = self.item_count self.item_count += 1 if genome_build not in [None, '?']: self.genome_build = genome_build - def add_folder( self, folder ): + def add_folder(self, folder): folder.parent_id = self.id folder.order_id = self.item_count self.item_count += 1 - def get_info_association( self, restrict=False, inherited=False ): + def get_info_association(self, restrict=False, inherited=False): # If restrict is True, we will return this folder's info_association, not inheriting. # If restrict is False, we'll return the next available info_association in the # inheritable hierarchy if it is "inheritable". True is also returned if the @@ -2741,12 +2743,12 @@ def get_info_association( self, restrict=False, inherited=False ): if restrict: return None, inherited if self.parent: - return self.parent.get_info_association( inherited=True ) + return self.parent.get_info_association(inherited=True) if self.library_root: - return self.library_root[0].get_info_association( inherited=True ) + return self.library_root[0].get_info_association(inherited=True) return None, inherited - def get_template_widgets( self, trans, get_contents=True ): + def get_template_widgets(self, trans, get_contents=True): # See if we have any associated templates. The get_contents # param is passed by callers that are inheriting a template - these # are usually new library datsets for which we want to include template @@ -2767,18 +2769,18 @@ def get_template_widgets( self, trans, get_contents=True ): if get_contents: info = info_association.info if info: - return template.get_widgets( trans.user, info.content ) + return template.get_widgets(trans.user, info.content) else: - return template.get_widgets( trans.user ) + return template.get_widgets(trans.user) return [] @property - def activatable_library_datasets( self ): + def activatable_library_datasets(self): # This needs to be a list - return [ ld for ld in self.datasets if ld.library_dataset_dataset_association and not ld.library_dataset_dataset_association.dataset.deleted ] + return [ld for ld in self.datasets if ld.library_dataset_dataset_association and not ld.library_dataset_dataset_association.dataset.deleted] - def to_dict( self, view='collection', value_mapper=None ): - rval = super( LibraryFolder, self ).to_dict( view=view, value_mapper=value_mapper ) + def to_dict(self, view='collection', value_mapper=None): + rval = super(LibraryFolder, self).to_dict(view=view, value_mapper=value_mapper) info_association, inherited = self.get_info_association() if info_association: if inherited: @@ -2800,34 +2802,34 @@ def library_path(self): return l_path @property - def parent_library( self ): + def parent_library(self): f = self while f.parent: f = f.parent return f.library_root[0] -class LibraryDataset( object ): +class LibraryDataset(object): # This class acts as a proxy to the currently selected LDDA - upload_options = [ ( 'upload_file', 'Upload files' ), - ( 'upload_directory', 'Upload directory of files' ), - ( 'upload_paths', 'Upload files from filesystem paths' ), - ( 'import_from_history', 'Import datasets from your current history' ) ] + upload_options = [('upload_file', 'Upload files'), + ('upload_directory', 'Upload directory of files'), + ('upload_paths', 'Upload files from filesystem paths'), + ('import_from_history', 'Import datasets from your current history')] - def __init__( self, folder=None, order_id=None, name=None, info=None, library_dataset_dataset_association=None, **kwd ): + def __init__(self, folder=None, order_id=None, name=None, info=None, library_dataset_dataset_association=None, **kwd): self.folder = folder self.order_id = order_id self.name = name self.info = info self.library_dataset_dataset_association = library_dataset_dataset_association - def set_library_dataset_dataset_association( self, ldda ): + def set_library_dataset_dataset_association(self, ldda): self.library_dataset_dataset_association = ldda ldda.library_dataset = self - object_session( self ).add_all( ( ldda, self ) ) - object_session( self ).flush() + object_session(self).add_all((ldda, self)) + object_session(self).flush() - def get_info( self ): + def get_info(self): if self.library_dataset_dataset_association: return self.library_dataset_dataset_association.info elif self._info: @@ -2835,11 +2837,11 @@ def get_info( self ): else: return 'no info' - def set_info( self, info ): + def set_info(self, info): self._info = info - info = property( get_info, set_info ) + info = property(get_info, set_info) - def get_name( self ): + def get_name(self): if self.library_dataset_dataset_association: return self.library_dataset_dataset_association.name elif self._name: @@ -2847,14 +2849,14 @@ def get_name( self ): else: return 'Unnamed dataset' - def set_name( self, name ): + def set_name(self, name): self._name = name - name = property( get_name, set_name ) + name = property(get_name, set_name) - def display_name( self ): + def display_name(self): self.library_dataset_dataset_association.display_name() - def to_dict( self, view='collection' ): + def to_dict(self, view='collection'): # Since this class is a proxy to rather complex attributes we want to # display in other objects, we can't use the simpler method used by # other model classes. @@ -2868,50 +2870,50 @@ def to_dict( self, view='collection' ): tmp_dict[field['label']] = content[field['name']] template_data[template.name] = tmp_dict - rval = dict( id=self.id, - ldda_id=ldda.id, - parent_library_id=self.folder.parent_library.id, - folder_id=self.folder_id, - model_class=self.__class__.__name__, - state=ldda.state, - name=ldda.name, - file_name=ldda.file_name, - uploaded_by=ldda.user.email, - message=ldda.message, - date_uploaded=ldda.create_time.isoformat(), - file_size=int( ldda.get_size() ), - file_ext=ldda.ext, - data_type=ldda.datatype.__class__.__module__ + '.' + ldda.datatype.__class__.__name__, - genome_build=ldda.dbkey, - misc_info=ldda.info, - misc_blurb=ldda.blurb, - peek=( lambda ldda: ldda.display_peek() if ldda.peek and ldda.peek != 'no peek' else None )( ldda ), - template_data=template_data ) + rval = dict(id=self.id, + ldda_id=ldda.id, + parent_library_id=self.folder.parent_library.id, + folder_id=self.folder_id, + model_class=self.__class__.__name__, + state=ldda.state, + name=ldda.name, + file_name=ldda.file_name, + uploaded_by=ldda.user.email, + message=ldda.message, + date_uploaded=ldda.create_time.isoformat(), + file_size=int(ldda.get_size()), + file_ext=ldda.ext, + data_type=ldda.datatype.__class__.__module__ + '.' + ldda.datatype.__class__.__name__, + genome_build=ldda.dbkey, + misc_info=ldda.info, + misc_blurb=ldda.blurb, + peek=(lambda ldda: ldda.display_peek() if ldda.peek and ldda.peek != 'no peek' else None)(ldda), + template_data=template_data) if ldda.dataset.uuid is None: rval['uuid'] = None else: rval['uuid'] = str(ldda.dataset.uuid) for name, spec in ldda.metadata.spec.items(): - val = ldda.metadata.get( name ) - if isinstance( val, MetadataFile ): + val = ldda.metadata.get(name) + if isinstance(val, MetadataFile): val = val.file_name - elif isinstance( val, list ): - val = ', '.join( [str(v) for v in val] ) + elif isinstance(val, list): + val = ', '.join([str(v) for v in val]) rval['metadata_' + name] = val return rval -class LibraryDatasetDatasetAssociation( DatasetInstance, HasName ): - def __init__( self, - copied_from_history_dataset_association=None, - copied_from_library_dataset_dataset_association=None, - library_dataset=None, - user=None, - sa_session=None, - **kwd ): +class LibraryDatasetDatasetAssociation(DatasetInstance, HasName): + def __init__(self, + copied_from_history_dataset_association=None, + copied_from_library_dataset_dataset_association=None, + library_dataset=None, + user=None, + sa_session=None, + **kwd): # FIXME: sa_session is must be passed to DataSetInstance if the create_dataset # parameter in kwd is True so that the new object can be flushed. Is there a better way? - DatasetInstance.__init__( self, sa_session=sa_session, **kwd ) + DatasetInstance.__init__(self, sa_session=sa_session, **kwd) if copied_from_history_dataset_association: self.copied_from_history_dataset_association_id = copied_from_history_dataset_association.id if copied_from_library_dataset_dataset_association: @@ -2919,74 +2921,84 @@ def __init__( self, self.library_dataset = library_dataset self.user = user - def to_history_dataset_association( self, target_history, parent_id=None, add_to_history=False ): - sa_session = object_session( self ) - hda = HistoryDatasetAssociation( name=self.name, - info=self.info, - blurb=self.blurb, - peek=self.peek, - tool_version=self.tool_version, - extension=self.extension, - dbkey=self.dbkey, - dataset=self.dataset, - visible=self.visible, - deleted=self.deleted, - parent_id=parent_id, - copied_from_library_dataset_dataset_association=self, - history=target_history ) - sa_session.add( hda ) + def to_history_dataset_association(self, target_history, parent_id=None, add_to_history=False): + sa_session = object_session(self) + hda = HistoryDatasetAssociation(name=self.name, + info=self.info, + blurb=self.blurb, + peek=self.peek, + tool_version=self.tool_version, + extension=self.extension, + dbkey=self.dbkey, + dataset=self.dataset, + visible=self.visible, + deleted=self.deleted, + parent_id=parent_id, + copied_from_library_dataset_dataset_association=self, + history=target_history) + + tag_manager = tags.GalaxyTagManager(sa_session) + src_ldda_tags = tag_manager.get_tags_str(self.tags) + tag_manager.apply_item_tags(user=self.user, item=hda, tags_str=src_ldda_tags) + + sa_session.add(hda) sa_session.flush() hda.metadata = self.metadata # need to set after flushed, as MetadataFiles require dataset.id if add_to_history and target_history: - target_history.add_dataset( hda ) + target_history.add_dataset(hda) for child in self.children: - child.to_history_dataset_association( target_history=target_history, parent_id=hda.id, add_to_history=False ) + child.to_history_dataset_association(target_history=target_history, parent_id=hda.id, add_to_history=False) if not self.datatype.copy_safe_peek: hda.set_peek() # in some instances peek relies on dataset_id, i.e. gmaj.zip for viewing MAFs sa_session.flush() return hda - def copy( self, copy_children=False, parent_id=None, target_folder=None ): - sa_session = object_session( self ) - ldda = LibraryDatasetDatasetAssociation( name=self.name, - info=self.info, - blurb=self.blurb, - peek=self.peek, - tool_version=self.tool_version, - extension=self.extension, - dbkey=self.dbkey, - dataset=self.dataset, - visible=self.visible, - deleted=self.deleted, - parent_id=parent_id, - copied_from_library_dataset_dataset_association=self, - folder=target_folder ) - sa_session.add( ldda ) + def copy(self, copy_children=False, parent_id=None, target_folder=None): + sa_session = object_session(self) + ldda = LibraryDatasetDatasetAssociation(name=self.name, + info=self.info, + blurb=self.blurb, + peek=self.peek, + tool_version=self.tool_version, + extension=self.extension, + dbkey=self.dbkey, + dataset=self.dataset, + visible=self.visible, + deleted=self.deleted, + parent_id=parent_id, + copied_from_library_dataset_dataset_association=self, + folder=target_folder) + + tag_manager = tags.GalaxyTagManager(sa_session) + src_ldda_tags = tag_manager.get_tags_str(self.tags) + tag_manager.apply_item_tags(user=self.user, item=ldda, tags_str=src_ldda_tags) + + sa_session.add(ldda) sa_session.flush() # Need to set after flushed, as MetadataFiles require dataset.id ldda.metadata = self.metadata if copy_children: for child in self.children: - child.copy( copy_children=copy_children, parent_id=ldda.id ) + child.copy(copy_children=copy_children, parent_id=ldda.id) if not self.datatype.copy_safe_peek: # In some instances peek relies on dataset_id, i.e. gmaj.zip for viewing MAFs ldda.set_peek() sa_session.flush() return ldda - def clear_associated_files( self, metadata_safe=False, purge=False ): + def clear_associated_files(self, metadata_safe=False, purge=False): return - def get_access_roles( self, trans ): - return self.dataset.get_access_roles( trans ) + def get_access_roles(self, trans): + return self.dataset.get_access_roles(trans) - def get_manage_permissions_roles( self, trans ): - return self.dataset.get_manage_permissions_roles( trans ) + def get_manage_permissions_roles(self, trans): + return self.dataset.get_manage_permissions_roles(trans) - def has_manage_permissions_roles( self, trans ): - return self.dataset.has_manage_permissions_roles( trans ) + def has_manage_permissions_roles(self, trans): + return self.dataset.has_manage_permissions_roles(trans) - def get_info_association( self, restrict=False, inherited=False ): + def get_info_association(self, restrict=False, inherited=False): # If restrict is True, we will return this ldda's info_association whether it # exists or not ( in which case None will be returned ). If restrict is False, # we'll return the next available info_association in the inheritable hierarchy. @@ -2997,34 +3009,35 @@ def get_info_association( self, restrict=False, inherited=False ): return self.info_association[0], inherited if restrict: return None, inherited - return self.library_dataset.folder.get_info_association( inherited=True ) + return self.library_dataset.folder.get_info_association(inherited=True) - def to_dict( self, view='collection' ): + def to_dict(self, view='collection'): # Since this class is a proxy to rather complex attributes we want to # display in other objects, we can't use the simpler method used by # other model classes. ldda = self try: - file_size = int( ldda.get_size() ) + file_size = int(ldda.get_size()) except OSError: file_size = 0 - rval = dict( id=ldda.id, - hda_ldda='ldda', - model_class=self.__class__.__name__, - name=ldda.name, - deleted=ldda.deleted, - visible=ldda.visible, - state=ldda.state, - library_dataset_id=ldda.library_dataset_id, - file_size=file_size, - file_name=ldda.file_name, - update_time=ldda.update_time.isoformat(), - file_ext=ldda.ext, - data_type=ldda.datatype.__class__.__module__ + '.' + ldda.datatype.__class__.__name__, - genome_build=ldda.dbkey, - misc_info=ldda.info, - misc_blurb=ldda.blurb ) + # TODO: render tags here + rval = dict(id=ldda.id, + hda_ldda='ldda', + model_class=self.__class__.__name__, + name=ldda.name, + deleted=ldda.deleted, + visible=ldda.visible, + state=ldda.state, + library_dataset_id=ldda.library_dataset_id, + file_size=file_size, + file_name=ldda.file_name, + update_time=ldda.update_time.isoformat(), + file_ext=ldda.ext, + data_type=ldda.datatype.__class__.__module__ + '.' + ldda.datatype.__class__.__name__, + genome_build=ldda.dbkey, + misc_info=ldda.info, + misc_blurb=ldda.blurb) if ldda.dataset.uuid is None: rval['uuid'] = None else: @@ -3033,16 +3046,16 @@ def to_dict( self, view='collection' ): if ldda.extended_metadata is not None: rval['extended_metadata'] = ldda.extended_metadata.data for name, spec in ldda.metadata.spec.items(): - val = ldda.metadata.get( name ) - if isinstance( val, MetadataFile ): + val = ldda.metadata.get(name) + if isinstance(val, MetadataFile): val = val.file_name # If no value for metadata, look in datatype for metadata. - elif val is None and hasattr( ldda.datatype, name ): - val = getattr( ldda.datatype, name ) + elif val is None and hasattr(ldda.datatype, name): + val = getattr(ldda.datatype, name) rval['metadata_' + name] = val return rval - def get_template_widgets( self, trans, get_contents=True ): + def get_template_widgets(self, trans, get_contents=True): # See if we have any associated templatesThe get_contents # param is passed by callers that are inheriting a template - these # are usually new library datsets for which we want to include template @@ -3064,12 +3077,12 @@ def get_template_widgets( self, trans, get_contents=True ): if get_contents: info = info_association.info if info: - return template.get_widgets( trans.user, info.content ) + return template.get_widgets(trans.user, info.content) else: - return template.get_widgets( trans.user ) + return template.get_widgets(trans.user) return [] - def templates_dict( self, use_name=False ): + def templates_dict(self, use_name=False): """ Returns a dict of template info """ @@ -3081,92 +3094,92 @@ def templates_dict( self, use_name=False ): tmp_dict = {} for field in template.fields: if use_name: - name = field[ 'name' ] + name = field['name'] else: - name = field[ 'label' ] - tmp_dict[ name ] = content.get( field[ 'name' ] ) + name = field['label'] + tmp_dict[name] = content.get(field['name']) template_data[template.name] = tmp_dict return template_data - def templates_json( self, use_name=False ): - return json.dumps( self.templates_dict( use_name=use_name ) ) + def templates_json(self, use_name=False): + return json.dumps(self.templates_dict(use_name=use_name)) -class ExtendedMetadata( object ): +class ExtendedMetadata(object): def __init__(self, data): self.data = data -class ExtendedMetadataIndex( object ): - def __init__( self, extended_metadata, path, value): +class ExtendedMetadataIndex(object): + def __init__(self, extended_metadata, path, value): self.extended_metadata = extended_metadata self.path = path self.value = value -class LibraryInfoAssociation( object ): - def __init__( self, library, form_definition, info, inheritable=False ): +class LibraryInfoAssociation(object): + def __init__(self, library, form_definition, info, inheritable=False): self.library = library self.template = form_definition self.info = info self.inheritable = inheritable -class LibraryFolderInfoAssociation( object ): - def __init__( self, folder, form_definition, info, inheritable=False ): +class LibraryFolderInfoAssociation(object): + def __init__(self, folder, form_definition, info, inheritable=False): self.folder = folder self.template = form_definition self.info = info self.inheritable = inheritable -class LibraryDatasetDatasetInfoAssociation( object ): - def __init__( self, library_dataset_dataset_association, form_definition, info ): +class LibraryDatasetDatasetInfoAssociation(object): + def __init__(self, library_dataset_dataset_association, form_definition, info): # TODO: need to figure out if this should be inheritable to the associated LibraryDataset self.library_dataset_dataset_association = library_dataset_dataset_association self.template = form_definition self.info = info @property - def inheritable( self ): + def inheritable(self): return True # always allow inheriting, used for replacement -class ValidationError( object ): - def __init__( self, message=None, err_type=None, attributes=None ): +class ValidationError(object): + def __init__(self, message=None, err_type=None, attributes=None): self.message = message self.err_type = err_type self.attributes = attributes -class DatasetToValidationErrorAssociation( object ): - def __init__( self, dataset, validation_error ): +class DatasetToValidationErrorAssociation(object): + def __init__(self, dataset, validation_error): self.dataset = dataset self.validation_error = validation_error -class ImplicitlyConvertedDatasetAssociation( object ): +class ImplicitlyConvertedDatasetAssociation(object): - def __init__( self, id=None, parent=None, dataset=None, file_type=None, deleted=False, purged=False, metadata_safe=True ): + def __init__(self, id=None, parent=None, dataset=None, file_type=None, deleted=False, purged=False, metadata_safe=True): self.id = id if isinstance(dataset, HistoryDatasetAssociation): self.dataset = dataset elif isinstance(dataset, LibraryDatasetDatasetAssociation): self.dataset_ldda = dataset else: - raise AttributeError( 'Unknown dataset type provided for dataset: %s' % type( dataset ) ) + raise AttributeError('Unknown dataset type provided for dataset: %s' % type(dataset)) if isinstance(parent, HistoryDatasetAssociation): self.parent_hda = parent elif isinstance(parent, LibraryDatasetDatasetAssociation): self.parent_ldda = parent else: - raise AttributeError( 'Unknown dataset type provided for parent: %s' % type( parent ) ) + raise AttributeError('Unknown dataset type provided for parent: %s' % type(parent)) self.type = file_type self.deleted = deleted self.purged = purged self.metadata_safe = metadata_safe - def clear( self, purge=False, delete_dataset=True ): + def clear(self, purge=False, delete_dataset=True): self.deleted = True if self.dataset: if delete_dataset: @@ -3176,19 +3189,19 @@ def clear( self, purge=False, delete_dataset=True ): if purge and self.dataset.deleted: # do something with purging self.purged = True try: - os.unlink( self.file_name ) + os.unlink(self.file_name) except Exception as e: - log.error( "Failed to purge associated file (%s) from disk: %s" % ( self.file_name, e ) ) + log.error("Failed to purge associated file (%s) from disk: %s" % (self.file_name, e)) DEFAULT_COLLECTION_NAME = "Unnamed Collection" -class DatasetCollection( object, Dictifiable, UsesAnnotations ): +class DatasetCollection(object, Dictifiable, UsesAnnotations): """ """ - dict_collection_visible_keys = ( 'id', 'collection_type' ) - dict_element_visible_keys = ( 'id', 'collection_type' ) + dict_collection_visible_keys = ('id', 'collection_type') + dict_element_visible_keys = ('id', 'collection_type') populated_states = Bunch( NEW='new', # New dataset collection, unpopulated elements OK='ok', # Collection elements populated (HDAs may or may not have errors) @@ -3207,65 +3220,65 @@ def __init__( self.populated_state = DatasetCollection.populated_states.NEW @property - def populated( self ): + def populated(self): top_level_populated = self.populated_state == DatasetCollection.populated_states.OK if top_level_populated and self.has_subcollections: return all(e.child_collection.populated for e in self.elements) return top_level_populated @property - def waiting_for_elements( self ): + def waiting_for_elements(self): top_level_waiting = self.populated_state == DatasetCollection.populated_states.NEW if not top_level_waiting and self.has_subcollections: return any(e.child_collection.waiting_for_elements for e in self.elements) return top_level_waiting - def mark_as_populated( self ): + def mark_as_populated(self): self.populated_state = DatasetCollection.populated_states.OK - def handle_population_failed( self, message ): + def handle_population_failed(self, message): self.populated_state = DatasetCollection.populated_states.FAILED self.populated_state_message = message @property - def dataset_instances( self ): + def dataset_instances(self): instances = [] for element in self.elements: if element.is_collection: - instances.extend( element.child_collection.dataset_instances ) + instances.extend(element.child_collection.dataset_instances) else: instance = element.dataset_instance - instances.append( instance ) + instances.append(instance) return instances @property - def dataset_elements( self ): + def dataset_elements(self): elements = [] for element in self.elements: if element.is_collection: - elements.extend( element.child_collection.dataset_elements ) + elements.extend(element.child_collection.dataset_elements) else: - elements.append( element ) + elements.append(element) return elements @property - def state( self ): + def state(self): # TODO: DatasetCollection state handling... return 'ok' - def validate( self ): + def validate(self): if self.collection_type is None: raise Exception("Each dataset collection must define a collection type.") - def __getitem__( self, key ): - get_by_attribute = "element_index" if isinstance( key, int ) else "element_identifier" + def __getitem__(self, key): + get_by_attribute = "element_index" if isinstance(key, int) else "element_identifier" for element in self.elements: - if getattr( element, get_by_attribute ) == key: + if getattr(element, get_by_attribute) == key: return element - error_message = "Dataset collection has no %s with key %s." % ( get_by_attribute, key ) - raise KeyError( error_message ) + error_message = "Dataset collection has no %s with key %s." % (get_by_attribute, key) + raise KeyError(error_message) - def copy( self, destination=None, element_destination=None ): + def copy(self, destination=None, element_destination=None): new_collection = DatasetCollection( collection_type=self.collection_type, ) @@ -3275,11 +3288,11 @@ def copy( self, destination=None, element_destination=None ): destination=destination, element_destination=element_destination, ) - object_session( self ).add( new_collection ) - object_session( self ).flush() + object_session(self).add(new_collection) + object_session(self).flush() return new_collection - def set_from_dict( self, new_data ): + def set_from_dict(self, new_data): # Nothing currently editable in this class. return {} @@ -3288,9 +3301,10 @@ def has_subcollections(self): return ":" in self.collection_type -class DatasetCollectionInstance( object, HasName ): +class DatasetCollectionInstance(object, HasName): """ """ + def __init__( self, collection=None, @@ -3304,21 +3318,21 @@ def __init__( self.deleted = deleted @property - def state( self ): + def state(self): return self.collection.state @property - def populated( self ): + def populated(self): return self.collection.populated @property - def dataset_instances( self ): + def dataset_instances(self): return self.collection.dataset_instances - def display_name( self ): + def display_name(self): return self.get_display_name() - def _base_to_dict( self, view ): + def _base_to_dict(self, view): return dict( id=self.id, name=self.name, @@ -3329,7 +3343,7 @@ def _base_to_dict( self, view ): type="collection", # contents type (distinguished from file or folder (in case of library)) ) - def set_from_dict( self, new_data ): + def set_from_dict(self, new_data): """ Set object attributes to the values in dictionary new_data limiting to only those keys in dict_element_visible_keys. @@ -3337,27 +3351,27 @@ def set_from_dict( self, new_data ): Returns a dictionary of the keys, values that have been changed. """ # precondition: keys are proper, values are parsed and validated - changed = self.collection.set_from_dict( new_data ) + changed = self.collection.set_from_dict(new_data) # unknown keys are ignored here - for key in ( k for k in new_data.keys() if k in self.editable_keys ): - new_val = new_data[ key ] - old_val = self.__getattribute__( key ) + for key in (k for k in new_data.keys() if k in self.editable_keys): + new_val = new_data[key] + old_val = self.__getattribute__(key) if new_val == old_val: continue - self.__setattr__( key, new_val ) - changed[ key ] = new_val + self.__setattr__(key, new_val) + changed[key] = new_val return changed -class HistoryDatasetCollectionAssociation( DatasetCollectionInstance, - HasTags, - Dictifiable, - UsesAnnotations ): +class HistoryDatasetCollectionAssociation(DatasetCollectionInstance, + HasTags, + Dictifiable, + UsesAnnotations): """ Associates a DatasetCollection with a History. """ - editable_keys = ( 'name', 'deleted', 'visible' ) + editable_keys = ('name', 'deleted', 'visible') def __init__( self, @@ -3372,7 +3386,7 @@ def __init__( implicit_output_name=None, implicit_input_collections=[], ): - super( HistoryDatasetCollectionAssociation, self ).__init__( + super(HistoryDatasetCollectionAssociation, self).__init__( collection=collection, deleted=deleted, ) @@ -3386,32 +3400,32 @@ def __init__( self.implicit_input_collections = implicit_input_collections @property - def history_content_type( self ): + def history_content_type(self): return "dataset_collection" # TODO: down into DatasetCollectionInstance content_type = u'dataset_collection' @hybrid.hybrid_property - def type_id( self ): - return u'-'.join([ self.content_type, str( self.id ) ]) + def type_id(self): + return u'-'.join([self.content_type, str(self.id)]) @type_id.expression - def type_id( cls ): - return (( type_coerce( cls.content_type, types.Unicode ) + u'-' + - type_coerce( cls.id, types.Unicode ) ).label( 'type_id' )) + def type_id(cls): + return ((type_coerce(cls.content_type, types.Unicode) + u'-' + + type_coerce(cls.id, types.Unicode)).label('type_id')) - def to_hda_representative( self, multiple=False ): + def to_hda_representative(self, multiple=False): rval = [] for dataset in self.collection.dataset_elements: - rval.append( dataset.dataset_instance ) + rval.append(dataset.dataset_instance) if multiple is False: break - if len( rval ) > 0: - return rval if multiple else rval[ 0 ] + if len(rval) > 0: + return rval if multiple else rval[0] - def to_dict( self, view='collection' ): - original_dict_value = super(HistoryDatasetCollectionAssociation, self).to_dict( view=view ) + def to_dict(self, view='collection'): + original_dict_value = super(HistoryDatasetCollectionAssociation, self).to_dict(view=view) dict_value = dict( hid=self.hid, history_id=self.history.id, @@ -3425,10 +3439,10 @@ def to_dict( self, view='collection' ): return dict_value - def add_implicit_input_collection( self, name, history_dataset_collection ): - self.implicit_input_collections.append( ImplicitlyCreatedDatasetCollectionInput( name, history_dataset_collection) ) + def add_implicit_input_collection(self, name, history_dataset_collection): + self.implicit_input_collections.append(ImplicitlyCreatedDatasetCollectionInput(name, history_dataset_collection)) - def find_implicit_input_collection( self, name ): + def find_implicit_input_collection(self, name): matching_collection = None for implicit_input_collection in self.implicit_input_collections: if implicit_input_collection.name == name: @@ -3436,7 +3450,7 @@ def find_implicit_input_collection( self, name ): break return matching_collection - def copy( self, element_destination=None ): + def copy(self, element_destination=None): """ Create a copy of this history dataset collection association. Copy underlying collection. @@ -3454,14 +3468,14 @@ def copy( self, element_destination=None ): element_destination=element_destination, ) hdca.collection = collection_copy - object_session( self ).add( hdca ) - object_session( self ).flush() + object_session(self).add(hdca) + object_session(self).flush() return hdca -class LibraryDatasetCollectionAssociation( DatasetCollectionInstance ): +class LibraryDatasetCollectionAssociation(DatasetCollectionInstance): """ Associates a DatasetCollection with a library folder. """ - editable_keys = ( 'name', 'deleted' ) + editable_keys = ('name', 'deleted') def __init__( self, @@ -3479,7 +3493,7 @@ def __init__( self.folder = folder self.name = name - def to_dict( self, view='collection' ): + def to_dict(self, view='collection'): dict_value = dict( folder_id=self.folder.id, **self._base_to_dict(view=view) @@ -3487,11 +3501,11 @@ def to_dict( self, view='collection' ): return dict_value -class DatasetCollectionElement( object, Dictifiable ): +class DatasetCollectionElement(object, Dictifiable): """ Associates a DatasetInstance (hda or ldda) with a DatasetCollection. """ # actionable dataset id needs to be available via API... - dict_collection_visible_keys = ( 'id', 'element_type', 'element_index', 'element_identifier' ) - dict_element_visible_keys = ( 'id', 'element_type', 'element_index', 'element_identifier' ) + dict_collection_visible_keys = ('id', 'element_type', 'element_index', 'element_identifier') + dict_element_visible_keys = ('id', 'element_type', 'element_index', 'element_identifier') def __init__( self, @@ -3505,10 +3519,10 @@ def __init__( self.hda = element elif isinstance(element, LibraryDatasetDatasetAssociation): self.ldda = element - elif isinstance( element, DatasetCollection ): + elif isinstance(element, DatasetCollection): self.child_collection = element else: - raise AttributeError( 'Unknown element type provided: %s' % type( element ) ) + raise AttributeError('Unknown element type provided: %s' % type(element)) self.id = id self.collection = collection @@ -3516,7 +3530,7 @@ def __init__( self.element_identifier = element_identifier or str(element_index) @property - def element_type( self ): + def element_type(self): if self.hda: return "hda" elif self.ldda: @@ -3525,14 +3539,14 @@ def element_type( self ): # TOOD: Rename element_type to element_type. return "dataset_collection" else: - raise Exception( "Unknown element instance type" ) + raise Exception("Unknown element instance type") @property - def is_collection( self ): + def is_collection(self): return self.element_type == "dataset_collection" @property - def element_object( self ): + def element_object(self): if self.hda: return self.hda elif self.ldda: @@ -3540,35 +3554,35 @@ def element_object( self ): elif self.child_collection: return self.child_collection else: - raise Exception( "Unknown element instance type" ) + raise Exception("Unknown element instance type") @property - def dataset_instance( self ): + def dataset_instance(self): element_object = self.element_object - if isinstance( element_object, DatasetCollection ): - raise AttributeError( "Nested collection has no associated dataset_instance." ) + if isinstance(element_object, DatasetCollection): + raise AttributeError("Nested collection has no associated dataset_instance.") return element_object @property - def dataset( self ): + def dataset(self): return self.dataset_instance.dataset - def first_dataset_instance( self ): + def first_dataset_instance(self): element_object = self.element_object - if isinstance( element_object, DatasetCollection ): - return element_object.dataset_instances[ 0 ] + if isinstance(element_object, DatasetCollection): + return element_object.dataset_instances[0] else: return element_object @property - def dataset_instances( self ): + def dataset_instances(self): element_object = self.element_object - if isinstance( element_object, DatasetCollection ): + if isinstance(element_object, DatasetCollection): return element_object.dataset_instances else: return [element_object] - def copy_to_collection( self, collection, destination=None, element_destination=None ): + def copy_to_collection(self, collection, destination=None, element_destination=None): element_object = self.element_object if element_destination: if self.is_collection: @@ -3577,13 +3591,13 @@ def copy_to_collection( self, collection, destination=None, element_destination= element_destination=element_destination ) else: - new_element_object = element_object.copy( copy_children=True ) + new_element_object = element_object.copy(copy_children=True) if destination is not None and element_object.hidden_beneath_collection_instance: new_element_object.hidden_beneath_collection_instance = destination # Ideally we would not need to give the following # element an HID and it would exist in the history only # as an element of the containing collection. - element_destination.add_dataset( new_element_object ) + element_destination.add_dataset(new_element_object) element_object = new_element_object new_element = DatasetCollectionElement( @@ -3595,8 +3609,8 @@ def copy_to_collection( self, collection, destination=None, element_destination= return new_element -class Event( object ): - def __init__( self, message=None, history=None, user=None, galaxy_session=None ): +class Event(object): + def __init__(self, message=None, history=None, user=None, galaxy_session=None): self.history = history self.galaxy_session = galaxy_session self.user = user @@ -3604,18 +3618,18 @@ def __init__( self, message=None, history=None, user=None, galaxy_session=None ) self.message = message -class GalaxySession( object ): - def __init__( self, - id=None, - user=None, - remote_host=None, - remote_addr=None, - referer=None, - current_history=None, - session_key=None, - is_valid=False, - prev_session_id=None, - last_action=None ): +class GalaxySession(object): + def __init__(self, + id=None, + user=None, + remote_host=None, + remote_addr=None, + referer=None, + current_history=None, + session_key=None, + is_valid=False, + prev_session_id=None, + last_action=None): self.id = id self.user = user self.remote_host = remote_host @@ -3628,40 +3642,40 @@ def __init__( self, self.histories = [] self.last_action = last_action or datetime.now() - def add_history( self, history, association=None ): + def add_history(self, history, association=None): if association is None: - self.histories.append( GalaxySessionToHistoryAssociation( self, history ) ) + self.histories.append(GalaxySessionToHistoryAssociation(self, history)) else: - self.histories.append( association ) + self.histories.append(association) - def get_disk_usage( self ): + def get_disk_usage(self): if self.disk_usage is None: return 0 return self.disk_usage - def set_disk_usage( self, bytes ): + def set_disk_usage(self, bytes): self.disk_usage = bytes - total_disk_usage = property( get_disk_usage, set_disk_usage ) + total_disk_usage = property(get_disk_usage, set_disk_usage) -class GalaxySessionToHistoryAssociation( object ): - def __init__( self, galaxy_session, history ): +class GalaxySessionToHistoryAssociation(object): + def __init__(self, galaxy_session, history): self.galaxy_session = galaxy_session self.history = history -class UCI( object ): - def __init__( self ): +class UCI(object): + def __init__(self): self.id = None self.user = None -class StoredWorkflow( HasTags, Dictifiable ): +class StoredWorkflow(HasTags, Dictifiable): - dict_collection_visible_keys = ( 'id', 'name', 'published', 'deleted' ) - dict_element_visible_keys = ( 'id', 'name', 'published', 'deleted' ) + dict_collection_visible_keys = ('id', 'name', 'published', 'deleted') + dict_element_visible_keys = ('id', 'name', 'published', 'deleted') - def __init__( self ): + def __init__(self): self.id = None self.user = None self.name = None @@ -3676,19 +3690,19 @@ def copy_tags_from(self, target_user, source_workflow): new_swta.user = target_user self.tags.append(new_swta) - def to_dict( self, view='collection', value_mapper=None ): - rval = super( StoredWorkflow, self ).to_dict( view=view, value_mapper=value_mapper ) - rval['latest_workflow_uuid'] = ( lambda uuid: str( uuid ) if self.latest_workflow.uuid else None )( self.latest_workflow.uuid ) + def to_dict(self, view='collection', value_mapper=None): + rval = super(StoredWorkflow, self).to_dict(view=view, value_mapper=value_mapper) + rval['latest_workflow_uuid'] = (lambda uuid: str(uuid) if self.latest_workflow.uuid else None)(self.latest_workflow.uuid) return rval -class Workflow( object, Dictifiable ): +class Workflow(object, Dictifiable): - dict_collection_visible_keys = ( 'name', 'has_cycles', 'has_errors' ) - dict_element_visible_keys = ( 'name', 'has_cycles', 'has_errors' ) + dict_collection_visible_keys = ('name', 'has_cycles', 'has_errors') + dict_element_visible_keys = ('name', 'has_cycles', 'has_errors') input_step_types = ['data_input', 'data_collection_input', 'parameter_input'] - def __init__( self, uuid=None ): + def __init__(self, uuid=None): self.user = None self.name = None self.has_cycles = None @@ -3708,17 +3722,17 @@ def has_outputs_defined(self): return True return False - def to_dict( self, view='collection', value_mapper=None): - rval = super( Workflow, self ).to_dict( view=view, value_mapper=value_mapper ) - rval['uuid'] = ( lambda uuid: str( uuid ) if uuid else None )( self.uuid ) + def to_dict(self, view='collection', value_mapper=None): + rval = super(Workflow, self).to_dict(view=view, value_mapper=value_mapper) + rval['uuid'] = (lambda uuid: str(uuid) if uuid else None)(self.uuid) return rval @property - def steps_by_id( self ): + def steps_by_id(self): steps = {} for step in self.steps: step_id = step.id - steps[ step_id ] = step + steps[step_id] = step return steps def step_by_index(self, order_index): @@ -3740,7 +3754,7 @@ def workflow_outputs(self): yield workflow_output @property - def top_level_workflow( self ): + def top_level_workflow(self): """ If this workflow is not attached to stored workflow directly, recursively grab its parents until it is the top level workflow which must have a stored workflow associated with it. @@ -3753,7 +3767,7 @@ def top_level_workflow( self ): return top_level_workflow @property - def top_level_stored_workflow( self ): + def top_level_stored_workflow(self): """ If this workflow is not attached to stored workflow directly, recursively grab its parents until it is the top level workflow which must have a stored workflow associated with it and then @@ -3790,9 +3804,9 @@ def log_str(self): return "Workflow[id=%d%s]" % (self.id, extra) -class WorkflowStep( object ): +class WorkflowStep(object): - def __init__( self ): + def __init__(self): self.id = None self.type = None self.tool_id = None @@ -3824,7 +3838,7 @@ def unique_workflow_outputs(self): return list(outputs.values()) @property - def content_id( self ): + def content_id(self): content_id = None if self.type == "tool": content_id = self.tool_id @@ -3896,17 +3910,17 @@ def copy_to(self, copied_step, step_mapping): if old_conn.input_subworkflow_step_id: new_conn.input_subworkflow_step = subworkflow_step_mapping[old_conn.input_subworkflow_step_id] for orig_pja in self.post_job_actions: - PostJobAction( orig_pja.action_type, - copied_step, - output_name=orig_pja.output_name, - action_arguments=orig_pja.action_arguments ) + PostJobAction(orig_pja.action_type, + copied_step, + output_name=orig_pja.output_name, + action_arguments=orig_pja.action_arguments) copied_step.workflow_outputs = copy_list(self.workflow_outputs, copied_step) def log_str(self): return "WorkflowStep[index=%d,type=%s]" % (self.order_index, self.type) -class WorkflowStepConnection( object ): +class WorkflowStepConnection(object): # Constant used in lieu of output_name and input_name to indicate an # implicit connection between two steps that is not dependent on a dataset # or a dataset collection. Allowing for instance data manager steps to setup @@ -3914,7 +3928,7 @@ class WorkflowStepConnection( object ): # outside of Galaxy. NON_DATA_CONNECTION = "__NO_INPUT_OUTPUT_NAME__" - def __init__( self ): + def __init__(self): self.output_step_id = None self.output_name = None self.input_step_id = None @@ -3939,7 +3953,7 @@ def copy(self): class WorkflowOutput(object): - def __init__( self, workflow_step, output_name=None, label=None, uuid=None): + def __init__(self, workflow_step, output_name=None, label=None, uuid=None): self.workflow_step = workflow_step self.output_name = output_name self.label = label @@ -3955,24 +3969,24 @@ def copy(self, copied_step): return copied_output -class StoredWorkflowUserShareAssociation( object ): +class StoredWorkflowUserShareAssociation(object): - def __init__( self ): + def __init__(self): self.stored_workflow = None self.user = None -class StoredWorkflowMenuEntry( object ): +class StoredWorkflowMenuEntry(object): - def __init__( self ): + def __init__(self): self.stored_workflow = None self.user = None self.order_index = None -class WorkflowInvocation( object, Dictifiable ): - dict_collection_visible_keys = ( 'id', 'update_time', 'workflow_id', 'history_id', 'uuid', 'state' ) - dict_element_visible_keys = ( 'id', 'update_time', 'workflow_id', 'history_id', 'uuid', 'state' ) +class WorkflowInvocation(object, Dictifiable): + dict_collection_visible_keys = ('id', 'update_time', 'workflow_id', 'history_id', 'uuid', 'state') + dict_element_visible_keys = ('id', 'update_time', 'workflow_id', 'history_id', 'uuid', 'state') states = Bunch( NEW='new', # Brand new workflow invocation... maybe this should be same as READY READY='ready', # Workflow ready for another iteration of scheduling. @@ -3986,12 +4000,12 @@ def __init__(self): self.step_states = [] self.steps = [] - def create_subworkflow_invocation_for_step( self, step ): + def create_subworkflow_invocation_for_step(self, step): assert step.type == "subworkflow" subworkflow_invocation = WorkflowInvocation() - return self.attach_subworkflow_invocation_for_step( step, subworkflow_invocation ) + return self.attach_subworkflow_invocation_for_step(step, subworkflow_invocation) - def attach_subworkflow_invocation_for_step( self, step, subworkflow_invocation ): + def attach_subworkflow_invocation_for_step(self, step, subworkflow_invocation): assert step.type == "subworkflow" assoc = WorkflowInvocationToSubworkflowInvocationAssociation() assoc.workflow_invocation = self @@ -4002,11 +4016,11 @@ def attach_subworkflow_invocation_for_step( self, step, subworkflow_invocation ) self.subworkflow_invocations.append(assoc) return assoc - def get_subworkflow_invocation_for_step( self, step ): + def get_subworkflow_invocation_for_step(self, step): assoc = self.get_subworkflow_invocation_association_for_step(step) return assoc.subworkflow_invocation - def get_subworkflow_invocation_association_for_step( self, step ): + def get_subworkflow_invocation_association_for_step(self, step): assert step.type == "subworkflow" assoc = None for subworkflow_invocation in self.subworkflow_invocations: @@ -4016,45 +4030,45 @@ def get_subworkflow_invocation_association_for_step( self, step ): return assoc @property - def active( self ): + def active(self): """ Indicates the workflow invocation is somehow active - and in particular valid actions may be performed on its WorkflowInvocationSteps. """ states = WorkflowInvocation.states - return self.state in [ states.NEW, states.READY ] + return self.state in [states.NEW, states.READY] - def cancel( self ): + def cancel(self): if not self.active: return False else: self.state = WorkflowInvocation.states.CANCELLED return True - def fail( self ): + def fail(self): self.state = WorkflowInvocation.states.FAILED - def step_states_by_step_id( self ): + def step_states_by_step_id(self): step_states = {} for step_state in self.step_states: step_id = step_state.workflow_step_id - step_states[ step_id ] = step_state + step_states[step_id] = step_state return step_states - def step_invocations_by_step_id( self ): + def step_invocations_by_step_id(self): step_invocations = {} for invocation_step in self.steps: step_id = invocation_step.workflow_step_id if step_id not in step_invocations: - step_invocations[ step_id ] = [] - step_invocations[ step_id ].append( invocation_step ) + step_invocations[step_id] = [] + step_invocations[step_id].append(invocation_step) return step_invocations - def step_invocations_for_step_id( self, step_id ): + def step_invocations_for_step_id(self, step_id): step_invocations = [] for invocation_step in self.steps: if step_id == invocation_step.workflow_step_id: - step_invocations.append( invocation_step ) + step_invocations.append(invocation_step) return step_invocations @staticmethod @@ -4070,19 +4084,19 @@ def poll_active_workflow_ids( ), ] if scheduler is not None: - and_conditions.append( WorkflowInvocation.scheduler == scheduler ) + and_conditions.append(WorkflowInvocation.scheduler == scheduler) if handler is not None: - and_conditions.append( WorkflowInvocation.handler == handler ) + and_conditions.append(WorkflowInvocation.handler == handler) query = sa_session.query( WorkflowInvocation.id - ).filter( and_( *and_conditions ) ).order_by( WorkflowInvocation.table.c.id.asc() ) + ).filter(and_(*and_conditions)).order_by(WorkflowInvocation.table.c.id.asc()) # Immediately just load all ids into memory so time slicing logic # is relatively intutitive. return [wid for wid in query.all()] - def to_dict( self, view='collection', value_mapper=None, step_details=False ): - rval = super( WorkflowInvocation, self ).to_dict( view=view, value_mapper=value_mapper ) + def to_dict(self, view='collection', value_mapper=None, step_details=False): + rval = super(WorkflowInvocation, self).to_dict(view=view, value_mapper=value_mapper) if view == 'element': steps = [] for step in self.steps: @@ -4090,7 +4104,7 @@ def to_dict( self, view='collection', value_mapper=None, step_details=False ): v = step.to_dict(view='element') else: v = step.to_dict(view='collection') - steps.append( v ) + steps.append(v) rval['steps'] = steps inputs = {} @@ -4098,7 +4112,7 @@ def to_dict( self, view='collection', value_mapper=None, step_details=False ): if step.workflow_step.type == 'tool': for step_input in step.workflow_step.input_connections: output_step_type = step_input.output_step.type - if output_step_type in [ 'data_input', 'data_collection_input' ]: + if output_step_type in ['data_input', 'data_collection_input']: src = "hda" if output_step_type == 'data_input' else 'hdca' for job_input in step.job.input_datasets: if job_input.name == step_input.input_name: @@ -4109,28 +4123,28 @@ def to_dict( self, view='collection', value_mapper=None, step_details=False ): rval['inputs'] = inputs return rval - def update( self ): + def update(self): self.update_time = galaxy.model.orm.now.now() - def add_input( self, content, step_id ): + def add_input(self, content, step_id): history_content_type = getattr(content, "history_content_type", None) if history_content_type == "dataset": request_to_content = WorkflowRequestToInputDatasetAssociation() request_to_content.dataset = content request_to_content.workflow_step_id = step_id - self.input_datasets.append( request_to_content ) + self.input_datasets.append(request_to_content) elif history_content_type == "dataset_collection": request_to_content = WorkflowRequestToInputDatasetCollectionAssociation() request_to_content.dataset_collection = content request_to_content.workflow_step_id = step_id - self.input_dataset_collections.append( request_to_content ) + self.input_dataset_collections.append(request_to_content) else: request_to_content = WorkflowRequestInputStepParmeter() request_to_content.parameter_value = content request_to_content.workflow_step_id = step_id - self.input_step_parameters.append( request_to_content ) + self.input_step_parameters.append(request_to_content) - def has_input_for_step( self, step_id ): + def has_input_for_step(self, step_id): for content in self.input_datasets: if content.workflow_step_id == step_id: return True @@ -4140,25 +4154,25 @@ def has_input_for_step( self, step_id ): return False @property - def seconds_since_created( self ): + def seconds_since_created(self): create_time = self.create_time or galaxy.model.orm.now.now() # In case not flushed yet return (galaxy.model.orm.now.now() - create_time).total_seconds() -class WorkflowInvocationToSubworkflowInvocationAssociation( object, Dictifiable ): - dict_collection_visible_keys = ( 'id', 'workflow_step_id', 'workflow_invocation_id', 'subworkflow_invocation_id' ) - dict_element_visible_keys = ( 'id', 'workflow_step_id', 'workflow_invocation_id', 'subworkflow_invocation_id' ) +class WorkflowInvocationToSubworkflowInvocationAssociation(object, Dictifiable): + dict_collection_visible_keys = ('id', 'workflow_step_id', 'workflow_invocation_id', 'subworkflow_invocation_id') + dict_element_visible_keys = ('id', 'workflow_step_id', 'workflow_invocation_id', 'subworkflow_invocation_id') -class WorkflowInvocationStep( object, Dictifiable ): - dict_collection_visible_keys = ( 'id', 'update_time', 'job_id', 'workflow_step_id', 'action' ) - dict_element_visible_keys = ( 'id', 'update_time', 'job_id', 'workflow_step_id', 'action' ) +class WorkflowInvocationStep(object, Dictifiable): + dict_collection_visible_keys = ('id', 'update_time', 'job_id', 'workflow_step_id', 'action') + dict_element_visible_keys = ('id', 'update_time', 'job_id', 'workflow_step_id', 'action') - def update( self ): + def update(self): self.workflow_invocation.update() - def to_dict( self, view='collection', value_mapper=None ): - rval = super( WorkflowInvocationStep, self ).to_dict( view=view, value_mapper=value_mapper ) + def to_dict(self, view='collection', value_mapper=None): + rval = super(WorkflowInvocationStep, self).to_dict(view=view, value_mapper=value_mapper) rval['order_index'] = self.workflow_step.order_index rval['workflow_step_label'] = self.workflow_step.label rval['workflow_step_uuid'] = str(self.workflow_step.uuid) @@ -4181,12 +4195,12 @@ def to_dict( self, view='collection', value_mapper=None ): return rval -class WorkflowRequest( object, Dictifiable ): - dict_collection_visible_keys = [ 'id', 'name', 'type', 'state', 'history_id', 'workflow_id' ] - dict_element_visible_keys = [ 'id', 'name', 'type', 'state', 'history_id', 'workflow_id' ] +class WorkflowRequest(object, Dictifiable): + dict_collection_visible_keys = ['id', 'name', 'type', 'state', 'history_id', 'workflow_id'] + dict_element_visible_keys = ['id', 'name', 'type', 'state', 'history_id', 'workflow_id'] - def to_dict( self, view='collection', value_mapper=None ): - rval = super( WorkflowRequest, self ).to_dict( view=view, value_mapper=value_mapper ) + def to_dict(self, view='collection', value_mapper=None): + rval = super(WorkflowRequest, self).to_dict(view=view, value_mapper=value_mapper) return rval @@ -4199,7 +4213,7 @@ class WorkflowRequestInputParameter(object, Dictifiable): META_PARAMETERS='meta', # ) - def __init__( self, name=None, value=None, type=None ): + def __init__(self, name=None, value=None, type=None): self.name = name self.value = value self.type = type @@ -4210,7 +4224,7 @@ class WorkflowRequestStepState(object, Dictifiable): """ dict_collection_visible_keys = ['id', 'name', 'value', 'workflow_step_id'] - def __init__( self, workflow_step=None, name=None, value=None ): + def __init__(self, workflow_step=None, name=None, value=None): self.workflow_step = workflow_step self.name = name self.value = value @@ -4220,71 +4234,71 @@ def __init__( self, workflow_step=None, name=None, value=None ): class WorkflowRequestToInputDatasetAssociation(object, Dictifiable): """ Workflow step input dataset parameters. """ - dict_collection_visible_keys = ['id', 'workflow_invocation_id', 'workflow_step_id', 'dataset_id', 'name' ] + dict_collection_visible_keys = ['id', 'workflow_invocation_id', 'workflow_step_id', 'dataset_id', 'name'] class WorkflowRequestToInputDatasetCollectionAssociation(object, Dictifiable): """ Workflow step input dataset collection parameters. """ - dict_collection_visible_keys = ['id', 'workflow_invocation_id', 'workflow_step_id', 'dataset_collection_id', 'name' ] + dict_collection_visible_keys = ['id', 'workflow_invocation_id', 'workflow_step_id', 'dataset_collection_id', 'name'] class WorkflowRequestInputStepParmeter(object, Dictifiable): """ Workflow step parameter inputs. """ - dict_collection_visible_keys = ['id', 'workflow_invocation_id', 'workflow_step_id', 'parameter_value' ] + dict_collection_visible_keys = ['id', 'workflow_invocation_id', 'workflow_step_id', 'parameter_value'] -class MetadataFile( StorableObject ): +class MetadataFile(StorableObject): - def __init__( self, dataset=None, name=None ): + def __init__(self, dataset=None, name=None): super(MetadataFile, self).__init__(id=None) - if isinstance( dataset, HistoryDatasetAssociation ): + if isinstance(dataset, HistoryDatasetAssociation): self.history_dataset = dataset - elif isinstance( dataset, LibraryDatasetDatasetAssociation ): + elif isinstance(dataset, LibraryDatasetDatasetAssociation): self.library_dataset = dataset self.name = name @property - def file_name( self ): + def file_name(self): assert self.id is not None, "ID must be set before filename used (commit the object)" # Ensure the directory structure and the metadata file object exist try: da = self.history_dataset or self.library_dataset if self.object_store_id is None and da is not None: self.object_store_id = da.dataset.object_store_id - if not da.dataset.object_store.exists( self, extra_dir='_metadata_files', extra_dir_at_root=True, alt_name="metadata_%d.dat" % self.id ): - da.dataset.object_store.create( self, extra_dir='_metadata_files', extra_dir_at_root=True, alt_name="metadata_%d.dat" % self.id ) - path = da.dataset.object_store.get_filename( self, extra_dir='_metadata_files', extra_dir_at_root=True, alt_name="metadata_%d.dat" % self.id ) + if not da.dataset.object_store.exists(self, extra_dir='_metadata_files', extra_dir_at_root=True, alt_name="metadata_%d.dat" % self.id): + da.dataset.object_store.create(self, extra_dir='_metadata_files', extra_dir_at_root=True, alt_name="metadata_%d.dat" % self.id) + path = da.dataset.object_store.get_filename(self, extra_dir='_metadata_files', extra_dir_at_root=True, alt_name="metadata_%d.dat" % self.id) return path except AttributeError: # In case we're not working with the history_dataset # print "Caught AttributeError" - path = os.path.join( Dataset.file_path, '_metadata_files', *directory_hash_id( self.id ) ) + path = os.path.join(Dataset.file_path, '_metadata_files', *directory_hash_id(self.id)) # Create directory if it does not exist try: - os.makedirs( path ) + os.makedirs(path) except OSError as e: # File Exists is okay, otherwise reraise if e.errno != errno.EEXIST: raise # Return filename inside hashed directory - return os.path.abspath( os.path.join( path, "metadata_%d.dat" % self.id ) ) + return os.path.abspath(os.path.join(path, "metadata_%d.dat" % self.id)) -class FormDefinition( object, Dictifiable ): +class FormDefinition(object, Dictifiable): # The following form_builder classes are supported by the FormDefinition class. - supported_field_types = [ AddressField, CheckboxField, PasswordField, SelectField, TextArea, TextField, WorkflowField, WorkflowMappingField, HistoryField ] - types = Bunch( REQUEST='Sequencing Request Form', - SAMPLE='Sequencing Sample Form', - EXTERNAL_SERVICE='External Service Information Form', - RUN_DETAILS_TEMPLATE='Sample run details template', - LIBRARY_INFO_TEMPLATE='Library information template', - USER_INFO='User Information' ) - dict_collection_visible_keys = ( 'id', 'name' ) - dict_element_visible_keys = ( 'id', 'name', 'desc', 'form_definition_current_id', 'fields', 'layout' ) - - def __init__( self, name=None, desc=None, fields=[], form_definition_current=None, form_type=None, layout=None ): + supported_field_types = [AddressField, CheckboxField, PasswordField, SelectField, TextArea, TextField, WorkflowField, WorkflowMappingField, HistoryField] + types = Bunch(REQUEST='Sequencing Request Form', + SAMPLE='Sequencing Sample Form', + EXTERNAL_SERVICE='External Service Information Form', + RUN_DETAILS_TEMPLATE='Sample run details template', + LIBRARY_INFO_TEMPLATE='Library information template', + USER_INFO='User Information') + dict_collection_visible_keys = ('id', 'name') + dict_element_visible_keys = ('id', 'name', 'desc', 'form_definition_current_id', 'fields', 'layout') + + def __init__(self, name=None, desc=None, fields=[], form_definition_current=None, form_type=None, layout=None): self.name = name self.desc = desc self.fields = fields @@ -4292,41 +4306,41 @@ def __init__( self, name=None, desc=None, fields=[], form_definition_current=Non self.type = form_type self.layout = layout - def to_dict( self, user=None, values=None, security=None ): + def to_dict(self, user=None, values=None, security=None): values = values or {} - form_def = { 'id': security.encode_id( self.id ) if security else self.id, 'name': self.name, 'inputs': [] } + form_def = {'id': security.encode_id(self.id) if security else self.id, 'name': self.name, 'inputs': []} for field in self.fields: - FieldClass = ( { 'AddressField' : AddressField, - 'CheckboxField' : CheckboxField, - 'HistoryField' : HistoryField, - 'PasswordField' : PasswordField, - 'SelectField' : SelectField, - 'TextArea' : TextArea, - 'TextField' : TextField, - 'WorkflowField' : WorkflowField } ).get( field[ 'type' ], TextField ) - form_def[ 'inputs' ].append( FieldClass( user=user, value=values.get( field[ 'name' ], field[ 'default' ] ), security=security, **field ).to_dict() ) + FieldClass = ({'AddressField' : AddressField, + 'CheckboxField' : CheckboxField, + 'HistoryField' : HistoryField, + 'PasswordField' : PasswordField, + 'SelectField' : SelectField, + 'TextArea' : TextArea, + 'TextField' : TextField, + 'WorkflowField' : WorkflowField}).get(field['type'], TextField) + form_def['inputs'].append(FieldClass(user=user, value=values.get(field['name'], field['default']), security=security, **field).to_dict()) return form_def - def grid_fields( self, grid_index ): + def grid_fields(self, grid_index): # Returns a dictionary whose keys are integers corresponding to field positions # on the grid and whose values are the field. gridfields = {} - for i, f in enumerate( self.fields ): - if str( f[ 'layout' ] ) == str( grid_index ): + for i, f in enumerate(self.fields): + if str(f['layout']) == str(grid_index): gridfields[i] = f return gridfields - def get_widgets( self, user, contents={}, **kwd ): + def get_widgets(self, user, contents={}, **kwd): ''' Return the list of widgets that comprise a form definition, including field contents if any. ''' - params = Params( kwd ) + params = Params(kwd) widgets = [] - for index, field in enumerate( self.fields ): - field_type = field[ 'type' ] + for index, field in enumerate(self.fields): + field_type = field['type'] if 'name' in field: - field_name = field[ 'name' ] + field_name = field['name'] else: # Default to names like field_0, field_1, etc for backward compatibility # (not sure this is necessary)... @@ -4335,13 +4349,13 @@ def get_widgets( self, user, contents={}, **kwd ): if field_name in kwd: # The form was submitted via refresh_on_change if field_type == 'CheckboxField': - value = CheckboxField.is_checked( params.get( field_name, False ) ) + value = CheckboxField.is_checked(params.get(field_name, False)) else: - value = restore_text( params.get( field_name, '' ) ) + value = restore_text(params.get(field_name, '')) elif contents: try: # This field has a saved value. - value = str( contents[ field[ 'name' ] ] ) + value = str(contents[field['name']]) except: # If there was an error getting the saved value, we'll still # display the widget, but it will be empty. @@ -4362,72 +4376,72 @@ def get_widgets( self, user, contents={}, **kwd ): value = False else: # Set other field types to the default value of the field - value = field.get( 'default', '' ) + value = field.get('default', '') # Create the field widget - field_widget = eval( field_type )( field_name ) - if field_type in [ 'TextField', 'PasswordField' ]: - field_widget.set_size( 40 ) + field_widget = eval(field_type)(field_name) + if field_type in ['TextField', 'PasswordField']: + field_widget.set_size(40) field_widget.value = value elif field_type == 'TextArea': - field_widget.set_size( 3, 40 ) + field_widget.set_size(3, 40) field_widget.value = value elif field_type in ['AddressField', 'WorkflowField', 'WorkflowMappingField', 'HistoryField']: field_widget.user = user field_widget.value = value field_widget.params = params elif field_type == 'SelectField': - for option in field[ 'selectlist' ]: + for option in field['selectlist']: if option == value: - field_widget.add_option( option, option, selected=True ) + field_widget.add_option(option, option, selected=True) else: - field_widget.add_option( option, option ) + field_widget.add_option(option, option) elif field_type == 'CheckboxField': - field_widget.set_checked( value ) - if field[ 'required' ] == 'required': + field_widget.set_checked(value) + if field['required'] == 'required': req = 'Required' else: req = 'Optional' - if field[ 'helptext' ]: - helptext = '%s (%s)' % ( field[ 'helptext' ], req ) + if field['helptext']: + helptext = '%s (%s)' % (field['helptext'], req) else: helptext = '(%s)' % req - widgets.append( dict( label=field[ 'label' ], widget=field_widget, helptext=helptext ) ) + widgets.append(dict(label=field['label'], widget=field_widget, helptext=helptext)) return widgets - def field_as_html( self, field ): + def field_as_html(self, field): """Generates disabled html for a field""" - type = field[ 'type' ] + type = field['type'] form_field = None for field_type in self.supported_field_types: if type == field_type.__name__: # Name it AddressField, CheckboxField, etc. - form_field = field_type( type ) + form_field = field_type(type) break if form_field: - return form_field.get_html( disabled=True ) + return form_field.get_html(disabled=True) # Return None if unsupported field type return None -class FormDefinitionCurrent( object ): +class FormDefinitionCurrent(object): def __init__(self, form_definition=None): self.latest_form = form_definition -class FormValues( object ): +class FormValues(object): def __init__(self, form_def=None, content=None): self.form_definition = form_def self.content = content -class Request( object, Dictifiable ): - states = Bunch( NEW='New', - SUBMITTED='In Progress', - REJECTED='Rejected', - COMPLETE='Complete' ) - dict_collection_visible_keys = ( 'id', 'name', 'state' ) +class Request(object, Dictifiable): + states = Bunch(NEW='New', + SUBMITTED='In Progress', + REJECTED='Rejected', + COMPLETE='Complete') + dict_collection_visible_keys = ('id', 'name', 'state') - def __init__( self, name=None, desc=None, request_type=None, user=None, form_values=None, notification=None ): + def __init__(self, name=None, desc=None, request_type=None, user=None, form_values=None, notification=None): self.name = name self.desc = desc self.type = request_type @@ -4437,20 +4451,20 @@ def __init__( self, name=None, desc=None, request_type=None, user=None, form_val self.samples_list = [] @property - def state( self ): + def state(self): latest_event = self.latest_event if latest_event: return latest_event.state return None @property - def latest_event( self ): + def latest_event(self): if self.events: return self.events[0] return None @property - def samples_have_common_state( self ): + def samples_have_common_state(self): """ Returns the state of this request's samples when they are all in one common state. Otherwise returns False. @@ -4466,7 +4480,7 @@ def samples_have_common_state( self ): return state_for_comparison @property - def last_comment( self ): + def last_comment(self): latest_event = self.latest_event if latest_event: if latest_event.comment: @@ -4474,59 +4488,59 @@ def last_comment( self ): return '' return 'No comment' - def get_sample( self, sample_name ): + def get_sample(self, sample_name): for sample in self.samples: if sample.name == sample_name: return sample return None @property - def is_unsubmitted( self ): - return self.state in [ self.states.REJECTED, self.states.NEW ] + def is_unsubmitted(self): + return self.state in [self.states.REJECTED, self.states.NEW] @property - def is_rejected( self ): + def is_rejected(self): return self.state == self.states.REJECTED @property - def is_submitted( self ): + def is_submitted(self): return self.state == self.states.SUBMITTED @property - def is_new( self ): + def is_new(self): return self.state == self.states.NEW @property - def is_complete( self ): + def is_complete(self): return self.state == self.states.COMPLETE @property - def samples_without_library_destinations( self ): + def samples_without_library_destinations(self): # Return all samples that are not associated with a library samples = [] for sample in self.samples: if not sample.library: - samples.append( sample ) + samples.append(sample) return samples @property - def samples_with_bar_code( self ): + def samples_with_bar_code(self): # Return all samples that have associated bar code samples = [] for sample in self.samples: if sample.bar_code: - samples.append( sample ) + samples.append(sample) return samples - def send_email_notification( self, trans, common_state, final_state=False ): + def send_email_notification(self, trans, common_state, final_state=False): # Check if an email notification is configured to be sent when the samples # are in this state - if self.notification and common_state.id not in self.notification[ 'sample_states' ]: + if self.notification and common_state.id not in self.notification['sample_states']: return comments = '' # Send email - if trans.app.config.smtp_server is not None and self.notification and self.notification[ 'email' ]: + if trans.app.config.smtp_server is not None and self.notification and self.notification['email']: body = """ Galaxy Sample Tracking Notification =================================== @@ -4541,14 +4555,14 @@ def send_email_notification( self, trans, common_state, final_state=False ): All samples in state: %(sample_state)s """ - values = dict( user=self.user.email, - request_name=self.name, - request_type=self.type.name, - request_state=self.state, - num_samples=str( len( self.samples ) ), - sample_state=common_state.name, - create_time=self.create_time, - submit_time=self.create_time ) + values = dict(user=self.user.email, + request_name=self.name, + request_type=self.type.name, + request_state=self.state, + num_samples=str(len(self.samples)), + sample_state=common_state.name, + create_time=self.create_time, + submit_time=self.create_time) body = body % values # check if this is the final state of the samples if final_state: @@ -4560,44 +4574,44 @@ def send_email_notification( self, trans, common_state, final_state=False ): else: library_name = 'No target data library' folder_name = 'No target data library folder' - txt = txt + "%s -> %s/%s\r\n" % ( s.name, library_name, folder_name ) + txt = txt + "%s -> %s/%s\r\n" % (s.name, library_name, folder_name) body = body + txt to = self.notification['email'] frm = trans.app.config.email_from if frm is None: - host = trans.request.host.split( ':' )[0] - if host in [ 'localhost', '127.0.0.1', '0.0.0.0' ]: + host = trans.request.host.split(':')[0] + if host in ['localhost', '127.0.0.1', '0.0.0.0']: host = socket.getfqdn() frm = 'galaxy-no-reply@' + host subject = "Galaxy Sample Tracking notification: '%s' sequencing request" % self.name try: - send_mail( frm, to, subject, body, trans.app.config ) - comments = "Email notification sent to %s." % ", ".join( to ).strip().strip( ',' ) + send_mail(frm, to, subject, body, trans.app.config) + comments = "Email notification sent to %s." % ", ".join(to).strip().strip(',') except Exception as e: comments = "Email notification failed. (%s)" % str(e) # update the request history with the email notification event elif not trans.app.config.smtp_server: comments = "Email notification failed as SMTP server not set in config file" if comments: - event = RequestEvent( self, self.state, comments ) - trans.sa_session.add( event ) + event = RequestEvent(self, self.state, comments) + trans.sa_session.add(event) trans.sa_session.flush() return comments -class RequestEvent( object ): +class RequestEvent(object): def __init__(self, request=None, request_state=None, comment=''): self.request = request self.state = request_state self.comment = comment -class ExternalService( object ): - data_transfer_protocol = Bunch( HTTP='http', - HTTPS='https', - SCP='scp' ) +class ExternalService(object): + data_transfer_protocol = Bunch(HTTP='http', + HTTPS='https', + SCP='scp') - def __init__( self, name=None, description=None, external_service_type_id=None, version=None, form_definition_id=None, form_values_id=None, deleted=None ): + def __init__(self, name=None, description=None, external_service_type_id=None, version=None, form_definition_id=None, form_values_id=None, deleted=None): self.name = name self.description = description self.external_service_type_id = external_service_type_id @@ -4607,102 +4621,102 @@ def __init__( self, name=None, description=None, external_service_type_id=None, self.deleted = deleted self.label = None # Used in the request_type controller's __build_external_service_select_field() method - def get_external_service_type( self, trans ): - return trans.app.external_service_types.all_external_service_types[ self.external_service_type_id ] + def get_external_service_type(self, trans): + return trans.app.external_service_types.all_external_service_types[self.external_service_type_id] - def load_data_transfer_settings( self, trans ): - trans.app.external_service_types.reload( self.external_service_type_id ) + def load_data_transfer_settings(self, trans): + trans.app.external_service_types.reload(self.external_service_type_id) self.data_transfer = {} - external_service_type = self.get_external_service_type( trans ) + external_service_type = self.get_external_service_type(trans) for data_transfer_protocol, data_transfer_obj in external_service_type.data_transfer.items(): if data_transfer_protocol == self.data_transfer_protocol.SCP: scp_configs = {} - automatic_transfer = data_transfer_obj.config.get( 'automatic_transfer', 'false' ) - scp_configs[ 'automatic_transfer' ] = galaxy.util.string_as_bool( automatic_transfer ) - scp_configs[ 'host' ] = self.form_values.content.get( data_transfer_obj.config.get( 'host', '' ), '' ) - scp_configs[ 'user_name' ] = self.form_values.content.get( data_transfer_obj.config.get( 'user_name', '' ), '' ) - scp_configs[ 'password' ] = self.form_values.content.get( data_transfer_obj.config.get( 'password', '' ), '' ) - scp_configs[ 'data_location' ] = self.form_values.content.get( data_transfer_obj.config.get( 'data_location', '' ), '' ) - scp_configs[ 'rename_dataset' ] = self.form_values.content.get( data_transfer_obj.config.get( 'rename_dataset', '' ), '' ) - self.data_transfer[ self.data_transfer_protocol.SCP ] = scp_configs + automatic_transfer = data_transfer_obj.config.get('automatic_transfer', 'false') + scp_configs['automatic_transfer'] = galaxy.util.string_as_bool(automatic_transfer) + scp_configs['host'] = self.form_values.content.get(data_transfer_obj.config.get('host', ''), '') + scp_configs['user_name'] = self.form_values.content.get(data_transfer_obj.config.get('user_name', ''), '') + scp_configs['password'] = self.form_values.content.get(data_transfer_obj.config.get('password', ''), '') + scp_configs['data_location'] = self.form_values.content.get(data_transfer_obj.config.get('data_location', ''), '') + scp_configs['rename_dataset'] = self.form_values.content.get(data_transfer_obj.config.get('rename_dataset', ''), '') + self.data_transfer[self.data_transfer_protocol.SCP] = scp_configs if data_transfer_protocol == self.data_transfer_protocol.HTTP: http_configs = {} - automatic_transfer = data_transfer_obj.config.get( 'automatic_transfer', 'false' ) - http_configs[ 'automatic_transfer' ] = galaxy.util.string_as_bool( automatic_transfer ) - self.data_transfer[ self.data_transfer_protocol.HTTP ] = http_configs + automatic_transfer = data_transfer_obj.config.get('automatic_transfer', 'false') + http_configs['automatic_transfer'] = galaxy.util.string_as_bool(automatic_transfer) + self.data_transfer[self.data_transfer_protocol.HTTP] = http_configs - def populate_actions( self, trans, item, param_dict=None ): - return self.get_external_service_type( trans ).actions.populate( self, item, param_dict=param_dict ) + def populate_actions(self, trans, item, param_dict=None): + return self.get_external_service_type(trans).actions.populate(self, item, param_dict=param_dict) -class RequestType( object, Dictifiable ): - dict_collection_visible_keys = ( 'id', 'name', 'desc' ) - dict_element_visible_keys = ( 'id', 'name', 'desc', 'request_form_id', 'sample_form_id' ) - rename_dataset_options = Bunch( NO='Do not rename', - SAMPLE_NAME='Preprend sample name', - EXPERIMENT_NAME='Prepend experiment name', - EXPERIMENT_AND_SAMPLE_NAME='Prepend experiment and sample name') - permitted_actions = get_permitted_actions( filter='REQUEST_TYPE' ) +class RequestType(object, Dictifiable): + dict_collection_visible_keys = ('id', 'name', 'desc') + dict_element_visible_keys = ('id', 'name', 'desc', 'request_form_id', 'sample_form_id') + rename_dataset_options = Bunch(NO='Do not rename', + SAMPLE_NAME='Preprend sample name', + EXPERIMENT_NAME='Prepend experiment name', + EXPERIMENT_AND_SAMPLE_NAME='Prepend experiment and sample name') + permitted_actions = get_permitted_actions(filter='REQUEST_TYPE') - def __init__( self, name=None, desc=None, request_form=None, sample_form=None ): + def __init__(self, name=None, desc=None, request_form=None, sample_form=None): self.name = name self.desc = desc self.request_form = request_form self.sample_form = sample_form @property - def external_services( self ): + def external_services(self): external_services = [] for rtesa in self.external_service_associations: - external_services.append( rtesa.external_service ) + external_services.append(rtesa.external_service) return external_services - def get_external_service( self, external_service_type_id ): + def get_external_service(self, external_service_type_id): for rtesa in self.external_service_associations: if rtesa.external_service.external_service_type_id == external_service_type_id: return rtesa.external_service return None - def get_external_services_for_manual_data_transfer( self, trans ): + def get_external_services_for_manual_data_transfer(self, trans): '''Returns all external services that use manual data transfer''' external_services = [] for rtesa in self.external_service_associations: external_service = rtesa.external_service # load data transfer settings - external_service.load_data_transfer_settings( trans ) + external_service.load_data_transfer_settings(trans) if external_service.data_transfer: for transfer_type, transfer_type_settings in external_service.data_transfer.items(): - if not transfer_type_settings[ 'automatic_transfer' ]: - external_services.append( external_service ) + if not transfer_type_settings['automatic_transfer']: + external_services.append(external_service) return external_services - def delete_external_service_associations( self, trans ): + def delete_external_service_associations(self, trans): '''Deletes all external service associations.''' flush_needed = False for rtesa in self.external_service_associations: - trans.sa_session.delete( rtesa ) + trans.sa_session.delete(rtesa) flush_needed = True if flush_needed: trans.sa_session.flush() - def add_external_service_association( self, trans, external_service ): - rtesa = trans.model.RequestTypeExternalServiceAssociation( self, external_service ) - trans.sa_session.add( rtesa ) + def add_external_service_association(self, trans, external_service): + rtesa = trans.model.RequestTypeExternalServiceAssociation(self, external_service) + trans.sa_session.add(rtesa) trans.sa_session.flush() @property - def final_sample_state( self ): + def final_sample_state(self): # The states mapper for this object orders ascending return self.states[-1] @property - def run_details( self ): + def run_details(self): if self.run: # self.run[0] is [RequestTypeRunAssociation] return self.run[0] return None - def get_template_widgets( self, trans, get_contents=True ): + def get_template_widgets(self, trans, get_contents=True): # See if we have any associated templates. The get_contents param # is passed by callers that are inheriting a template - these are # usually new samples for which we want to include template fields, @@ -4715,30 +4729,30 @@ def get_template_widgets( self, trans, get_contents=True ): # See if we have any field contents info = run.info if info: - return template.get_widgets( trans.user, contents=info.content ) - return template.get_widgets( trans.user ) + return template.get_widgets(trans.user, contents=info.content) + return template.get_widgets(trans.user) return [] -class RequestTypeExternalServiceAssociation( object ): - def __init__( self, request_type, external_service ): +class RequestTypeExternalServiceAssociation(object): + def __init__(self, request_type, external_service): self.request_type = request_type self.external_service = external_service -class RequestTypePermissions( object ): - def __init__( self, action, request_type, role ): +class RequestTypePermissions(object): + def __init__(self, action, request_type, role): self.action = action self.request_type = request_type self.role = role -class Sample( object, Dictifiable ): +class Sample(object, Dictifiable): # The following form_builder classes are supported by the Sample class. - supported_field_types = [ CheckboxField, SelectField, TextField, WorkflowField, WorkflowMappingField, HistoryField ] - bulk_operations = Bunch( CHANGE_STATE='Change state', - SELECT_LIBRARY='Select data library and folder' ) - dict_collection_visible_keys = ( 'id', 'name' ) + supported_field_types = [CheckboxField, SelectField, TextField, WorkflowField, WorkflowMappingField, HistoryField] + bulk_operations = Bunch(CHANGE_STATE='Change state', + SELECT_LIBRARY='Select data library and folder') + dict_collection_visible_keys = ('id', 'name') def __init__(self, name=None, desc=None, request=None, form_values=None, bar_code=None, library=None, folder=None, workflow=None, history=None): self.name = name @@ -4752,76 +4766,76 @@ def __init__(self, name=None, desc=None, request=None, form_values=None, bar_cod self.workflow = workflow @property - def state( self ): + def state(self): latest_event = self.latest_event if latest_event: return latest_event.state return None @property - def latest_event( self ): + def latest_event(self): if self.events: return self.events[0] return None @property - def adding_to_library_dataset_files( self ): + def adding_to_library_dataset_files(self): adding_to_library_datasets = [] for dataset in self.datasets: if dataset.status == SampleDataset.transfer_status.ADD_TO_LIBRARY: - adding_to_library_datasets.append( dataset ) + adding_to_library_datasets.append(dataset) return adding_to_library_datasets @property - def inprogress_dataset_files( self ): + def inprogress_dataset_files(self): inprogress_datasets = [] for dataset in self.datasets: - if dataset.status not in [ SampleDataset.transfer_status.NOT_STARTED, SampleDataset.transfer_status.COMPLETE ]: - inprogress_datasets.append( dataset ) + if dataset.status not in [SampleDataset.transfer_status.NOT_STARTED, SampleDataset.transfer_status.COMPLETE]: + inprogress_datasets.append(dataset) return inprogress_datasets @property - def queued_dataset_files( self ): + def queued_dataset_files(self): queued_datasets = [] for dataset in self.datasets: if dataset.status == SampleDataset.transfer_status.IN_QUEUE: - queued_datasets.append( dataset ) + queued_datasets.append(dataset) return queued_datasets @property - def transfer_error_dataset_files( self ): + def transfer_error_dataset_files(self): transfer_error_datasets = [] for dataset in self.datasets: if dataset.status == SampleDataset.transfer_status.ERROR: - transfer_error_datasets.append( dataset ) + transfer_error_datasets.append(dataset) return transfer_error_datasets @property - def transferred_dataset_files( self ): + def transferred_dataset_files(self): transferred_datasets = [] for dataset in self.datasets: if dataset.status == SampleDataset.transfer_status.COMPLETE: - transferred_datasets.append( dataset ) + transferred_datasets.append(dataset) return transferred_datasets @property - def transferring_dataset_files( self ): + def transferring_dataset_files(self): transferring_datasets = [] for dataset in self.datasets: if dataset.status == SampleDataset.transfer_status.TRANSFERRING: - transferring_datasets.append( dataset ) + transferring_datasets.append(dataset) return transferring_datasets @property - def untransferred_dataset_files( self ): + def untransferred_dataset_files(self): untransferred_datasets = [] for dataset in self.datasets: if dataset.status != SampleDataset.transfer_status.COMPLETE: - untransferred_datasets.append( dataset ) + untransferred_datasets.append(dataset) return untransferred_datasets @property - def run_details( self ): + def run_details(self): # self.runs is a list of SampleRunAssociations ordered descending on update_time. if self.runs: # Always use the latest run details template, self.runs[0] is a SampleRunAssociation @@ -4829,7 +4843,7 @@ def run_details( self ): # Inherit this sample's RequestType run details, if one exists. return self.request.type.run_details - def get_template_widgets( self, trans, get_contents=True ): + def get_template_widgets(self, trans, get_contents=True): # Samples have a one-to-many relationship with run details, so we return the # widgets for last associated template. The get_contents param will populate # the widget fields with values from the template inherited from the sample's @@ -4851,38 +4865,38 @@ def get_template_widgets( self, trans, get_contents=True ): # See if we have any field contents info = run.info if info: - return template.get_widgets( trans.user, contents=info.content ) - return template.get_widgets( trans.user ) + return template.get_widgets(trans.user, contents=info.content) + return template.get_widgets(trans.user) return [] - def populate_external_services( self, param_dict=None, trans=None ): + def populate_external_services(self, param_dict=None, trans=None): if self.request and self.request.type: - return [ service.populate_actions( item=self, param_dict=param_dict, trans=trans ) for service in self.request.type.external_services ] + return [service.populate_actions(item=self, param_dict=param_dict, trans=trans) for service in self.request.type.external_services] -class SampleState( object ): +class SampleState(object): def __init__(self, name=None, desc=None, request_type=None): self.name = name self.desc = desc self.request_type = request_type -class SampleEvent( object ): +class SampleEvent(object): def __init__(self, sample=None, sample_state=None, comment=''): self.sample = sample self.state = sample_state self.comment = comment -class SampleDataset( object ): - transfer_status = Bunch( NOT_STARTED='Not started', - IN_QUEUE='In queue', - TRANSFERRING='Transferring dataset', - ADD_TO_LIBRARY='Adding to data library', - COMPLETE='Complete', - ERROR='Error' ) +class SampleDataset(object): + transfer_status = Bunch(NOT_STARTED='Not started', + IN_QUEUE='In queue', + TRANSFERRING='Transferring dataset', + ADD_TO_LIBRARY='Adding to data library', + COMPLETE='Complete', + ERROR='Error') - def __init__( self, sample=None, name=None, file_path=None, status=None, error_msg=None, size=None, external_service=None ): + def __init__(self, sample=None, name=None, file_path=None, status=None, error_msg=None, size=None, external_service=None): self.sample = sample self.name = name self.file_path = file_path @@ -4892,29 +4906,29 @@ def __init__( self, sample=None, name=None, file_path=None, status=None, error_m self.external_service = external_service -class Run( object ): - def __init__( self, form_definition, form_values, subindex=None ): +class Run(object): + def __init__(self, form_definition, form_values, subindex=None): self.template = form_definition self.info = form_values self.subindex = subindex -class RequestTypeRunAssociation( object ): - def __init__( self, request_type, run ): +class RequestTypeRunAssociation(object): + def __init__(self, request_type, run): self.request_type = request_type self.run = run -class SampleRunAssociation( object ): - def __init__( self, sample, run ): +class SampleRunAssociation(object): + def __init__(self, sample, run): self.sample = sample self.run = run -class UserAddress( object ): - def __init__( self, user=None, desc=None, name=None, institution=None, - address=None, city=None, state=None, postal_code=None, - country=None, phone=None ): +class UserAddress(object): + def __init__(self, user=None, desc=None, name=None, institution=None, + address=None, city=None, state=None, postal_code=None, + country=None, phone=None): self.user = user self.desc = desc self.name = name @@ -4926,17 +4940,17 @@ def __init__( self, user=None, desc=None, name=None, institution=None, self.country = country self.phone = phone - def to_dict( self, trans ): - return { 'id' : trans.security.encode_id( self.id ), - 'name' : sanitize_html( self.name ), - 'desc' : sanitize_html( self.desc ), - 'institution' : sanitize_html( self.institution ), - 'address' : sanitize_html( self.address ), - 'city' : sanitize_html( self.city ), - 'state' : sanitize_html( self.state ), - 'postal_code' : sanitize_html( self.postal_code ), - 'country' : sanitize_html( self.country ), - 'phone' : sanitize_html( self.phone ) } + def to_dict(self, trans): + return {'id' : trans.security.encode_id(self.id), + 'name' : sanitize_html(self.name), + 'desc' : sanitize_html(self.desc), + 'institution' : sanitize_html(self.institution), + 'address' : sanitize_html(self.address), + 'city' : sanitize_html(self.city), + 'state' : sanitize_html(self.state), + 'postal_code' : sanitize_html(self.postal_code), + 'country' : sanitize_html(self.country), + 'phone' : sanitize_html(self.phone)} def get_html(self): # This should probably be deprecated eventually. It should currently @@ -4963,17 +4977,17 @@ def get_html(self): return html -class UserOpenID( object ): - def __init__( self, user=None, session=None, openid=None ): +class UserOpenID(object): + def __init__(self, user=None, session=None, openid=None): self.user = user self.session = session self.openid = openid -class Page( object, Dictifiable ): - dict_element_visible_keys = [ 'id', 'title', 'latest_revision_id', 'slug', 'published', 'importable', 'deleted' ] +class Page(object, Dictifiable): + dict_element_visible_keys = ['id', 'title', 'latest_revision_id', 'slug', 'published', 'importable', 'deleted'] - def __init__( self ): + def __init__(self): self.id = None self.user = None self.title = None @@ -4983,8 +4997,8 @@ def __init__( self ): self.importable = None self.published = None - def to_dict( self, view='element' ): - rval = super( Page, self ).to_dict( view=view ) + def to_dict(self, view='element'): + rval = super(Page, self).to_dict(view=view) rev = [] for a in self.revisions: rev.append(a.id) @@ -4992,29 +5006,29 @@ def to_dict( self, view='element' ): return rval -class PageRevision( object, Dictifiable ): - dict_element_visible_keys = [ 'id', 'page_id', 'title', 'content' ] +class PageRevision(object, Dictifiable): + dict_element_visible_keys = ['id', 'page_id', 'title', 'content'] - def __init__( self ): + def __init__(self): self.user = None self.title = None self.content = None - def to_dict( self, view='element' ): - rval = super( PageRevision, self ).to_dict( view=view ) + def to_dict(self, view='element'): + rval = super(PageRevision, self).to_dict(view=view) rval['create_time'] = str(self.create_time) rval['update_time'] = str(self.update_time) return rval -class PageUserShareAssociation( object ): - def __init__( self ): +class PageUserShareAssociation(object): + def __init__(self): self.page = None self.user = None -class Visualization( object ): - def __init__( self, id=None, user=None, type=None, title=None, dbkey=None, slug=None, latest_revision=None ): +class Visualization(object): + def __init__(self, id=None, user=None, type=None, title=None, dbkey=None, slug=None, latest_revision=None): self.id = id self.user = user self.type = type @@ -5024,9 +5038,9 @@ def __init__( self, id=None, user=None, type=None, title=None, dbkey=None, slug= self.latest_revision = latest_revision self.revisions = [] if self.latest_revision: - self.revisions.append( latest_revision ) + self.revisions.append(latest_revision) - def copy( self, user=None, title=None ): + def copy(self, user=None, title=None): """ Provide copy of visualization with only its latest revision. """ @@ -5042,21 +5056,21 @@ def copy( self, user=None, title=None ): if not title: title = self.title - copy_viz = Visualization( user=user, type=self.type, title=title, dbkey=self.dbkey ) - copy_revision = self.latest_revision.copy( visualization=copy_viz ) + copy_viz = Visualization(user=user, type=self.type, title=title, dbkey=self.dbkey) + copy_revision = self.latest_revision.copy(visualization=copy_viz) copy_viz.latest_revision = copy_revision return copy_viz -class VisualizationRevision( object ): - def __init__( self, visualization=None, title=None, dbkey=None, config=None ): +class VisualizationRevision(object): + def __init__(self, visualization=None, title=None, dbkey=None, config=None): self.id = None self.visualization = visualization self.title = title self.dbkey = dbkey self.config = config - def copy( self, visualization=None ): + def copy(self, visualization=None): """ Returns a copy of this object. """ @@ -5071,25 +5085,25 @@ def copy( self, visualization=None ): ) -class VisualizationUserShareAssociation( object ): - def __init__( self ): +class VisualizationUserShareAssociation(object): + def __init__(self): self.visualization = None self.user = None -class TransferJob( object ): +class TransferJob(object): # These states are used both by the transfer manager's IPC and the object # state in the database. Not all states are used by both. - states = Bunch( NEW='new', - UNKNOWN='unknown', - PROGRESS='progress', - RUNNING='running', - ERROR='error', - DONE='done' ) - terminal_states = [ states.ERROR, - states.DONE ] - - def __init__( self, state=None, path=None, info=None, pid=None, socket=None, params=None ): + states = Bunch(NEW='new', + UNKNOWN='unknown', + PROGRESS='progress', + RUNNING='running', + ERROR='error', + DONE='done') + terminal_states = [states.ERROR, + states.DONE] + + def __init__(self, state=None, path=None, info=None, pid=None, socket=None, params=None): self.state = state self.path = path self.info = info @@ -5098,22 +5112,22 @@ def __init__( self, state=None, path=None, info=None, pid=None, socket=None, par self.params = params -class Tag ( object ): - def __init__( self, id=None, type=None, parent_id=None, name=None ): +class Tag (object): + def __init__(self, id=None, type=None, parent_id=None, name=None): self.id = id self.type = type self.parent_id = parent_id self.name = name - def __str__( self ): - return "Tag(id=%s, type=%i, parent_id=%s, name=%s)" % ( self.id, self.type, self.parent_id, self.name ) + def __str__(self): + return "Tag(id=%s, type=%i, parent_id=%s, name=%s)" % (self.id, self.type, self.parent_id, self.name) -class ItemTagAssociation ( object, Dictifiable ): - dict_collection_visible_keys = ( 'id', 'user_tname', 'user_value' ) +class ItemTagAssociation (object, Dictifiable): + dict_collection_visible_keys = ('id', 'user_tname', 'user_value') dict_element_visible_keys = dict_collection_visible_keys - def __init__( self, id=None, user=None, item_id=None, tag_id=None, user_tname=None, value=None ): + def __init__(self, id=None, user=None, item_id=None, tag_id=None, user_tname=None, value=None): self.id = id self.user = user self.item_id = item_id @@ -5131,44 +5145,48 @@ def copy(self): return new_ta -class HistoryTagAssociation ( ItemTagAssociation ): +class HistoryTagAssociation (ItemTagAssociation): + pass + + +class DatasetTagAssociation (ItemTagAssociation): pass -class DatasetTagAssociation ( ItemTagAssociation ): +class HistoryDatasetAssociationTagAssociation (ItemTagAssociation): pass -class HistoryDatasetAssociationTagAssociation ( ItemTagAssociation ): +class LibraryDatasetDatasetAssociationTagAssociation (ItemTagAssociation): pass -class PageTagAssociation ( ItemTagAssociation ): +class PageTagAssociation (ItemTagAssociation): pass -class WorkflowStepTagAssociation ( ItemTagAssociation ): +class WorkflowStepTagAssociation (ItemTagAssociation): pass -class StoredWorkflowTagAssociation ( ItemTagAssociation ): +class StoredWorkflowTagAssociation (ItemTagAssociation): pass -class VisualizationTagAssociation ( ItemTagAssociation ): +class VisualizationTagAssociation (ItemTagAssociation): pass -class HistoryDatasetCollectionTagAssociation( ItemTagAssociation ): +class HistoryDatasetCollectionTagAssociation(ItemTagAssociation): pass -class LibraryDatasetCollectionTagAssociation( ItemTagAssociation ): +class LibraryDatasetCollectionTagAssociation(ItemTagAssociation): pass -class ToolTagAssociation( ItemTagAssociation ): - def __init__( self, id=None, user=None, tool_id=None, tag_id=None, user_tname=None, value=None ): +class ToolTagAssociation(ItemTagAssociation): + def __init__(self, id=None, user=None, tool_id=None, tag_id=None, user_tname=None, value=None): self.id = id self.user = user self.tool_id = tool_id @@ -5178,8 +5196,8 @@ def __init__( self, id=None, user=None, tool_id=None, tag_id=None, user_tname=No self.user_value = None -class WorkRequestTagAssociation( ItemTagAssociation ): - def __init__( self, id=None, user=None, workflow_request_id=None, tag_id=None, user_tname=None, value=None ): +class WorkRequestTagAssociation(ItemTagAssociation): + def __init__(self, id=None, user=None, workflow_request_id=None, tag_id=None, user_tname=None, value=None): self.id = id self.user = user self.workflow_request_id = workflow_request_id @@ -5190,111 +5208,111 @@ def __init__( self, id=None, user=None, workflow_request_id=None, tag_id=None, u # Item annotation classes. -class HistoryAnnotationAssociation( object ): +class HistoryAnnotationAssociation(object): pass -class HistoryDatasetAssociationAnnotationAssociation( object ): +class HistoryDatasetAssociationAnnotationAssociation(object): pass -class StoredWorkflowAnnotationAssociation( object ): +class StoredWorkflowAnnotationAssociation(object): pass -class WorkflowStepAnnotationAssociation( object ): +class WorkflowStepAnnotationAssociation(object): pass -class PageAnnotationAssociation( object ): +class PageAnnotationAssociation(object): pass -class VisualizationAnnotationAssociation( object ): +class VisualizationAnnotationAssociation(object): pass -class HistoryDatasetCollectionAnnotationAssociation( object ): +class HistoryDatasetCollectionAnnotationAssociation(object): pass -class LibraryDatasetCollectionAnnotationAssociation( object ): +class LibraryDatasetCollectionAnnotationAssociation(object): pass # Item rating classes. -class ItemRatingAssociation( object ): - def __init__( self, id=None, user=None, item=None, rating=0 ): +class ItemRatingAssociation(object): + def __init__(self, id=None, user=None, item=None, rating=0): self.id = id self.user = user self.item = item self.rating = rating - def set_item( self, item ): + def set_item(self, item): """ Set association's item. """ pass -class HistoryRatingAssociation( ItemRatingAssociation ): - def set_item( self, history ): +class HistoryRatingAssociation(ItemRatingAssociation): + def set_item(self, history): self.history = history -class HistoryDatasetAssociationRatingAssociation( ItemRatingAssociation ): - def set_item( self, history_dataset_association ): +class HistoryDatasetAssociationRatingAssociation(ItemRatingAssociation): + def set_item(self, history_dataset_association): self.history_dataset_association = history_dataset_association -class StoredWorkflowRatingAssociation( ItemRatingAssociation ): - def set_item( self, stored_workflow ): +class StoredWorkflowRatingAssociation(ItemRatingAssociation): + def set_item(self, stored_workflow): self.stored_workflow = stored_workflow -class PageRatingAssociation( ItemRatingAssociation ): - def set_item( self, page ): +class PageRatingAssociation(ItemRatingAssociation): + def set_item(self, page): self.page = page -class VisualizationRatingAssociation( ItemRatingAssociation ): - def set_item( self, visualization ): +class VisualizationRatingAssociation(ItemRatingAssociation): + def set_item(self, visualization): self.visualization = visualization -class HistoryDatasetCollectionRatingAssociation( ItemRatingAssociation ): - def set_item( self, dataset_collection ): +class HistoryDatasetCollectionRatingAssociation(ItemRatingAssociation): + def set_item(self, dataset_collection): self.dataset_collection = dataset_collection -class LibraryDatasetCollectionRatingAssociation( ItemRatingAssociation ): - def set_item( self, dataset_collection ): +class LibraryDatasetCollectionRatingAssociation(ItemRatingAssociation): + def set_item(self, dataset_collection): self.dataset_collection = dataset_collection # Data Manager Classes -class DataManagerHistoryAssociation( object ): - def __init__( self, id=None, history=None, user=None ): +class DataManagerHistoryAssociation(object): + def __init__(self, id=None, history=None, user=None): self.id = id self.history = history self.user = user -class DataManagerJobAssociation( object ): - def __init__( self, id=None, job=None, data_manager_id=None ): +class DataManagerJobAssociation(object): + def __init__(self, id=None, job=None, data_manager_id=None): self.id = id self.job = job self.data_manager_id = data_manager_id # end of Data Manager Classes -class UserPreference ( object ): - def __init__( self, name=None, value=None): +class UserPreference (object): + def __init__(self, name=None, value=None): self.name = name self.value = value -class UserAction( object ): - def __init__( self, id=None, create_time=None, user_id=None, session_id=None, action=None, params=None, context=None): +class UserAction(object): + def __init__(self, id=None, create_time=None, user_id=None, session_id=None, action=None, params=None, context=None): self.id = id self.create_time = create_time self.user_id = user_id @@ -5304,8 +5322,8 @@ def __init__( self, id=None, create_time=None, user_id=None, session_id=None, ac self.context = context -class APIKeys( object ): - def __init__( self, id=None, user_id=None, key=None): +class APIKeys(object): + def __init__(self, id=None, user_id=None, key=None): self.id = id self.user_id = user_id self.key = key diff --git a/lib/galaxy/model/base.py b/lib/galaxy/model/base.py index 63661ba84e9f..01e834619da4 100644 --- a/lib/galaxy/model/base.py +++ b/lib/galaxy/model/base.py @@ -20,7 +20,7 @@ class ModelMapping(Bunch): def __init__(self, model_modules, engine): self.engine = engine - context = scoped_session( sessionmaker( autoflush=False, autocommit=True ) ) + context = scoped_session(sessionmaker(autoflush=False, autocommit=True)) # For backward compatibility with "context.current" # deprecated? context.current = context @@ -36,7 +36,7 @@ def __init__(self, model_modules, engine): super(ModelMapping, self).__init__(**model_classes) context.remove() - context.configure( bind=engine ) + context.configure(bind=engine) @property def Session(self): diff --git a/lib/galaxy/model/custom_types.py b/lib/galaxy/model/custom_types.py index 54b793637bc8..a60c8c33f806 100644 --- a/lib/galaxy/model/custom_types.py +++ b/lib/galaxy/model/custom_types.py @@ -19,11 +19,11 @@ from galaxy import app from galaxy.util.aliaspickler import AliasPickleModule -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) # Default JSON encoder and decoder -json_encoder = json.JSONEncoder( sort_keys=True ) -json_decoder = json.JSONDecoder( ) +json_encoder = json.JSONEncoder(sort_keys=True) +json_decoder = json.JSONDecoder() def _sniffnfix_pg9_hex(value): @@ -33,8 +33,8 @@ def _sniffnfix_pg9_hex(value): try: if value[0] == 'x': return binascii.unhexlify(value[1:]) - elif value.startswith( '\\x' ): - return binascii.unhexlify( value[2:] ) + elif value.startswith('\\x'): + return binascii.unhexlify(value[2:]) else: return value except Exception: @@ -61,7 +61,7 @@ def process_bind_param(self, value, dialect): def process_result_value(self, value, dialect): if value is not None: - value = json_decoder.decode( str( _sniffnfix_pg9_hex( value ) ) ) + value = json_decoder.decode(str(_sniffnfix_pg9_hex(value))) return value def load_dialect_impl(self, dialect): @@ -70,11 +70,11 @@ def load_dialect_impl(self, dialect): else: return self.impl - def copy_value( self, value ): - return copy.deepcopy( value ) + def copy_value(self, value): + return copy.deepcopy(value) - def compare_values( self, x, y ): - return ( x == y ) + def compare_values(self, x, y): + return (x == y) class MutationObj(Mutable): @@ -219,9 +219,9 @@ def remove(self, value): MutationObj.associate_with(JSONType) -metadata_pickler = AliasPickleModule( { - ( "cookbook.patterns", "Bunch" ): ( "galaxy.util.bunch", "Bunch" ) -} ) +metadata_pickler = AliasPickleModule({ + ("cookbook.patterns", "Bunch"): ("galaxy.util.bunch", "Bunch") +}) def total_size(o, handlers={}, verbose=False): @@ -239,12 +239,12 @@ def total_size(o, handlers={}, verbose=False): def dict_handler(d): return chain.from_iterable(d.items()) - all_handlers = { tuple: iter, - list: iter, - deque: iter, - dict: dict_handler, - set: iter, - frozenset: iter } + all_handlers = {tuple: iter, + list: iter, + deque: iter, + dict: dict_handler, + set: iter, + frozenset: iter} all_handlers.update(handlers) # user handlers take precedence seen = set() # track which object id's have already been seen default_size = getsizeof(0) # estimate sizeof object without __sizeof__ @@ -264,7 +264,7 @@ def sizeof(o): return sizeof(o) -class MetadataType( JSONType ): +class MetadataType(JSONType): """ Backward compatible metadata type. Can read pickles or JSON, but always writes in JSON. @@ -281,17 +281,17 @@ def process_bind_param(self, value, dialect): value = json_encoder.encode(value) return value - def process_result_value( self, value, dialect ): + def process_result_value(self, value, dialect): if value is None: return None ret = None try: - ret = metadata_pickler.loads( str( value ) ) + ret = metadata_pickler.loads(str(value)) if ret: - ret = dict( ret.__dict__ ) + ret = dict(ret.__dict__) except: try: - ret = json_decoder.decode( str( _sniffnfix_pg9_hex(value) ) ) + ret = json_decoder.decode(str(_sniffnfix_pg9_hex(value))) except: ret = None return ret @@ -328,10 +328,10 @@ def process_result_value(self, value, dialect): return uuid.UUID(value) -class TrimmedString( TypeDecorator ): +class TrimmedString(TypeDecorator): impl = String - def process_bind_param( self, value, dialect ): + def process_bind_param(self, value, dialect): """Automatically truncate string values""" if self.impl.length and value is not None: value = value[0:self.impl.length] diff --git a/lib/galaxy/model/item_attrs.py b/lib/galaxy/model/item_attrs.py index b4dd1f305d9b..ae2061fa7efa 100644 --- a/lib/galaxy/model/item_attrs.py +++ b/lib/galaxy/model/item_attrs.py @@ -5,10 +5,10 @@ # Cannot import galaxy.model b/c it creates a circular import graph. import galaxy -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class RuntimeException( Exception ): +class RuntimeException(Exception): pass @@ -21,36 +21,37 @@ class UsesItemRatings: (2) item-rating association table has a column with a foreign key referencing item table that contains the item's id. """ - def get_ave_item_rating_data( self, db_session, item, webapp_model=None ): + + def get_ave_item_rating_data(self, db_session, item, webapp_model=None): """ Returns the average rating for an item.""" if webapp_model is None: webapp_model = galaxy.model - item_rating_assoc_class = self._get_item_rating_assoc_class( item, webapp_model=webapp_model ) + item_rating_assoc_class = self._get_item_rating_assoc_class(item, webapp_model=webapp_model) if not item_rating_assoc_class: - raise RuntimeException( "Item does not have ratings: %s" % item.__class__.__name__ ) - item_id_filter = self._get_item_id_filter_str( item, item_rating_assoc_class ) - ave_rating = db_session.query( func.avg( item_rating_assoc_class.rating ) ).filter( item_id_filter ).scalar() + raise RuntimeException("Item does not have ratings: %s" % item.__class__.__name__) + item_id_filter = self._get_item_id_filter_str(item, item_rating_assoc_class) + ave_rating = db_session.query(func.avg(item_rating_assoc_class.rating)).filter(item_id_filter).scalar() # Convert ave_rating to float; note: if there are no item ratings, ave rating is None. if ave_rating: - ave_rating = float( ave_rating ) + ave_rating = float(ave_rating) else: ave_rating = 0 - num_ratings = int( db_session.query( func.count( item_rating_assoc_class.rating ) ).filter( item_id_filter ).scalar() ) - return ( ave_rating, num_ratings ) + num_ratings = int(db_session.query(func.count(item_rating_assoc_class.rating)).filter(item_id_filter).scalar()) + return (ave_rating, num_ratings) - def rate_item( self, db_session, user, item, rating, webapp_model=None ): + def rate_item(self, db_session, user, item, rating, webapp_model=None): """ Rate an item. Return type is RatingAssociation. """ if webapp_model is None: webapp_model = galaxy.model - item_rating = self.get_user_item_rating( db_session, user, item, webapp_model=webapp_model ) + item_rating = self.get_user_item_rating(db_session, user, item, webapp_model=webapp_model) if not item_rating: # User has not yet rated item; create rating. - item_rating_assoc_class = self._get_item_rating_assoc_class( item, webapp_model=webapp_model ) + item_rating_assoc_class = self._get_item_rating_assoc_class(item, webapp_model=webapp_model) item_rating = item_rating_assoc_class() item_rating.user = user - item_rating.set_item( item ) + item_rating.set_item(item) item_rating.rating = rating - db_session.add( item_rating ) + db_session.add(item_rating) db_session.flush() elif item_rating.rating != rating: # User has rated item; update rating. @@ -58,110 +59,111 @@ def rate_item( self, db_session, user, item, rating, webapp_model=None ): db_session.flush() return item_rating - def get_user_item_rating( self, db_session, user, item, webapp_model=None ): + def get_user_item_rating(self, db_session, user, item, webapp_model=None): """ Returns user's rating for an item. Return type is RatingAssociation. """ if webapp_model is None: webapp_model = galaxy.model - item_rating_assoc_class = self._get_item_rating_assoc_class( item, webapp_model=webapp_model ) + item_rating_assoc_class = self._get_item_rating_assoc_class(item, webapp_model=webapp_model) if not item_rating_assoc_class: - raise RuntimeException( "Item does not have ratings: %s" % item.__class__.__name__ ) + raise RuntimeException("Item does not have ratings: %s" % item.__class__.__name__) # Query rating table by user and item id. - item_id_filter = self._get_item_id_filter_str( item, item_rating_assoc_class ) - return db_session.query( item_rating_assoc_class ).filter_by( user=user ).filter( item_id_filter ).first() + item_id_filter = self._get_item_id_filter_str(item, item_rating_assoc_class) + return db_session.query(item_rating_assoc_class).filter_by(user=user).filter(item_id_filter).first() - def _get_item_rating_assoc_class( self, item, webapp_model=None ): + def _get_item_rating_assoc_class(self, item, webapp_model=None): """ Returns an item's item-rating association class. """ if webapp_model is None: webapp_model = galaxy.model item_rating_assoc_class = '%sRatingAssociation' % item.__class__.__name__ - return getattr( webapp_model, item_rating_assoc_class, None ) + return getattr(webapp_model, item_rating_assoc_class, None) - def _get_item_id_filter_str( self, item, item_rating_assoc_class, webapp_model=None ): + def _get_item_id_filter_str(self, item, item_rating_assoc_class, webapp_model=None): # Get foreign key in item-rating association table that references item table. if webapp_model is None: webapp_model = galaxy.model item_fk = None for fk in item_rating_assoc_class.table.foreign_keys: - if fk.references( item.table ): + if fk.references(item.table): item_fk = fk break if not item_fk: - raise RuntimeException( "Cannot find item id column in item-rating association table: %s, %s" % item_rating_assoc_class.__name__, item_rating_assoc_class.table.name ) + raise RuntimeException("Cannot find item id column in item-rating association table: %s, %s" % item_rating_assoc_class.__name__, item_rating_assoc_class.table.name) # TODO: can we provide a better filter than a raw string? - return "%s=%i" % ( item_fk.parent.name, item.id ) + return "%s=%i" % (item_fk.parent.name, item.id) class UsesAnnotations: """ Mixin for getting and setting item annotations. """ - def get_item_annotation_str( self, db_session, user, item ): + + def get_item_annotation_str(self, db_session, user, item): """ Returns a user's annotation string for an item. """ - annotation_obj = self.get_item_annotation_obj( db_session, user, item ) + annotation_obj = self.get_item_annotation_obj(db_session, user, item) if annotation_obj: - return galaxy.util.unicodify( annotation_obj.annotation ) + return galaxy.util.unicodify(annotation_obj.annotation) return None - def get_item_annotation_obj( self, db_session, user, item ): + def get_item_annotation_obj(self, db_session, user, item): """ Returns a user's annotation object for an item. """ # Get annotation association class. - annotation_assoc_class = self._get_annotation_assoc_class( item ) + annotation_assoc_class = self._get_annotation_assoc_class(item) if not annotation_assoc_class: return None # Get annotation association object. - annotation_assoc = db_session.query( annotation_assoc_class ).filter_by( user=user ) + annotation_assoc = db_session.query(annotation_assoc_class).filter_by(user=user) # TODO: use filtering like that in _get_item_id_filter_str() if item.__class__ == galaxy.model.History: - annotation_assoc = annotation_assoc.filter_by( history=item ) + annotation_assoc = annotation_assoc.filter_by(history=item) elif item.__class__ == galaxy.model.HistoryDatasetAssociation: - annotation_assoc = annotation_assoc.filter_by( hda=item ) + annotation_assoc = annotation_assoc.filter_by(hda=item) elif item.__class__ == galaxy.model.StoredWorkflow: - annotation_assoc = annotation_assoc.filter_by( stored_workflow=item ) + annotation_assoc = annotation_assoc.filter_by(stored_workflow=item) elif item.__class__ == galaxy.model.WorkflowStep: - annotation_assoc = annotation_assoc.filter_by( workflow_step=item ) + annotation_assoc = annotation_assoc.filter_by(workflow_step=item) elif item.__class__ == galaxy.model.Page: - annotation_assoc = annotation_assoc.filter_by( page=item ) + annotation_assoc = annotation_assoc.filter_by(page=item) elif item.__class__ == galaxy.model.Visualization: - annotation_assoc = annotation_assoc.filter_by( visualization=item ) + annotation_assoc = annotation_assoc.filter_by(visualization=item) return annotation_assoc.first() - def add_item_annotation( self, db_session, user, item, annotation ): + def add_item_annotation(self, db_session, user, item, annotation): """ Add or update an item's annotation; a user can only have a single annotation for an item. """ # Get/create annotation association object. - annotation_assoc = self.get_item_annotation_obj( db_session, user, item ) + annotation_assoc = self.get_item_annotation_obj(db_session, user, item) if not annotation_assoc: - annotation_assoc_class = self._get_annotation_assoc_class( item ) + annotation_assoc_class = self._get_annotation_assoc_class(item) if not annotation_assoc_class: return None annotation_assoc = annotation_assoc_class() - item.annotations.append( annotation_assoc ) + item.annotations.append(annotation_assoc) annotation_assoc.user = user # Set annotation. annotation_assoc.annotation = annotation return annotation_assoc - def delete_item_annotation( self, db_session, user, item): - annotation_assoc = self.get_item_annotation_obj( db_session, user, item ) + def delete_item_annotation(self, db_session, user, item): + annotation_assoc = self.get_item_annotation_obj(db_session, user, item) if annotation_assoc: db_session.delete(annotation_assoc) db_session.flush() - def copy_item_annotation( self, db_session, source_user, source_item, target_user, target_item ): + def copy_item_annotation(self, db_session, source_user, source_item, target_user, target_item): """ Copy an annotation from a user/item source to a user/item target. """ if source_user and target_user: - annotation_str = self.get_item_annotation_str( db_session, source_user, source_item ) + annotation_str = self.get_item_annotation_str(db_session, source_user, source_item) if annotation_str: - annotation = self.add_item_annotation( db_session, target_user, target_item, annotation_str ) + annotation = self.add_item_annotation(db_session, target_user, target_item, annotation_str) return annotation return None - def _get_annotation_assoc_class( self, item ): + def _get_annotation_assoc_class(self, item): """ Returns an item's item-annotation association class. """ class_name = '%sAnnotationAssociation' % item.__class__.__name__ - return getattr( galaxy.model, class_name, None ) + return getattr(galaxy.model, class_name, None) __all__ = ( diff --git a/lib/galaxy/model/mapping.py b/lib/galaxy/model/mapping.py index 5388fc6524c6..d4f0e0c245e1 100644 --- a/lib/galaxy/model/mapping.py +++ b/lib/galaxy/model/mapping.py @@ -40,2492 +40,2506 @@ from galaxy.model.orm.now import now from galaxy.security import GalaxyRBACAgent -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() model.User.table = Table( "galaxy_user", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "email", TrimmedString( 255 ), index=True, nullable=False ), - Column( "username", TrimmedString( 255 ), index=True, unique=True ), - Column( "password", TrimmedString( 255 ), nullable=False ), - Column( "last_password_change", DateTime, default=now ), - Column( "external", Boolean, default=False ), - Column( "form_values_id", Integer, ForeignKey( "form_values.id" ), index=True ), - Column( "deleted", Boolean, index=True, default=False ), - Column( "purged", Boolean, index=True, default=False ), - Column( "disk_usage", Numeric( 15, 0 ), index=True ), - Column( "active", Boolean, index=True, default=True, nullable=False ), - Column( "activation_token", TrimmedString( 64 ), nullable=True, index=True ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("email", TrimmedString(255), index=True, nullable=False), + Column("username", TrimmedString(255), index=True, unique=True), + Column("password", TrimmedString(255), nullable=False), + Column("last_password_change", DateTime, default=now), + Column("external", Boolean, default=False), + Column("form_values_id", Integer, ForeignKey("form_values.id"), index=True), + Column("deleted", Boolean, index=True, default=False), + Column("purged", Boolean, index=True, default=False), + Column("disk_usage", Numeric(15, 0), index=True), + Column("active", Boolean, index=True, default=True, nullable=False), + Column("activation_token", TrimmedString(64), nullable=True, index=True)) model.UserAddress.table = Table( "user_address", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "desc", TrimmedString( 255 ) ), - Column( "name", TrimmedString( 255 ), nullable=False ), - Column( "institution", TrimmedString( 255 ) ), - Column( "address", TrimmedString( 255 ), nullable=False ), - Column( "city", TrimmedString( 255 ), nullable=False ), - Column( "state", TrimmedString( 255 ), nullable=False ), - Column( "postal_code", TrimmedString( 255 ), nullable=False ), - Column( "country", TrimmedString( 255 ), nullable=False ), - Column( "phone", TrimmedString( 255 ) ), - Column( "deleted", Boolean, index=True, default=False ), - Column( "purged", Boolean, index=True, default=False ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("desc", TrimmedString(255)), + Column("name", TrimmedString(255), nullable=False), + Column("institution", TrimmedString(255)), + Column("address", TrimmedString(255), nullable=False), + Column("city", TrimmedString(255), nullable=False), + Column("state", TrimmedString(255), nullable=False), + Column("postal_code", TrimmedString(255), nullable=False), + Column("country", TrimmedString(255), nullable=False), + Column("phone", TrimmedString(255)), + Column("deleted", Boolean, index=True, default=False), + Column("purged", Boolean, index=True, default=False)) model.UserOpenID.table = Table( "galaxy_user_openid", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, index=True, default=now, onupdate=now ), - Column( "session_id", Integer, ForeignKey( "galaxy_session.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "openid", TEXT, index=True, unique=True ), - Column( "provider", TrimmedString( 255 ) ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, index=True, default=now, onupdate=now), + Column("session_id", Integer, ForeignKey("galaxy_session.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("openid", TEXT, index=True, unique=True), + Column("provider", TrimmedString(255))) model.PasswordResetToken.table = Table( "password_reset_token", metadata, - Column( "token", String( 32 ), primary_key=True, unique=True, index=True ), - Column( "expiration_time", DateTime ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ) ) + Column("token", String(32), primary_key=True, unique=True, index=True), + Column("expiration_time", DateTime), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True)) model.History.table = Table( "history", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, index=True, default=now, onupdate=now ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "name", TrimmedString( 255 ) ), - Column( "hid_counter", Integer, default=1 ), - Column( "deleted", Boolean, index=True, default=False ), - Column( "purged", Boolean, index=True, default=False ), - Column( "importing", Boolean, index=True, default=False ), - Column( "genome_build", TrimmedString( 40 ) ), - Column( "importable", Boolean, default=False ), - Column( "slug", TEXT, index=True ), - Column( "published", Boolean, index=True, default=False ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, index=True, default=now, onupdate=now), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("name", TrimmedString(255)), + Column("hid_counter", Integer, default=1), + Column("deleted", Boolean, index=True, default=False), + Column("purged", Boolean, index=True, default=False), + Column("importing", Boolean, index=True, default=False), + Column("genome_build", TrimmedString(40)), + Column("importable", Boolean, default=False), + Column("slug", TEXT, index=True), + Column("published", Boolean, index=True, default=False)) model.HistoryUserShareAssociation.table = Table( "history_user_share_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ) ) + Column("id", Integer, primary_key=True), + Column("history_id", Integer, ForeignKey("history.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True)) model.HistoryDatasetAssociation.table = Table( "history_dataset_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ), - Column( "dataset_id", Integer, ForeignKey( "dataset.id" ), index=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "state", TrimmedString( 64 ), index=True, key="_state" ), - Column( "copied_from_history_dataset_association_id", Integer, - ForeignKey( "history_dataset_association.id" ), nullable=True ), - Column( "copied_from_library_dataset_dataset_association_id", Integer, - ForeignKey( "library_dataset_dataset_association.id" ), nullable=True ), - Column( "name", TrimmedString( 255 ) ), - Column( "info", TrimmedString( 255 ) ), - Column( "blurb", TrimmedString( 255 ) ), - Column( "peek", TEXT ), - Column( "tool_version", TEXT ), - Column( "extension", TrimmedString( 64 ) ), - Column( "metadata", MetadataType(), key="_metadata" ), - Column( "parent_id", Integer, ForeignKey( "history_dataset_association.id" ), nullable=True ), - Column( "designation", TrimmedString( 255 ) ), - Column( "deleted", Boolean, index=True, default=False ), - Column( "visible", Boolean ), - Column( "extended_metadata_id", Integer, ForeignKey( "extended_metadata.id" ), index=True ), - Column( "hid", Integer ), - Column( "purged", Boolean, index=True, default=False ), - Column( "hidden_beneath_collection_instance_id", - ForeignKey( "history_dataset_collection_association.id" ), nullable=True ) ) + Column("id", Integer, primary_key=True), + Column("history_id", Integer, ForeignKey("history.id"), index=True), + Column("dataset_id", Integer, ForeignKey("dataset.id"), index=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("state", TrimmedString(64), index=True, key="_state"), + Column("copied_from_history_dataset_association_id", Integer, + ForeignKey("history_dataset_association.id"), nullable=True), + Column("copied_from_library_dataset_dataset_association_id", Integer, + ForeignKey("library_dataset_dataset_association.id"), nullable=True), + Column("name", TrimmedString(255)), + Column("info", TrimmedString(255)), + Column("blurb", TrimmedString(255)), + Column("peek", TEXT), + Column("tool_version", TEXT), + Column("extension", TrimmedString(64)), + Column("metadata", MetadataType(), key="_metadata"), + Column("parent_id", Integer, ForeignKey("history_dataset_association.id"), nullable=True), + Column("designation", TrimmedString(255)), + Column("deleted", Boolean, index=True, default=False), + Column("visible", Boolean), + Column("extended_metadata_id", Integer, ForeignKey("extended_metadata.id"), index=True), + Column("hid", Integer), + Column("purged", Boolean, index=True, default=False), + Column("hidden_beneath_collection_instance_id", + ForeignKey("history_dataset_collection_association.id"), nullable=True)) model.Dataset.table = Table( "dataset", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, index=True, default=now, onupdate=now ), - Column( "state", TrimmedString( 64 ), index=True ), - Column( "deleted", Boolean, index=True, default=False ), - Column( "purged", Boolean, index=True, default=False ), - Column( "purgable", Boolean, default=True ), - Column( "object_store_id", TrimmedString( 255 ), index=True ), - Column( "external_filename", TEXT ), - Column( "_extra_files_path", TEXT ), - Column( 'file_size', Numeric( 15, 0 ) ), - Column( 'total_size', Numeric( 15, 0 ) ), - Column( 'uuid', UUIDType() ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, index=True, default=now, onupdate=now), + Column("state", TrimmedString(64), index=True), + Column("deleted", Boolean, index=True, default=False), + Column("purged", Boolean, index=True, default=False), + Column("purgable", Boolean, default=True), + Column("object_store_id", TrimmedString(255), index=True), + Column("external_filename", TEXT), + Column("_extra_files_path", TEXT), + Column('file_size', Numeric(15, 0)), + Column('total_size', Numeric(15, 0)), + Column('uuid', UUIDType())) # hda read access permission given by a user to a specific site (gen. for external display applications) model.HistoryDatasetAssociationDisplayAtAuthorization.table = Table( "history_dataset_association_display_at_authorization", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, index=True, default=now, onupdate=now ), - Column( "history_dataset_association_id", Integer, ForeignKey( "history_dataset_association.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "site", TrimmedString( 255 ) ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, index=True, default=now, onupdate=now), + Column("history_dataset_association_id", Integer, ForeignKey("history_dataset_association.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("site", TrimmedString(255))) model.HistoryDatasetAssociationSubset.table = Table( "history_dataset_association_subset", metadata, - Column( "id", Integer, primary_key=True ), - Column( "history_dataset_association_id", Integer, ForeignKey( "history_dataset_association.id" ), index=True ), - Column( "history_dataset_association_subset_id", Integer, ForeignKey( "history_dataset_association.id" ), index=True ), - Column( "location", Unicode( 255 ), index=True ) ) + Column("id", Integer, primary_key=True), + Column("history_dataset_association_id", Integer, ForeignKey("history_dataset_association.id"), index=True), + Column("history_dataset_association_subset_id", Integer, ForeignKey("history_dataset_association.id"), index=True), + Column("location", Unicode(255), index=True)) model.ImplicitlyConvertedDatasetAssociation.table = Table( "implicitly_converted_dataset_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "hda_id", Integer, ForeignKey( "history_dataset_association.id" ), index=True, nullable=True ), - Column( "ldda_id", Integer, ForeignKey( "library_dataset_dataset_association.id" ), index=True, nullable=True ), - Column( "hda_parent_id", Integer, ForeignKey( "history_dataset_association.id" ), index=True ), - Column( "ldda_parent_id", Integer, ForeignKey( "library_dataset_dataset_association.id" ), index=True ), - Column( "deleted", Boolean, index=True, default=False ), - Column( "metadata_safe", Boolean, index=True, default=True ), - Column( "type", TrimmedString( 255 ) ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("hda_id", Integer, ForeignKey("history_dataset_association.id"), index=True, nullable=True), + Column("ldda_id", Integer, ForeignKey("library_dataset_dataset_association.id"), index=True, nullable=True), + Column("hda_parent_id", Integer, ForeignKey("history_dataset_association.id"), index=True), + Column("ldda_parent_id", Integer, ForeignKey("library_dataset_dataset_association.id"), index=True), + Column("deleted", Boolean, index=True, default=False), + Column("metadata_safe", Boolean, index=True, default=True), + Column("type", TrimmedString(255))) model.ValidationError.table = Table( "validation_error", metadata, - Column( "id", Integer, primary_key=True ), - Column( "dataset_id", Integer, ForeignKey( "history_dataset_association.id" ), index=True ), - Column( "message", TrimmedString( 255 ) ), - Column( "err_type", TrimmedString( 64 ) ), - Column( "attributes", TEXT ) ) + Column("id", Integer, primary_key=True), + Column("dataset_id", Integer, ForeignKey("history_dataset_association.id"), index=True), + Column("message", TrimmedString(255)), + Column("err_type", TrimmedString(64)), + Column("attributes", TEXT)) model.Group.table = Table( "galaxy_group", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "name", String( 255 ), index=True, unique=True ), - Column( "deleted", Boolean, index=True, default=False ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("name", String(255), index=True, unique=True), + Column("deleted", Boolean, index=True, default=False)) model.UserGroupAssociation.table = Table( "user_group_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "group_id", Integer, ForeignKey( "galaxy_group.id" ), index=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ) ) + Column("id", Integer, primary_key=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("group_id", Integer, ForeignKey("galaxy_group.id"), index=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now)) model.UserRoleAssociation.table = Table( "user_role_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "role_id", Integer, ForeignKey( "role.id" ), index=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ) ) + Column("id", Integer, primary_key=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("role_id", Integer, ForeignKey("role.id"), index=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now)) model.GroupRoleAssociation.table = Table( "group_role_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "group_id", Integer, ForeignKey( "galaxy_group.id" ), index=True ), - Column( "role_id", Integer, ForeignKey( "role.id" ), index=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ) ) + Column("id", Integer, primary_key=True), + Column("group_id", Integer, ForeignKey("galaxy_group.id"), index=True), + Column("role_id", Integer, ForeignKey("role.id"), index=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now)) model.Role.table = Table( "role", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "name", String( 255 ), index=True, unique=True ), - Column( "description", TEXT ), - Column( "type", String( 40 ), index=True ), - Column( "deleted", Boolean, index=True, default=False ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("name", String(255), index=True, unique=True), + Column("description", TEXT), + Column("type", String(40), index=True), + Column("deleted", Boolean, index=True, default=False)) model.UserQuotaAssociation.table = Table( "user_quota_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "quota_id", Integer, ForeignKey( "quota.id" ), index=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ) ) + Column("id", Integer, primary_key=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("quota_id", Integer, ForeignKey("quota.id"), index=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now)) model.GroupQuotaAssociation.table = Table( "group_quota_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "group_id", Integer, ForeignKey( "galaxy_group.id" ), index=True ), - Column( "quota_id", Integer, ForeignKey( "quota.id" ), index=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ) ) + Column("id", Integer, primary_key=True), + Column("group_id", Integer, ForeignKey("galaxy_group.id"), index=True), + Column("quota_id", Integer, ForeignKey("quota.id"), index=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now)) model.Quota.table = Table( "quota", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "name", String( 255 ), index=True, unique=True ), - Column( "description", TEXT ), - Column( "bytes", BigInteger ), - Column( "operation", String( 8 ) ), - Column( "deleted", Boolean, index=True, default=False ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("name", String(255), index=True, unique=True), + Column("description", TEXT), + Column("bytes", BigInteger), + Column("operation", String(8)), + Column("deleted", Boolean, index=True, default=False)) model.DefaultQuotaAssociation.table = Table( "default_quota_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "type", String( 32 ), index=True, unique=True ), - Column( "quota_id", Integer, ForeignKey( "quota.id" ), index=True ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("type", String(32), index=True, unique=True), + Column("quota_id", Integer, ForeignKey("quota.id"), index=True)) model.DatasetPermissions.table = Table( "dataset_permissions", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "action", TEXT ), - Column( "dataset_id", Integer, ForeignKey( "dataset.id" ), index=True ), - Column( "role_id", Integer, ForeignKey( "role.id" ), index=True ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("action", TEXT), + Column("dataset_id", Integer, ForeignKey("dataset.id"), index=True), + Column("role_id", Integer, ForeignKey("role.id"), index=True)) model.LibraryPermissions.table = Table( "library_permissions", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "action", TEXT ), - Column( "library_id", Integer, ForeignKey( "library.id" ), nullable=True, index=True ), - Column( "role_id", Integer, ForeignKey( "role.id" ), index=True ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("action", TEXT), + Column("library_id", Integer, ForeignKey("library.id"), nullable=True, index=True), + Column("role_id", Integer, ForeignKey("role.id"), index=True)) model.LibraryFolderPermissions.table = Table( "library_folder_permissions", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "action", TEXT ), - Column( "library_folder_id", Integer, ForeignKey( "library_folder.id" ), nullable=True, index=True ), - Column( "role_id", Integer, ForeignKey( "role.id" ), index=True ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("action", TEXT), + Column("library_folder_id", Integer, ForeignKey("library_folder.id"), nullable=True, index=True), + Column("role_id", Integer, ForeignKey("role.id"), index=True)) model.LibraryDatasetPermissions.table = Table( "library_dataset_permissions", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "action", TEXT ), - Column( "library_dataset_id", Integer, ForeignKey( "library_dataset.id" ), nullable=True, index=True ), - Column( "role_id", Integer, ForeignKey( "role.id" ), index=True ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("action", TEXT), + Column("library_dataset_id", Integer, ForeignKey("library_dataset.id"), nullable=True, index=True), + Column("role_id", Integer, ForeignKey("role.id"), index=True)) model.LibraryDatasetDatasetAssociationPermissions.table = Table( "library_dataset_dataset_association_permissions", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "action", TEXT ), - Column( "library_dataset_dataset_association_id", Integer, - ForeignKey("library_dataset_dataset_association.id" ), - nullable=True, index=True ), - Column( "role_id", Integer, ForeignKey( "role.id" ), index=True ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("action", TEXT), + Column("library_dataset_dataset_association_id", Integer, + ForeignKey("library_dataset_dataset_association.id"), + nullable=True, index=True), + Column("role_id", Integer, ForeignKey("role.id"), index=True)) model.DefaultUserPermissions.table = Table( "default_user_permissions", metadata, - Column( "id", Integer, primary_key=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "action", TEXT ), - Column( "role_id", Integer, ForeignKey( "role.id" ), index=True ) ) + Column("id", Integer, primary_key=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("action", TEXT), + Column("role_id", Integer, ForeignKey("role.id"), index=True)) model.DefaultHistoryPermissions.table = Table( "default_history_permissions", metadata, - Column( "id", Integer, primary_key=True ), - Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ), - Column( "action", TEXT ), - Column( "role_id", Integer, ForeignKey( "role.id" ), index=True ) ) + Column("id", Integer, primary_key=True), + Column("history_id", Integer, ForeignKey("history.id"), index=True), + Column("action", TEXT), + Column("role_id", Integer, ForeignKey("role.id"), index=True)) model.LibraryDataset.table = Table( "library_dataset", metadata, - Column( "id", Integer, primary_key=True ), + Column("id", Integer, primary_key=True), # current version of dataset, if null, there is not a current version selected - Column( "library_dataset_dataset_association_id", Integer, - ForeignKey( "library_dataset_dataset_association.id", use_alter=True, name="library_dataset_dataset_association_id_fk" ), - nullable=True, index=True ), - Column( "folder_id", Integer, ForeignKey( "library_folder.id" ), index=True ), + Column("library_dataset_dataset_association_id", Integer, + ForeignKey("library_dataset_dataset_association.id", use_alter=True, name="library_dataset_dataset_association_id_fk"), + nullable=True, index=True), + Column("folder_id", Integer, ForeignKey("library_folder.id"), index=True), # not currently being used, but for possible future use - Column( "order_id", Integer ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), + Column("order_id", Integer), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), # when not None/null this will supercede display in library (but not when imported into user's history?) - Column( "name", TrimmedString( 255 ), key="_name", index=True ), + Column("name", TrimmedString(255), key="_name", index=True), # when not None/null this will supercede display in library (but not when imported into user's history?) - Column( "info", TrimmedString( 255 ), key="_info" ), - Column( "deleted", Boolean, index=True, default=False ), - Column( "purged", Boolean, index=True, default=False ) ) + Column("info", TrimmedString(255), key="_info"), + Column("deleted", Boolean, index=True, default=False), + Column("purged", Boolean, index=True, default=False)) model.LibraryDatasetDatasetAssociation.table = Table( "library_dataset_dataset_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "library_dataset_id", Integer, ForeignKey( "library_dataset.id" ), index=True ), - Column( "dataset_id", Integer, ForeignKey( "dataset.id" ), index=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "state", TrimmedString( 64 ), index=True, key="_state" ), - Column( "copied_from_history_dataset_association_id", Integer, - ForeignKey( "history_dataset_association.id", use_alter=True, name='history_dataset_association_dataset_id_fkey' ), - nullable=True ), - Column( "copied_from_library_dataset_dataset_association_id", Integer, - ForeignKey( "library_dataset_dataset_association.id", use_alter=True, name='library_dataset_dataset_association_id_fkey' ), - nullable=True ), - Column( "name", TrimmedString( 255 ), index=True ), - Column( "info", TrimmedString( 255 ) ), - Column( "blurb", TrimmedString( 255 ) ), - Column( "peek", TEXT ), - Column( "tool_version", TEXT ), - Column( "extension", TrimmedString( 64 ) ), - Column( "metadata", MetadataType(), key="_metadata" ), - Column( "parent_id", Integer, ForeignKey( "library_dataset_dataset_association.id" ), nullable=True ), - Column( "designation", TrimmedString( 255 ) ), - Column( "deleted", Boolean, index=True, default=False ), - Column( "visible", Boolean ), - Column( "extended_metadata_id", Integer, ForeignKey( "extended_metadata.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "message", TrimmedString( 255 ) ) ) + Column("id", Integer, primary_key=True), + Column("library_dataset_id", Integer, ForeignKey("library_dataset.id"), index=True), + Column("dataset_id", Integer, ForeignKey("dataset.id"), index=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("state", TrimmedString(64), index=True, key="_state"), + Column("copied_from_history_dataset_association_id", Integer, + ForeignKey("history_dataset_association.id", use_alter=True, name='history_dataset_association_dataset_id_fkey'), + nullable=True), + Column("copied_from_library_dataset_dataset_association_id", Integer, + ForeignKey("library_dataset_dataset_association.id", use_alter=True, name='library_dataset_dataset_association_id_fkey'), + nullable=True), + Column("name", TrimmedString(255), index=True), + Column("info", TrimmedString(255)), + Column("blurb", TrimmedString(255)), + Column("peek", TEXT), + Column("tool_version", TEXT), + Column("extension", TrimmedString(64)), + Column("metadata", MetadataType(), key="_metadata"), + Column("parent_id", Integer, ForeignKey("library_dataset_dataset_association.id"), nullable=True), + Column("designation", TrimmedString(255)), + Column("deleted", Boolean, index=True, default=False), + Column("visible", Boolean), + Column("extended_metadata_id", Integer, ForeignKey("extended_metadata.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("message", TrimmedString(255))) model.ExtendedMetadata.table = Table( "extended_metadata", metadata, - Column( "id", Integer, primary_key=True ), - Column( "data", JSONType ) ) + Column("id", Integer, primary_key=True), + Column("data", JSONType)) model.ExtendedMetadataIndex.table = Table( "extended_metadata_index", metadata, - Column( "id", Integer, primary_key=True ), - Column( "extended_metadata_id", Integer, - ForeignKey("extended_metadata.id", onupdate="CASCADE", ondelete="CASCADE" ), index=True ), - Column( "path", String( 255 ) ), - Column( "value", TEXT ) ) + Column("id", Integer, primary_key=True), + Column("extended_metadata_id", Integer, + ForeignKey("extended_metadata.id", onupdate="CASCADE", ondelete="CASCADE"), index=True), + Column("path", String(255)), + Column("value", TEXT)) model.Library.table = Table( "library", metadata, - Column( "id", Integer, primary_key=True ), - Column( "root_folder_id", Integer, ForeignKey( "library_folder.id" ), index=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "name", String( 255 ), index=True ), - Column( "deleted", Boolean, index=True, default=False ), - Column( "purged", Boolean, index=True, default=False ), - Column( "description", TEXT ), - Column( "synopsis", TEXT ) ) + Column("id", Integer, primary_key=True), + Column("root_folder_id", Integer, ForeignKey("library_folder.id"), index=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("name", String(255), index=True), + Column("deleted", Boolean, index=True, default=False), + Column("purged", Boolean, index=True, default=False), + Column("description", TEXT), + Column("synopsis", TEXT)) model.LibraryFolder.table = Table( "library_folder", metadata, - Column( "id", Integer, primary_key=True ), - Column( "parent_id", Integer, ForeignKey( "library_folder.id" ), nullable=True, index=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "name", TEXT, index=True ), - Column( "description", TEXT ), - Column( "order_id", Integer ), # not currently being used, but for possible future use - Column( "item_count", Integer ), - Column( "deleted", Boolean, index=True, default=False ), - Column( "purged", Boolean, index=True, default=False ), - Column( "genome_build", TrimmedString( 40 ) ) ) + Column("id", Integer, primary_key=True), + Column("parent_id", Integer, ForeignKey("library_folder.id"), nullable=True, index=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("name", TEXT, index=True), + Column("description", TEXT), + Column("order_id", Integer), # not currently being used, but for possible future use + Column("item_count", Integer), + Column("deleted", Boolean, index=True, default=False), + Column("purged", Boolean, index=True, default=False), + Column("genome_build", TrimmedString(40))) model.LibraryInfoAssociation.table = Table( "library_info_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "library_id", Integer, ForeignKey( "library.id" ), index=True ), - Column( "form_definition_id", Integer, ForeignKey( "form_definition.id" ), index=True ), - Column( "form_values_id", Integer, ForeignKey( "form_values.id" ), index=True ), - Column( "inheritable", Boolean, index=True, default=False ), - Column( "deleted", Boolean, index=True, default=False ) ) + Column("id", Integer, primary_key=True), + Column("library_id", Integer, ForeignKey("library.id"), index=True), + Column("form_definition_id", Integer, ForeignKey("form_definition.id"), index=True), + Column("form_values_id", Integer, ForeignKey("form_values.id"), index=True), + Column("inheritable", Boolean, index=True, default=False), + Column("deleted", Boolean, index=True, default=False)) model.LibraryFolderInfoAssociation.table = Table( "library_folder_info_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "library_folder_id", Integer, ForeignKey( "library_folder.id" ), nullable=True, index=True ), - Column( "form_definition_id", Integer, ForeignKey( "form_definition.id" ), index=True ), - Column( "form_values_id", Integer, ForeignKey( "form_values.id" ), index=True ), - Column( "inheritable", Boolean, index=True, default=False ), - Column( "deleted", Boolean, index=True, default=False ) ) + Column("id", Integer, primary_key=True), + Column("library_folder_id", Integer, ForeignKey("library_folder.id"), nullable=True, index=True), + Column("form_definition_id", Integer, ForeignKey("form_definition.id"), index=True), + Column("form_values_id", Integer, ForeignKey("form_values.id"), index=True), + Column("inheritable", Boolean, index=True, default=False), + Column("deleted", Boolean, index=True, default=False)) model.LibraryDatasetDatasetInfoAssociation.table = Table( "library_dataset_dataset_info_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "library_dataset_dataset_association_id", Integer, - ForeignKey( "library_dataset_dataset_association.id" ), nullable=True, index=True ), - Column( "form_definition_id", Integer, ForeignKey( "form_definition.id" ), index=True ), - Column( "form_values_id", Integer, ForeignKey( "form_values.id" ), index=True ), - Column( "deleted", Boolean, index=True, default=False ) ) + Column("id", Integer, primary_key=True), + Column("library_dataset_dataset_association_id", Integer, + ForeignKey("library_dataset_dataset_association.id"), nullable=True, index=True), + Column("form_definition_id", Integer, ForeignKey("form_definition.id"), index=True), + Column("form_values_id", Integer, ForeignKey("form_values.id"), index=True), + Column("deleted", Boolean, index=True, default=False)) model.Job.table = Table( "job", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ), - Column( "library_folder_id", Integer, ForeignKey( "library_folder.id" ), index=True ), - Column( "tool_id", String( 255 ) ), - Column( "tool_version", TEXT, default="1.0.0" ), - Column( "state", String( 64 ), index=True ), - Column( "info", TrimmedString( 255 ) ), - Column( "command_line", TEXT ), - Column( "dependencies", JSONType, nullable=True), - Column( "param_filename", String( 1024 ) ), - Column( "runner_name", String( 255 ) ), - Column( "stdout", TEXT ), - Column( "stderr", TEXT ), - Column( "exit_code", Integer, nullable=True ), - Column( "traceback", TEXT ), - Column( "session_id", Integer, ForeignKey( "galaxy_session.id" ), index=True, nullable=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True, nullable=True ), - Column( "job_runner_name", String( 255 ) ), - Column( "job_runner_external_id", String( 255 ) ), - Column( "destination_id", String( 255 ), nullable=True ), - Column( "destination_params", JSONType, nullable=True ), - Column( "object_store_id", TrimmedString( 255 ), index=True ), - Column( "imported", Boolean, default=False, index=True ), - Column( "params", TrimmedString( 255 ), index=True ), - Column( "handler", TrimmedString( 255 ), index=True ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("history_id", Integer, ForeignKey("history.id"), index=True), + Column("library_folder_id", Integer, ForeignKey("library_folder.id"), index=True), + Column("tool_id", String(255)), + Column("tool_version", TEXT, default="1.0.0"), + Column("state", String(64), index=True), + Column("info", TrimmedString(255)), + Column("command_line", TEXT), + Column("dependencies", JSONType, nullable=True), + Column("param_filename", String(1024)), + Column("runner_name", String(255)), + Column("stdout", TEXT), + Column("stderr", TEXT), + Column("exit_code", Integer, nullable=True), + Column("traceback", TEXT), + Column("session_id", Integer, ForeignKey("galaxy_session.id"), index=True, nullable=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True, nullable=True), + Column("job_runner_name", String(255)), + Column("job_runner_external_id", String(255)), + Column("destination_id", String(255), nullable=True), + Column("destination_params", JSONType, nullable=True), + Column("object_store_id", TrimmedString(255), index=True), + Column("imported", Boolean, default=False, index=True), + Column("params", TrimmedString(255), index=True), + Column("handler", TrimmedString(255), index=True)) model.JobStateHistory.table = Table( "job_state_history", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ), - Column( "state", String( 64 ), index=True ), - Column( "info", TrimmedString( 255 ) ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("job_id", Integer, ForeignKey("job.id"), index=True), + Column("state", String(64), index=True), + Column("info", TrimmedString(255))) model.JobParameter.table = Table( "job_parameter", metadata, - Column( "id", Integer, primary_key=True ), - Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ), - Column( "name", String( 255 ) ), - Column( "value", TEXT ) ) + Column("id", Integer, primary_key=True), + Column("job_id", Integer, ForeignKey("job.id"), index=True), + Column("name", String(255)), + Column("value", TEXT)) model.JobToInputDatasetAssociation.table = Table( "job_to_input_dataset", metadata, - Column( "id", Integer, primary_key=True ), - Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ), - Column( "dataset_id", Integer, ForeignKey( "history_dataset_association.id" ), index=True ), - Column( "name", String( 255 ) ) ) + Column("id", Integer, primary_key=True), + Column("job_id", Integer, ForeignKey("job.id"), index=True), + Column("dataset_id", Integer, ForeignKey("history_dataset_association.id"), index=True), + Column("name", String(255))) model.JobToOutputDatasetAssociation.table = Table( "job_to_output_dataset", metadata, - Column( "id", Integer, primary_key=True ), - Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ), - Column( "dataset_id", Integer, ForeignKey( "history_dataset_association.id" ), index=True ), - Column( "name", String( 255 ) ) ) + Column("id", Integer, primary_key=True), + Column("job_id", Integer, ForeignKey("job.id"), index=True), + Column("dataset_id", Integer, ForeignKey("history_dataset_association.id"), index=True), + Column("name", String(255))) model.JobToInputDatasetCollectionAssociation.table = Table( "job_to_input_dataset_collection", metadata, - Column( "id", Integer, primary_key=True ), - Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ), - Column( "dataset_collection_id", Integer, ForeignKey( "history_dataset_collection_association.id" ), index=True ), - Column( "name", Unicode( 255 ) ) ) + Column("id", Integer, primary_key=True), + Column("job_id", Integer, ForeignKey("job.id"), index=True), + Column("dataset_collection_id", Integer, ForeignKey("history_dataset_collection_association.id"), index=True), + Column("name", Unicode(255))) model.JobToImplicitOutputDatasetCollectionAssociation.table = Table( "job_to_implicit_output_dataset_collection", metadata, - Column( "id", Integer, primary_key=True ), - Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ), - Column( "dataset_collection_id", Integer, ForeignKey( "dataset_collection.id" ), index=True ), - Column( "name", Unicode( 255 ) ) ) + Column("id", Integer, primary_key=True), + Column("job_id", Integer, ForeignKey("job.id"), index=True), + Column("dataset_collection_id", Integer, ForeignKey("dataset_collection.id"), index=True), + Column("name", Unicode(255))) model.JobToOutputDatasetCollectionAssociation.table = Table( "job_to_output_dataset_collection", metadata, - Column( "id", Integer, primary_key=True ), - Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ), - Column( "dataset_collection_id", Integer, ForeignKey( "history_dataset_collection_association.id" ), index=True ), - Column( "name", Unicode( 255 ) ) ) + Column("id", Integer, primary_key=True), + Column("job_id", Integer, ForeignKey("job.id"), index=True), + Column("dataset_collection_id", Integer, ForeignKey("history_dataset_collection_association.id"), index=True), + Column("name", Unicode(255))) model.JobToInputLibraryDatasetAssociation.table = Table( "job_to_input_library_dataset", metadata, - Column( "id", Integer, primary_key=True ), - Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ), - Column( "ldda_id", Integer, ForeignKey( "library_dataset_dataset_association.id" ), index=True ), - Column( "name", String( 255 ) ) ) + Column("id", Integer, primary_key=True), + Column("job_id", Integer, ForeignKey("job.id"), index=True), + Column("ldda_id", Integer, ForeignKey("library_dataset_dataset_association.id"), index=True), + Column("name", String(255))) model.JobToOutputLibraryDatasetAssociation.table = Table( "job_to_output_library_dataset", metadata, - Column( "id", Integer, primary_key=True ), - Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ), - Column( "ldda_id", Integer, ForeignKey( "library_dataset_dataset_association.id" ), index=True ), - Column( "name", String( 255 ) ) ) + Column("id", Integer, primary_key=True), + Column("job_id", Integer, ForeignKey("job.id"), index=True), + Column("ldda_id", Integer, ForeignKey("library_dataset_dataset_association.id"), index=True), + Column("name", String(255))) model.ImplicitlyCreatedDatasetCollectionInput.table = Table( "implicitly_created_dataset_collection_inputs", metadata, - Column( "id", Integer, primary_key=True ), - Column( "dataset_collection_id", Integer, - ForeignKey( "history_dataset_collection_association.id" ), index=True ), - Column( "input_dataset_collection_id", Integer, - ForeignKey( "history_dataset_collection_association.id" ), index=True ), - Column( "name", Unicode( 255 ) ) ) + Column("id", Integer, primary_key=True), + Column("dataset_collection_id", Integer, + ForeignKey("history_dataset_collection_association.id"), index=True), + Column("input_dataset_collection_id", Integer, + ForeignKey("history_dataset_collection_association.id"), index=True), + Column("name", Unicode(255))) model.JobExternalOutputMetadata.table = Table( "job_external_output_metadata", metadata, - Column( "id", Integer, primary_key=True ), - Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ), - Column( "history_dataset_association_id", Integer, - ForeignKey( "history_dataset_association.id" ), index=True, nullable=True ), - Column( "library_dataset_dataset_association_id", Integer, - ForeignKey( "library_dataset_dataset_association.id" ), index=True, nullable=True ), - Column( "is_valid", Boolean, default=True ), - Column( "filename_in", String( 255 ) ), - Column( "filename_out", String( 255 ) ), - Column( "filename_results_code", String( 255 ) ), - Column( "filename_kwds", String( 255 ) ), - Column( "filename_override_metadata", String( 255 ) ), - Column( "job_runner_external_pid", String( 255 ) ) ) + Column("id", Integer, primary_key=True), + Column("job_id", Integer, ForeignKey("job.id"), index=True), + Column("history_dataset_association_id", Integer, + ForeignKey("history_dataset_association.id"), index=True, nullable=True), + Column("library_dataset_dataset_association_id", Integer, + ForeignKey("library_dataset_dataset_association.id"), index=True, nullable=True), + Column("is_valid", Boolean, default=True), + Column("filename_in", String(255)), + Column("filename_out", String(255)), + Column("filename_results_code", String(255)), + Column("filename_kwds", String(255)), + Column("filename_override_metadata", String(255)), + Column("job_runner_external_pid", String(255))) model.JobExportHistoryArchive.table = Table( "job_export_history_archive", metadata, - Column( "id", Integer, primary_key=True ), - Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ), - Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ), - Column( "dataset_id", Integer, ForeignKey( "dataset.id" ), index=True ), - Column( "compressed", Boolean, index=True, default=False ), - Column( "history_attrs_filename", TEXT ), - Column( "datasets_attrs_filename", TEXT ), - Column( "jobs_attrs_filename", TEXT ) ) + Column("id", Integer, primary_key=True), + Column("job_id", Integer, ForeignKey("job.id"), index=True), + Column("history_id", Integer, ForeignKey("history.id"), index=True), + Column("dataset_id", Integer, ForeignKey("dataset.id"), index=True), + Column("compressed", Boolean, index=True, default=False), + Column("history_attrs_filename", TEXT), + Column("datasets_attrs_filename", TEXT), + Column("jobs_attrs_filename", TEXT)) model.JobImportHistoryArchive.table = Table( "job_import_history_archive", metadata, - Column( "id", Integer, primary_key=True ), - Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ), - Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ), - Column( "archive_dir", TEXT ) ) + Column("id", Integer, primary_key=True), + Column("job_id", Integer, ForeignKey("job.id"), index=True), + Column("history_id", Integer, ForeignKey("history.id"), index=True), + Column("archive_dir", TEXT)) JOB_METRIC_MAX_LENGTH = 1023 model.JobMetricText.table = Table( "job_metric_text", metadata, - Column( "id", Integer, primary_key=True ), - Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ), - Column( "plugin", Unicode( 255 ) ), - Column( "metric_name", Unicode( 255 ) ), - Column( "metric_value", Unicode( JOB_METRIC_MAX_LENGTH ) ) ) + Column("id", Integer, primary_key=True), + Column("job_id", Integer, ForeignKey("job.id"), index=True), + Column("plugin", Unicode(255)), + Column("metric_name", Unicode(255)), + Column("metric_value", Unicode(JOB_METRIC_MAX_LENGTH))) model.TaskMetricText.table = Table( "task_metric_text", metadata, - Column( "id", Integer, primary_key=True ), - Column( "task_id", Integer, ForeignKey( "task.id" ), index=True ), - Column( "plugin", Unicode( 255 ) ), - Column( "metric_name", Unicode( 255 ) ), - Column( "metric_value", Unicode( JOB_METRIC_MAX_LENGTH ) ) ) + Column("id", Integer, primary_key=True), + Column("task_id", Integer, ForeignKey("task.id"), index=True), + Column("plugin", Unicode(255)), + Column("metric_name", Unicode(255)), + Column("metric_value", Unicode(JOB_METRIC_MAX_LENGTH))) model.JobMetricNumeric.table = Table( "job_metric_numeric", metadata, - Column( "id", Integer, primary_key=True ), - Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ), - Column( "plugin", Unicode( 255 ) ), - Column( "metric_name", Unicode( 255 ) ), - Column( "metric_value", Numeric( 22, 7 ) ) ) + Column("id", Integer, primary_key=True), + Column("job_id", Integer, ForeignKey("job.id"), index=True), + Column("plugin", Unicode(255)), + Column("metric_name", Unicode(255)), + Column("metric_value", Numeric(22, 7))) model.TaskMetricNumeric.table = Table( "task_metric_numeric", metadata, - Column( "id", Integer, primary_key=True ), - Column( "task_id", Integer, ForeignKey( "task.id" ), index=True ), - Column( "plugin", Unicode(255) ), - Column( "metric_name", Unicode( 255 ) ), - Column( "metric_value", Numeric( 22, 7 ) ) ) + Column("id", Integer, primary_key=True), + Column("task_id", Integer, ForeignKey("task.id"), index=True), + Column("plugin", Unicode(255)), + Column("metric_name", Unicode(255)), + Column("metric_value", Numeric(22, 7))) model.GenomeIndexToolData.table = Table( "genome_index_tool_data", metadata, - Column( "id", Integer, primary_key=True ), - Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ), - Column( "deferred_job_id", Integer, ForeignKey( "deferred_job.id" ), index=True ), - Column( "transfer_job_id", Integer, ForeignKey( "transfer_job.id" ), index=True ), - Column( "dataset_id", Integer, ForeignKey( "dataset.id" ), index=True ), - Column( "fasta_path", String( 255 ) ), - Column( "created_time", DateTime, default=now ), - Column( "modified_time", DateTime, default=now, onupdate=now ), - Column( "indexer", String( 64 ) ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ) ) + Column("id", Integer, primary_key=True), + Column("job_id", Integer, ForeignKey("job.id"), index=True), + Column("deferred_job_id", Integer, ForeignKey("deferred_job.id"), index=True), + Column("transfer_job_id", Integer, ForeignKey("transfer_job.id"), index=True), + Column("dataset_id", Integer, ForeignKey("dataset.id"), index=True), + Column("fasta_path", String(255)), + Column("created_time", DateTime, default=now), + Column("modified_time", DateTime, default=now, onupdate=now), + Column("indexer", String(64)), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True)) model.Task.table = Table( "task", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "execution_time", DateTime ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "state", String( 64 ), index=True ), - Column( "command_line", TEXT ), - Column( "param_filename", String( 1024 ) ), - Column( "runner_name", String( 255 ) ), - Column( "stdout", TEXT ), - Column( "stderr", TEXT ), - Column( "exit_code", Integer, nullable=True ), - Column( "info", TrimmedString( 255 ) ), - Column( "traceback", TEXT ), - Column( "job_id", Integer, ForeignKey( "job.id" ), index=True, nullable=False ), - Column( "working_directory", String( 1024 )), - Column( "task_runner_name", String( 255 ) ), - Column( "task_runner_external_id", String( 255 ) ), - Column( "prepare_input_files_cmd", TEXT ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("execution_time", DateTime), + Column("update_time", DateTime, default=now, onupdate=now), + Column("state", String(64), index=True), + Column("command_line", TEXT), + Column("param_filename", String(1024)), + Column("runner_name", String(255)), + Column("stdout", TEXT), + Column("stderr", TEXT), + Column("exit_code", Integer, nullable=True), + Column("info", TrimmedString(255)), + Column("traceback", TEXT), + Column("job_id", Integer, ForeignKey("job.id"), index=True, nullable=False), + Column("working_directory", String(1024)), + Column("task_runner_name", String(255)), + Column("task_runner_external_id", String(255)), + Column("prepare_input_files_cmd", TEXT)) model.PostJobAction.table = Table( "post_job_action", metadata, - Column( "id", Integer, primary_key=True ), - Column( "workflow_step_id", Integer, ForeignKey( "workflow_step.id" ), index=True, nullable=False ), - Column( "action_type", String( 255 ), nullable=False ), - Column( "output_name", String( 255 ), nullable=True ), - Column( "action_arguments", JSONType, nullable=True ) ) + Column("id", Integer, primary_key=True), + Column("workflow_step_id", Integer, ForeignKey("workflow_step.id"), index=True, nullable=False), + Column("action_type", String(255), nullable=False), + Column("output_name", String(255), nullable=True), + Column("action_arguments", JSONType, nullable=True)) model.PostJobActionAssociation.table = Table( "post_job_action_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "job_id", Integer, ForeignKey( "job.id" ), index=True, nullable=False ), - Column( "post_job_action_id", Integer, ForeignKey( "post_job_action.id" ), index=True, nullable=False ) ) + Column("id", Integer, primary_key=True), + Column("job_id", Integer, ForeignKey("job.id"), index=True, nullable=False), + Column("post_job_action_id", Integer, ForeignKey("post_job_action.id"), index=True, nullable=False)) model.DeferredJob.table = Table( "deferred_job", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "state", String( 64 ), index=True ), - Column( "plugin", String( 128 ), index=True ), - Column( "params", JSONType ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("state", String(64), index=True), + Column("plugin", String(128), index=True), + Column("params", JSONType)) model.TransferJob.table = Table( "transfer_job", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "state", String( 64 ), index=True ), - Column( "path", String( 1024 ) ), - Column( "info", TEXT ), - Column( "pid", Integer ), - Column( "socket", Integer ), - Column( "params", JSONType ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("state", String(64), index=True), + Column("path", String(1024)), + Column("info", TEXT), + Column("pid", Integer), + Column("socket", Integer), + Column("params", JSONType)) model.DatasetCollection.table = Table( "dataset_collection", metadata, - Column( "id", Integer, primary_key=True ), - Column( "collection_type", Unicode(255), nullable=False ), - Column( "populated_state", TrimmedString( 64 ), default='ok', nullable=False ), - Column( "populated_state_message", TEXT ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ) ) + Column("id", Integer, primary_key=True), + Column("collection_type", Unicode(255), nullable=False), + Column("populated_state", TrimmedString(64), default='ok', nullable=False), + Column("populated_state_message", TEXT), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now)) model.HistoryDatasetCollectionAssociation.table = Table( "history_dataset_collection_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "collection_id", Integer, ForeignKey( "dataset_collection.id" ), index=True ), - Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ), - Column( "name", TrimmedString( 255 ) ), - Column( "hid", Integer ), - Column( "visible", Boolean ), - Column( "deleted", Boolean, default=False ), - Column( "copied_from_history_dataset_collection_association_id", Integer, - ForeignKey( "history_dataset_collection_association.id" ), nullable=True ), - Column( "implicit_output_name", Unicode( 255 ), nullable=True ) ) + Column("id", Integer, primary_key=True), + Column("collection_id", Integer, ForeignKey("dataset_collection.id"), index=True), + Column("history_id", Integer, ForeignKey("history.id"), index=True), + Column("name", TrimmedString(255)), + Column("hid", Integer), + Column("visible", Boolean), + Column("deleted", Boolean, default=False), + Column("copied_from_history_dataset_collection_association_id", Integer, + ForeignKey("history_dataset_collection_association.id"), nullable=True), + Column("implicit_output_name", Unicode(255), nullable=True)) model.LibraryDatasetCollectionAssociation.table = Table( "library_dataset_collection_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "collection_id", Integer, ForeignKey( "dataset_collection.id" ), index=True ), - Column( "folder_id", Integer, ForeignKey( "library_folder.id" ), index=True ), - Column( "name", TrimmedString( 255 ) ), - Column( "deleted", Boolean, default=False ) ) + Column("id", Integer, primary_key=True), + Column("collection_id", Integer, ForeignKey("dataset_collection.id"), index=True), + Column("folder_id", Integer, ForeignKey("library_folder.id"), index=True), + Column("name", TrimmedString(255)), + Column("deleted", Boolean, default=False)) model.DatasetCollectionElement.table = Table( "dataset_collection_element", metadata, - Column( "id", Integer, primary_key=True ), + Column("id", Integer, primary_key=True), # Parent collection id describing what collection this element belongs to. - Column( "dataset_collection_id", Integer, ForeignKey( "dataset_collection.id" ), index=True, nullable=False ), + Column("dataset_collection_id", Integer, ForeignKey("dataset_collection.id"), index=True, nullable=False), # Child defined by this association - HDA, LDDA, or another dataset association... - Column( "hda_id", Integer, ForeignKey( "history_dataset_association.id" ), index=True, nullable=True ), - Column( "ldda_id", Integer, ForeignKey( "library_dataset_dataset_association.id" ), index=True, nullable=True ), - Column( "child_collection_id", Integer, ForeignKey( "dataset_collection.id" ), index=True, nullable=True ), + Column("hda_id", Integer, ForeignKey("history_dataset_association.id"), index=True, nullable=True), + Column("ldda_id", Integer, ForeignKey("library_dataset_dataset_association.id"), index=True, nullable=True), + Column("child_collection_id", Integer, ForeignKey("dataset_collection.id"), index=True, nullable=True), # Element index and identifier to define this parent-child relationship. - Column( "element_index", Integer ), - Column( "element_identifier", Unicode( 255 ), ) ) + Column("element_index", Integer), + Column("element_identifier", Unicode(255), )) model.Event.table = Table( "event", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "history_id", Integer, ForeignKey( "history.id" ), index=True, nullable=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True, nullable=True ), - Column( "message", TrimmedString( 1024 ) ), - Column( "session_id", Integer, ForeignKey( "galaxy_session.id" ), index=True, nullable=True ), - Column( "tool_id", String( 255 ) ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("history_id", Integer, ForeignKey("history.id"), index=True, nullable=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True, nullable=True), + Column("message", TrimmedString(1024)), + Column("session_id", Integer, ForeignKey("galaxy_session.id"), index=True, nullable=True), + Column("tool_id", String(255))) model.GalaxySession.table = Table( "galaxy_session", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True, nullable=True ), - Column( "remote_host", String( 255 ) ), - Column( "remote_addr", String( 255 ) ), - Column( "referer", TEXT ), - Column( "current_history_id", Integer, ForeignKey( "history.id" ), nullable=True ), + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True, nullable=True), + Column("remote_host", String(255)), + Column("remote_addr", String(255)), + Column("referer", TEXT), + Column("current_history_id", Integer, ForeignKey("history.id"), nullable=True), # unique 128 bit random number coerced to a string - Column( "session_key", TrimmedString( 255 ), index=True, unique=True ), - Column( "is_valid", Boolean, default=False ), + Column("session_key", TrimmedString(255), index=True, unique=True), + Column("is_valid", Boolean, default=False), # saves a reference to the previous session so we have a way to chain them together - Column( "prev_session_id", Integer ), - Column( "disk_usage", Numeric( 15, 0 ), index=True ), - Column( "last_action", DateTime ) ) + Column("prev_session_id", Integer), + Column("disk_usage", Numeric(15, 0), index=True), + Column("last_action", DateTime)) model.GalaxySessionToHistoryAssociation.table = Table( "galaxy_session_to_history", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "session_id", Integer, ForeignKey( "galaxy_session.id" ), index=True ), - Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("session_id", Integer, ForeignKey("galaxy_session.id"), index=True), + Column("history_id", Integer, ForeignKey("history.id"), index=True)) model.StoredWorkflow.table = Table( "stored_workflow", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True, nullable=False ), - Column( "latest_workflow_id", Integer, - ForeignKey( "workflow.id", use_alter=True, name='stored_workflow_latest_workflow_id_fk' ), index=True ), - Column( "name", TEXT ), - Column( "deleted", Boolean, default=False ), - Column( "importable", Boolean, default=False ), - Column( "slug", TEXT, index=True ), - Column( "published", Boolean, index=True, default=False ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True, nullable=False), + Column("latest_workflow_id", Integer, + ForeignKey("workflow.id", use_alter=True, name='stored_workflow_latest_workflow_id_fk'), index=True), + Column("name", TEXT), + Column("deleted", Boolean, default=False), + Column("importable", Boolean, default=False), + Column("slug", TEXT, index=True), + Column("published", Boolean, index=True, default=False)) model.Workflow.table = Table( "workflow", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), # workflows will belong to either a stored workflow or a parent/nesting workflow. - Column( "stored_workflow_id", Integer, ForeignKey( "stored_workflow.id" ), index=True, nullable=True ), - Column( "parent_workflow_id", Integer, ForeignKey( "workflow.id" ), index=True, nullable=True ), - Column( "name", TEXT ), - Column( "has_cycles", Boolean ), - Column( "has_errors", Boolean ), - Column( "uuid", UUIDType, nullable=True ) ) + Column("stored_workflow_id", Integer, ForeignKey("stored_workflow.id"), index=True, nullable=True), + Column("parent_workflow_id", Integer, ForeignKey("workflow.id"), index=True, nullable=True), + Column("name", TEXT), + Column("has_cycles", Boolean), + Column("has_errors", Boolean), + Column("uuid", UUIDType, nullable=True)) model.WorkflowStep.table = Table( "workflow_step", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "workflow_id", Integer, ForeignKey( "workflow.id" ), index=True, nullable=False ), - Column( "subworkflow_id", Integer, ForeignKey( "workflow.id" ), index=True, nullable=True ), - Column( "type", String( 64 ) ), - Column( "tool_id", TEXT ), + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("workflow_id", Integer, ForeignKey("workflow.id"), index=True, nullable=False), + Column("subworkflow_id", Integer, ForeignKey("workflow.id"), index=True, nullable=True), + Column("type", String(64)), + Column("tool_id", TEXT), # Reserved for future - Column( "tool_version", TEXT ), - Column( "tool_inputs", JSONType ), - Column( "tool_errors", JSONType ), - Column( "position", JSONType ), - Column( "config", JSONType ), - Column( "order_index", Integer ), - Column( "uuid", UUIDType ), + Column("tool_version", TEXT), + Column("tool_inputs", JSONType), + Column("tool_errors", JSONType), + Column("position", JSONType), + Column("config", JSONType), + Column("order_index", Integer), + Column("uuid", UUIDType), # Column( "input_connections", JSONType ), - Column( "label", Unicode( 255 ) ) ) + Column("label", Unicode(255))) model.WorkflowRequestStepState.table = Table( "workflow_request_step_states", metadata, - Column( "id", Integer, primary_key=True ), - Column( "workflow_invocation_id", Integer, - ForeignKey( "workflow_invocation.id", onupdate="CASCADE", ondelete="CASCADE" ) ), - Column( "workflow_step_id", Integer, ForeignKey( "workflow_step.id" ) ), - Column( "value", JSONType ) ) + Column("id", Integer, primary_key=True), + Column("workflow_invocation_id", Integer, + ForeignKey("workflow_invocation.id", onupdate="CASCADE", ondelete="CASCADE")), + Column("workflow_step_id", Integer, ForeignKey("workflow_step.id")), + Column("value", JSONType)) model.WorkflowRequestInputParameter.table = Table( "workflow_request_input_parameters", metadata, - Column( "id", Integer, primary_key=True ), - Column( "workflow_invocation_id", Integer, - ForeignKey("workflow_invocation.id", onupdate="CASCADE", ondelete="CASCADE" ) ), - Column( "name", Unicode( 255 ) ), - Column( "value", TEXT ), - Column( "type", Unicode( 255 ) ) ) + Column("id", Integer, primary_key=True), + Column("workflow_invocation_id", Integer, + ForeignKey("workflow_invocation.id", onupdate="CASCADE", ondelete="CASCADE")), + Column("name", Unicode(255)), + Column("value", TEXT), + Column("type", Unicode(255))) model.WorkflowRequestInputStepParmeter.table = Table( "workflow_request_input_step_parameter", metadata, - Column( "id", Integer, primary_key=True ), - Column( "workflow_invocation_id", Integer, ForeignKey( "workflow_invocation.id" ), index=True ), - Column( "workflow_step_id", Integer, ForeignKey("workflow_step.id") ), - Column( "parameter_value", JSONType ), + Column("id", Integer, primary_key=True), + Column("workflow_invocation_id", Integer, ForeignKey("workflow_invocation.id"), index=True), + Column("workflow_step_id", Integer, ForeignKey("workflow_step.id")), + Column("parameter_value", JSONType), ) model.WorkflowRequestToInputDatasetAssociation.table = Table( "workflow_request_to_input_dataset", metadata, - Column( "id", Integer, primary_key=True ), - Column( "name", String( 255 ) ), - Column( "workflow_invocation_id", Integer, ForeignKey( "workflow_invocation.id" ), index=True ), - Column( "workflow_step_id", Integer, ForeignKey( "workflow_step.id" ) ), - Column( "dataset_id", Integer, ForeignKey( "history_dataset_association.id" ), index=True ) ) + Column("id", Integer, primary_key=True), + Column("name", String(255)), + Column("workflow_invocation_id", Integer, ForeignKey("workflow_invocation.id"), index=True), + Column("workflow_step_id", Integer, ForeignKey("workflow_step.id")), + Column("dataset_id", Integer, ForeignKey("history_dataset_association.id"), index=True)) model.WorkflowRequestToInputDatasetCollectionAssociation.table = Table( "workflow_request_to_input_collection_dataset", metadata, - Column( "id", Integer, primary_key=True ), - Column( "name", String( 255 ) ), - Column( "workflow_invocation_id", Integer, ForeignKey( "workflow_invocation.id" ), index=True ), - Column( "workflow_step_id", Integer, ForeignKey( "workflow_step.id" ) ), - Column( "dataset_collection_id", Integer, ForeignKey( "history_dataset_collection_association.id" ), index=True ) ) + Column("id", Integer, primary_key=True), + Column("name", String(255)), + Column("workflow_invocation_id", Integer, ForeignKey("workflow_invocation.id"), index=True), + Column("workflow_step_id", Integer, ForeignKey("workflow_step.id")), + Column("dataset_collection_id", Integer, ForeignKey("history_dataset_collection_association.id"), index=True)) model.WorkflowStepConnection.table = Table( "workflow_step_connection", metadata, - Column( "id", Integer, primary_key=True ), - Column( "output_step_id", Integer, ForeignKey( "workflow_step.id" ), index=True ), - Column( "input_step_id", Integer, ForeignKey( "workflow_step.id" ), index=True ), - Column( "output_name", TEXT ), - Column( "input_name", TEXT ), - Column( "input_subworkflow_step_id", Integer, ForeignKey( "workflow_step.id" ), index=True ), + Column("id", Integer, primary_key=True), + Column("output_step_id", Integer, ForeignKey("workflow_step.id"), index=True), + Column("input_step_id", Integer, ForeignKey("workflow_step.id"), index=True), + Column("output_name", TEXT), + Column("input_name", TEXT), + Column("input_subworkflow_step_id", Integer, ForeignKey("workflow_step.id"), index=True), ) model.WorkflowOutput.table = Table( "workflow_output", metadata, - Column( "id", Integer, primary_key=True ), - Column( "workflow_step_id", Integer, ForeignKey( "workflow_step.id" ), index=True, nullable=False ), - Column( "output_name", String( 255 ), nullable=True ), - Column( "label", Unicode( 255 ) ), - Column( "uuid", UUIDType ), + Column("id", Integer, primary_key=True), + Column("workflow_step_id", Integer, ForeignKey("workflow_step.id"), index=True, nullable=False), + Column("output_name", String(255), nullable=True), + Column("label", Unicode(255)), + Column("uuid", UUIDType), ) model.WorkflowInvocation.table = Table( "workflow_invocation", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "workflow_id", Integer, ForeignKey( "workflow.id" ), index=True, nullable=False ), - Column( "state", TrimmedString( 64 ), index=True ), - Column( "scheduler", TrimmedString( 255 ), index=True ), - Column( "handler", TrimmedString( 255 ), index=True ), - Column( 'uuid', UUIDType() ), - Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("workflow_id", Integer, ForeignKey("workflow.id"), index=True, nullable=False), + Column("state", TrimmedString(64), index=True), + Column("scheduler", TrimmedString(255), index=True), + Column("handler", TrimmedString(255), index=True), + Column('uuid', UUIDType()), + Column("history_id", Integer, ForeignKey("history.id"), index=True)) model.WorkflowInvocationStep.table = Table( "workflow_invocation_step", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "workflow_invocation_id", Integer, ForeignKey( "workflow_invocation.id" ), index=True, nullable=False ), - Column( "workflow_step_id", Integer, ForeignKey( "workflow_step.id" ), index=True, nullable=False ), - Column( "job_id", Integer, ForeignKey( "job.id" ), index=True, nullable=True ), - Column( "action", JSONType, nullable=True ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("workflow_invocation_id", Integer, ForeignKey("workflow_invocation.id"), index=True, nullable=False), + Column("workflow_step_id", Integer, ForeignKey("workflow_step.id"), index=True, nullable=False), + Column("job_id", Integer, ForeignKey("job.id"), index=True, nullable=True), + Column("action", JSONType, nullable=True)) model.WorkflowInvocationToSubworkflowInvocationAssociation.table = Table( "workflow_invocation_to_subworkflow_invocation_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "workflow_invocation_id", Integer, ForeignKey( "workflow_invocation.id" ), index=True ), - Column( "subworkflow_invocation_id", Integer, ForeignKey( "workflow_invocation.id" ), index=True ), - Column( "workflow_step_id", Integer, ForeignKey( "workflow_step.id" ) ), + Column("id", Integer, primary_key=True), + Column("workflow_invocation_id", Integer, ForeignKey("workflow_invocation.id"), index=True), + Column("subworkflow_invocation_id", Integer, ForeignKey("workflow_invocation.id"), index=True), + Column("workflow_step_id", Integer, ForeignKey("workflow_step.id")), ) model.StoredWorkflowUserShareAssociation.table = Table( "stored_workflow_user_share_connection", metadata, - Column( "id", Integer, primary_key=True ), - Column( "stored_workflow_id", Integer, ForeignKey( "stored_workflow.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ) ) + Column("id", Integer, primary_key=True), + Column("stored_workflow_id", Integer, ForeignKey("stored_workflow.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True)) model.StoredWorkflowMenuEntry.table = Table( "stored_workflow_menu_entry", metadata, - Column( "id", Integer, primary_key=True ), - Column( "stored_workflow_id", Integer, ForeignKey( "stored_workflow.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "order_index", Integer ) ) + Column("id", Integer, primary_key=True), + Column("stored_workflow_id", Integer, ForeignKey("stored_workflow.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("order_index", Integer)) model.MetadataFile.table = Table( "metadata_file", metadata, - Column( "id", Integer, primary_key=True ), - Column( "name", TEXT ), - Column( "hda_id", Integer, ForeignKey( "history_dataset_association.id" ), index=True, nullable=True ), - Column( "lda_id", Integer, ForeignKey( "library_dataset_dataset_association.id" ), index=True, nullable=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, index=True, default=now, onupdate=now ), - Column( "object_store_id", TrimmedString( 255 ), index=True ), - Column( "deleted", Boolean, index=True, default=False ), - Column( "purged", Boolean, index=True, default=False ) ) + Column("id", Integer, primary_key=True), + Column("name", TEXT), + Column("hda_id", Integer, ForeignKey("history_dataset_association.id"), index=True, nullable=True), + Column("lda_id", Integer, ForeignKey("library_dataset_dataset_association.id"), index=True, nullable=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, index=True, default=now, onupdate=now), + Column("object_store_id", TrimmedString(255), index=True), + Column("deleted", Boolean, index=True, default=False), + Column("purged", Boolean, index=True, default=False)) model.FormDefinitionCurrent.table = Table( "form_definition_current", metadata, - Column( "id", Integer, primary_key=True), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "latest_form_id", Integer, ForeignKey( "form_definition.id" ), index=True ), - Column( "deleted", Boolean, index=True, default=False ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("latest_form_id", Integer, ForeignKey("form_definition.id"), index=True), + Column("deleted", Boolean, index=True, default=False)) model.FormDefinition.table = Table( "form_definition", metadata, - Column( "id", Integer, primary_key=True), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "name", TrimmedString( 255 ), nullable=False ), - Column( "desc", TEXT ), - Column( "form_definition_current_id", Integer, - ForeignKey( "form_definition_current.id", name='for_def_form_def_current_id_fk', use_alter=True ), index=True ), - Column( "fields", JSONType() ), - Column( "type", TrimmedString( 255 ), index=True ), - Column( "layout", JSONType() ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("name", TrimmedString(255), nullable=False), + Column("desc", TEXT), + Column("form_definition_current_id", Integer, + ForeignKey("form_definition_current.id", name='for_def_form_def_current_id_fk', use_alter=True), index=True), + Column("fields", JSONType()), + Column("type", TrimmedString(255), index=True), + Column("layout", JSONType())) model.ExternalService.table = Table( "external_service", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "name", TrimmedString( 255 ), nullable=False ), - Column( "description", TEXT ), - Column( "external_service_type_id", TrimmedString( 255 ), nullable=False ), - Column( "version", TrimmedString( 255 ) ), - Column( "form_definition_id", Integer, ForeignKey( "form_definition.id" ), index=True ), - Column( "form_values_id", Integer, ForeignKey( "form_values.id" ), index=True ), - Column( "deleted", Boolean, index=True, default=False ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("name", TrimmedString(255), nullable=False), + Column("description", TEXT), + Column("external_service_type_id", TrimmedString(255), nullable=False), + Column("version", TrimmedString(255)), + Column("form_definition_id", Integer, ForeignKey("form_definition.id"), index=True), + Column("form_values_id", Integer, ForeignKey("form_values.id"), index=True), + Column("deleted", Boolean, index=True, default=False)) model.RequestType.table = Table( "request_type", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "name", TrimmedString( 255 ), nullable=False ), - Column( "desc", TEXT ), - Column( "request_form_id", Integer, ForeignKey( "form_definition.id" ), index=True ), - Column( "sample_form_id", Integer, ForeignKey( "form_definition.id" ), index=True ), - Column( "deleted", Boolean, index=True, default=False ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("name", TrimmedString(255), nullable=False), + Column("desc", TEXT), + Column("request_form_id", Integer, ForeignKey("form_definition.id"), index=True), + Column("sample_form_id", Integer, ForeignKey("form_definition.id"), index=True), + Column("deleted", Boolean, index=True, default=False)) model.RequestTypeExternalServiceAssociation.table = Table( "request_type_external_service_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "request_type_id", Integer, ForeignKey( "request_type.id" ), index=True ), - Column( "external_service_id", Integer, ForeignKey( "external_service.id" ), index=True ) ) + Column("id", Integer, primary_key=True), + Column("request_type_id", Integer, ForeignKey("request_type.id"), index=True), + Column("external_service_id", Integer, ForeignKey("external_service.id"), index=True)) model.RequestTypePermissions.table = Table( "request_type_permissions", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "action", TEXT ), - Column( "request_type_id", Integer, ForeignKey( "request_type.id" ), nullable=True, index=True ), - Column( "role_id", Integer, ForeignKey( "role.id" ), index=True ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("action", TEXT), + Column("request_type_id", Integer, ForeignKey("request_type.id"), nullable=True, index=True), + Column("role_id", Integer, ForeignKey("role.id"), index=True)) model.FormValues.table = Table( "form_values", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "form_definition_id", Integer, ForeignKey( "form_definition.id" ), index=True ), - Column( "content", JSONType() ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("form_definition_id", Integer, ForeignKey("form_definition.id"), index=True), + Column("content", JSONType())) model.Request.table = Table( "request", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "name", TrimmedString( 255 ), nullable=False ), - Column( "desc", TEXT ), - Column( "notification", JSONType() ), - Column( "form_values_id", Integer, ForeignKey( "form_values.id" ), index=True ), - Column( "request_type_id", Integer, ForeignKey( "request_type.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "deleted", Boolean, index=True, default=False ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("name", TrimmedString(255), nullable=False), + Column("desc", TEXT), + Column("notification", JSONType()), + Column("form_values_id", Integer, ForeignKey("form_values.id"), index=True), + Column("request_type_id", Integer, ForeignKey("request_type.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("deleted", Boolean, index=True, default=False)) model.RequestEvent.table = Table( "request_event", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "request_id", Integer, ForeignKey( "request.id" ), index=True ), - Column( "state", TrimmedString( 255 ), index=True ), - Column( "comment", TEXT ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("request_id", Integer, ForeignKey("request.id"), index=True), + Column("state", TrimmedString(255), index=True), + Column("comment", TEXT)) model.Sample.table = Table( "sample", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "name", TrimmedString( 255 ), nullable=False ), - Column( "desc", TEXT ), - Column( "form_values_id", Integer, ForeignKey( "form_values.id" ), index=True ), - Column( "request_id", Integer, ForeignKey( "request.id" ), index=True ), - Column( "bar_code", TrimmedString( 255 ), index=True ), - Column( "library_id", Integer, ForeignKey( "library.id" ), index=True ), - Column( "folder_id", Integer, ForeignKey( "library_folder.id" ), index=True ), - Column( "deleted", Boolean, index=True, default=False ), - Column( "workflow", JSONType, nullable=True ), - Column( "history_id", Integer, ForeignKey( "history.id" ), nullable=True ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("name", TrimmedString(255), nullable=False), + Column("desc", TEXT), + Column("form_values_id", Integer, ForeignKey("form_values.id"), index=True), + Column("request_id", Integer, ForeignKey("request.id"), index=True), + Column("bar_code", TrimmedString(255), index=True), + Column("library_id", Integer, ForeignKey("library.id"), index=True), + Column("folder_id", Integer, ForeignKey("library_folder.id"), index=True), + Column("deleted", Boolean, index=True, default=False), + Column("workflow", JSONType, nullable=True), + Column("history_id", Integer, ForeignKey("history.id"), nullable=True)) model.SampleState.table = Table( "sample_state", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "name", TrimmedString( 255 ), nullable=False ), - Column( "desc", TEXT ), - Column( "request_type_id", Integer, ForeignKey( "request_type.id" ), index=True ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("name", TrimmedString(255), nullable=False), + Column("desc", TEXT), + Column("request_type_id", Integer, ForeignKey("request_type.id"), index=True)) model.SampleEvent.table = Table( "sample_event", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "sample_id", Integer, ForeignKey( "sample.id" ), index=True ), - Column( "sample_state_id", Integer, ForeignKey( "sample_state.id" ), index=True ), - Column( "comment", TEXT ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("sample_id", Integer, ForeignKey("sample.id"), index=True), + Column("sample_state_id", Integer, ForeignKey("sample_state.id"), index=True), + Column("comment", TEXT)) model.SampleDataset.table = Table( "sample_dataset", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "sample_id", Integer, ForeignKey( "sample.id" ), index=True ), - Column( "name", TrimmedString( 255 ), nullable=False ), - Column( "file_path", TEXT ), - Column( "status", TrimmedString( 255 ), nullable=False ), - Column( "error_msg", TEXT ), - Column( "size", TrimmedString( 255 ) ), - Column( "external_service_id", Integer, ForeignKey( "external_service.id" ), index=True ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("sample_id", Integer, ForeignKey("sample.id"), index=True), + Column("name", TrimmedString(255), nullable=False), + Column("file_path", TEXT), + Column("status", TrimmedString(255), nullable=False), + Column("error_msg", TEXT), + Column("size", TrimmedString(255)), + Column("external_service_id", Integer, ForeignKey("external_service.id"), index=True)) model.Run.table = Table( "run", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "form_definition_id", Integer, ForeignKey( "form_definition.id" ), index=True ), - Column( "form_values_id", Integer, ForeignKey( "form_values.id" ), index=True ), - Column( "deleted", Boolean, index=True, default=False ), - Column( "subindex", TrimmedString( 255 ), index=True ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("form_definition_id", Integer, ForeignKey("form_definition.id"), index=True), + Column("form_values_id", Integer, ForeignKey("form_values.id"), index=True), + Column("deleted", Boolean, index=True, default=False), + Column("subindex", TrimmedString(255), index=True)) model.RequestTypeRunAssociation.table = Table( "request_type_run_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "request_type_id", Integer, ForeignKey( "request_type.id" ), index=True, nullable=False ), - Column( "run_id", Integer, ForeignKey( "run.id" ), index=True, nullable=False ) ) + Column("id", Integer, primary_key=True), + Column("request_type_id", Integer, ForeignKey("request_type.id"), index=True, nullable=False), + Column("run_id", Integer, ForeignKey("run.id"), index=True, nullable=False)) model.SampleRunAssociation.table = Table( "sample_run_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "sample_id", Integer, ForeignKey( "sample.id" ), index=True, nullable=False ), - Column( "run_id", Integer, ForeignKey( "run.id" ), index=True, nullable=False ) ) + Column("id", Integer, primary_key=True), + Column("sample_id", Integer, ForeignKey("sample.id"), index=True, nullable=False), + Column("run_id", Integer, ForeignKey("run.id"), index=True, nullable=False)) model.Page.table = Table( "page", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True, nullable=False ), - Column( "latest_revision_id", Integer, - ForeignKey( "page_revision.id", use_alter=True, name='page_latest_revision_id_fk' ), index=True ), - Column( "title", TEXT ), - Column( "deleted", Boolean, index=True, default=False ), - Column( "importable", Boolean, index=True, default=False ), - Column( "slug", TEXT, unique=True, index=True ), - Column( "published", Boolean, index=True, default=False ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True, nullable=False), + Column("latest_revision_id", Integer, + ForeignKey("page_revision.id", use_alter=True, name='page_latest_revision_id_fk'), index=True), + Column("title", TEXT), + Column("deleted", Boolean, index=True, default=False), + Column("importable", Boolean, index=True, default=False), + Column("slug", TEXT, unique=True, index=True), + Column("published", Boolean, index=True, default=False)) model.PageRevision.table = Table( "page_revision", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "page_id", Integer, ForeignKey( "page.id" ), index=True, nullable=False ), - Column( "title", TEXT ), - Column( "content", TEXT ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("page_id", Integer, ForeignKey("page.id"), index=True, nullable=False), + Column("title", TEXT), + Column("content", TEXT)) model.PageUserShareAssociation.table = Table( "page_user_share_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "page_id", Integer, ForeignKey( "page.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ) ) + Column("id", Integer, primary_key=True), + Column("page_id", Integer, ForeignKey("page.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True)) model.Visualization.table = Table( "visualization", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True, nullable=False ), - Column( "latest_revision_id", Integer, - ForeignKey( "visualization_revision.id", use_alter=True, name='visualization_latest_revision_id_fk' ), index=True ), - Column( "title", TEXT ), - Column( "type", TEXT ), - Column( "dbkey", TEXT, index=True ), - Column( "deleted", Boolean, default=False, index=True ), - Column( "importable", Boolean, default=False, index=True ), - Column( "slug", TEXT, index=True ), - Column( "published", Boolean, default=False, index=True ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True, nullable=False), + Column("latest_revision_id", Integer, + ForeignKey("visualization_revision.id", use_alter=True, name='visualization_latest_revision_id_fk'), index=True), + Column("title", TEXT), + Column("type", TEXT), + Column("dbkey", TEXT, index=True), + Column("deleted", Boolean, default=False, index=True), + Column("importable", Boolean, default=False, index=True), + Column("slug", TEXT, index=True), + Column("published", Boolean, default=False, index=True)) model.VisualizationRevision.table = Table( "visualization_revision", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "visualization_id", Integer, ForeignKey( "visualization.id" ), index=True, nullable=False ), - Column( "title", TEXT ), - Column( "dbkey", TEXT, index=True ), - Column( "config", JSONType ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("visualization_id", Integer, ForeignKey("visualization.id"), index=True, nullable=False), + Column("title", TEXT), + Column("dbkey", TEXT, index=True), + Column("config", JSONType)) model.VisualizationUserShareAssociation.table = Table( "visualization_user_share_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "visualization_id", Integer, ForeignKey( "visualization.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ) ) + Column("id", Integer, primary_key=True), + Column("visualization_id", Integer, ForeignKey("visualization.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True)) # Data Manager tables model.DataManagerHistoryAssociation.table = Table( "data_manager_history_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, index=True, default=now, onupdate=now ), - Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, index=True, default=now, onupdate=now), + Column("history_id", Integer, ForeignKey("history.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True)) model.DataManagerJobAssociation.table = Table( "data_manager_job_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, index=True, default=now, onupdate=now ), - Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ), - Column( "data_manager_id", TEXT, index=True ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, index=True, default=now, onupdate=now), + Column("job_id", Integer, ForeignKey("job.id"), index=True), + Column("data_manager_id", TEXT, index=True)) # Tagging tables. model.Tag.table = Table( "tag", metadata, - Column( "id", Integer, primary_key=True ), - Column( "type", Integer ), - Column( "parent_id", Integer, ForeignKey( "tag.id" ) ), - Column( "name", TrimmedString(255) ), - UniqueConstraint( "name" ) ) + Column("id", Integer, primary_key=True), + Column("type", Integer), + Column("parent_id", Integer, ForeignKey("tag.id")), + Column("name", TrimmedString(255)), + UniqueConstraint("name")) model.HistoryTagAssociation.table = Table( "history_tag_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ), - Column( "tag_id", Integer, ForeignKey( "tag.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "user_tname", TrimmedString( 255 ), index=True ), - Column( "value", TrimmedString( 255 ), index=True ), - Column( "user_value", TrimmedString( 255 ), index=True ) ) + Column("id", Integer, primary_key=True), + Column("history_id", Integer, ForeignKey("history.id"), index=True), + Column("tag_id", Integer, ForeignKey("tag.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("user_tname", TrimmedString(255), index=True), + Column("value", TrimmedString(255), index=True), + Column("user_value", TrimmedString(255), index=True)) model.DatasetTagAssociation.table = Table( "dataset_tag_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "dataset_id", Integer, ForeignKey( "dataset.id" ), index=True ), - Column( "tag_id", Integer, ForeignKey( "tag.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "user_tname", TrimmedString( 255 ), index=True ), - Column( "value", TrimmedString( 255 ), index=True ), - Column( "user_value", TrimmedString( 255 ), index=True ) ) + Column("id", Integer, primary_key=True), + Column("dataset_id", Integer, ForeignKey("dataset.id"), index=True), + Column("tag_id", Integer, ForeignKey("tag.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("user_tname", TrimmedString(255), index=True), + Column("value", TrimmedString(255), index=True), + Column("user_value", TrimmedString(255), index=True)) model.HistoryDatasetAssociationTagAssociation.table = Table( "history_dataset_association_tag_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "history_dataset_association_id", Integer, ForeignKey( "history_dataset_association.id" ), index=True ), - Column( "tag_id", Integer, ForeignKey( "tag.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "user_tname", TrimmedString( 255 ), index=True ), - Column( "value", TrimmedString( 255 ), index=True ), - Column( "user_value", TrimmedString( 255 ), index=True ) ) + Column("id", Integer, primary_key=True), + Column("history_dataset_association_id", Integer, ForeignKey("history_dataset_association.id"), index=True), + Column("tag_id", Integer, ForeignKey("tag.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("user_tname", TrimmedString(255), index=True), + Column("value", TrimmedString(255), index=True), + Column("user_value", TrimmedString(255), index=True)) + +model.LibraryDatasetDatasetAssociationTagAssociation.table = Table( + "library_dataset_dataset_association_tag_association", metadata, + Column("id", Integer, primary_key=True), + Column("library_dataset_dataset_association_id", Integer, ForeignKey("library_dataset_dataset_association.id"), index=True), + Column("tag_id", Integer, ForeignKey("tag.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("user_tname", TrimmedString(255), index=True), + Column("value", TrimmedString(255), index=True), + Column("user_value", TrimmedString(255), index=True)) model.StoredWorkflowTagAssociation.table = Table( "stored_workflow_tag_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "stored_workflow_id", Integer, ForeignKey( "stored_workflow.id" ), index=True ), - Column( "tag_id", Integer, ForeignKey( "tag.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "user_tname", Unicode( 255 ), index=True ), - Column( "value", Unicode( 255 ), index=True ), - Column( "user_value", Unicode( 255 ), index=True ) ) + Column("id", Integer, primary_key=True), + Column("stored_workflow_id", Integer, ForeignKey("stored_workflow.id"), index=True), + Column("tag_id", Integer, ForeignKey("tag.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("user_tname", Unicode(255), index=True), + Column("value", Unicode(255), index=True), + Column("user_value", Unicode(255), index=True)) model.PageTagAssociation.table = Table( "page_tag_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "page_id", Integer, ForeignKey( "page.id" ), index=True ), - Column( "tag_id", Integer, ForeignKey( "tag.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "user_tname", TrimmedString( 255 ), index=True ), - Column( "value", TrimmedString( 255 ), index=True ), - Column( "user_value", TrimmedString( 255 ), index=True ) ) + Column("id", Integer, primary_key=True), + Column("page_id", Integer, ForeignKey("page.id"), index=True), + Column("tag_id", Integer, ForeignKey("tag.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("user_tname", TrimmedString(255), index=True), + Column("value", TrimmedString(255), index=True), + Column("user_value", TrimmedString(255), index=True)) model.WorkflowStepTagAssociation.table = Table( "workflow_step_tag_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "workflow_step_id", Integer, ForeignKey( "workflow_step.id" ), index=True ), - Column( "tag_id", Integer, ForeignKey( "tag.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "user_tname", Unicode( 255 ), index=True ), - Column( "value", Unicode( 255 ), index=True ), - Column( "user_value", Unicode( 255 ), index=True ) ) + Column("id", Integer, primary_key=True), + Column("workflow_step_id", Integer, ForeignKey("workflow_step.id"), index=True), + Column("tag_id", Integer, ForeignKey("tag.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("user_tname", Unicode(255), index=True), + Column("value", Unicode(255), index=True), + Column("user_value", Unicode(255), index=True)) model.VisualizationTagAssociation.table = Table( "visualization_tag_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "visualization_id", Integer, ForeignKey( "visualization.id" ), index=True ), - Column( "tag_id", Integer, ForeignKey( "tag.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "user_tname", TrimmedString( 255 ), index=True ), - Column( "value", TrimmedString( 255 ), index=True ), - Column( "user_value", TrimmedString( 255 ), index=True ) ) + Column("id", Integer, primary_key=True), + Column("visualization_id", Integer, ForeignKey("visualization.id"), index=True), + Column("tag_id", Integer, ForeignKey("tag.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("user_tname", TrimmedString(255), index=True), + Column("value", TrimmedString(255), index=True), + Column("user_value", TrimmedString(255), index=True)) model.HistoryDatasetCollectionTagAssociation.table = Table( "history_dataset_collection_tag_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "history_dataset_collection_id", Integer, - ForeignKey( "history_dataset_collection_association.id" ), index=True ), - Column( "tag_id", Integer, ForeignKey( "tag.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "user_tname", TrimmedString( 255 ), index=True ), - Column( "value", TrimmedString( 255 ), index=True ), - Column( "user_value", TrimmedString( 255 ), index=True ) ) + Column("id", Integer, primary_key=True), + Column("history_dataset_collection_id", Integer, + ForeignKey("history_dataset_collection_association.id"), index=True), + Column("tag_id", Integer, ForeignKey("tag.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("user_tname", TrimmedString(255), index=True), + Column("value", TrimmedString(255), index=True), + Column("user_value", TrimmedString(255), index=True)) model.LibraryDatasetCollectionTagAssociation.table = Table( "library_dataset_collection_tag_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "library_dataset_collection_id", Integer, - ForeignKey( "library_dataset_collection_association.id" ), index=True ), - Column( "tag_id", Integer, ForeignKey( "tag.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "user_tname", TrimmedString( 255 ), index=True ), - Column( "value", TrimmedString( 255 ), index=True ), - Column( "user_value", TrimmedString( 255 ), index=True ) ) + Column("id", Integer, primary_key=True), + Column("library_dataset_collection_id", Integer, + ForeignKey("library_dataset_collection_association.id"), index=True), + Column("tag_id", Integer, ForeignKey("tag.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("user_tname", TrimmedString(255), index=True), + Column("value", TrimmedString(255), index=True), + Column("user_value", TrimmedString(255), index=True)) model.ToolTagAssociation.table = Table( "tool_tag_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "tool_id", TrimmedString(255), index=True ), - Column( "tag_id", Integer, ForeignKey( "tag.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "user_tname", TrimmedString( 255 ), index=True ), - Column( "value", TrimmedString( 255 ), index=True ), - Column( "user_value", TrimmedString( 255 ), index=True ) ) + Column("id", Integer, primary_key=True), + Column("tool_id", TrimmedString(255), index=True), + Column("tag_id", Integer, ForeignKey("tag.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("user_tname", TrimmedString(255), index=True), + Column("value", TrimmedString(255), index=True), + Column("user_value", TrimmedString(255), index=True)) # Annotation tables. model.HistoryAnnotationAssociation.table = Table( "history_annotation_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "annotation", TEXT, index=True ) ) + Column("id", Integer, primary_key=True), + Column("history_id", Integer, ForeignKey("history.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("annotation", TEXT, index=True)) model.HistoryDatasetAssociationAnnotationAssociation.table = Table( "history_dataset_association_annotation_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "history_dataset_association_id", Integer, - ForeignKey( "history_dataset_association.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "annotation", TEXT, index=True ) ) + Column("id", Integer, primary_key=True), + Column("history_dataset_association_id", Integer, + ForeignKey("history_dataset_association.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("annotation", TEXT, index=True)) model.StoredWorkflowAnnotationAssociation.table = Table( "stored_workflow_annotation_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "stored_workflow_id", Integer, ForeignKey( "stored_workflow.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "annotation", TEXT, index=True ) ) + Column("id", Integer, primary_key=True), + Column("stored_workflow_id", Integer, ForeignKey("stored_workflow.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("annotation", TEXT, index=True)) model.WorkflowStepAnnotationAssociation.table = Table( "workflow_step_annotation_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "workflow_step_id", Integer, ForeignKey( "workflow_step.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "annotation", TEXT, index=True ) ) + Column("id", Integer, primary_key=True), + Column("workflow_step_id", Integer, ForeignKey("workflow_step.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("annotation", TEXT, index=True)) model.PageAnnotationAssociation.table = Table( "page_annotation_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "page_id", Integer, ForeignKey( "page.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "annotation", TEXT, index=True ) ) + Column("id", Integer, primary_key=True), + Column("page_id", Integer, ForeignKey("page.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("annotation", TEXT, index=True)) model.VisualizationAnnotationAssociation.table = Table( "visualization_annotation_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "visualization_id", Integer, ForeignKey( "visualization.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "annotation", TEXT, index=True ) ) + Column("id", Integer, primary_key=True), + Column("visualization_id", Integer, ForeignKey("visualization.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("annotation", TEXT, index=True)) model.HistoryDatasetCollectionAnnotationAssociation.table = Table( "history_dataset_collection_annotation_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "history_dataset_collection_id", Integer, - ForeignKey( "history_dataset_collection_association.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "annotation", TEXT, index=True ) ) + Column("id", Integer, primary_key=True), + Column("history_dataset_collection_id", Integer, + ForeignKey("history_dataset_collection_association.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("annotation", TEXT, index=True)) model.LibraryDatasetCollectionAnnotationAssociation.table = Table( "library_dataset_collection_annotation_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "library_dataset_collection_id", Integer, - ForeignKey( "library_dataset_collection_association.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "annotation", TEXT, index=True ) ) + Column("id", Integer, primary_key=True), + Column("library_dataset_collection_id", Integer, + ForeignKey("library_dataset_collection_association.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("annotation", TEXT, index=True)) # Ratings tables. -model.HistoryRatingAssociation.table = Table( "history_rating_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "rating", Integer, index=True ) ) +model.HistoryRatingAssociation.table = Table("history_rating_association", metadata, + Column("id", Integer, primary_key=True), + Column("history_id", Integer, ForeignKey("history.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("rating", Integer, index=True)) model.HistoryDatasetAssociationRatingAssociation.table = Table( "history_dataset_association_rating_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "history_dataset_association_id", Integer, - ForeignKey( "history_dataset_association.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "rating", Integer, index=True ) ) + Column("id", Integer, primary_key=True), + Column("history_dataset_association_id", Integer, + ForeignKey("history_dataset_association.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("rating", Integer, index=True)) model.StoredWorkflowRatingAssociation.table = Table( "stored_workflow_rating_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "stored_workflow_id", Integer, ForeignKey( "stored_workflow.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "rating", Integer, index=True ) ) + Column("id", Integer, primary_key=True), + Column("stored_workflow_id", Integer, ForeignKey("stored_workflow.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("rating", Integer, index=True)) model.PageRatingAssociation.table = Table( "page_rating_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "page_id", Integer, ForeignKey( "page.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "rating", Integer, index=True ) ) + Column("id", Integer, primary_key=True), + Column("page_id", Integer, ForeignKey("page.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("rating", Integer, index=True)) model.VisualizationRatingAssociation.table = Table( "visualization_rating_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "visualization_id", Integer, ForeignKey( "visualization.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "rating", Integer, index=True ) ) + Column("id", Integer, primary_key=True), + Column("visualization_id", Integer, ForeignKey("visualization.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("rating", Integer, index=True)) model.HistoryDatasetCollectionRatingAssociation.table = Table( "history_dataset_collection_rating_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "history_dataset_collection_id", Integer, - ForeignKey( "history_dataset_collection_association.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "rating", Integer, index=True ) ) + Column("id", Integer, primary_key=True), + Column("history_dataset_collection_id", Integer, + ForeignKey("history_dataset_collection_association.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("rating", Integer, index=True)) model.LibraryDatasetCollectionRatingAssociation.table = Table( "library_dataset_collection_rating_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "library_dataset_collection_id", Integer, - ForeignKey( "library_dataset_collection_association.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "rating", Integer, index=True ) ) + Column("id", Integer, primary_key=True), + Column("library_dataset_collection_id", Integer, + ForeignKey("library_dataset_collection_association.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("rating", Integer, index=True)) # User tables. model.UserPreference.table = Table( "user_preference", metadata, - Column( "id", Integer, primary_key=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "name", Unicode( 255 ), index=True), - Column( "value", Text ) ) + Column("id", Integer, primary_key=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("name", Unicode(255), index=True), + Column("value", Text)) model.UserAction.table = Table( "user_action", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "session_id", Integer, ForeignKey( "galaxy_session.id" ), index=True ), - Column( "action", Unicode( 255 ) ), - Column( "context", Unicode( 512 ) ), - Column( "params", Unicode( 1024 ) ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("session_id", Integer, ForeignKey("galaxy_session.id"), index=True), + Column("action", Unicode(255)), + Column("context", Unicode(512)), + Column("params", Unicode(1024))) model.APIKeys.table = Table( "api_keys", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "key", TrimmedString( 32 ), index=True, unique=True ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("key", TrimmedString(32), index=True, unique=True)) # With the tables defined we can define the mappers and setup the # relationships between the model objects. -def simple_mapping( model, **kwds ): - mapper( model, model.table, properties=kwds ) +def simple_mapping(model, **kwds): + mapper(model, model.table, properties=kwds) -mapper( model.Sample, model.Sample.table, properties=dict( - events=relation( model.SampleEvent, +mapper(model.Sample, model.Sample.table, properties=dict( + events=relation(model.SampleEvent, backref="sample", - order_by=desc( model.SampleEvent.table.c.update_time ) ), - datasets=relation( model.SampleDataset, + order_by=desc(model.SampleEvent.table.c.update_time)), + datasets=relation(model.SampleDataset, backref="sample", - order_by=desc( model.SampleDataset.table.c.update_time ) ), - values=relation( model.FormValues, - primaryjoin=( model.Sample.table.c.form_values_id == model.FormValues.table.c.id ) ), - request=relation( model.Request, - primaryjoin=( model.Sample.table.c.request_id == model.Request.table.c.id ) ), - folder=relation( model.LibraryFolder, - primaryjoin=( model.Sample.table.c.folder_id == model.LibraryFolder.table.c.id ) ), - library=relation( model.Library, - primaryjoin=( model.Sample.table.c.library_id == model.Library.table.c.id ) ), - history=relation( model.History, - primaryjoin=( model.Sample.table.c.history_id == model.History.table.c.id ) ), -) ) - -mapper( model.FormValues, model.FormValues.table, properties=dict( - form_definition=relation( model.FormDefinition, - primaryjoin=( model.FormValues.table.c.form_definition_id == model.FormDefinition.table.c.id ) ) -) ) - -mapper( model.Request, model.Request.table, properties=dict( - values=relation( model.FormValues, - primaryjoin=( model.Request.table.c.form_values_id == model.FormValues.table.c.id ) ), - type=relation( model.RequestType, - primaryjoin=( model.Request.table.c.request_type_id == model.RequestType.table.c.id ) ), - user=relation( model.User, - primaryjoin=( model.Request.table.c.user_id == model.User.table.c.id ), - backref="requests" ), - samples=relation( model.Sample, - primaryjoin=( model.Request.table.c.id == model.Sample.table.c.request_id ), - order_by=asc( model.Sample.table.c.id ) ), - events=relation( model.RequestEvent, + order_by=desc(model.SampleDataset.table.c.update_time)), + values=relation(model.FormValues, + primaryjoin=(model.Sample.table.c.form_values_id == model.FormValues.table.c.id)), + request=relation(model.Request, + primaryjoin=(model.Sample.table.c.request_id == model.Request.table.c.id)), + folder=relation(model.LibraryFolder, + primaryjoin=(model.Sample.table.c.folder_id == model.LibraryFolder.table.c.id)), + library=relation(model.Library, + primaryjoin=(model.Sample.table.c.library_id == model.Library.table.c.id)), + history=relation(model.History, + primaryjoin=(model.Sample.table.c.history_id == model.History.table.c.id)), +)) + +mapper(model.FormValues, model.FormValues.table, properties=dict( + form_definition=relation(model.FormDefinition, + primaryjoin=(model.FormValues.table.c.form_definition_id == model.FormDefinition.table.c.id)) +)) + +mapper(model.Request, model.Request.table, properties=dict( + values=relation(model.FormValues, + primaryjoin=(model.Request.table.c.form_values_id == model.FormValues.table.c.id)), + type=relation(model.RequestType, + primaryjoin=(model.Request.table.c.request_type_id == model.RequestType.table.c.id)), + user=relation(model.User, + primaryjoin=(model.Request.table.c.user_id == model.User.table.c.id), + backref="requests"), + samples=relation(model.Sample, + primaryjoin=(model.Request.table.c.id == model.Sample.table.c.request_id), + order_by=asc(model.Sample.table.c.id)), + events=relation(model.RequestEvent, backref="request", - order_by=desc( model.RequestEvent.table.c.update_time ) ) -) ) + order_by=desc(model.RequestEvent.table.c.update_time)) +)) -mapper( model.RequestEvent, model.RequestEvent.table, properties=None ) +mapper(model.RequestEvent, model.RequestEvent.table, properties=None) -mapper( model.ExternalService, model.ExternalService.table, properties=dict( - form_definition=relation( model.FormDefinition, - primaryjoin=( model.ExternalService.table.c.form_definition_id == model.FormDefinition.table.c.id ) ), - form_values=relation( model.FormValues, - primaryjoin=( model.ExternalService.table.c.form_values_id == model.FormValues.table.c.id ) ) -) ) +mapper(model.ExternalService, model.ExternalService.table, properties=dict( + form_definition=relation(model.FormDefinition, + primaryjoin=(model.ExternalService.table.c.form_definition_id == model.FormDefinition.table.c.id)), + form_values=relation(model.FormValues, + primaryjoin=(model.ExternalService.table.c.form_values_id == model.FormValues.table.c.id)) +)) -mapper( model.RequestType, model.RequestType.table, properties=dict( - states=relation( model.SampleState, +mapper(model.RequestType, model.RequestType.table, properties=dict( + states=relation(model.SampleState, backref="request_type", - primaryjoin=( model.RequestType.table.c.id == model.SampleState.table.c.request_type_id ), - order_by=asc( model.SampleState.table.c.update_time ) ), - request_form=relation( model.FormDefinition, - primaryjoin=( model.RequestType.table.c.request_form_id == model.FormDefinition.table.c.id ) ), - sample_form=relation( model.FormDefinition, - primaryjoin=( model.RequestType.table.c.sample_form_id == model.FormDefinition.table.c.id ) ), -) ) - -mapper( model.RequestTypeExternalServiceAssociation, model.RequestTypeExternalServiceAssociation.table, properties=dict( - request_type=relation( model.RequestType, - primaryjoin=( ( model.RequestTypeExternalServiceAssociation.table.c.request_type_id == model.RequestType.table.c.id ) ), - backref="external_service_associations" ), - external_service=relation( model.ExternalService, - primaryjoin=( model.RequestTypeExternalServiceAssociation.table.c.external_service_id == model.ExternalService.table.c.id ) ) -) ) - - -mapper( model.RequestTypePermissions, model.RequestTypePermissions.table, properties=dict( - request_type=relation( model.RequestType, backref="actions" ), - role=relation( model.Role, backref="request_type_actions" ) -) ) - -mapper( model.FormDefinition, model.FormDefinition.table, properties=dict( - current=relation( model.FormDefinitionCurrent, - primaryjoin=( model.FormDefinition.table.c.form_definition_current_id == model.FormDefinitionCurrent.table.c.id ) ) -) ) - -mapper( model.FormDefinitionCurrent, model.FormDefinitionCurrent.table, properties=dict( - forms=relation( model.FormDefinition, + primaryjoin=(model.RequestType.table.c.id == model.SampleState.table.c.request_type_id), + order_by=asc(model.SampleState.table.c.update_time)), + request_form=relation(model.FormDefinition, + primaryjoin=(model.RequestType.table.c.request_form_id == model.FormDefinition.table.c.id)), + sample_form=relation(model.FormDefinition, + primaryjoin=(model.RequestType.table.c.sample_form_id == model.FormDefinition.table.c.id)), +)) + +mapper(model.RequestTypeExternalServiceAssociation, model.RequestTypeExternalServiceAssociation.table, properties=dict( + request_type=relation(model.RequestType, + primaryjoin=((model.RequestTypeExternalServiceAssociation.table.c.request_type_id == model.RequestType.table.c.id)), + backref="external_service_associations"), + external_service=relation(model.ExternalService, + primaryjoin=(model.RequestTypeExternalServiceAssociation.table.c.external_service_id == model.ExternalService.table.c.id)) +)) + + +mapper(model.RequestTypePermissions, model.RequestTypePermissions.table, properties=dict( + request_type=relation(model.RequestType, backref="actions"), + role=relation(model.Role, backref="request_type_actions") +)) + +mapper(model.FormDefinition, model.FormDefinition.table, properties=dict( + current=relation(model.FormDefinitionCurrent, + primaryjoin=(model.FormDefinition.table.c.form_definition_current_id == model.FormDefinitionCurrent.table.c.id)) +)) + +mapper(model.FormDefinitionCurrent, model.FormDefinitionCurrent.table, properties=dict( + forms=relation(model.FormDefinition, backref='form_definition_current', cascade="all, delete-orphan", - primaryjoin=( model.FormDefinitionCurrent.table.c.id == model.FormDefinition.table.c.form_definition_current_id ) ), - latest_form=relation( model.FormDefinition, + primaryjoin=(model.FormDefinitionCurrent.table.c.id == model.FormDefinition.table.c.form_definition_current_id)), + latest_form=relation(model.FormDefinition, post_update=True, - primaryjoin=( model.FormDefinitionCurrent.table.c.latest_form_id == model.FormDefinition.table.c.id ) ) -) ) - -mapper( model.SampleEvent, model.SampleEvent.table, properties=dict( - state=relation( model.SampleState, - primaryjoin=( model.SampleEvent.table.c.sample_state_id == model.SampleState.table.c.id ) ), -) ) - -mapper( model.SampleState, model.SampleState.table, properties=None ) - -mapper( model.SampleDataset, model.SampleDataset.table, properties=dict( - external_service=relation( model.ExternalService, - primaryjoin=( model.SampleDataset.table.c.external_service_id == model.ExternalService.table.c.id ) ) -) ) - - -mapper( model.SampleRunAssociation, model.SampleRunAssociation.table, properties=dict( - sample=relation( model.Sample, backref="runs", order_by=desc( model.Run.table.c.update_time ) ), - run=relation( model.Run, backref="samples", order_by=asc( model.Sample.table.c.id ) ) -) ) - -mapper( model.RequestTypeRunAssociation, model.RequestTypeRunAssociation.table, properties=dict( - request_type=relation( model.RequestType, backref="run" ), - run=relation( model.Run, backref="request_type" ) -) ) - -mapper( model.Run, model.Run.table, properties=dict( - template=relation( model.FormDefinition, - primaryjoin=( model.Run.table.c.form_definition_id == model.FormDefinition.table.c.id ) ), - info=relation( model.FormValues, - primaryjoin=( model.Run.table.c.form_values_id == model.FormValues.table.c.id ) ) -) ) - -mapper( model.UserAddress, model.UserAddress.table, properties=dict( - user=relation( model.User, - primaryjoin=( model.UserAddress.table.c.user_id == model.User.table.c.id ), + primaryjoin=(model.FormDefinitionCurrent.table.c.latest_form_id == model.FormDefinition.table.c.id)) +)) + +mapper(model.SampleEvent, model.SampleEvent.table, properties=dict( + state=relation(model.SampleState, + primaryjoin=(model.SampleEvent.table.c.sample_state_id == model.SampleState.table.c.id)), +)) + +mapper(model.SampleState, model.SampleState.table, properties=None) + +mapper(model.SampleDataset, model.SampleDataset.table, properties=dict( + external_service=relation(model.ExternalService, + primaryjoin=(model.SampleDataset.table.c.external_service_id == model.ExternalService.table.c.id)) +)) + + +mapper(model.SampleRunAssociation, model.SampleRunAssociation.table, properties=dict( + sample=relation(model.Sample, backref="runs", order_by=desc(model.Run.table.c.update_time)), + run=relation(model.Run, backref="samples", order_by=asc(model.Sample.table.c.id)) +)) + +mapper(model.RequestTypeRunAssociation, model.RequestTypeRunAssociation.table, properties=dict( + request_type=relation(model.RequestType, backref="run"), + run=relation(model.Run, backref="request_type") +)) + +mapper(model.Run, model.Run.table, properties=dict( + template=relation(model.FormDefinition, + primaryjoin=(model.Run.table.c.form_definition_id == model.FormDefinition.table.c.id)), + info=relation(model.FormValues, + primaryjoin=(model.Run.table.c.form_values_id == model.FormValues.table.c.id)) +)) + +mapper(model.UserAddress, model.UserAddress.table, properties=dict( + user=relation(model.User, + primaryjoin=(model.UserAddress.table.c.user_id == model.User.table.c.id), backref='addresses', - order_by=desc(model.UserAddress.table.c.update_time ) ), -) ) + order_by=desc(model.UserAddress.table.c.update_time)), +)) -mapper( model.UserOpenID, model.UserOpenID.table, properties=dict( - session=relation( model.GalaxySession, - primaryjoin=( model.UserOpenID.table.c.session_id == model.GalaxySession.table.c.id ), +mapper(model.UserOpenID, model.UserOpenID.table, properties=dict( + session=relation(model.GalaxySession, + primaryjoin=(model.UserOpenID.table.c.session_id == model.GalaxySession.table.c.id), backref='openids', - order_by=desc( model.UserOpenID.table.c.update_time ) ), - user=relation( model.User, - primaryjoin=( model.UserOpenID.table.c.user_id == model.User.table.c.id ), + order_by=desc(model.UserOpenID.table.c.update_time)), + user=relation(model.User, + primaryjoin=(model.UserOpenID.table.c.user_id == model.User.table.c.id), backref='openids', - order_by=desc( model.UserOpenID.table.c.update_time ) ) -) ) + order_by=desc(model.UserOpenID.table.c.update_time)) +)) -mapper( model.ValidationError, model.ValidationError.table ) +mapper(model.ValidationError, model.ValidationError.table) -simple_mapping( model.HistoryDatasetAssociation, - dataset=relation( model.Dataset, - primaryjoin=( model.Dataset.table.c.id == model.HistoryDatasetAssociation.table.c.dataset_id ), lazy=False ), +simple_mapping(model.HistoryDatasetAssociation, + dataset=relation(model.Dataset, + primaryjoin=(model.Dataset.table.c.id == model.HistoryDatasetAssociation.table.c.dataset_id), lazy=False), # .history defined in History mapper - copied_from_history_dataset_association=relation( model.HistoryDatasetAssociation, - primaryjoin=( model.HistoryDatasetAssociation.table.c.copied_from_history_dataset_association_id == - model.HistoryDatasetAssociation.table.c.id ), + copied_from_history_dataset_association=relation(model.HistoryDatasetAssociation, + primaryjoin=(model.HistoryDatasetAssociation.table.c.copied_from_history_dataset_association_id == + model.HistoryDatasetAssociation.table.c.id), remote_side=[model.HistoryDatasetAssociation.table.c.id], - uselist=False ), - copied_to_history_dataset_associations=relation( model.HistoryDatasetAssociation, - primaryjoin=( model.HistoryDatasetAssociation.table.c.copied_from_history_dataset_association_id == - model.HistoryDatasetAssociation.table.c.id ) ), + uselist=False), + copied_to_history_dataset_associations=relation(model.HistoryDatasetAssociation, + primaryjoin=(model.HistoryDatasetAssociation.table.c.copied_from_history_dataset_association_id == + model.HistoryDatasetAssociation.table.c.id)), copied_from_library_dataset_dataset_association=relation( model.LibraryDatasetDatasetAssociation, - primaryjoin=( model.HistoryDatasetAssociation.table.c.copied_from_library_dataset_dataset_association_id == - model.LibraryDatasetDatasetAssociation.table.c.id ), - uselist=False ), - copied_to_library_dataset_dataset_associations=relation( model.LibraryDatasetDatasetAssociation, - primaryjoin=( model.HistoryDatasetAssociation.table.c.copied_from_library_dataset_dataset_association_id == - model.LibraryDatasetDatasetAssociation.table.c.id ) ), - implicitly_converted_datasets=relation( model.ImplicitlyConvertedDatasetAssociation, - primaryjoin=( model.ImplicitlyConvertedDatasetAssociation.table.c.hda_parent_id == - model.HistoryDatasetAssociation.table.c.id ) ), - implicitly_converted_parent_datasets=relation( model.ImplicitlyConvertedDatasetAssociation, - primaryjoin=( model.ImplicitlyConvertedDatasetAssociation.table.c.hda_id == - model.HistoryDatasetAssociation.table.c.id ) ), - children=relation( model.HistoryDatasetAssociation, - primaryjoin=( model.HistoryDatasetAssociation.table.c.parent_id == - model.HistoryDatasetAssociation.table.c.id ), - backref=backref( "parent", - primaryjoin=( model.HistoryDatasetAssociation.table.c.parent_id == - model.HistoryDatasetAssociation.table.c.id ), - remote_side=[model.HistoryDatasetAssociation.table.c.id], uselist=False ) ), - visible_children=relation( model.HistoryDatasetAssociation, + primaryjoin=(model.HistoryDatasetAssociation.table.c.copied_from_library_dataset_dataset_association_id == + model.LibraryDatasetDatasetAssociation.table.c.id), + uselist=False), + copied_to_library_dataset_dataset_associations=relation(model.LibraryDatasetDatasetAssociation, + primaryjoin=(model.HistoryDatasetAssociation.table.c.copied_from_library_dataset_dataset_association_id == + model.LibraryDatasetDatasetAssociation.table.c.id)), + implicitly_converted_datasets=relation(model.ImplicitlyConvertedDatasetAssociation, + primaryjoin=(model.ImplicitlyConvertedDatasetAssociation.table.c.hda_parent_id == + model.HistoryDatasetAssociation.table.c.id)), + implicitly_converted_parent_datasets=relation(model.ImplicitlyConvertedDatasetAssociation, + primaryjoin=(model.ImplicitlyConvertedDatasetAssociation.table.c.hda_id == + model.HistoryDatasetAssociation.table.c.id)), + children=relation(model.HistoryDatasetAssociation, + primaryjoin=(model.HistoryDatasetAssociation.table.c.parent_id == + model.HistoryDatasetAssociation.table.c.id), + backref=backref("parent", + primaryjoin=(model.HistoryDatasetAssociation.table.c.parent_id == + model.HistoryDatasetAssociation.table.c.id), + remote_side=[model.HistoryDatasetAssociation.table.c.id], uselist=False)), + visible_children=relation(model.HistoryDatasetAssociation, primaryjoin=( - ( model.HistoryDatasetAssociation.table.c.parent_id == model.HistoryDatasetAssociation.table.c.id ) & - ( model.HistoryDatasetAssociation.table.c.visible == true() ) ), - remote_side=[model.HistoryDatasetAssociation.table.c.id] ), - tags=relation( model.HistoryDatasetAssociationTagAssociation, + (model.HistoryDatasetAssociation.table.c.parent_id == model.HistoryDatasetAssociation.table.c.id) & + (model.HistoryDatasetAssociation.table.c.visible == true())), + remote_side=[model.HistoryDatasetAssociation.table.c.id]), + tags=relation(model.HistoryDatasetAssociationTagAssociation, order_by=model.HistoryDatasetAssociationTagAssociation.table.c.id, - backref='history_tag_associations' ), - annotations=relation( model.HistoryDatasetAssociationAnnotationAssociation, + backref='history_tag_associations'), + annotations=relation(model.HistoryDatasetAssociationAnnotationAssociation, order_by=model.HistoryDatasetAssociationAnnotationAssociation.table.c.id, - backref="hdas" ), - ratings=relation( model.HistoryDatasetAssociationRatingAssociation, + backref="hdas"), + ratings=relation(model.HistoryDatasetAssociationRatingAssociation, order_by=model.HistoryDatasetAssociationRatingAssociation.table.c.id, - backref="hdas" ), - extended_metadata=relation( model.ExtendedMetadata, - primaryjoin=( ( model.HistoryDatasetAssociation.table.c.extended_metadata_id == - model.ExtendedMetadata.table.c.id ) ) ), - hidden_beneath_collection_instance=relation( model.HistoryDatasetCollectionAssociation, - primaryjoin=( ( model.HistoryDatasetAssociation.table.c.hidden_beneath_collection_instance_id == - model.HistoryDatasetCollectionAssociation.table.c.id ) ), + backref="hdas"), + extended_metadata=relation(model.ExtendedMetadata, + primaryjoin=((model.HistoryDatasetAssociation.table.c.extended_metadata_id == + model.ExtendedMetadata.table.c.id))), + hidden_beneath_collection_instance=relation(model.HistoryDatasetCollectionAssociation, + primaryjoin=((model.HistoryDatasetAssociation.table.c.hidden_beneath_collection_instance_id == + model.HistoryDatasetCollectionAssociation.table.c.id)), uselist=False, backref="hidden_dataset_instances"), _metadata=deferred(model.HistoryDatasetAssociation.table.c._metadata) ) -simple_mapping( model.Dataset, - history_associations=relation( model.HistoryDatasetAssociation, - primaryjoin=( model.Dataset.table.c.id == model.HistoryDatasetAssociation.table.c.dataset_id ) ), - active_history_associations=relation( model.HistoryDatasetAssociation, +simple_mapping(model.Dataset, + history_associations=relation(model.HistoryDatasetAssociation, + primaryjoin=(model.Dataset.table.c.id == model.HistoryDatasetAssociation.table.c.dataset_id)), + active_history_associations=relation(model.HistoryDatasetAssociation, primaryjoin=( - ( model.Dataset.table.c.id == model.HistoryDatasetAssociation.table.c.dataset_id ) & - ( model.HistoryDatasetAssociation.table.c.deleted == false() ) & - ( model.HistoryDatasetAssociation.table.c.purged == false() ) ) ), - purged_history_associations=relation( model.HistoryDatasetAssociation, + (model.Dataset.table.c.id == model.HistoryDatasetAssociation.table.c.dataset_id) & + (model.HistoryDatasetAssociation.table.c.deleted == false()) & + (model.HistoryDatasetAssociation.table.c.purged == false()))), + purged_history_associations=relation(model.HistoryDatasetAssociation, primaryjoin=( - ( model.Dataset.table.c.id == model.HistoryDatasetAssociation.table.c.dataset_id ) & - ( model.HistoryDatasetAssociation.table.c.purged == true() ) ) ), - library_associations=relation( model.LibraryDatasetDatasetAssociation, - primaryjoin=( model.Dataset.table.c.id == model.LibraryDatasetDatasetAssociation.table.c.dataset_id ) ), - active_library_associations=relation( model.LibraryDatasetDatasetAssociation, + (model.Dataset.table.c.id == model.HistoryDatasetAssociation.table.c.dataset_id) & + (model.HistoryDatasetAssociation.table.c.purged == true()))), + library_associations=relation(model.LibraryDatasetDatasetAssociation, + primaryjoin=(model.Dataset.table.c.id == model.LibraryDatasetDatasetAssociation.table.c.dataset_id)), + active_library_associations=relation(model.LibraryDatasetDatasetAssociation, primaryjoin=( - ( model.Dataset.table.c.id == model.LibraryDatasetDatasetAssociation.table.c.dataset_id ) & - ( model.LibraryDatasetDatasetAssociation.table.c.deleted == false() ) ) ), + (model.Dataset.table.c.id == model.LibraryDatasetDatasetAssociation.table.c.dataset_id) & + (model.LibraryDatasetDatasetAssociation.table.c.deleted == false()))), tags=relation(model.DatasetTagAssociation, order_by=model.DatasetTagAssociation.table.c.id, backref='datasets') ) -mapper( model.HistoryDatasetAssociationDisplayAtAuthorization, model.HistoryDatasetAssociationDisplayAtAuthorization.table, properties=dict( - history_dataset_association=relation( model.HistoryDatasetAssociation ), - user=relation( model.User ) -) ) - -mapper( model.HistoryDatasetAssociationSubset, model.HistoryDatasetAssociationSubset.table, properties=dict( - hda=relation( model.HistoryDatasetAssociation, - primaryjoin=( model.HistoryDatasetAssociationSubset.table.c.history_dataset_association_id == - model.HistoryDatasetAssociation.table.c.id ) ), - subset=relation( model.HistoryDatasetAssociation, - primaryjoin=( model.HistoryDatasetAssociationSubset.table.c.history_dataset_association_subset_id == - model.HistoryDatasetAssociation.table.c.id ) ) -) ) - -mapper( model.ImplicitlyConvertedDatasetAssociation, model.ImplicitlyConvertedDatasetAssociation.table, properties=dict( - parent_hda=relation( model.HistoryDatasetAssociation, - primaryjoin=( model.ImplicitlyConvertedDatasetAssociation.table.c.hda_parent_id == - model.HistoryDatasetAssociation.table.c.id ) ), - parent_ldda=relation( model.LibraryDatasetDatasetAssociation, - primaryjoin=( model.ImplicitlyConvertedDatasetAssociation.table.c.ldda_parent_id == - model.LibraryDatasetDatasetAssociation.table.c.id ) ), - dataset_ldda=relation( model.LibraryDatasetDatasetAssociation, - primaryjoin=( model.ImplicitlyConvertedDatasetAssociation.table.c.ldda_id == - model.LibraryDatasetDatasetAssociation.table.c.id ) ), - dataset=relation( model.HistoryDatasetAssociation, - primaryjoin=( model.ImplicitlyConvertedDatasetAssociation.table.c.hda_id == - model.HistoryDatasetAssociation.table.c.id ) ) -) ) - -mapper( model.History, model.History.table, properties=dict( - galaxy_sessions=relation( model.GalaxySessionToHistoryAssociation ), - datasets=relation( model.HistoryDatasetAssociation, +mapper(model.HistoryDatasetAssociationDisplayAtAuthorization, model.HistoryDatasetAssociationDisplayAtAuthorization.table, properties=dict( + history_dataset_association=relation(model.HistoryDatasetAssociation), + user=relation(model.User) +)) + +mapper(model.HistoryDatasetAssociationSubset, model.HistoryDatasetAssociationSubset.table, properties=dict( + hda=relation(model.HistoryDatasetAssociation, + primaryjoin=(model.HistoryDatasetAssociationSubset.table.c.history_dataset_association_id == + model.HistoryDatasetAssociation.table.c.id)), + subset=relation(model.HistoryDatasetAssociation, + primaryjoin=(model.HistoryDatasetAssociationSubset.table.c.history_dataset_association_subset_id == + model.HistoryDatasetAssociation.table.c.id)) +)) + +mapper(model.ImplicitlyConvertedDatasetAssociation, model.ImplicitlyConvertedDatasetAssociation.table, properties=dict( + parent_hda=relation(model.HistoryDatasetAssociation, + primaryjoin=(model.ImplicitlyConvertedDatasetAssociation.table.c.hda_parent_id == + model.HistoryDatasetAssociation.table.c.id)), + parent_ldda=relation(model.LibraryDatasetDatasetAssociation, + primaryjoin=(model.ImplicitlyConvertedDatasetAssociation.table.c.ldda_parent_id == + model.LibraryDatasetDatasetAssociation.table.c.id)), + dataset_ldda=relation(model.LibraryDatasetDatasetAssociation, + primaryjoin=(model.ImplicitlyConvertedDatasetAssociation.table.c.ldda_id == + model.LibraryDatasetDatasetAssociation.table.c.id)), + dataset=relation(model.HistoryDatasetAssociation, + primaryjoin=(model.ImplicitlyConvertedDatasetAssociation.table.c.hda_id == + model.HistoryDatasetAssociation.table.c.id)) +)) + +mapper(model.History, model.History.table, properties=dict( + galaxy_sessions=relation(model.GalaxySessionToHistoryAssociation), + datasets=relation(model.HistoryDatasetAssociation, backref="history", - order_by=asc( model.HistoryDatasetAssociation.table.c.hid ) ), - exports=relation( model.JobExportHistoryArchive, - primaryjoin=( model.JobExportHistoryArchive.table.c.history_id == model.History.table.c.id ), - order_by=desc( model.JobExportHistoryArchive.table.c.id ) ), - active_datasets=relation( model.HistoryDatasetAssociation, + order_by=asc(model.HistoryDatasetAssociation.table.c.hid)), + exports=relation(model.JobExportHistoryArchive, + primaryjoin=(model.JobExportHistoryArchive.table.c.history_id == model.History.table.c.id), + order_by=desc(model.JobExportHistoryArchive.table.c.id)), + active_datasets=relation(model.HistoryDatasetAssociation, primaryjoin=( - ( model.HistoryDatasetAssociation.table.c.history_id == model.History.table.c.id ) & - not_( model.HistoryDatasetAssociation.table.c.deleted ) + (model.HistoryDatasetAssociation.table.c.history_id == model.History.table.c.id) & + not_(model.HistoryDatasetAssociation.table.c.deleted) ), - order_by=asc( model.HistoryDatasetAssociation.table.c.hid ), - viewonly=True ), - active_dataset_collections=relation( model.HistoryDatasetCollectionAssociation, + order_by=asc(model.HistoryDatasetAssociation.table.c.hid), + viewonly=True), + active_dataset_collections=relation(model.HistoryDatasetCollectionAssociation, primaryjoin=( - ( model.HistoryDatasetCollectionAssociation.table.c.history_id == model.History.table.c.id ) & - not_( model.HistoryDatasetCollectionAssociation.table.c.deleted ) + (model.HistoryDatasetCollectionAssociation.table.c.history_id == model.History.table.c.id) & + not_(model.HistoryDatasetCollectionAssociation.table.c.deleted) ), - order_by=asc( model.HistoryDatasetCollectionAssociation.table.c.hid ), - viewonly=True ), - visible_datasets=relation( model.HistoryDatasetAssociation, + order_by=asc(model.HistoryDatasetCollectionAssociation.table.c.hid), + viewonly=True), + visible_datasets=relation(model.HistoryDatasetAssociation, primaryjoin=( - ( model.HistoryDatasetAssociation.table.c.history_id == model.History.table.c.id ) & - not_( model.HistoryDatasetAssociation.table.c.deleted ) & + (model.HistoryDatasetAssociation.table.c.history_id == model.History.table.c.id) & + not_(model.HistoryDatasetAssociation.table.c.deleted) & model.HistoryDatasetAssociation.table.c.visible ), - order_by=asc( model.HistoryDatasetAssociation.table.c.hid ), - viewonly=True ), - visible_dataset_collections=relation( model.HistoryDatasetCollectionAssociation, + order_by=asc(model.HistoryDatasetAssociation.table.c.hid), + viewonly=True), + visible_dataset_collections=relation(model.HistoryDatasetCollectionAssociation, primaryjoin=( - ( model.HistoryDatasetCollectionAssociation.table.c.history_id == model.History.table.c.id ) & - not_( model.HistoryDatasetCollectionAssociation.table.c.deleted ) & + (model.HistoryDatasetCollectionAssociation.table.c.history_id == model.History.table.c.id) & + not_(model.HistoryDatasetCollectionAssociation.table.c.deleted) & model.HistoryDatasetCollectionAssociation.table.c.visible ), - order_by=asc( model.HistoryDatasetCollectionAssociation.table.c.hid ), - viewonly=True ), - tags=relation( model.HistoryTagAssociation, + order_by=asc(model.HistoryDatasetCollectionAssociation.table.c.hid), + viewonly=True), + tags=relation(model.HistoryTagAssociation, order_by=model.HistoryTagAssociation.table.c.id, - backref="histories" ), - annotations=relation( model.HistoryAnnotationAssociation, + backref="histories"), + annotations=relation(model.HistoryAnnotationAssociation, order_by=model.HistoryAnnotationAssociation.table.c.id, - backref="histories" ), - ratings=relation( model.HistoryRatingAssociation, + backref="histories"), + ratings=relation(model.HistoryRatingAssociation, order_by=model.HistoryRatingAssociation.table.c.id, - backref="histories" ) -) ) + backref="histories") +)) # Set up proxy so that # History.users_shared_with # returns a list of users that history is shared with. -model.History.users_shared_with_dot_users = association_proxy( 'users_shared_with', 'user' ) +model.History.users_shared_with_dot_users = association_proxy('users_shared_with', 'user') -mapper( model.HistoryUserShareAssociation, model.HistoryUserShareAssociation.table, properties=dict( - user=relation( model.User, backref='histories_shared_by_others' ), - history=relation( model.History, backref='users_shared_with' ) -) ) +mapper(model.HistoryUserShareAssociation, model.HistoryUserShareAssociation.table, properties=dict( + user=relation(model.User, backref='histories_shared_by_others'), + history=relation(model.History, backref='users_shared_with') +)) -mapper( model.User, model.User.table, properties=dict( - histories=relation( model.History, +mapper(model.User, model.User.table, properties=dict( + histories=relation(model.History, backref="user", - order_by=desc( model.History.table.c.update_time ) ), - active_histories=relation( model.History, + order_by=desc(model.History.table.c.update_time)), + active_histories=relation(model.History, primaryjoin=( - ( model.History.table.c.user_id == model.User.table.c.id ) & - ( not_( model.History.table.c.deleted ) ) + (model.History.table.c.user_id == model.User.table.c.id) & + (not_(model.History.table.c.deleted)) ), - order_by=desc( model.History.table.c.update_time ) ), + order_by=desc(model.History.table.c.update_time)), - galaxy_sessions=relation( model.GalaxySession, - order_by=desc( model.GalaxySession.table.c.update_time ) ), - stored_workflow_menu_entries=relation( model.StoredWorkflowMenuEntry, + galaxy_sessions=relation(model.GalaxySession, + order_by=desc(model.GalaxySession.table.c.update_time)), + stored_workflow_menu_entries=relation(model.StoredWorkflowMenuEntry, backref="user", cascade="all, delete-orphan", - collection_class=ordering_list( 'order_index' ) ), - _preferences=relation( model.UserPreference, + collection_class=ordering_list('order_index')), + _preferences=relation(model.UserPreference, backref="user", - collection_class=attribute_mapped_collection( 'name' ) ), + collection_class=attribute_mapped_collection('name')), # addresses=relation( UserAddress, # primaryjoin=( User.table.c.id == UserAddress.table.c.user_id ) ), - values=relation( model.FormValues, - primaryjoin=( model.User.table.c.form_values_id == model.FormValues.table.c.id ) ), - api_keys=relation( model.APIKeys, + values=relation(model.FormValues, + primaryjoin=(model.User.table.c.form_values_id == model.FormValues.table.c.id)), + api_keys=relation(model.APIKeys, backref="user", - order_by=desc( model.APIKeys.table.c.create_time ) ), -) ) + order_by=desc(model.APIKeys.table.c.create_time)), +)) -mapper( model.PasswordResetToken, model.PasswordResetToken.table, - properties=dict( user=relation( model.User, backref="reset_tokens") ) ) +mapper(model.PasswordResetToken, model.PasswordResetToken.table, + properties=dict(user=relation(model.User, backref="reset_tokens"))) # Set up proxy so that this syntax is possible: # .preferences[pref_name] = pref_value model.User.preferences = association_proxy('_preferences', 'value', creator=model.UserPreference) -mapper( model.Group, model.Group.table, properties=dict( - users=relation( model.UserGroupAssociation ) -) ) +mapper(model.Group, model.Group.table, properties=dict( + users=relation(model.UserGroupAssociation) +)) -mapper( model.UserGroupAssociation, model.UserGroupAssociation.table, properties=dict( - user=relation( model.User, backref="groups" ), - group=relation( model.Group, backref="members" ) -) ) +mapper(model.UserGroupAssociation, model.UserGroupAssociation.table, properties=dict( + user=relation(model.User, backref="groups"), + group=relation(model.Group, backref="members") +)) -mapper( model.DefaultUserPermissions, model.DefaultUserPermissions.table, properties=dict( - user=relation( model.User, backref="default_permissions" ), - role=relation( model.Role ) -) ) +mapper(model.DefaultUserPermissions, model.DefaultUserPermissions.table, properties=dict( + user=relation(model.User, backref="default_permissions"), + role=relation(model.Role) +)) -mapper( model.DefaultHistoryPermissions, model.DefaultHistoryPermissions.table, properties=dict( - history=relation( model.History, backref="default_permissions" ), - role=relation( model.Role ) -) ) +mapper(model.DefaultHistoryPermissions, model.DefaultHistoryPermissions.table, properties=dict( + history=relation(model.History, backref="default_permissions"), + role=relation(model.Role) +)) -mapper( model.Role, model.Role.table, properties=dict( - users=relation( model.UserRoleAssociation ), - groups=relation( model.GroupRoleAssociation ) -) ) +mapper(model.Role, model.Role.table, properties=dict( + users=relation(model.UserRoleAssociation), + groups=relation(model.GroupRoleAssociation) +)) -mapper( model.UserRoleAssociation, model.UserRoleAssociation.table, properties=dict( - user=relation( model.User, backref="roles" ), +mapper(model.UserRoleAssociation, model.UserRoleAssociation.table, properties=dict( + user=relation(model.User, backref="roles"), non_private_roles=relation( model.User, backref="non_private_roles", primaryjoin=( - ( model.User.table.c.id == model.UserRoleAssociation.table.c.user_id ) & - ( model.UserRoleAssociation.table.c.role_id == model.Role.table.c.id ) & - not_( model.Role.table.c.name == model.User.table.c.email ) ) + (model.User.table.c.id == model.UserRoleAssociation.table.c.user_id) & + (model.UserRoleAssociation.table.c.role_id == model.Role.table.c.id) & + not_(model.Role.table.c.name == model.User.table.c.email)) ), - role=relation( model.Role ) -) ) - -mapper( model.GroupRoleAssociation, model.GroupRoleAssociation.table, properties=dict( - group=relation( model.Group, backref="roles" ), - role=relation( model.Role ) -) ) - -mapper( model.Quota, model.Quota.table, properties=dict( - users=relation( model.UserQuotaAssociation ), - groups=relation( model.GroupQuotaAssociation ) -) ) - -mapper( model.UserQuotaAssociation, model.UserQuotaAssociation.table, properties=dict( - user=relation( model.User, backref="quotas" ), - quota=relation( model.Quota ) -) ) - -mapper( model.GroupQuotaAssociation, model.GroupQuotaAssociation.table, properties=dict( - group=relation( model.Group, backref="quotas" ), - quota=relation( model.Quota ) -) ) - -mapper( model.DefaultQuotaAssociation, model.DefaultQuotaAssociation.table, properties=dict( - quota=relation( model.Quota, backref="default" ) -) ) - -mapper( model.DatasetPermissions, model.DatasetPermissions.table, properties=dict( - dataset=relation( model.Dataset, backref="actions" ), - role=relation( model.Role, backref="dataset_actions" ) -) ) - -mapper( model.LibraryPermissions, model.LibraryPermissions.table, properties=dict( - library=relation( model.Library, backref="actions" ), - role=relation( model.Role, backref="library_actions" ) -) ) - -mapper( model.LibraryFolderPermissions, model.LibraryFolderPermissions.table, properties=dict( - folder=relation( model.LibraryFolder, backref="actions" ), - role=relation( model.Role, backref="library_folder_actions" ) -) ) - -mapper( model.LibraryDatasetPermissions, model.LibraryDatasetPermissions.table, properties=dict( - library_dataset=relation( model.LibraryDataset, backref="actions" ), - role=relation( model.Role, backref="library_dataset_actions" ) -) ) - -mapper( model.LibraryDatasetDatasetAssociationPermissions, model.LibraryDatasetDatasetAssociationPermissions.table, properties=dict( - library_dataset_dataset_association=relation( model.LibraryDatasetDatasetAssociation, backref="actions" ), - role=relation( model.Role, backref="library_dataset_dataset_actions" ) -) ) - -mapper( model.Library, model.Library.table, properties=dict( - root_folder=relation( model.LibraryFolder, backref=backref( "library_root" ) ) -) ) - -mapper( model.ExtendedMetadata, model.ExtendedMetadata.table, properties=dict( - children=relation( model.ExtendedMetadataIndex, - primaryjoin=( model.ExtendedMetadataIndex.table.c.extended_metadata_id == model.ExtendedMetadata.table.c.id ), - backref=backref( "parent", - primaryjoin=( model.ExtendedMetadataIndex.table.c.extended_metadata_id == model.ExtendedMetadata.table.c.id ) ) ) -) ) - -mapper( model.ExtendedMetadataIndex, model.ExtendedMetadataIndex.table, properties=dict( - extended_metadata=relation( model.ExtendedMetadata, - primaryjoin=( ( model.ExtendedMetadataIndex.table.c.extended_metadata_id == model.ExtendedMetadata.table.c.id ) ) ) -) ) - - -mapper( model.LibraryInfoAssociation, model.LibraryInfoAssociation.table, properties=dict( - library=relation( model.Library, + role=relation(model.Role) +)) + +mapper(model.GroupRoleAssociation, model.GroupRoleAssociation.table, properties=dict( + group=relation(model.Group, backref="roles"), + role=relation(model.Role) +)) + +mapper(model.Quota, model.Quota.table, properties=dict( + users=relation(model.UserQuotaAssociation), + groups=relation(model.GroupQuotaAssociation) +)) + +mapper(model.UserQuotaAssociation, model.UserQuotaAssociation.table, properties=dict( + user=relation(model.User, backref="quotas"), + quota=relation(model.Quota) +)) + +mapper(model.GroupQuotaAssociation, model.GroupQuotaAssociation.table, properties=dict( + group=relation(model.Group, backref="quotas"), + quota=relation(model.Quota) +)) + +mapper(model.DefaultQuotaAssociation, model.DefaultQuotaAssociation.table, properties=dict( + quota=relation(model.Quota, backref="default") +)) + +mapper(model.DatasetPermissions, model.DatasetPermissions.table, properties=dict( + dataset=relation(model.Dataset, backref="actions"), + role=relation(model.Role, backref="dataset_actions") +)) + +mapper(model.LibraryPermissions, model.LibraryPermissions.table, properties=dict( + library=relation(model.Library, backref="actions"), + role=relation(model.Role, backref="library_actions") +)) + +mapper(model.LibraryFolderPermissions, model.LibraryFolderPermissions.table, properties=dict( + folder=relation(model.LibraryFolder, backref="actions"), + role=relation(model.Role, backref="library_folder_actions") +)) + +mapper(model.LibraryDatasetPermissions, model.LibraryDatasetPermissions.table, properties=dict( + library_dataset=relation(model.LibraryDataset, backref="actions"), + role=relation(model.Role, backref="library_dataset_actions") +)) + +mapper(model.LibraryDatasetDatasetAssociationPermissions, model.LibraryDatasetDatasetAssociationPermissions.table, properties=dict( + library_dataset_dataset_association=relation(model.LibraryDatasetDatasetAssociation, backref="actions"), + role=relation(model.Role, backref="library_dataset_dataset_actions") +)) + +mapper(model.Library, model.Library.table, properties=dict( + root_folder=relation(model.LibraryFolder, backref=backref("library_root")) +)) + +mapper(model.ExtendedMetadata, model.ExtendedMetadata.table, properties=dict( + children=relation(model.ExtendedMetadataIndex, + primaryjoin=(model.ExtendedMetadataIndex.table.c.extended_metadata_id == model.ExtendedMetadata.table.c.id), + backref=backref("parent", + primaryjoin=(model.ExtendedMetadataIndex.table.c.extended_metadata_id == model.ExtendedMetadata.table.c.id))) +)) + +mapper(model.ExtendedMetadataIndex, model.ExtendedMetadataIndex.table, properties=dict( + extended_metadata=relation(model.ExtendedMetadata, + primaryjoin=((model.ExtendedMetadataIndex.table.c.extended_metadata_id == model.ExtendedMetadata.table.c.id))) +)) + + +mapper(model.LibraryInfoAssociation, model.LibraryInfoAssociation.table, properties=dict( + library=relation(model.Library, primaryjoin=( - ( model.LibraryInfoAssociation.table.c.library_id == model.Library.table.c.id ) & - ( not_( model.LibraryInfoAssociation.table.c.deleted ) ) + (model.LibraryInfoAssociation.table.c.library_id == model.Library.table.c.id) & + (not_(model.LibraryInfoAssociation.table.c.deleted)) ), - backref="info_association" ), - template=relation( model.FormDefinition, - primaryjoin=( model.LibraryInfoAssociation.table.c.form_definition_id == model.FormDefinition.table.c.id ) ), - info=relation( model.FormValues, - primaryjoin=( model.LibraryInfoAssociation.table.c.form_values_id == model.FormValues.table.c.id ) ) -) ) - -mapper( model.LibraryFolder, model.LibraryFolder.table, properties=dict( - folders=relation( model.LibraryFolder, - primaryjoin=( model.LibraryFolder.table.c.parent_id == model.LibraryFolder.table.c.id ), - order_by=asc( model.LibraryFolder.table.c.name ), - backref=backref( "parent", - primaryjoin=( model.LibraryFolder.table.c.parent_id == model.LibraryFolder.table.c.id ), - remote_side=[model.LibraryFolder.table.c.id] ) ), - active_folders=relation( model.LibraryFolder, + backref="info_association"), + template=relation(model.FormDefinition, + primaryjoin=(model.LibraryInfoAssociation.table.c.form_definition_id == model.FormDefinition.table.c.id)), + info=relation(model.FormValues, + primaryjoin=(model.LibraryInfoAssociation.table.c.form_values_id == model.FormValues.table.c.id)) +)) + +mapper(model.LibraryFolder, model.LibraryFolder.table, properties=dict( + folders=relation(model.LibraryFolder, + primaryjoin=(model.LibraryFolder.table.c.parent_id == model.LibraryFolder.table.c.id), + order_by=asc(model.LibraryFolder.table.c.name), + backref=backref("parent", + primaryjoin=(model.LibraryFolder.table.c.parent_id == model.LibraryFolder.table.c.id), + remote_side=[model.LibraryFolder.table.c.id])), + active_folders=relation(model.LibraryFolder, primaryjoin=( - ( model.LibraryFolder.table.c.parent_id == model.LibraryFolder.table.c.id ) & - ( not_( model.LibraryFolder.table.c.deleted ) ) + (model.LibraryFolder.table.c.parent_id == model.LibraryFolder.table.c.id) & + (not_(model.LibraryFolder.table.c.deleted)) ), - order_by=asc( model.LibraryFolder.table.c.name ), + order_by=asc(model.LibraryFolder.table.c.name), # """sqlalchemy.exc.ArgumentError: Error creating eager relationship 'active_folders' # on parent class '' to child class '': # Cant use eager loading on a self referential relationship.""" lazy=True, - viewonly=True ), - datasets=relation( model.LibraryDataset, - primaryjoin=( ( model.LibraryDataset.table.c.folder_id == model.LibraryFolder.table.c.id ) ), - order_by=asc( model.LibraryDataset.table.c._name ), + viewonly=True), + datasets=relation(model.LibraryDataset, + primaryjoin=((model.LibraryDataset.table.c.folder_id == model.LibraryFolder.table.c.id)), + order_by=asc(model.LibraryDataset.table.c._name), lazy=True, - viewonly=True ), - active_datasets=relation( model.LibraryDataset, + viewonly=True), + active_datasets=relation(model.LibraryDataset, primaryjoin=( - ( model.LibraryDataset.table.c.folder_id == model.LibraryFolder.table.c.id ) & - ( not_( model.LibraryDataset.table.c.deleted ) ) + (model.LibraryDataset.table.c.folder_id == model.LibraryFolder.table.c.id) & + (not_(model.LibraryDataset.table.c.deleted)) ), - order_by=asc( model.LibraryDataset.table.c._name ), + order_by=asc(model.LibraryDataset.table.c._name), lazy=True, - viewonly=True ) -) ) + viewonly=True) +)) -mapper( model.LibraryFolderInfoAssociation, model.LibraryFolderInfoAssociation.table, properties=dict( - folder=relation( model.LibraryFolder, +mapper(model.LibraryFolderInfoAssociation, model.LibraryFolderInfoAssociation.table, properties=dict( + folder=relation(model.LibraryFolder, primaryjoin=( - ( model.LibraryFolderInfoAssociation.table.c.library_folder_id == model.LibraryFolder.table.c.id ) & - ( not_( model.LibraryFolderInfoAssociation.table.c.deleted ) ) + (model.LibraryFolderInfoAssociation.table.c.library_folder_id == model.LibraryFolder.table.c.id) & + (not_(model.LibraryFolderInfoAssociation.table.c.deleted)) ), - backref="info_association" ), - template=relation( model.FormDefinition, - primaryjoin=( model.LibraryFolderInfoAssociation.table.c.form_definition_id == model.FormDefinition.table.c.id ) ), - info=relation( model.FormValues, - primaryjoin=( model.LibraryFolderInfoAssociation.table.c.form_values_id == model.FormValues.table.c.id ) ) -) ) - -mapper( model.LibraryDataset, model.LibraryDataset.table, properties=dict( - folder=relation( model.LibraryFolder ), - library_dataset_dataset_association=relation( model.LibraryDatasetDatasetAssociation, - primaryjoin=( model.LibraryDataset.table.c.library_dataset_dataset_association_id == - model.LibraryDatasetDatasetAssociation.table.c.id ) ), - expired_datasets=relation( model.LibraryDatasetDatasetAssociation, - foreign_keys=[model.LibraryDataset.table.c.id, model.LibraryDataset.table.c.library_dataset_dataset_association_id ], + backref="info_association"), + template=relation(model.FormDefinition, + primaryjoin=(model.LibraryFolderInfoAssociation.table.c.form_definition_id == model.FormDefinition.table.c.id)), + info=relation(model.FormValues, + primaryjoin=(model.LibraryFolderInfoAssociation.table.c.form_values_id == model.FormValues.table.c.id)) +)) + +mapper(model.LibraryDataset, model.LibraryDataset.table, properties=dict( + folder=relation(model.LibraryFolder), + library_dataset_dataset_association=relation(model.LibraryDatasetDatasetAssociation, + primaryjoin=(model.LibraryDataset.table.c.library_dataset_dataset_association_id == + model.LibraryDatasetDatasetAssociation.table.c.id)), + expired_datasets=relation(model.LibraryDatasetDatasetAssociation, + foreign_keys=[model.LibraryDataset.table.c.id, model.LibraryDataset.table.c.library_dataset_dataset_association_id], primaryjoin=( - ( model.LibraryDataset.table.c.id == model.LibraryDatasetDatasetAssociation.table.c.library_dataset_id ) & - ( not_( model.LibraryDataset.table.c.library_dataset_dataset_association_id == - model.LibraryDatasetDatasetAssociation.table.c.id ) ) + (model.LibraryDataset.table.c.id == model.LibraryDatasetDatasetAssociation.table.c.library_dataset_id) & + (not_(model.LibraryDataset.table.c.library_dataset_dataset_association_id == + model.LibraryDatasetDatasetAssociation.table.c.id)) ), viewonly=True, - uselist=True ) -) ) + uselist=True) +)) -mapper( model.LibraryDatasetDatasetAssociation, model.LibraryDatasetDatasetAssociation.table, properties=dict( - dataset=relation( model.Dataset ), - library_dataset=relation( model.LibraryDataset, - primaryjoin=( model.LibraryDatasetDatasetAssociation.table.c.library_dataset_id == model.LibraryDataset.table.c.id ) ), +mapper(model.LibraryDatasetDatasetAssociation, model.LibraryDatasetDatasetAssociation.table, properties=dict( + dataset=relation(model.Dataset), + library_dataset=relation(model.LibraryDataset, + primaryjoin=(model.LibraryDatasetDatasetAssociation.table.c.library_dataset_id == model.LibraryDataset.table.c.id)), # user=relation( model.User.mapper ), - user=relation( model.User ), - copied_from_library_dataset_dataset_association=relation( model.LibraryDatasetDatasetAssociation, - primaryjoin=( model.LibraryDatasetDatasetAssociation.table.c.copied_from_library_dataset_dataset_association_id == - model.LibraryDatasetDatasetAssociation.table.c.id ), + user=relation(model.User), + copied_from_library_dataset_dataset_association=relation(model.LibraryDatasetDatasetAssociation, + primaryjoin=(model.LibraryDatasetDatasetAssociation.table.c.copied_from_library_dataset_dataset_association_id == + model.LibraryDatasetDatasetAssociation.table.c.id), remote_side=[model.LibraryDatasetDatasetAssociation.table.c.id], - uselist=False ), - copied_to_library_dataset_dataset_associations=relation( model.LibraryDatasetDatasetAssociation, - primaryjoin=( model.LibraryDatasetDatasetAssociation.table.c.copied_from_library_dataset_dataset_association_id == - model.LibraryDatasetDatasetAssociation.table.c.id ) ), - copied_from_history_dataset_association=relation( model.HistoryDatasetAssociation, - primaryjoin=( model.LibraryDatasetDatasetAssociation.table.c.copied_from_history_dataset_association_id == - model.HistoryDatasetAssociation.table.c.id ), - uselist=False ), - copied_to_history_dataset_associations=relation( model.HistoryDatasetAssociation, - primaryjoin=( model.HistoryDatasetAssociation.table.c.copied_from_library_dataset_dataset_association_id == - model.LibraryDatasetDatasetAssociation.table.c.id ) ), - implicitly_converted_datasets=relation( model.ImplicitlyConvertedDatasetAssociation, - primaryjoin=( model.ImplicitlyConvertedDatasetAssociation.table.c.ldda_parent_id == - model.LibraryDatasetDatasetAssociation.table.c.id ) ), - children=relation( model.LibraryDatasetDatasetAssociation, - primaryjoin=( model.LibraryDatasetDatasetAssociation.table.c.parent_id == - model.LibraryDatasetDatasetAssociation.table.c.id ), - backref=backref( "parent", - primaryjoin=( model.LibraryDatasetDatasetAssociation.table.c.parent_id == - model.LibraryDatasetDatasetAssociation.table.c.id ), - remote_side=[model.LibraryDatasetDatasetAssociation.table.c.id] ) ), - visible_children=relation( model.LibraryDatasetDatasetAssociation, + uselist=False), + copied_to_library_dataset_dataset_associations=relation(model.LibraryDatasetDatasetAssociation, + primaryjoin=(model.LibraryDatasetDatasetAssociation.table.c.copied_from_library_dataset_dataset_association_id == + model.LibraryDatasetDatasetAssociation.table.c.id)), + copied_from_history_dataset_association=relation(model.HistoryDatasetAssociation, + primaryjoin=(model.LibraryDatasetDatasetAssociation.table.c.copied_from_history_dataset_association_id == + model.HistoryDatasetAssociation.table.c.id), + uselist=False), + copied_to_history_dataset_associations=relation(model.HistoryDatasetAssociation, + primaryjoin=(model.HistoryDatasetAssociation.table.c.copied_from_library_dataset_dataset_association_id == + model.LibraryDatasetDatasetAssociation.table.c.id)), + implicitly_converted_datasets=relation(model.ImplicitlyConvertedDatasetAssociation, + primaryjoin=(model.ImplicitlyConvertedDatasetAssociation.table.c.ldda_parent_id == + model.LibraryDatasetDatasetAssociation.table.c.id)), + children=relation(model.LibraryDatasetDatasetAssociation, + primaryjoin=(model.LibraryDatasetDatasetAssociation.table.c.parent_id == + model.LibraryDatasetDatasetAssociation.table.c.id), + backref=backref("parent", + primaryjoin=(model.LibraryDatasetDatasetAssociation.table.c.parent_id == + model.LibraryDatasetDatasetAssociation.table.c.id), + remote_side=[model.LibraryDatasetDatasetAssociation.table.c.id])), + visible_children=relation(model.LibraryDatasetDatasetAssociation, primaryjoin=( - ( model.LibraryDatasetDatasetAssociation.table.c.parent_id == model.LibraryDatasetDatasetAssociation.table.c.id ) & - ( model.LibraryDatasetDatasetAssociation.table.c.visible == true() ) + (model.LibraryDatasetDatasetAssociation.table.c.parent_id == model.LibraryDatasetDatasetAssociation.table.c.id) & + (model.LibraryDatasetDatasetAssociation.table.c.visible == true()) ), - remote_side=[model.LibraryDatasetDatasetAssociation.table.c.id] ), - extended_metadata=relation( model.ExtendedMetadata, - primaryjoin=( ( model.LibraryDatasetDatasetAssociation.table.c.extended_metadata_id == model.ExtendedMetadata.table.c.id ) ) + remote_side=[model.LibraryDatasetDatasetAssociation.table.c.id]), + tags=relation(model.LibraryDatasetDatasetAssociationTagAssociation, + order_by=model.LibraryDatasetDatasetAssociationTagAssociation.table.c.id, + backref='history_tag_associations'), + extended_metadata=relation(model.ExtendedMetadata, + primaryjoin=((model.LibraryDatasetDatasetAssociation.table.c.extended_metadata_id == model.ExtendedMetadata.table.c.id)) ), - _metadata=deferred( model.LibraryDatasetDatasetAssociation.table.c._metadata ) -) ) + _metadata=deferred(model.LibraryDatasetDatasetAssociation.table.c._metadata) +)) -mapper( model.LibraryDatasetDatasetInfoAssociation, model.LibraryDatasetDatasetInfoAssociation.table, properties=dict( - library_dataset_dataset_association=relation( model.LibraryDatasetDatasetAssociation, +mapper(model.LibraryDatasetDatasetInfoAssociation, model.LibraryDatasetDatasetInfoAssociation.table, properties=dict( + library_dataset_dataset_association=relation(model.LibraryDatasetDatasetAssociation, primaryjoin=( - ( model.LibraryDatasetDatasetInfoAssociation.table.c.library_dataset_dataset_association_id == - model.LibraryDatasetDatasetAssociation.table.c.id ) & - ( not_( model.LibraryDatasetDatasetInfoAssociation.table.c.deleted ) ) + (model.LibraryDatasetDatasetInfoAssociation.table.c.library_dataset_dataset_association_id == + model.LibraryDatasetDatasetAssociation.table.c.id) & + (not_(model.LibraryDatasetDatasetInfoAssociation.table.c.deleted)) ), - backref="info_association" ), - template=relation( model.FormDefinition, - primaryjoin=( model.LibraryDatasetDatasetInfoAssociation.table.c.form_definition_id == model.FormDefinition.table.c.id ) ), - info=relation( model.FormValues, - primaryjoin=( model.LibraryDatasetDatasetInfoAssociation.table.c.form_values_id == model.FormValues.table.c.id ) ) -) ) - -mapper( model.JobToInputDatasetAssociation, model.JobToInputDatasetAssociation.table, properties=dict( - job=relation( model.Job ), - dataset=relation( model.HistoryDatasetAssociation, + backref="info_association"), + template=relation(model.FormDefinition, + primaryjoin=(model.LibraryDatasetDatasetInfoAssociation.table.c.form_definition_id == model.FormDefinition.table.c.id)), + info=relation(model.FormValues, + primaryjoin=(model.LibraryDatasetDatasetInfoAssociation.table.c.form_values_id == model.FormValues.table.c.id)) +)) + +mapper(model.JobToInputDatasetAssociation, model.JobToInputDatasetAssociation.table, properties=dict( + job=relation(model.Job), + dataset=relation(model.HistoryDatasetAssociation, lazy=False, - backref="dependent_jobs" ) -) ) - -mapper( model.JobToOutputDatasetAssociation, model.JobToOutputDatasetAssociation.table, properties=dict( - job=relation( model.Job ), - dataset=relation( model.HistoryDatasetAssociation, - lazy=False ) -) ) - -mapper( model.JobToInputDatasetCollectionAssociation, model.JobToInputDatasetCollectionAssociation.table, properties=dict( - job=relation( model.Job ), - dataset_collection=relation( model.HistoryDatasetCollectionAssociation, - lazy=False ) -) ) - -mapper( model.JobToOutputDatasetCollectionAssociation, model.JobToOutputDatasetCollectionAssociation.table, properties=dict( - job=relation( model.Job ), - dataset_collection_instance=relation( model.HistoryDatasetCollectionAssociation, + backref="dependent_jobs") +)) + +mapper(model.JobToOutputDatasetAssociation, model.JobToOutputDatasetAssociation.table, properties=dict( + job=relation(model.Job), + dataset=relation(model.HistoryDatasetAssociation, + lazy=False) +)) + +mapper(model.JobToInputDatasetCollectionAssociation, model.JobToInputDatasetCollectionAssociation.table, properties=dict( + job=relation(model.Job), + dataset_collection=relation(model.HistoryDatasetCollectionAssociation, + lazy=False) +)) + +mapper(model.JobToOutputDatasetCollectionAssociation, model.JobToOutputDatasetCollectionAssociation.table, properties=dict( + job=relation(model.Job), + dataset_collection_instance=relation(model.HistoryDatasetCollectionAssociation, lazy=False, - backref="output_dataset_collection_instances" ) -) ) - -mapper( model.JobToImplicitOutputDatasetCollectionAssociation, model.JobToImplicitOutputDatasetCollectionAssociation.table, properties=dict( - job=relation( model.Job ), - dataset_collection=relation( model.DatasetCollection, - backref="output_dataset_collections" ) -) ) - -mapper( model.JobToInputLibraryDatasetAssociation, model.JobToInputLibraryDatasetAssociation.table, properties=dict( - job=relation( model.Job ), - dataset=relation( model.LibraryDatasetDatasetAssociation, + backref="output_dataset_collection_instances") +)) + +mapper(model.JobToImplicitOutputDatasetCollectionAssociation, model.JobToImplicitOutputDatasetCollectionAssociation.table, properties=dict( + job=relation(model.Job), + dataset_collection=relation(model.DatasetCollection, + backref="output_dataset_collections") +)) + +mapper(model.JobToInputLibraryDatasetAssociation, model.JobToInputLibraryDatasetAssociation.table, properties=dict( + job=relation(model.Job), + dataset=relation(model.LibraryDatasetDatasetAssociation, lazy=False, - backref="dependent_jobs" ) -) ) + backref="dependent_jobs") +)) -mapper( model.JobToOutputLibraryDatasetAssociation, model.JobToOutputLibraryDatasetAssociation.table, properties=dict( - job=relation( model.Job ), - dataset=relation( model.LibraryDatasetDatasetAssociation, - lazy=False ) -) ) +mapper(model.JobToOutputLibraryDatasetAssociation, model.JobToOutputLibraryDatasetAssociation.table, properties=dict( + job=relation(model.Job), + dataset=relation(model.LibraryDatasetDatasetAssociation, + lazy=False) +)) -simple_mapping( model.JobStateHistory, - job=relation( model.Job, backref="state_history" ) ) +simple_mapping(model.JobStateHistory, + job=relation(model.Job, backref="state_history")) -simple_mapping( model.JobMetricText, - job=relation( model.Job, backref="text_metrics" ) ) +simple_mapping(model.JobMetricText, + job=relation(model.Job, backref="text_metrics")) -simple_mapping( model.TaskMetricText, - task=relation( model.Task, backref="text_metrics" ) ) +simple_mapping(model.TaskMetricText, + task=relation(model.Task, backref="text_metrics")) -simple_mapping( model.JobMetricNumeric, - job=relation( model.Job, backref="numeric_metrics" ) ) +simple_mapping(model.JobMetricNumeric, + job=relation(model.Job, backref="numeric_metrics")) -simple_mapping( model.TaskMetricNumeric, - task=relation( model.Task, backref="numeric_metrics" ) ) +simple_mapping(model.TaskMetricNumeric, + task=relation(model.Task, backref="numeric_metrics")) -simple_mapping( model.ImplicitlyCreatedDatasetCollectionInput, - input_dataset_collection=relation( model.HistoryDatasetCollectionAssociation, - primaryjoin=( ( model.HistoryDatasetCollectionAssociation.table.c.id == - model.ImplicitlyCreatedDatasetCollectionInput.table.c.input_dataset_collection_id ) ), +simple_mapping(model.ImplicitlyCreatedDatasetCollectionInput, + input_dataset_collection=relation(model.HistoryDatasetCollectionAssociation, + primaryjoin=((model.HistoryDatasetCollectionAssociation.table.c.id == + model.ImplicitlyCreatedDatasetCollectionInput.table.c.input_dataset_collection_id)), # backref="implicitly_created_dataset_collections", ), ) -mapper( model.JobParameter, model.JobParameter.table ) - -mapper( model.JobExternalOutputMetadata, model.JobExternalOutputMetadata.table, properties=dict( - job=relation( model.Job ), - history_dataset_association=relation( model.HistoryDatasetAssociation, lazy=False ), - library_dataset_dataset_association=relation( model.LibraryDatasetDatasetAssociation, lazy=False ) -) ) - -mapper( model.JobExportHistoryArchive, model.JobExportHistoryArchive.table, properties=dict( - job=relation( model.Job ), - history=relation( model.History ), - dataset=relation( model.Dataset ) -) ) - -mapper( model.JobImportHistoryArchive, model.JobImportHistoryArchive.table, properties=dict( - job=relation( model.Job ), - history=relation( model.History ) -) ) - -mapper( model.GenomeIndexToolData, model.GenomeIndexToolData.table, properties=dict( - job=relation( model.Job, backref='job' ), - dataset=relation( model.Dataset ), - user=relation( model.User ), - deferred=relation( model.DeferredJob, backref='deferred_job' ), - transfer=relation( model.TransferJob, backref='transfer_job' ) -) ) +mapper(model.JobParameter, model.JobParameter.table) + +mapper(model.JobExternalOutputMetadata, model.JobExternalOutputMetadata.table, properties=dict( + job=relation(model.Job), + history_dataset_association=relation(model.HistoryDatasetAssociation, lazy=False), + library_dataset_dataset_association=relation(model.LibraryDatasetDatasetAssociation, lazy=False) +)) + +mapper(model.JobExportHistoryArchive, model.JobExportHistoryArchive.table, properties=dict( + job=relation(model.Job), + history=relation(model.History), + dataset=relation(model.Dataset) +)) + +mapper(model.JobImportHistoryArchive, model.JobImportHistoryArchive.table, properties=dict( + job=relation(model.Job), + history=relation(model.History) +)) + +mapper(model.GenomeIndexToolData, model.GenomeIndexToolData.table, properties=dict( + job=relation(model.Job, backref='job'), + dataset=relation(model.Dataset), + user=relation(model.User), + deferred=relation(model.DeferredJob, backref='deferred_job'), + transfer=relation(model.TransferJob, backref='transfer_job') +)) mapper(model.PostJobAction, model.PostJobAction.table, properties=dict( - workflow_step=relation( model.WorkflowStep, + workflow_step=relation(model.WorkflowStep, backref='post_job_actions', - primaryjoin=( model.WorkflowStep.table.c.id == model.PostJobAction.table.c.workflow_step_id ) ) -) ) + primaryjoin=(model.WorkflowStep.table.c.id == model.PostJobAction.table.c.workflow_step_id)) +)) -mapper( model.PostJobActionAssociation, model.PostJobActionAssociation.table, properties=dict( - job=relation( model.Job ), - post_job_action=relation( model.PostJobAction) -) ) +mapper(model.PostJobActionAssociation, model.PostJobActionAssociation.table, properties=dict( + job=relation(model.Job), + post_job_action=relation(model.PostJobAction) +)) -mapper( model.Job, model.Job.table, properties=dict( +mapper(model.Job, model.Job.table, properties=dict( # user=relation( model.User.mapper ), - user=relation( model.User ), - galaxy_session=relation( model.GalaxySession ), - history=relation( model.History ), - library_folder=relation( model.LibraryFolder, lazy=True ), - parameters=relation( model.JobParameter, lazy=True ), - input_datasets=relation( model.JobToInputDatasetAssociation ), - output_datasets=relation( model.JobToOutputDatasetAssociation, lazy=True ), - output_dataset_collection_instances=relation( model.JobToOutputDatasetCollectionAssociation, lazy=True ), - output_dataset_collections=relation( model.JobToImplicitOutputDatasetCollectionAssociation, lazy=True ), - post_job_actions=relation( model.PostJobActionAssociation, lazy=False ), - input_library_datasets=relation( model.JobToInputLibraryDatasetAssociation ), - output_library_datasets=relation( model.JobToOutputLibraryDatasetAssociation, lazy=True ), - external_output_metadata=relation( model.JobExternalOutputMetadata, lazy=True ), - tasks=relation( model.Task ) -) ) - -mapper( model.Task, model.Task.table, properties=dict( - job=relation( model.Job ) -) ) - -mapper( model.DeferredJob, model.DeferredJob.table, properties={} ) - -mapper( model.TransferJob, model.TransferJob.table, properties={} ) - - -simple_mapping( model.DatasetCollection, - elements=relation( model.DatasetCollectionElement, - primaryjoin=( model.DatasetCollection.table.c.id == model.DatasetCollectionElement.table.c.dataset_collection_id ), - remote_side=[ model.DatasetCollectionElement.table.c.dataset_collection_id ], + user=relation(model.User), + galaxy_session=relation(model.GalaxySession), + history=relation(model.History), + library_folder=relation(model.LibraryFolder, lazy=True), + parameters=relation(model.JobParameter, lazy=True), + input_datasets=relation(model.JobToInputDatasetAssociation), + output_datasets=relation(model.JobToOutputDatasetAssociation, lazy=True), + output_dataset_collection_instances=relation(model.JobToOutputDatasetCollectionAssociation, lazy=True), + output_dataset_collections=relation(model.JobToImplicitOutputDatasetCollectionAssociation, lazy=True), + post_job_actions=relation(model.PostJobActionAssociation, lazy=False), + input_library_datasets=relation(model.JobToInputLibraryDatasetAssociation), + output_library_datasets=relation(model.JobToOutputLibraryDatasetAssociation, lazy=True), + external_output_metadata=relation(model.JobExternalOutputMetadata, lazy=True), + tasks=relation(model.Task) +)) + +mapper(model.Task, model.Task.table, properties=dict( + job=relation(model.Job) +)) + +mapper(model.DeferredJob, model.DeferredJob.table, properties={}) + +mapper(model.TransferJob, model.TransferJob.table, properties={}) + + +simple_mapping(model.DatasetCollection, + elements=relation(model.DatasetCollectionElement, + primaryjoin=(model.DatasetCollection.table.c.id == model.DatasetCollectionElement.table.c.dataset_collection_id), + remote_side=[model.DatasetCollectionElement.table.c.dataset_collection_id], backref="collection", - order_by=model.DatasetCollectionElement.table.c.element_index ) + order_by=model.DatasetCollectionElement.table.c.element_index) ) -simple_mapping( model.HistoryDatasetCollectionAssociation, - collection=relation( model.DatasetCollection ), - history=relation( model.History, - backref='dataset_collections' ), - copied_from_history_dataset_collection_association=relation( model.HistoryDatasetCollectionAssociation, - primaryjoin=( model.HistoryDatasetCollectionAssociation.table.c.copied_from_history_dataset_collection_association_id == - model.HistoryDatasetCollectionAssociation.table.c.id ), +simple_mapping(model.HistoryDatasetCollectionAssociation, + collection=relation(model.DatasetCollection), + history=relation(model.History, + backref='dataset_collections'), + copied_from_history_dataset_collection_association=relation(model.HistoryDatasetCollectionAssociation, + primaryjoin=(model.HistoryDatasetCollectionAssociation.table.c.copied_from_history_dataset_collection_association_id == + model.HistoryDatasetCollectionAssociation.table.c.id), remote_side=[model.HistoryDatasetCollectionAssociation.table.c.id], - uselist=False ), - copied_to_history_dataset_collection_associations=relation( model.HistoryDatasetCollectionAssociation, - primaryjoin=( model.HistoryDatasetCollectionAssociation.table.c.copied_from_history_dataset_collection_association_id == - model.HistoryDatasetCollectionAssociation.table.c.id ) ), - implicit_input_collections=relation( model.ImplicitlyCreatedDatasetCollectionInput, - primaryjoin=( ( model.HistoryDatasetCollectionAssociation.table.c.id == - model.ImplicitlyCreatedDatasetCollectionInput.table.c.dataset_collection_id ) ), + uselist=False), + copied_to_history_dataset_collection_associations=relation(model.HistoryDatasetCollectionAssociation, + primaryjoin=(model.HistoryDatasetCollectionAssociation.table.c.copied_from_history_dataset_collection_association_id == + model.HistoryDatasetCollectionAssociation.table.c.id)), + implicit_input_collections=relation(model.ImplicitlyCreatedDatasetCollectionInput, + primaryjoin=((model.HistoryDatasetCollectionAssociation.table.c.id == + model.ImplicitlyCreatedDatasetCollectionInput.table.c.dataset_collection_id)), backref="dataset_collection", ), - tags=relation( model.HistoryDatasetCollectionTagAssociation, + tags=relation(model.HistoryDatasetCollectionTagAssociation, order_by=model.HistoryDatasetCollectionTagAssociation.table.c.id, - backref='dataset_collections' ), - annotations=relation( model.HistoryDatasetCollectionAnnotationAssociation, + backref='dataset_collections'), + annotations=relation(model.HistoryDatasetCollectionAnnotationAssociation, order_by=model.HistoryDatasetCollectionAnnotationAssociation.table.c.id, - backref="dataset_collections" ), - ratings=relation( model.HistoryDatasetCollectionRatingAssociation, + backref="dataset_collections"), + ratings=relation(model.HistoryDatasetCollectionRatingAssociation, order_by=model.HistoryDatasetCollectionRatingAssociation.table.c.id, - backref="dataset_collections" ) + backref="dataset_collections") ) -simple_mapping( model.LibraryDatasetCollectionAssociation, - collection=relation( model.DatasetCollection ), - folder=relation( model.LibraryFolder, - backref='dataset_collections' ), - tags=relation( model.LibraryDatasetCollectionTagAssociation, +simple_mapping(model.LibraryDatasetCollectionAssociation, + collection=relation(model.DatasetCollection), + folder=relation(model.LibraryFolder, + backref='dataset_collections'), + tags=relation(model.LibraryDatasetCollectionTagAssociation, order_by=model.LibraryDatasetCollectionTagAssociation.table.c.id, - backref='dataset_collections' ), - annotations=relation( model.LibraryDatasetCollectionAnnotationAssociation, + backref='dataset_collections'), + annotations=relation(model.LibraryDatasetCollectionAnnotationAssociation, order_by=model.LibraryDatasetCollectionAnnotationAssociation.table.c.id, - backref="dataset_collections" ), - ratings=relation( model.LibraryDatasetCollectionRatingAssociation, + backref="dataset_collections"), + ratings=relation(model.LibraryDatasetCollectionRatingAssociation, order_by=model.LibraryDatasetCollectionRatingAssociation.table.c.id, - backref="dataset_collections" ) ) - -simple_mapping( model.DatasetCollectionElement, - hda=relation( model.HistoryDatasetAssociation, - primaryjoin=( model.DatasetCollectionElement.table.c.hda_id == model.HistoryDatasetAssociation.table.c.id ) ), - ldda=relation( model.LibraryDatasetDatasetAssociation, - primaryjoin=( model.DatasetCollectionElement.table.c.ldda_id == model.LibraryDatasetDatasetAssociation.table.c.id ) ), - child_collection=relation( model.DatasetCollection, - primaryjoin=( model.DatasetCollectionElement.table.c.child_collection_id == model.DatasetCollection.table.c.id ) ) ) - -mapper( model.Event, model.Event.table, properties=dict( - history=relation( model.History ), - galaxy_session=relation( model.GalaxySession ), + backref="dataset_collections")) + +simple_mapping(model.DatasetCollectionElement, + hda=relation(model.HistoryDatasetAssociation, + primaryjoin=(model.DatasetCollectionElement.table.c.hda_id == model.HistoryDatasetAssociation.table.c.id)), + ldda=relation(model.LibraryDatasetDatasetAssociation, + primaryjoin=(model.DatasetCollectionElement.table.c.ldda_id == model.LibraryDatasetDatasetAssociation.table.c.id)), + child_collection=relation(model.DatasetCollection, + primaryjoin=(model.DatasetCollectionElement.table.c.child_collection_id == model.DatasetCollection.table.c.id))) + +mapper(model.Event, model.Event.table, properties=dict( + history=relation(model.History), + galaxy_session=relation(model.GalaxySession), # user=relation( model.User.mapper ) ) ) - user=relation( model.User ) -) ) + user=relation(model.User) +)) -mapper( model.GalaxySession, model.GalaxySession.table, properties=dict( - histories=relation( model.GalaxySessionToHistoryAssociation ), - current_history=relation( model.History ), +mapper(model.GalaxySession, model.GalaxySession.table, properties=dict( + histories=relation(model.GalaxySessionToHistoryAssociation), + current_history=relation(model.History), # user=relation( model.User.mapper ) ) ) - user=relation( model.User ) -) ) + user=relation(model.User) +)) -mapper( model.GalaxySessionToHistoryAssociation, model.GalaxySessionToHistoryAssociation.table, properties=dict( - galaxy_session=relation( model.GalaxySession ), - history=relation( model.History ) -) ) +mapper(model.GalaxySessionToHistoryAssociation, model.GalaxySessionToHistoryAssociation.table, properties=dict( + galaxy_session=relation(model.GalaxySession), + history=relation(model.History) +)) -mapper( model.Workflow, model.Workflow.table, properties=dict( - steps=relation( model.WorkflowStep, +mapper(model.Workflow, model.Workflow.table, properties=dict( + steps=relation(model.WorkflowStep, backref='workflow', - primaryjoin=( ( model.Workflow.table.c.id == model.WorkflowStep.table.c.workflow_id ) ), - order_by=asc( model.WorkflowStep.table.c.order_index ), + primaryjoin=((model.Workflow.table.c.id == model.WorkflowStep.table.c.workflow_id)), + order_by=asc(model.WorkflowStep.table.c.order_index), cascade="all, delete-orphan", - lazy=False ) -) ) + lazy=False) +)) -mapper( model.WorkflowStep, model.WorkflowStep.table, properties=dict( - subworkflow=relation( model.Workflow, - primaryjoin=( ( model.Workflow.table.c.id == model.WorkflowStep.table.c.subworkflow_id ) ), +mapper(model.WorkflowStep, model.WorkflowStep.table, properties=dict( + subworkflow=relation(model.Workflow, + primaryjoin=((model.Workflow.table.c.id == model.WorkflowStep.table.c.subworkflow_id)), backref="parent_workflow_steps"), - tags=relation( model.WorkflowStepTagAssociation, + tags=relation(model.WorkflowStepTagAssociation, order_by=model.WorkflowStepTagAssociation.table.c.id, - backref="workflow_steps" ), - annotations=relation( model.WorkflowStepAnnotationAssociation, + backref="workflow_steps"), + annotations=relation(model.WorkflowStepAnnotationAssociation, order_by=model.WorkflowStepAnnotationAssociation.table.c.id, - backref="workflow_steps" ) -) ) + backref="workflow_steps") +)) -mapper( model.WorkflowOutput, model.WorkflowOutput.table, properties=dict( - workflow_step=relation( model.WorkflowStep, +mapper(model.WorkflowOutput, model.WorkflowOutput.table, properties=dict( + workflow_step=relation(model.WorkflowStep, backref='workflow_outputs', - primaryjoin=( model.WorkflowStep.table.c.id == model.WorkflowOutput.table.c.workflow_step_id ) ) -) ) + primaryjoin=(model.WorkflowStep.table.c.id == model.WorkflowOutput.table.c.workflow_step_id)) +)) -mapper( model.WorkflowStepConnection, model.WorkflowStepConnection.table, properties=dict( - input_step=relation( model.WorkflowStep, +mapper(model.WorkflowStepConnection, model.WorkflowStepConnection.table, properties=dict( + input_step=relation(model.WorkflowStep, backref="input_connections", cascade="all", - primaryjoin=( model.WorkflowStepConnection.table.c.input_step_id == model.WorkflowStep.table.c.id ) ), - input_subworkflow_step=relation( model.WorkflowStep, + primaryjoin=(model.WorkflowStepConnection.table.c.input_step_id == model.WorkflowStep.table.c.id)), + input_subworkflow_step=relation(model.WorkflowStep, backref=backref("parent_workflow_input_connections", uselist=True), - primaryjoin=( model.WorkflowStepConnection.table.c.input_subworkflow_step_id == model.WorkflowStep.table.c.id ), + primaryjoin=(model.WorkflowStepConnection.table.c.input_subworkflow_step_id == model.WorkflowStep.table.c.id), ), - output_step=relation( model.WorkflowStep, + output_step=relation(model.WorkflowStep, backref="output_connections", cascade="all", - primaryjoin=( model.WorkflowStepConnection.table.c.output_step_id == model.WorkflowStep.table.c.id ) ), -) ) + primaryjoin=(model.WorkflowStepConnection.table.c.output_step_id == model.WorkflowStep.table.c.id)), +)) -mapper( model.StoredWorkflow, model.StoredWorkflow.table, properties=dict( - user=relation( model.User, - primaryjoin=( model.User.table.c.id == model.StoredWorkflow.table.c.user_id ), - backref='stored_workflows' ), - workflows=relation( model.Workflow, +mapper(model.StoredWorkflow, model.StoredWorkflow.table, properties=dict( + user=relation(model.User, + primaryjoin=(model.User.table.c.id == model.StoredWorkflow.table.c.user_id), + backref='stored_workflows'), + workflows=relation(model.Workflow, backref='stored_workflow', cascade="all, delete-orphan", - primaryjoin=( model.StoredWorkflow.table.c.id == model.Workflow.table.c.stored_workflow_id ) ), - latest_workflow=relation( model.Workflow, + primaryjoin=(model.StoredWorkflow.table.c.id == model.Workflow.table.c.stored_workflow_id)), + latest_workflow=relation(model.Workflow, post_update=True, - primaryjoin=( model.StoredWorkflow.table.c.latest_workflow_id == model.Workflow.table.c.id ), - lazy=False ), - tags=relation( model.StoredWorkflowTagAssociation, + primaryjoin=(model.StoredWorkflow.table.c.latest_workflow_id == model.Workflow.table.c.id), + lazy=False), + tags=relation(model.StoredWorkflowTagAssociation, order_by=model.StoredWorkflowTagAssociation.table.c.id, - backref="stored_workflows" ), - owner_tags=relation( model.StoredWorkflowTagAssociation, + backref="stored_workflows"), + owner_tags=relation(model.StoredWorkflowTagAssociation, primaryjoin=( - and_( model.StoredWorkflow.table.c.id == model.StoredWorkflowTagAssociation.table.c.stored_workflow_id, - model.StoredWorkflow.table.c.user_id == model.StoredWorkflowTagAssociation.table.c.user_id ) + and_(model.StoredWorkflow.table.c.id == model.StoredWorkflowTagAssociation.table.c.stored_workflow_id, + model.StoredWorkflow.table.c.user_id == model.StoredWorkflowTagAssociation.table.c.user_id) ), - order_by=model.StoredWorkflowTagAssociation.table.c.id ), - annotations=relation( model.StoredWorkflowAnnotationAssociation, + order_by=model.StoredWorkflowTagAssociation.table.c.id), + annotations=relation(model.StoredWorkflowAnnotationAssociation, order_by=model.StoredWorkflowAnnotationAssociation.table.c.id, - backref="stored_workflows" ), - ratings=relation( model.StoredWorkflowRatingAssociation, + backref="stored_workflows"), + ratings=relation(model.StoredWorkflowRatingAssociation, order_by=model.StoredWorkflowRatingAssociation.table.c.id, - backref="stored_workflows" ) -) ) + backref="stored_workflows") +)) # Set up proxy so that # StoredWorkflow.users_shared_with # returns a list of users that workflow is shared with. -model.StoredWorkflow.users_shared_with_dot_users = association_proxy( 'users_shared_with', 'user' ) - -mapper( model.StoredWorkflowUserShareAssociation, model.StoredWorkflowUserShareAssociation.table, properties=dict( - user=relation( model.User, - backref='workflows_shared_by_others' ), - stored_workflow=relation( model.StoredWorkflow, - backref='users_shared_with' ) -) ) - -mapper( model.StoredWorkflowMenuEntry, model.StoredWorkflowMenuEntry.table, properties=dict( - stored_workflow=relation( model.StoredWorkflow ) -) ) - -mapper( model.WorkflowInvocation, model.WorkflowInvocation.table, properties=dict( - history=relation( model.History, backref=backref('workflow_invocations', uselist=True ) ), - input_parameters=relation( model.WorkflowRequestInputParameter ), - step_states=relation( model.WorkflowRequestStepState ), - input_step_parameters=relation( model.WorkflowRequestInputStepParmeter ), - input_datasets=relation( model.WorkflowRequestToInputDatasetAssociation ), - input_dataset_collections=relation( model.WorkflowRequestToInputDatasetCollectionAssociation ), - subworkflow_invocations=relation( model.WorkflowInvocationToSubworkflowInvocationAssociation, - primaryjoin=( ( model.WorkflowInvocationToSubworkflowInvocationAssociation.table.c.workflow_invocation_id == model.WorkflowInvocation.table.c.id ) ), +model.StoredWorkflow.users_shared_with_dot_users = association_proxy('users_shared_with', 'user') + +mapper(model.StoredWorkflowUserShareAssociation, model.StoredWorkflowUserShareAssociation.table, properties=dict( + user=relation(model.User, + backref='workflows_shared_by_others'), + stored_workflow=relation(model.StoredWorkflow, + backref='users_shared_with') +)) + +mapper(model.StoredWorkflowMenuEntry, model.StoredWorkflowMenuEntry.table, properties=dict( + stored_workflow=relation(model.StoredWorkflow) +)) + +mapper(model.WorkflowInvocation, model.WorkflowInvocation.table, properties=dict( + history=relation(model.History, backref=backref('workflow_invocations', uselist=True)), + input_parameters=relation(model.WorkflowRequestInputParameter), + step_states=relation(model.WorkflowRequestStepState), + input_step_parameters=relation(model.WorkflowRequestInputStepParmeter), + input_datasets=relation(model.WorkflowRequestToInputDatasetAssociation), + input_dataset_collections=relation(model.WorkflowRequestToInputDatasetCollectionAssociation), + subworkflow_invocations=relation(model.WorkflowInvocationToSubworkflowInvocationAssociation, + primaryjoin=((model.WorkflowInvocationToSubworkflowInvocationAssociation.table.c.workflow_invocation_id == model.WorkflowInvocation.table.c.id)), backref=backref("parent_workflow_invocation", uselist=False), uselist=True, ), - steps=relation( model.WorkflowInvocationStep, - backref='workflow_invocation' ), - workflow=relation( model.Workflow ) -) ) - -mapper( model.WorkflowInvocationToSubworkflowInvocationAssociation, model.WorkflowInvocationToSubworkflowInvocationAssociation.table, properties=dict( - subworkflow_invocation=relation( model.WorkflowInvocation, - primaryjoin=( ( model.WorkflowInvocationToSubworkflowInvocationAssociation.table.c.subworkflow_invocation_id == model.WorkflowInvocation.table.c.id ) ), + steps=relation(model.WorkflowInvocationStep, + backref='workflow_invocation'), + workflow=relation(model.Workflow) +)) + +mapper(model.WorkflowInvocationToSubworkflowInvocationAssociation, model.WorkflowInvocationToSubworkflowInvocationAssociation.table, properties=dict( + subworkflow_invocation=relation(model.WorkflowInvocation, + primaryjoin=((model.WorkflowInvocationToSubworkflowInvocationAssociation.table.c.subworkflow_invocation_id == model.WorkflowInvocation.table.c.id)), backref="parent_workflow_invocation_association", uselist=False, ), - workflow_step=relation( model.WorkflowStep ), -) ) + workflow_step=relation(model.WorkflowStep), +)) -mapper( model.WorkflowInvocationStep, model.WorkflowInvocationStep.table, properties=dict( - workflow_step=relation( model.WorkflowStep ), - job=relation( model.Job, - backref=backref( 'workflow_invocation_step', - uselist=False ) ) -) ) +mapper(model.WorkflowInvocationStep, model.WorkflowInvocationStep.table, properties=dict( + workflow_step=relation(model.WorkflowStep), + job=relation(model.Job, + backref=backref('workflow_invocation_step', + uselist=False)) +)) -simple_mapping( model.WorkflowRequestInputParameter, - workflow_invocation=relation( model.WorkflowInvocation ) ) +simple_mapping(model.WorkflowRequestInputParameter, + workflow_invocation=relation(model.WorkflowInvocation)) -simple_mapping( model.WorkflowRequestStepState, - workflow_invocation=relation( model.WorkflowInvocation ), - workflow_step=relation( model.WorkflowStep ) ) +simple_mapping(model.WorkflowRequestStepState, + workflow_invocation=relation(model.WorkflowInvocation), + workflow_step=relation(model.WorkflowStep)) -simple_mapping( model.WorkflowRequestInputStepParmeter, - workflow_invocation=relation( model.WorkflowInvocation ), - workflow_step=relation( model.WorkflowStep ) ) +simple_mapping(model.WorkflowRequestInputStepParmeter, + workflow_invocation=relation(model.WorkflowInvocation), + workflow_step=relation(model.WorkflowStep)) -simple_mapping( model.WorkflowRequestToInputDatasetAssociation, - workflow_invocation=relation( model.WorkflowInvocation ), - workflow_step=relation( model.WorkflowStep ), - dataset=relation( model.HistoryDatasetAssociation ) ) +simple_mapping(model.WorkflowRequestToInputDatasetAssociation, + workflow_invocation=relation(model.WorkflowInvocation), + workflow_step=relation(model.WorkflowStep), + dataset=relation(model.HistoryDatasetAssociation)) -simple_mapping( model.WorkflowRequestToInputDatasetCollectionAssociation, - workflow_invocation=relation( model.WorkflowInvocation ), - workflow_step=relation( model.WorkflowStep ), - dataset_collection=relation( model.HistoryDatasetCollectionAssociation ) ) +simple_mapping(model.WorkflowRequestToInputDatasetCollectionAssociation, + workflow_invocation=relation(model.WorkflowInvocation), + workflow_step=relation(model.WorkflowStep), + dataset_collection=relation(model.HistoryDatasetCollectionAssociation)) -mapper( model.MetadataFile, model.MetadataFile.table, properties=dict( - history_dataset=relation( model.HistoryDatasetAssociation ), - library_dataset=relation( model.LibraryDatasetDatasetAssociation ) -) ) +mapper(model.MetadataFile, model.MetadataFile.table, properties=dict( + history_dataset=relation(model.HistoryDatasetAssociation), + library_dataset=relation(model.LibraryDatasetDatasetAssociation) +)) -mapper( model.PageRevision, model.PageRevision.table ) +mapper(model.PageRevision, model.PageRevision.table) -mapper( model.Page, model.Page.table, properties=dict( - user=relation( model.User ), - revisions=relation( model.PageRevision, +mapper(model.Page, model.Page.table, properties=dict( + user=relation(model.User), + revisions=relation(model.PageRevision, backref='page', cascade="all, delete-orphan", - primaryjoin=( model.Page.table.c.id == model.PageRevision.table.c.page_id ) ), - latest_revision=relation( model.PageRevision, + primaryjoin=(model.Page.table.c.id == model.PageRevision.table.c.page_id)), + latest_revision=relation(model.PageRevision, post_update=True, - primaryjoin=( model.Page.table.c.latest_revision_id == model.PageRevision.table.c.id ), - lazy=False ), + primaryjoin=(model.Page.table.c.latest_revision_id == model.PageRevision.table.c.id), + lazy=False), tags=relation(model.PageTagAssociation, order_by=model.PageTagAssociation.table.c.id, - backref="pages" ), - annotations=relation( model.PageAnnotationAssociation, + backref="pages"), + annotations=relation(model.PageAnnotationAssociation, order_by=model.PageAnnotationAssociation.table.c.id, - backref="pages" ), - ratings=relation( model.PageRatingAssociation, + backref="pages"), + ratings=relation(model.PageRatingAssociation, order_by=model.PageRatingAssociation.table.c.id, - backref="pages" ) -) ) + backref="pages") +)) # Set up proxy so that # Page.users_shared_with # returns a list of users that page is shared with. -model.Page.users_shared_with_dot_users = association_proxy( 'users_shared_with', 'user' ) +model.Page.users_shared_with_dot_users = association_proxy('users_shared_with', 'user') -mapper( model.PageUserShareAssociation, model.PageUserShareAssociation.table, - properties=dict( user=relation( model.User, backref='pages_shared_by_others' ), - page=relation( model.Page, backref='users_shared_with' ) ) ) +mapper(model.PageUserShareAssociation, model.PageUserShareAssociation.table, + properties=dict(user=relation(model.User, backref='pages_shared_by_others'), + page=relation(model.Page, backref='users_shared_with'))) -mapper( model.VisualizationRevision, model.VisualizationRevision.table ) +mapper(model.VisualizationRevision, model.VisualizationRevision.table) -mapper( model.Visualization, model.Visualization.table, properties=dict( - user=relation( model.User ), - revisions=relation( model.VisualizationRevision, +mapper(model.Visualization, model.Visualization.table, properties=dict( + user=relation(model.User), + revisions=relation(model.VisualizationRevision, backref='visualization', cascade="all, delete-orphan", - primaryjoin=( model.Visualization.table.c.id == model.VisualizationRevision.table.c.visualization_id ) ), - latest_revision=relation( model.VisualizationRevision, + primaryjoin=(model.Visualization.table.c.id == model.VisualizationRevision.table.c.visualization_id)), + latest_revision=relation(model.VisualizationRevision, post_update=True, - primaryjoin=( model.Visualization.table.c.latest_revision_id == model.VisualizationRevision.table.c.id ), - lazy=False ), - tags=relation( model.VisualizationTagAssociation, + primaryjoin=(model.Visualization.table.c.latest_revision_id == model.VisualizationRevision.table.c.id), + lazy=False), + tags=relation(model.VisualizationTagAssociation, order_by=model.VisualizationTagAssociation.table.c.id, - backref="visualizations" ), - annotations=relation( model.VisualizationAnnotationAssociation, + backref="visualizations"), + annotations=relation(model.VisualizationAnnotationAssociation, order_by=model.VisualizationAnnotationAssociation.table.c.id, - backref="visualizations" ), - ratings=relation( model.VisualizationRatingAssociation, + backref="visualizations"), + ratings=relation(model.VisualizationRatingAssociation, order_by=model.VisualizationRatingAssociation.table.c.id, - backref="visualizations" ) -) ) + backref="visualizations") +)) # Set up proxy so that # Visualization.users_shared_with # returns a list of users that visualization is shared with. -model.Visualization.users_shared_with_dot_users = association_proxy( 'users_shared_with', 'user' ) +model.Visualization.users_shared_with_dot_users = association_proxy('users_shared_with', 'user') -mapper( model.VisualizationUserShareAssociation, model.VisualizationUserShareAssociation.table, properties=dict( - user=relation( model.User, - backref='visualizations_shared_by_others' ), - visualization=relation( model.Visualization, - backref='users_shared_with' ) -) ) +mapper(model.VisualizationUserShareAssociation, model.VisualizationUserShareAssociation.table, properties=dict( + user=relation(model.User, + backref='visualizations_shared_by_others'), + visualization=relation(model.Visualization, + backref='users_shared_with') +)) # Tag tables. -simple_mapping( model.Tag, - children=relation( model.Tag, backref=backref( 'parent', remote_side=[model.Tag.table.c.id] ) ) ) +simple_mapping(model.Tag, + children=relation(model.Tag, backref=backref('parent', remote_side=[model.Tag.table.c.id]))) -def tag_mapping( tag_association_class, backref_name ): - simple_mapping( tag_association_class, tag=relation( model.Tag, backref=backref_name ), user=relation( model.User ) ) +def tag_mapping(tag_association_class, backref_name): + simple_mapping(tag_association_class, tag=relation(model.Tag, backref=backref_name), user=relation(model.User)) -tag_mapping( model.HistoryTagAssociation, "tagged_histories" ) -tag_mapping( model.DatasetTagAssociation, "tagged_datasets" ) -tag_mapping( model.HistoryDatasetAssociationTagAssociation, "tagged_history_dataset_associations" ) -tag_mapping( model.PageTagAssociation, "tagged_pages" ) -tag_mapping( model.StoredWorkflowTagAssociation, "tagged_workflows" ) -tag_mapping( model.WorkflowStepTagAssociation, "tagged_workflow_steps" ) -tag_mapping( model.VisualizationTagAssociation, "tagged_visualizations" ) -tag_mapping( model.HistoryDatasetCollectionTagAssociation, "tagged_history_dataset_collections" ) -tag_mapping( model.LibraryDatasetCollectionTagAssociation, "tagged_library_dataset_collections" ) -tag_mapping( model.ToolTagAssociation, "tagged_tools" ) +tag_mapping(model.HistoryTagAssociation, "tagged_histories") +tag_mapping(model.DatasetTagAssociation, "tagged_datasets") +tag_mapping(model.HistoryDatasetAssociationTagAssociation, "tagged_history_dataset_associations") +tag_mapping(model.LibraryDatasetDatasetAssociationTagAssociation, "tagged_library_dataset_dataset_associations") +tag_mapping(model.PageTagAssociation, "tagged_pages") +tag_mapping(model.StoredWorkflowTagAssociation, "tagged_workflows") +tag_mapping(model.WorkflowStepTagAssociation, "tagged_workflow_steps") +tag_mapping(model.VisualizationTagAssociation, "tagged_visualizations") +tag_mapping(model.HistoryDatasetCollectionTagAssociation, "tagged_history_dataset_collections") +tag_mapping(model.LibraryDatasetCollectionTagAssociation, "tagged_library_dataset_collections") +tag_mapping(model.ToolTagAssociation, "tagged_tools") # Annotation tables. -def annotation_mapping( annotation_class, **kwds ): - kwds = dict( ( key, relation( value ) ) for key, value in kwds.items() ) - simple_mapping( annotation_class, **dict(user=relation( model.User ), **kwds ) ) +def annotation_mapping(annotation_class, **kwds): + kwds = dict((key, relation(value)) for key, value in kwds.items()) + simple_mapping(annotation_class, **dict(user=relation(model.User), **kwds)) -annotation_mapping( model.HistoryAnnotationAssociation, history=model.History ) -annotation_mapping( model.HistoryDatasetAssociationAnnotationAssociation, hda=model.HistoryDatasetAssociation ) -annotation_mapping( model.StoredWorkflowAnnotationAssociation, stored_workflow=model.StoredWorkflow ) -annotation_mapping( model.WorkflowStepAnnotationAssociation, workflow_step=model.WorkflowStep ) -annotation_mapping( model.PageAnnotationAssociation, page=model.Page ) -annotation_mapping( model.VisualizationAnnotationAssociation, visualization=model.Visualization ) -annotation_mapping( model.HistoryDatasetCollectionAnnotationAssociation, - history_dataset_collection=model.HistoryDatasetCollectionAssociation ) -annotation_mapping( model.LibraryDatasetCollectionAnnotationAssociation, - library_dataset_collection=model.LibraryDatasetCollectionAssociation ) +annotation_mapping(model.HistoryAnnotationAssociation, history=model.History) +annotation_mapping(model.HistoryDatasetAssociationAnnotationAssociation, hda=model.HistoryDatasetAssociation) +annotation_mapping(model.StoredWorkflowAnnotationAssociation, stored_workflow=model.StoredWorkflow) +annotation_mapping(model.WorkflowStepAnnotationAssociation, workflow_step=model.WorkflowStep) +annotation_mapping(model.PageAnnotationAssociation, page=model.Page) +annotation_mapping(model.VisualizationAnnotationAssociation, visualization=model.Visualization) +annotation_mapping(model.HistoryDatasetCollectionAnnotationAssociation, + history_dataset_collection=model.HistoryDatasetCollectionAssociation) +annotation_mapping(model.LibraryDatasetCollectionAnnotationAssociation, + library_dataset_collection=model.LibraryDatasetCollectionAssociation) # Rating tables. -def rating_mapping( rating_class, **kwds ): - kwds = dict( ( key, relation( value ) ) for key, value in kwds.items() ) - simple_mapping( rating_class, **dict( user=relation( model.User ), **kwds ) ) +def rating_mapping(rating_class, **kwds): + kwds = dict((key, relation(value)) for key, value in kwds.items()) + simple_mapping(rating_class, **dict(user=relation(model.User), **kwds)) -rating_mapping( model.HistoryRatingAssociation, history=model.History ) -rating_mapping( model.HistoryDatasetAssociationRatingAssociation, hda=model.HistoryDatasetAssociation ) -rating_mapping( model.StoredWorkflowRatingAssociation, stored_workflow=model.StoredWorkflow ) -rating_mapping( model.PageRatingAssociation, page=model.Page ) -rating_mapping( model.VisualizationRatingAssociation, visualizaiton=model.Visualization ) -rating_mapping( model.HistoryDatasetCollectionRatingAssociation, - history_dataset_collection=model.HistoryDatasetCollectionAssociation ) -rating_mapping( model.LibraryDatasetCollectionRatingAssociation, - libary_dataset_collection=model.LibraryDatasetCollectionAssociation ) +rating_mapping(model.HistoryRatingAssociation, history=model.History) +rating_mapping(model.HistoryDatasetAssociationRatingAssociation, hda=model.HistoryDatasetAssociation) +rating_mapping(model.StoredWorkflowRatingAssociation, stored_workflow=model.StoredWorkflow) +rating_mapping(model.PageRatingAssociation, page=model.Page) +rating_mapping(model.VisualizationRatingAssociation, visualizaiton=model.Visualization) +rating_mapping(model.HistoryDatasetCollectionRatingAssociation, + history_dataset_collection=model.HistoryDatasetCollectionAssociation) +rating_mapping(model.LibraryDatasetCollectionRatingAssociation, + libary_dataset_collection=model.LibraryDatasetCollectionAssociation) # Data Manager tables -mapper( model.DataManagerHistoryAssociation, model.DataManagerHistoryAssociation.table, properties=dict( - history=relation( model.History ), - user=relation( model.User, - backref='data_manager_histories' ) -) ) - -mapper( model.DataManagerJobAssociation, model.DataManagerJobAssociation.table, properties=dict( - job=relation( model.Job, - backref=backref( 'data_manager_association', uselist=False ), - uselist=False ) -) ) +mapper(model.DataManagerHistoryAssociation, model.DataManagerHistoryAssociation.table, properties=dict( + history=relation(model.History), + user=relation(model.User, + backref='data_manager_histories') +)) + +mapper(model.DataManagerJobAssociation, model.DataManagerJobAssociation.table, properties=dict( + job=relation(model.Job, + backref=backref('data_manager_association', uselist=False), + uselist=False) +)) # User tables. -mapper( model.UserPreference, model.UserPreference.table, properties={} ) -mapper( model.UserAction, model.UserAction.table, properties=dict( +mapper(model.UserPreference, model.UserPreference.table, properties={}) +mapper(model.UserAction, model.UserAction.table, properties=dict( # user=relation( model.User.mapper ) - user=relation( model.User ) -) ) -mapper( model.APIKeys, model.APIKeys.table, properties={} ) + user=relation(model.User) +)) +mapper(model.APIKeys, model.APIKeys.table, properties={}) # model.HistoryDatasetAssociation.mapper.add_property( "creating_job_associations", # relation( model.JobToOutputDatasetAssociation ) ) # model.LibraryDatasetDatasetAssociation.mapper.add_property( "creating_job_associations", # relation( model.JobToOutputLibraryDatasetAssociation ) ) -class_mapper( model.HistoryDatasetAssociation ).add_property( - "creating_job_associations", relation( model.JobToOutputDatasetAssociation ) ) -class_mapper( model.LibraryDatasetDatasetAssociation ).add_property( - "creating_job_associations", relation( model.JobToOutputLibraryDatasetAssociation ) ) -class_mapper( model.HistoryDatasetCollectionAssociation ).add_property( - "creating_job_associations", relation( model.JobToOutputDatasetCollectionAssociation ) ) +class_mapper(model.HistoryDatasetAssociation).add_property( + "creating_job_associations", relation(model.JobToOutputDatasetAssociation)) +class_mapper(model.LibraryDatasetDatasetAssociation).add_property( + "creating_job_associations", relation(model.JobToOutputLibraryDatasetAssociation)) +class_mapper(model.HistoryDatasetCollectionAssociation).add_property( + "creating_job_associations", relation(model.JobToOutputDatasetCollectionAssociation)) # Helper methods. -def db_next_hid( self, n=1 ): +def db_next_hid(self, n=1): """ db_next_hid( self ) @@ -2536,12 +2550,12 @@ def db_next_hid( self, n=1 ): :rtype: int :returns: the next history id """ - conn = object_session( self ).connection() + conn = object_session(self).connection() table = self.table trans = conn.begin() try: - next_hid = select( [table.c.hid_counter], table.c.id == self.id, for_update=True ).scalar() - table.update( table.c.id == self.id ).execute( hid_counter=( next_hid + n ) ) + next_hid = select([table.c.hid_counter], table.c.id == self.id, for_update=True).scalar() + table.update(table.c.id == self.id).execute(hid_counter=(next_hid + n)) trans.commit() return next_hid except: @@ -2552,18 +2566,18 @@ def db_next_hid( self, n=1 ): model.History._next_hid = db_next_hid -def _workflow_invocation_update( self ): - conn = object_session( self ).connection() +def _workflow_invocation_update(self): + conn = object_session(self).connection() table = self.table now_val = now() - stmt = table.update().values( update_time=now_val ).where( and_( table.c.id == self.id, table.c.update_time < now_val ) ) - conn.execute( stmt ) + stmt = table.update().values(update_time=now_val).where(and_(table.c.id == self.id, table.c.update_time < now_val)) + conn.execute(stmt) model.WorkflowInvocation.update = _workflow_invocation_update -def init( file_path, url, engine_options={}, create_tables=False, map_install_models=False, +def init(file_path, url, engine_options={}, create_tables=False, map_install_models=False, database_query_profiling_proxy=False, object_store=None, trace_logger=None, use_pbkdf2=True, slow_query_log_threshold=0): """Connect mappings to the database""" @@ -2574,7 +2588,7 @@ def init( file_path, url, engine_options={}, create_tables=False, map_install_mo # Use PBKDF2 password hashing? model.User.use_pbkdf2 = use_pbkdf2 # Load the appropriate db module - engine = build_engine( url, engine_options, database_query_profiling_proxy, trace_logger, slow_query_log_threshold ) + engine = build_engine(url, engine_options, database_query_profiling_proxy, trace_logger, slow_query_log_threshold) # Connect the metadata to the database. metadata.bind = engine @@ -2583,9 +2597,9 @@ def init( file_path, url, engine_options={}, create_tables=False, map_install_mo if map_install_models: import galaxy.model.tool_shed_install.mapping # noqa: F401 from galaxy.model import tool_shed_install - model_modules.append( tool_shed_install ) + model_modules.append(tool_shed_install) - result = ModelMapping( model_modules, engine=engine ) + result = ModelMapping(model_modules, engine=engine) # Create tables if needed if create_tables: @@ -2594,5 +2608,5 @@ def init( file_path, url, engine_options={}, create_tables=False, map_install_mo result.create_tables = create_tables # load local galaxy security policy - result.security_agent = GalaxyRBACAgent( result ) + result.security_agent = GalaxyRBACAgent(result) return result diff --git a/lib/galaxy/model/metadata.py b/lib/galaxy/model/metadata.py index 932020ba3aa2..b9730131b333 100644 --- a/lib/galaxy/model/metadata.py +++ b/lib/galaxy/model/metadata.py @@ -30,202 +30,211 @@ STATEMENTS = "__galaxy_statements__" # this is the name of the property in a Datatype class where new metadata spec element Statements are stored -class Statement( object ): +class Statement(object): """ This class inserts its target into a list in the surrounding class. the data.Data class has a metaclass which executes these statements. This is how we shove the metadata element spec into the class. """ - def __init__( self, target ): + + def __init__(self, target): self.target = target - def __call__( self, *args, **kwargs ): + def __call__(self, *args, **kwargs): # get the locals dictionary of the frame object one down in the call stack (i.e. the Datatype class calling MetadataElement) - class_locals = sys._getframe( 1 ).f_locals + class_locals = sys._getframe(1).f_locals # get and set '__galaxy_statments__' to an empty list if not in locals dict - statements = class_locals.setdefault( STATEMENTS, [] ) + statements = class_locals.setdefault(STATEMENTS, []) # add Statement containing info to populate a MetadataElementSpec - statements.append( ( self, args, kwargs ) ) + statements.append((self, args, kwargs)) @classmethod - def process( cls, element ): - for statement, args, kwargs in getattr( element, STATEMENTS, [] ): - statement.target( element, *args, **kwargs ) # statement.target is MetadataElementSpec, element is a Datatype class + def process(cls, element): + for statement, args, kwargs in getattr(element, STATEMENTS, []): + statement.target(element, *args, **kwargs) # statement.target is MetadataElementSpec, element is a Datatype class -class MetadataCollection( object ): +class MetadataCollection(object): """ MetadataCollection is not a collection at all, but rather a proxy to the real metadata which is stored as a Dictionary. This class handles processing the metadata elements when they are set and retrieved, returning default values in cases when metadata is not set. """ - def __init__(self, parent ): + + def __init__(self, parent): self.parent = parent # initialize dict if needed if self.parent._metadata is None: self.parent._metadata = {} - def get_parent( self ): + def get_parent(self): if "_parent" in self.__dict__: return self.__dict__["_parent"]() return None - def set_parent( self, parent ): + def set_parent(self, parent): # use weakref to prevent a circular reference interfering with garbage # collection: hda/lda (parent) <--> MetadataCollection (self) ; needs to be # hashable, so cannot use proxy. - self.__dict__["_parent"] = weakref.ref( parent ) - parent = property( get_parent, set_parent ) + self.__dict__["_parent"] = weakref.ref(parent) + parent = property(get_parent, set_parent) @property - def spec( self ): + def spec(self): return self.parent.datatype.metadata_spec - def __iter__( self ): + def __iter__(self): return self.parent._metadata.__iter__() - def get( self, key, default=None ): + def get(self, key, default=None): try: - return self.__getattr__( key ) or default + return self.__getattr__(key) or default except: return default def items(self): - return iter( [ ( k, self.get( k ) ) for k in self.spec.keys() ] ) + return iter([(k, self.get(k)) for k in self.spec.keys()]) def __str__(self): - return dict( self.items() ).__str__() + return dict(self.items()).__str__() - def __bool__( self ): - return bool( self.parent._metadata ) + def __bool__(self): + return bool(self.parent._metadata) __nonzero__ = __bool__ - def __getattr__( self, name ): + def __getattr__(self, name): if name in self.spec: if name in self.parent._metadata: - return self.spec[name].wrap( self.parent._metadata[name], object_session( self.parent ) ) - return self.spec[name].wrap( self.spec[name].default, object_session( self.parent ) ) + return self.spec[name].wrap(self.parent._metadata[name], object_session(self.parent)) + return self.spec[name].wrap(self.spec[name].default, object_session(self.parent)) if name in self.parent._metadata: return self.parent._metadata[name] - def __setattr__( self, name, value ): + def __setattr__(self, name, value): if name == "parent": - return self.set_parent( value ) + return self.set_parent(value) else: if name in self.spec: - self.parent._metadata[name] = self.spec[name].unwrap( value ) + self.parent._metadata[name] = self.spec[name].unwrap(value) else: self.parent._metadata[name] = value - def remove_key( self, name ): + def remove_key(self, name): if name in self.parent._metadata: del self.parent._metadata[name] else: - log.info( "Attempted to delete invalid key '%s' from MetadataCollection" % name ) + log.info("Attempted to delete invalid key '%s' from MetadataCollection" % name) - def element_is_set( self, name ): - return bool( self.parent._metadata.get( name, False ) ) + def element_is_set(self, name): + return bool(self.parent._metadata.get(name, False)) - def get_html_by_name( self, name, **kwd ): + def get_html_by_name(self, name, **kwd): if name in self.spec: - rval = self.spec[name].param.get_html( value=getattr( self, name ), context=self, **kwd ) + rval = self.spec[name].param.get_html(value=getattr(self, name), context=self, **kwd) if rval is None: return self.spec[name].no_value return rval - def make_dict_copy( self, to_copy ): + def get_metadata_parameter(self, name, **kwd): + if name in self.spec: + html_field = self.spec[name].param.get_html_field(getattr(self, name), self, None, **kwd) + html_field.value = getattr(self, name) + return html_field + + def make_dict_copy(self, to_copy): """Makes a deep copy of input iterable to_copy according to self.spec""" rval = {} for key, value in to_copy.items(): if key in self.spec: - rval[key] = self.spec[key].param.make_copy( value, target_context=self, source_context=to_copy ) + rval[key] = self.spec[key].param.make_copy(value, target_context=self, source_context=to_copy) return rval - def from_JSON_dict( self, filename=None, path_rewriter=None, json_dict=None ): + def from_JSON_dict(self, filename=None, path_rewriter=None, json_dict=None): dataset = self.parent if filename is not None: - log.debug( 'loading metadata from file for: %s %s' % ( dataset.__class__.__name__, dataset.id ) ) - JSONified_dict = json.load( open( filename ) ) + log.debug('loading metadata from file for: %s %s' % (dataset.__class__.__name__, dataset.id)) + JSONified_dict = json.load(open(filename)) elif json_dict is not None: - log.debug( 'loading metadata from dict for: %s %s' % ( dataset.__class__.__name__, dataset.id ) ) - if isinstance( json_dict, string_types ): - JSONified_dict = json.loads( json_dict ) - elif isinstance( json_dict, dict ): + log.debug('loading metadata from dict for: %s %s' % (dataset.__class__.__name__, dataset.id)) + if isinstance(json_dict, string_types): + JSONified_dict = json.loads(json_dict) + elif isinstance(json_dict, dict): JSONified_dict = json_dict else: - raise ValueError( "json_dict must be either a dictionary or a string, got %s." % ( type( json_dict ) ) ) + raise ValueError("json_dict must be either a dictionary or a string, got %s." % (type(json_dict))) else: - raise ValueError( "You must provide either a filename or a json_dict" ) + raise ValueError("You must provide either a filename or a json_dict") for name, spec in self.spec.items(): if name in JSONified_dict: from_ext_kwds = {} - external_value = JSONified_dict[ name ] + external_value = JSONified_dict[name] param = spec.param - if isinstance( param, FileParameter ): - from_ext_kwds[ 'path_rewriter' ] = path_rewriter - dataset._metadata[ name ] = param.from_external_value( external_value, dataset, **from_ext_kwds ) + if isinstance(param, FileParameter): + from_ext_kwds['path_rewriter'] = path_rewriter + dataset._metadata[name] = param.from_external_value(external_value, dataset, **from_ext_kwds) elif name in dataset._metadata: # if the metadata value is not found in our externally set metadata but it has a value in the 'old' # metadata associated with our dataset, we'll delete it from our dataset's metadata dict - del dataset._metadata[ name ] + del dataset._metadata[name] if '__extension__' in JSONified_dict: dataset.extension = JSONified_dict['__extension__'] - def to_JSON_dict( self, filename=None ): + def to_JSON_dict(self, filename=None): # galaxy.model.customtypes.json_encoder.encode() meta_dict = {} dataset_meta_dict = self.parent._metadata for name, spec in self.spec.items(): if name in dataset_meta_dict: - meta_dict[ name ] = spec.param.to_external_value( dataset_meta_dict[ name ] ) + meta_dict[name] = spec.param.to_external_value(dataset_meta_dict[name]) if '__extension__' in dataset_meta_dict: - meta_dict[ '__extension__' ] = dataset_meta_dict['__extension__'] + meta_dict['__extension__'] = dataset_meta_dict['__extension__'] if filename is None: - return json.dumps( meta_dict ) - json.dump( meta_dict, open( filename, 'wb+' ) ) + return json.dumps(meta_dict) + json.dump(meta_dict, open(filename, 'wb+')) - def __getstate__( self ): + def __getstate__(self): # cannot pickle a weakref item (self._parent), when # data._metadata_collection is None, it will be recreated on demand return None -class MetadataSpecCollection( odict ): +class MetadataSpecCollection(odict): """ A simple extension of dict which allows cleaner access to items and allows the values to be iterated over directly as if it were a list. append() is also implemented for simplicity and does not "append". """ - def __init__( self, dict=None ): - odict.__init__( self, dict=None ) - def append( self, item ): + def __init__(self, dict=None): + odict.__init__(self, dict=None) + + def append(self, item): self[item.name] = item - def iter( self ): + def iter(self): return iter(self.values()) - def __getattr__( self, name ): - return self.get( name ) + def __getattr__(self, name): + return self.get(name) - def __repr__( self ): + def __repr__(self): # force elements to draw with __str__ for sphinx-apidoc - return ', '.join([ item.__str__() for item in self.iter() ]) + return ', '.join([item.__str__() for item in self.iter()]) -class MetadataParameter( object ): - def __init__( self, spec ): +class MetadataParameter(object): + def __init__(self, spec): self.spec = spec - def get_html_field( self, value=None, context=None, other_values=None, **kwd ): + def get_html_field(self, value=None, context=None, other_values=None, **kwd): context = context or {} other_values = other_values or {} - return form_builder.TextField( self.spec.name, value=value ) + return form_builder.TextField(self.spec.name, value=value) - def get_html( self, value, context=None, other_values=None, **kwd ): + def get_html(self, value, context=None, other_values=None, **kwd): """ The "context" is simply the metadata collection/bunch holding this piece of metadata. This is passed in to allow for @@ -243,69 +252,70 @@ def get_html( self, value, context=None, other_values=None, **kwd ): checked = False if value: checked = "true" - checkbox = form_builder.CheckboxField( "is_" + self.spec.name, checked=checked ) - return checkbox.get_html() + self.get_html_field( value=value, context=context, other_values=other_values, **kwd ).get_html() + checkbox = form_builder.CheckboxField("is_" + self.spec.name, checked=checked) + return checkbox.get_html() + self.get_html_field(value=value, context=context, other_values=other_values, **kwd).get_html() else: - return self.get_html_field( value=value, context=context, other_values=other_values, **kwd ).get_html() + return self.get_html_field(value=value, context=context, other_values=other_values, **kwd).get_html() - def to_string( self, value ): - return str( value ) + def to_string(self, value): + return str(value) - def to_safe_string( self, value ): - return sanitize_lists_to_string( self.to_string( value ) ) + def to_safe_string(self, value): + return sanitize_lists_to_string(self.to_string(value)) - def make_copy( self, value, target_context=None, source_context=None ): - return copy.deepcopy( value ) + def make_copy(self, value, target_context=None, source_context=None): + return copy.deepcopy(value) @classmethod - def marshal( cls, value ): + def marshal(cls, value): """ This method should/can be overridden to convert the incoming value to whatever type it is supposed to be. """ return value - def validate( self, value ): + def validate(self, value): """ Throw an exception if the value is invalid. """ pass - def unwrap( self, form_value ): + def unwrap(self, form_value): """ Turns a value into its storable form. """ - value = self.marshal( form_value ) - self.validate( value ) + value = self.marshal(form_value) + self.validate(value) return value - def wrap( self, value, session ): + def wrap(self, value, session): """ Turns a value into its usable form. """ return value - def from_external_value( self, value, parent ): + def from_external_value(self, value, parent): """ Turns a value read from an external dict into its value to be pushed directly into the metadata dict. """ return value - def to_external_value( self, value ): + def to_external_value(self, value): """ Turns a value read from a metadata into its value to be pushed directly into the external dict. """ return value -class MetadataElementSpec( object ): +class MetadataElementSpec(object): """ Defines a metadata element and adds it to the metadata_spec (which is a MetadataSpecCollection) of datatype. """ - def __init__( self, datatype, name=None, desc=None, - param=MetadataParameter, default=None, no_value=None, - visible=True, set_in_upload=False, **kwargs ): + + def __init__(self, datatype, name=None, desc=None, + param=MetadataParameter, default=None, no_value=None, + visible=True, set_in_upload=False, **kwargs): self.name = name self.desc = desc or name self.default = default @@ -315,35 +325,35 @@ def __init__( self, datatype, name=None, desc=None, # Catch-all, allows for extra attributes to be set self.__dict__.update(kwargs) # set up param last, as it uses values set above - self.param = param( self ) + self.param = param(self) # add spec element to the spec - datatype.metadata_spec.append( self ) + datatype.metadata_spec.append(self) - def get( self, name, default=None ): + def get(self, name, default=None): return self.__dict__.get(name, default) - def wrap( self, value, session ): + def wrap(self, value, session): """ Turns a stored value into its usable form. """ - return self.param.wrap( value, session ) + return self.param.wrap(value, session) - def unwrap( self, value ): + def unwrap(self, value): """ Turns an incoming value into its storable form. """ - return self.param.unwrap( value ) + return self.param.unwrap(value) - def __str__( self ): + def __str__(self): # TODO??: assuming param is the class of this MetadataElementSpec - add the plain class name for that - spec_dict = dict( param_class=self.param.__class__.__name__ ) - spec_dict.update( self.__dict__ ) - return ( "{name} ({param_class}): {desc}, defaults to '{default}'".format( **spec_dict ) ) + spec_dict = dict(param_class=self.param.__class__.__name__) + spec_dict.update(self.__dict__) + return ("{name} ({param_class}): {desc}, defaults to '{default}'".format(**spec_dict)) # create a statement class that, when called, # will add a new MetadataElementSpec to a class's metadata_spec -MetadataElement = Statement( MetadataElementSpec ) +MetadataElement = Statement(MetadataElementSpec) """ @@ -351,58 +361,58 @@ def __str__( self ): """ -class SelectParameter( MetadataParameter ): - def __init__( self, spec ): - MetadataParameter.__init__( self, spec ) - self.values = self.spec.get( "values" ) - self.multiple = string_as_bool( self.spec.get( "multiple" ) ) +class SelectParameter(MetadataParameter): + def __init__(self, spec): + MetadataParameter.__init__(self, spec) + self.values = self.spec.get("values") + self.multiple = string_as_bool(self.spec.get("multiple")) - def to_string( self, value ): - if value in [ None, [] ]: - return str( self.spec.no_value ) - if not isinstance( value, list ): + def to_string(self, value): + if value in [None, []]: + return str(self.spec.no_value) + if not isinstance(value, list): value = [value] - return ",".join( map( str, value ) ) + return ",".join(map(str, value)) - def get_html_field( self, value=None, context=None, other_values=None, values=None, **kwd ): + def get_html_field(self, value=None, context=None, other_values=None, values=None, **kwd): context = context or {} other_values = other_values or {} - field = form_builder.SelectField( self.spec.name, multiple=self.multiple, display=self.spec.get("display") ) + field = form_builder.SelectField(self.spec.name, multiple=self.multiple, display=self.spec.get("display")) if self.values: value_list = self.values elif values: value_list = values elif value: - value_list = [ ( v, v ) for v in listify( value )] + value_list = [(v, v) for v in listify(value)] else: value_list = [] for val, label in value_list: try: - if ( self.multiple and val in value ) or ( not self.multiple and val == value ): - field.add_option( label, val, selected=True ) + if (self.multiple and val in value) or (not self.multiple and val == value): + field.add_option(label, val, selected=True) else: - field.add_option( label, val, selected=False ) + field.add_option(label, val, selected=False) except TypeError: - field.add_option( val, label, selected=False ) + field.add_option(val, label, selected=False) return field - def get_html( self, value, context=None, other_values=None, values=None, **kwd ): + def get_html(self, value, context=None, other_values=None, values=None, **kwd): context = context or {} other_values = other_values or {} if self.spec.get("readonly"): - if value in [ None, [] ]: - return str( self.spec.no_value ) - return ", ".join( map( str, value ) ) - return MetadataParameter.get_html( self, value, context=context, other_values=other_values, values=values, **kwd ) + if value in [None, []]: + return str(self.spec.no_value) + return ", ".join(map(str, value)) + return MetadataParameter.get_html(self, value, context=context, other_values=other_values, values=values, **kwd) - def wrap( self, value, session ): + def wrap(self, value, session): # do we really need this (wasteful)? - yes because we are not sure that # all existing selects have been stored previously as lists. Also this # will handle the case where defaults/no_values are specified and are # single non-list values. - value = self.marshal( value ) + value = self.marshal(value) if self.multiple: return value elif value: @@ -410,272 +420,272 @@ def wrap( self, value, session ): return None @classmethod - def marshal( cls, value ): + def marshal(cls, value): # Store select as list, even if single item if value is None: return [] - if not isinstance( value, list ): + if not isinstance(value, list): return [value] return value -class DBKeyParameter( SelectParameter ): +class DBKeyParameter(SelectParameter): - def get_html_field( self, value=None, context=None, other_values=None, values=None, **kwd): + def get_html_field(self, value=None, context=None, other_values=None, values=None, **kwd): context = context or {} other_values = other_values or {} try: - values = kwd['trans'].app.genome_builds.get_genome_build_names( kwd['trans'] ) + values = kwd['trans'].app.genome_builds.get_genome_build_names(kwd['trans']) except KeyError: pass - return super(DBKeyParameter, self).get_html_field( value, context, other_values, values, **kwd) + return super(DBKeyParameter, self).get_html_field(value, context, other_values, values, **kwd) - def get_html( self, value=None, context=None, other_values=None, values=None, **kwd): + def get_html(self, value=None, context=None, other_values=None, values=None, **kwd): context = context or {} other_values = other_values or {} try: - values = kwd['trans'].app.genome_builds.get_genome_build_names( kwd['trans'] ) + values = kwd['trans'].app.genome_builds.get_genome_build_names(kwd['trans']) except KeyError: pass - return super(DBKeyParameter, self).get_html( value, context, other_values, values, **kwd) + return super(DBKeyParameter, self).get_html(value, context, other_values, values, **kwd) -class RangeParameter( SelectParameter ): +class RangeParameter(SelectParameter): - def __init__( self, spec ): - SelectParameter.__init__( self, spec ) + def __init__(self, spec): + SelectParameter.__init__(self, spec) # The spec must be set with min and max values - self.min = spec.get( "min" ) or 1 - self.max = spec.get( "max" ) or 1 - self.step = self.spec.get( "step" ) or 1 + self.min = spec.get("min") or 1 + self.max = spec.get("max") or 1 + self.step = self.spec.get("step") or 1 - def get_html_field( self, value=None, context=None, other_values=None, values=None, **kwd ): + def get_html_field(self, value=None, context=None, other_values=None, values=None, **kwd): context = context or {} other_values = other_values or {} if values is None: - values = list(zip( range( self.min, self.max, self.step ), range( self.min, self.max, self.step ) )) - return SelectParameter.get_html_field( self, value=value, context=context, other_values=other_values, values=values, **kwd ) + values = list(zip(range(self.min, self.max, self.step), range(self.min, self.max, self.step))) + return SelectParameter.get_html_field(self, value=value, context=context, other_values=other_values, values=values, **kwd) - def get_html( self, value, context=None, other_values=None, values=None, **kwd ): + def get_html(self, value, context=None, other_values=None, values=None, **kwd): context = context or {} other_values = other_values or {} if values is None: - values = list(zip( range( self.min, self.max, self.step ), range( self.min, self.max, self.step ) )) - return SelectParameter.get_html( self, value, context=context, other_values=other_values, values=values, **kwd ) + values = list(zip(range(self.min, self.max, self.step), range(self.min, self.max, self.step))) + return SelectParameter.get_html(self, value, context=context, other_values=other_values, values=values, **kwd) @classmethod - def marshal( cls, value ): - value = SelectParameter.marshal( value ) - values = [ int(x) for x in value ] + def marshal(cls, value): + value = SelectParameter.marshal(value) + values = [int(x) for x in value] return values -class ColumnParameter( RangeParameter ): +class ColumnParameter(RangeParameter): - def get_html_field( self, value=None, context=None, other_values=None, values=None, **kwd ): + def get_html_field(self, value=None, context=None, other_values=None, values=None, **kwd): context = context or {} other_values = other_values or {} if values is None and context: - column_range = range( 1, ( context.columns or 0 ) + 1, 1 ) - values = list(zip( column_range, column_range )) - return RangeParameter.get_html_field( self, value=value, context=context, other_values=other_values, values=values, **kwd ) + column_range = range(1, (context.columns or 0) + 1, 1) + values = list(zip(column_range, column_range)) + return RangeParameter.get_html_field(self, value=value, context=context, other_values=other_values, values=values, **kwd) - def get_html( self, value, context=None, other_values=None, values=None, **kwd ): + def get_html(self, value, context=None, other_values=None, values=None, **kwd): context = context or {} other_values = other_values or {} if values is None and context: - column_range = range( 1, ( context.columns or 0 ) + 1, 1 ) - values = list(zip( column_range, column_range )) - return RangeParameter.get_html( self, value, context=context, other_values=other_values, values=values, **kwd ) + column_range = range(1, (context.columns or 0) + 1, 1) + values = list(zip(column_range, column_range)) + return RangeParameter.get_html(self, value, context=context, other_values=other_values, values=values, **kwd) -class ColumnTypesParameter( MetadataParameter ): +class ColumnTypesParameter(MetadataParameter): - def to_string( self, value ): - return ",".join( map( str, value ) ) + def to_string(self, value): + return ",".join(map(str, value)) -class ListParameter( MetadataParameter ): +class ListParameter(MetadataParameter): - def to_string( self, value ): - return ",".join( [str(x) for x in value] ) + def to_string(self, value): + return ",".join([str(x) for x in value]) -class DictParameter( MetadataParameter ): +class DictParameter(MetadataParameter): - def to_string( self, value ): - return json.dumps( value ) + def to_string(self, value): + return json.dumps(value) - def to_safe_string( self, value ): + def to_safe_string(self, value): # We do not sanitize json dicts - return safe_dumps( value ) + return safe_dumps(value) -class PythonObjectParameter( MetadataParameter ): +class PythonObjectParameter(MetadataParameter): - def to_string( self, value ): + def to_string(self, value): if not value: - return self.spec._to_string( self.spec.no_value ) - return self.spec._to_string( value ) + return self.spec._to_string(self.spec.no_value) + return self.spec._to_string(value) - def get_html_field( self, value=None, context=None, other_values=None, **kwd ): + def get_html_field(self, value=None, context=None, other_values=None, **kwd): context = context or {} other_values = other_values or {} - return form_builder.TextField( self.spec.name, value=self._to_string( value ) ) + return form_builder.TextField(self.spec.name, value=self._to_string(value)) - def get_html( self, value=None, context=None, other_values=None, **kwd ): + def get_html(self, value=None, context=None, other_values=None, **kwd): context = context or {} other_values = other_values or {} - return str( self ) + return str(self) @classmethod - def marshal( cls, value ): + def marshal(cls, value): return value -class FileParameter( MetadataParameter ): +class FileParameter(MetadataParameter): - def to_string( self, value ): + def to_string(self, value): if not value: - return str( self.spec.no_value ) + return str(self.spec.no_value) return value.file_name - def to_safe_string( self, value ): + def to_safe_string(self, value): # We do not sanitize file names - return self.to_string( value ) + return self.to_string(value) - def get_html_field( self, value=None, context=None, other_values=None, **kwd ): + def get_html_field(self, value=None, context=None, other_values=None, **kwd): context = context or {} other_values = other_values or {} - return form_builder.TextField( self.spec.name, value=str( value.id ) ) + return form_builder.TextField(self.spec.name, value=str(value.id)) - def get_html( self, value=None, context=None, other_values=None, **kwd ): + def get_html(self, value=None, context=None, other_values=None, **kwd): context = context or {} other_values = other_values or {} return "
    No display available for Metadata Files
    " - def wrap( self, value, session ): + def wrap(self, value, session): if value is None: return None - if isinstance( value, galaxy.model.MetadataFile ) or isinstance( value, MetadataTempFile ): + if isinstance(value, galaxy.model.MetadataFile) or isinstance(value, MetadataTempFile): return value - mf = session.query( galaxy.model.MetadataFile ).get( value ) + mf = session.query(galaxy.model.MetadataFile).get(value) return mf - def make_copy( self, value, target_context, source_context ): - value = self.wrap( value, object_session( target_context.parent ) ) + def make_copy(self, value, target_context, source_context): + value = self.wrap(value, object_session(target_context.parent)) if value: - new_value = galaxy.model.MetadataFile( dataset=target_context.parent, name=self.spec.name ) - object_session( target_context.parent ).add( new_value ) - object_session( target_context.parent ).flush() - shutil.copy( value.file_name, new_value.file_name ) - return self.unwrap( new_value ) + new_value = galaxy.model.MetadataFile(dataset=target_context.parent, name=self.spec.name) + object_session(target_context.parent).add(new_value) + object_session(target_context.parent).flush() + shutil.copy(value.file_name, new_value.file_name) + return self.unwrap(new_value) return None @classmethod - def marshal( cls, value ): - if isinstance( value, galaxy.model.MetadataFile ): + def marshal(cls, value): + if isinstance(value, galaxy.model.MetadataFile): value = value.id return value - def from_external_value( self, value, parent, path_rewriter=None ): + def from_external_value(self, value, parent, path_rewriter=None): """ Turns a value read from a external dict into its value to be pushed directly into the metadata dict. """ - if MetadataTempFile.is_JSONified_value( value ): - value = MetadataTempFile.from_JSON( value ) - if isinstance( value, MetadataTempFile ): - mf = parent.metadata.get( self.spec.name, None) + if MetadataTempFile.is_JSONified_value(value): + value = MetadataTempFile.from_JSON(value) + if isinstance(value, MetadataTempFile): + mf = parent.metadata.get(self.spec.name, None) if mf is None: - mf = self.new_file( dataset=parent, **value.kwds ) + mf = self.new_file(dataset=parent, **value.kwds) # Ensure the metadata file gets updated with content file_name = value.file_name if path_rewriter: # Job may have run with a different (non-local) tmp/working # directory. Correct. - file_name = path_rewriter( file_name ) - parent.dataset.object_store.update_from_file( mf, - file_name=file_name, - extra_dir='_metadata_files', - extra_dir_at_root=True, - alt_name=os.path.basename(mf.file_name) ) - os.unlink( file_name ) + file_name = path_rewriter(file_name) + parent.dataset.object_store.update_from_file(mf, + file_name=file_name, + extra_dir='_metadata_files', + extra_dir_at_root=True, + alt_name=os.path.basename(mf.file_name)) + os.unlink(file_name) value = mf.id return value - def to_external_value( self, value ): + def to_external_value(self, value): """ Turns a value read from a metadata into its value to be pushed directly into the external dict. """ - if isinstance( value, galaxy.model.MetadataFile ): + if isinstance(value, galaxy.model.MetadataFile): value = value.id - elif isinstance( value, MetadataTempFile ): - value = MetadataTempFile.to_JSON( value ) + elif isinstance(value, MetadataTempFile): + value = MetadataTempFile.to_JSON(value) return value - def new_file( self, dataset=None, **kwds ): - if object_session( dataset ): - mf = galaxy.model.MetadataFile( name=self.spec.name, dataset=dataset, **kwds ) - object_session( dataset ).add( mf ) - object_session( dataset ).flush() # flush to assign id + def new_file(self, dataset=None, **kwds): + if object_session(dataset): + mf = galaxy.model.MetadataFile(name=self.spec.name, dataset=dataset, **kwds) + object_session(dataset).add(mf) + object_session(dataset).flush() # flush to assign id return mf else: # we need to make a tmp file that is accessable to the head node, # we will be copying its contents into the MetadataFile objects filename after restoring from JSON # we do not include 'dataset' in the kwds passed, as from_JSON_value() will handle this for us - return MetadataTempFile( **kwds ) + return MetadataTempFile(**kwds) # This class is used when a database file connection is not available -class MetadataTempFile( object ): +class MetadataTempFile(object): tmp_dir = 'database/tmp' # this should be overwritten as necessary in calling scripts - def __init__( self, **kwds ): + def __init__(self, **kwds): self.kwds = kwds self._filename = None @property - def file_name( self ): + def file_name(self): if self._filename is None: # we need to create a tmp file, accessable across all nodes/heads, save the name, and return it - self._filename = abspath( tempfile.NamedTemporaryFile( dir=self.tmp_dir, prefix="metadata_temp_file_" ).name ) - open( self._filename, 'wb+' ) # create an empty file, so it can't be reused using tempfile + self._filename = abspath(tempfile.NamedTemporaryFile(dir=self.tmp_dir, prefix="metadata_temp_file_").name) + open(self._filename, 'wb+') # create an empty file, so it can't be reused using tempfile return self._filename - def to_JSON( self ): - return { '__class__': self.__class__.__name__, - 'filename': self.file_name, - 'kwds': self.kwds } + def to_JSON(self): + return {'__class__': self.__class__.__name__, + 'filename': self.file_name, + 'kwds': self.kwds} @classmethod - def from_JSON( cls, json_dict ): + def from_JSON(cls, json_dict): # need to ensure our keywords are not unicode - rval = cls( **stringify_dictionary_keys( json_dict['kwds'] ) ) + rval = cls(**stringify_dictionary_keys(json_dict['kwds'])) rval._filename = json_dict['filename'] return rval @classmethod - def is_JSONified_value( cls, value ): - return ( isinstance( value, dict ) and value.get( '__class__', None ) == cls.__name__ ) + def is_JSONified_value(cls, value): + return (isinstance(value, dict) and value.get('__class__', None) == cls.__name__) @classmethod - def cleanup_from_JSON_dict_filename( cls, filename ): + def cleanup_from_JSON_dict_filename(cls, filename): try: - for key, value in json.load( open( filename ) ).items(): - if cls.is_JSONified_value( value ): - value = cls.from_JSON( value ) - if isinstance( value, cls ) and os.path.exists( value.file_name ): - log.debug( 'Cleaning up abandoned MetadataTempFile file: %s' % value.file_name ) - os.unlink( value.file_name ) + for key, value in json.load(open(filename)).items(): + if cls.is_JSONified_value(value): + value = cls.from_JSON(value) + if isinstance(value, cls) and os.path.exists(value.file_name): + log.debug('Cleaning up abandoned MetadataTempFile file: %s' % value.file_name) + os.unlink(value.file_name) except Exception as e: - log.debug( 'Failed to cleanup MetadataTempFile temp files from %s: %s' % ( filename, e ) ) + log.debug('Failed to cleanup MetadataTempFile temp files from %s: %s' % (filename, e)) -class JobExternalOutputMetadataWrapper( object ): +class JobExternalOutputMetadataWrapper(object): """ Class with methods allowing set_meta() to be called externally to the Galaxy head. @@ -686,46 +696,46 @@ class JobExternalOutputMetadataWrapper( object ): JSONified as well) """ - def __init__( self, job ): + def __init__(self, job): self.job_id = job.id - def get_output_filenames_by_dataset( self, dataset, sa_session ): - if isinstance( dataset, galaxy.model.HistoryDatasetAssociation ): - return sa_session.query( galaxy.model.JobExternalOutputMetadata ) \ - .filter_by( job_id=self.job_id, - history_dataset_association_id=dataset.id, - is_valid=True ) \ + def get_output_filenames_by_dataset(self, dataset, sa_session): + if isinstance(dataset, galaxy.model.HistoryDatasetAssociation): + return sa_session.query(galaxy.model.JobExternalOutputMetadata) \ + .filter_by(job_id=self.job_id, + history_dataset_association_id=dataset.id, + is_valid=True) \ .first() # there should only be one or None - elif isinstance( dataset, galaxy.model.LibraryDatasetDatasetAssociation ): - return sa_session.query( galaxy.model.JobExternalOutputMetadata ) \ - .filter_by( job_id=self.job_id, - library_dataset_dataset_association_id=dataset.id, - is_valid=True ) \ + elif isinstance(dataset, galaxy.model.LibraryDatasetDatasetAssociation): + return sa_session.query(galaxy.model.JobExternalOutputMetadata) \ + .filter_by(job_id=self.job_id, + library_dataset_dataset_association_id=dataset.id, + is_valid=True) \ .first() # there should only be one or None return None - def get_dataset_metadata_key( self, dataset ): + def get_dataset_metadata_key(self, dataset): # Set meta can be called on library items and history items, # need to make different keys for them, since ids can overlap - return "%s_%d" % ( dataset.__class__.__name__, dataset.id ) + return "%s_%d" % (dataset.__class__.__name__, dataset.id) - def invalidate_external_metadata( self, datasets, sa_session ): + def invalidate_external_metadata(self, datasets, sa_session): for dataset in datasets: - jeom = self.get_output_filenames_by_dataset( dataset, sa_session ) + jeom = self.get_output_filenames_by_dataset(dataset, sa_session) # shouldn't be more than one valid, but you never know while jeom: jeom.is_valid = False - sa_session.add( jeom ) + sa_session.add(jeom) sa_session.flush() - jeom = self.get_output_filenames_by_dataset( dataset, sa_session ) - - def setup_external_metadata( self, datasets, sa_session, exec_dir=None, - tmp_dir=None, dataset_files_path=None, - output_fnames=None, config_root=None, - config_file=None, datatypes_config=None, - job_metadata=None, compute_tmp_dir=None, - include_command=True, max_metadata_value_size=0, - kwds=None): + jeom = self.get_output_filenames_by_dataset(dataset, sa_session) + + def setup_external_metadata(self, datasets, sa_session, exec_dir=None, + tmp_dir=None, dataset_files_path=None, + output_fnames=None, config_root=None, + config_file=None, datatypes_config=None, + job_metadata=None, compute_tmp_dir=None, + include_command=True, max_metadata_value_size=0, + kwds=None): kwds = kwds or {} if tmp_dir is None: tmp_dir = MetadataTempFile.tmp_dir @@ -745,7 +755,7 @@ def metadata_path_on_compute(path): return compute_path # fill in metadata_files_dict and return the command with args required to set metadata - def __metadata_files_list_to_cmd_line( metadata_files ): + def __metadata_files_list_to_cmd_line(metadata_files): def __get_filename_override(): if output_fnames: for dataset_path in output_fnames: @@ -761,35 +771,35 @@ def __get_filename_override(): metadata_path_on_compute(metadata_files.filename_override_metadata), ) return line - if not isinstance( datasets, list ): - datasets = [ datasets ] + if not isinstance(datasets, list): + datasets = [datasets] if exec_dir is None: - exec_dir = os.path.abspath( os.getcwd() ) + exec_dir = os.path.abspath(os.getcwd()) if dataset_files_path is None: dataset_files_path = galaxy.model.Dataset.file_path if config_root is None: - config_root = os.path.abspath( os.getcwd() ) + config_root = os.path.abspath(os.getcwd()) if datatypes_config is None: - raise Exception( 'In setup_external_metadata, the received datatypes_config is None.' ) + raise Exception('In setup_external_metadata, the received datatypes_config is None.') datatypes_config = 'datatypes_conf.xml' metadata_files_list = [] for dataset in datasets: - key = self.get_dataset_metadata_key( dataset ) + key = self.get_dataset_metadata_key(dataset) # future note: # wonkiness in job execution causes build command line to be called more than once # when setting metadata externally, via 'auto-detect' button in edit attributes, etc., # we don't want to overwrite (losing the ability to cleanup) our existing dataset keys and files, # so we will only populate the dictionary once - metadata_files = self.get_output_filenames_by_dataset( dataset, sa_session ) + metadata_files = self.get_output_filenames_by_dataset(dataset, sa_session) if not metadata_files: - job = sa_session.query( galaxy.model.Job ).get( self.job_id ) - metadata_files = galaxy.model.JobExternalOutputMetadata( job=job, dataset=dataset ) + job = sa_session.query(galaxy.model.Job).get(self.job_id) + metadata_files = galaxy.model.JobExternalOutputMetadata(job=job, dataset=dataset) # we are using tempfile to create unique filenames, tempfile always returns an absolute path # we will use pathnames relative to the galaxy root, to accommodate instances where the galaxy root # is located differently, i.e. on a cluster node with a different filesystem structure # file to store existing dataset - metadata_files.filename_in = abspath( tempfile.NamedTemporaryFile( dir=tmp_dir, prefix="metadata_in_%s_" % key ).name ) + metadata_files.filename_in = abspath(tempfile.NamedTemporaryFile(dir=tmp_dir, prefix="metadata_in_%s_" % key).name) # FIXME: HACK # sqlalchemy introduced 'expire_on_commit' flag for sessionmaker at version 0.5x @@ -801,76 +811,76 @@ def __get_filename_override(): # Touch also deferred column dataset._metadata - cPickle.dump( dataset, open( metadata_files.filename_in, 'wb+' ) ) + cPickle.dump(dataset, open(metadata_files.filename_in, 'wb+')) # file to store metadata results of set_meta() - metadata_files.filename_out = abspath( tempfile.NamedTemporaryFile( dir=tmp_dir, prefix="metadata_out_%s_" % key ).name ) - open( metadata_files.filename_out, 'wb+' ) # create the file on disk, so it cannot be reused by tempfile (unlikely, but possible) + metadata_files.filename_out = abspath(tempfile.NamedTemporaryFile(dir=tmp_dir, prefix="metadata_out_%s_" % key).name) + open(metadata_files.filename_out, 'wb+') # create the file on disk, so it cannot be reused by tempfile (unlikely, but possible) # file to store a 'return code' indicating the results of the set_meta() call # results code is like (True/False - if setting metadata was successful/failed , exception or string of reason of success/failure ) - metadata_files.filename_results_code = abspath( tempfile.NamedTemporaryFile( dir=tmp_dir, prefix="metadata_results_%s_" % key ).name ) + metadata_files.filename_results_code = abspath(tempfile.NamedTemporaryFile(dir=tmp_dir, prefix="metadata_results_%s_" % key).name) # create the file on disk, so it cannot be reused by tempfile (unlikely, but possible) - json.dump( ( False, 'External set_meta() not called' ), open( metadata_files.filename_results_code, 'wb+' ) ) + json.dump((False, 'External set_meta() not called'), open(metadata_files.filename_results_code, 'wb+')) # file to store kwds passed to set_meta() - metadata_files.filename_kwds = abspath( tempfile.NamedTemporaryFile( dir=tmp_dir, prefix="metadata_kwds_%s_" % key ).name ) - json.dump( kwds, open( metadata_files.filename_kwds, 'wb+' ), ensure_ascii=True ) + metadata_files.filename_kwds = abspath(tempfile.NamedTemporaryFile(dir=tmp_dir, prefix="metadata_kwds_%s_" % key).name) + json.dump(kwds, open(metadata_files.filename_kwds, 'wb+'), ensure_ascii=True) # existing metadata file parameters need to be overridden with cluster-writable file locations - metadata_files.filename_override_metadata = abspath( tempfile.NamedTemporaryFile( dir=tmp_dir, prefix="metadata_override_%s_" % key ).name ) - open( metadata_files.filename_override_metadata, 'wb+' ) # create the file on disk, so it cannot be reused by tempfile (unlikely, but possible) + metadata_files.filename_override_metadata = abspath(tempfile.NamedTemporaryFile(dir=tmp_dir, prefix="metadata_override_%s_" % key).name) + open(metadata_files.filename_override_metadata, 'wb+') # create the file on disk, so it cannot be reused by tempfile (unlikely, but possible) override_metadata = [] for meta_key, spec_value in dataset.metadata.spec.items(): - if isinstance( spec_value.param, FileParameter ) and dataset.metadata.get( meta_key, None ) is not None: + if isinstance(spec_value.param, FileParameter) and dataset.metadata.get(meta_key, None) is not None: metadata_temp = MetadataTempFile() - shutil.copy( dataset.metadata.get( meta_key, None ).file_name, metadata_temp.file_name ) - override_metadata.append( ( meta_key, metadata_temp.to_JSON() ) ) - json.dump( override_metadata, open( metadata_files.filename_override_metadata, 'wb+' ) ) + shutil.copy(dataset.metadata.get(meta_key, None).file_name, metadata_temp.file_name) + override_metadata.append((meta_key, metadata_temp.to_JSON())) + json.dump(override_metadata, open(metadata_files.filename_override_metadata, 'wb+')) # add to session and flush - sa_session.add( metadata_files ) + sa_session.add(metadata_files) sa_session.flush() - metadata_files_list.append( metadata_files ) - args = '"%s" "%s" %s %s' % ( datatypes_config, - job_metadata, - " ".join( map( __metadata_files_list_to_cmd_line, metadata_files_list ) ), - max_metadata_value_size) + metadata_files_list.append(metadata_files) + args = '"%s" "%s" %s %s' % (datatypes_config, + job_metadata, + " ".join(map(__metadata_files_list_to_cmd_line, metadata_files_list)), + max_metadata_value_size) if include_command: # return command required to build - fd, fp = tempfile.mkstemp( suffix='.py', dir=tmp_dir, prefix="set_metadata_" ) - metadata_script_file = abspath( fp ) - os.fdopen( fd, 'w' ).write( 'from galaxy_ext.metadata.set_metadata import set_metadata; set_metadata()' ) - return 'python "%s" %s' % ( metadata_path_on_compute(metadata_script_file), args ) + fd, fp = tempfile.mkstemp(suffix='.py', dir=tmp_dir, prefix="set_metadata_") + metadata_script_file = abspath(fp) + os.fdopen(fd, 'w').write('from galaxy_ext.metadata.set_metadata import set_metadata; set_metadata()') + return 'python "%s" %s' % (metadata_path_on_compute(metadata_script_file), args) else: # return args to galaxy_ext.metadata.set_metadata required to build return args - def external_metadata_set_successfully( self, dataset, sa_session ): - metadata_files = self.get_output_filenames_by_dataset( dataset, sa_session ) + def external_metadata_set_successfully(self, dataset, sa_session): + metadata_files = self.get_output_filenames_by_dataset(dataset, sa_session) if not metadata_files: return False # this file doesn't exist - rval, rstring = json.load( open( metadata_files.filename_results_code ) ) + rval, rstring = json.load(open(metadata_files.filename_results_code)) if not rval: - log.debug( 'setting metadata externally failed for %s %s: %s' % ( dataset.__class__.__name__, dataset.id, rstring ) ) + log.debug('setting metadata externally failed for %s %s: %s' % (dataset.__class__.__name__, dataset.id, rstring)) return rval - def cleanup_external_metadata( self, sa_session ): - log.debug( 'Cleaning up external metadata files' ) - for metadata_files in sa_session.query( galaxy.model.Job ).get( self.job_id ).external_output_metadata: + def cleanup_external_metadata(self, sa_session): + log.debug('Cleaning up external metadata files') + for metadata_files in sa_session.query(galaxy.model.Job).get(self.job_id).external_output_metadata: # we need to confirm that any MetadataTempFile files were removed, if not we need to remove them # can occur if the job was stopped before completion, but a MetadataTempFile is used in the set_meta - MetadataTempFile.cleanup_from_JSON_dict_filename( metadata_files.filename_out ) - dataset_key = self.get_dataset_metadata_key( metadata_files.dataset ) - for key, fname in [ ( 'filename_in', metadata_files.filename_in ), - ( 'filename_out', metadata_files.filename_out ), - ( 'filename_results_code', metadata_files.filename_results_code ), - ( 'filename_kwds', metadata_files.filename_kwds ), - ( 'filename_override_metadata', metadata_files.filename_override_metadata ) ]: + MetadataTempFile.cleanup_from_JSON_dict_filename(metadata_files.filename_out) + dataset_key = self.get_dataset_metadata_key(metadata_files.dataset) + for key, fname in [('filename_in', metadata_files.filename_in), + ('filename_out', metadata_files.filename_out), + ('filename_results_code', metadata_files.filename_results_code), + ('filename_kwds', metadata_files.filename_kwds), + ('filename_override_metadata', metadata_files.filename_override_metadata)]: try: - os.remove( fname ) + os.remove(fname) except Exception as e: - log.debug( 'Failed to cleanup external metadata file (%s) for %s: %s' % ( key, dataset_key, e ) ) + log.debug('Failed to cleanup external metadata file (%s) for %s: %s' % (key, dataset_key, e)) - def set_job_runner_external_pid( self, pid, sa_session ): - for metadata_files in sa_session.query( galaxy.model.Job ).get( self.job_id ).external_output_metadata: + def set_job_runner_external_pid(self, pid, sa_session): + for metadata_files in sa_session.query(galaxy.model.Job).get(self.job_id).external_output_metadata: metadata_files.job_runner_external_pid = pid - sa_session.add( metadata_files ) + sa_session.add(metadata_files) sa_session.flush() diff --git a/lib/galaxy/model/migrate/check.py b/lib/galaxy/model/migrate/check.py index eec24078e0d1..9674b75c0cbe 100644 --- a/lib/galaxy/model/migrate/check.py +++ b/lib/galaxy/model/migrate/check.py @@ -11,14 +11,14 @@ ) from sqlalchemy.exc import NoSuchTableError -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) # path relative to galaxy -migrate_repository_directory = os.path.abspath(os.path.dirname( __file__ )).replace( os.getcwd() + os.path.sep, '', 1 ) -migrate_repository = repository.Repository( migrate_repository_directory ) +migrate_repository_directory = os.path.abspath(os.path.dirname(__file__)).replace(os.getcwd() + os.path.sep, '', 1) +migrate_repository = repository.Repository(migrate_repository_directory) -def create_or_verify_database( url, galaxy_config_file, engine_options={}, app=None ): +def create_or_verify_database(url, galaxy_config_file, engine_options={}, app=None): """ Check that the database is use-able, possibly creating it if empty (this is the only time we automatically create tables, otherwise we force the @@ -30,38 +30,38 @@ def create_or_verify_database( url, galaxy_config_file, engine_options={}, app=N 4) Database versioned but out of date --> fail with informative message, user must run "sh manage_db.sh upgrade" """ # Create engine and metadata - engine = create_engine( url, **engine_options ) + engine = create_engine(url, **engine_options) def migrate(): try: # Declare the database to be under a repository's version control - db_schema = schema.ControlledSchema.create( engine, migrate_repository ) + db_schema = schema.ControlledSchema.create(engine, migrate_repository) except: # The database is already under version control - db_schema = schema.ControlledSchema( engine, migrate_repository ) + db_schema = schema.ControlledSchema(engine, migrate_repository) # Apply all scripts to get to current version - migrate_to_current_version( engine, db_schema ) + migrate_to_current_version(engine, db_schema) - meta = MetaData( bind=engine ) - if app and getattr( app.config, 'database_auto_migrate', False ): + meta = MetaData(bind=engine) + if app and getattr(app.config, 'database_auto_migrate', False): migrate() return # Try to load dataset table try: - Table( "dataset", meta, autoload=True ) + Table("dataset", meta, autoload=True) except NoSuchTableError: # No 'dataset' table means a completely uninitialized database. If we have an app, we'll # set its new_installation setting to True so the tool migration process will be skipped. if app: app.new_installation = True - log.info( "No database, initializing" ) + log.info("No database, initializing") migrate() return try: - hda_table = Table( "history_dataset_association", meta, autoload=True ) + hda_table = Table("history_dataset_association", meta, autoload=True) except NoSuchTableError: - raise Exception( "Your database is older than hg revision 1464:c7acaa1bb88f and will need to be updated manually" ) + raise Exception("Your database is older than hg revision 1464:c7acaa1bb88f and will need to be updated manually") # There is a 'history_dataset_association' table, so we (hopefully) have # version 1 of the database, but without the migrate_version table. This # happens if the user has a build from right before migration was added. @@ -71,59 +71,59 @@ def migrate(): # The 'copied_from_history_dataset_association_id' column was added in # rev 1464:c7acaa1bb88f. This is the oldest revision we currently do # automated versioning for, so stop here - raise Exception( "Your database is older than hg revision 1464:c7acaa1bb88f and will need to be updated manually" ) + raise Exception("Your database is older than hg revision 1464:c7acaa1bb88f and will need to be updated manually") # At revision 1464:c7acaa1bb88f or greater (database version 1), make sure # that the db has version information. This is the trickiest case -- we # have a database but no version control, and are assuming it is a certain # version. If the user has postion version 1 changes this could cause # problems try: - Table( "migrate_version", meta, autoload=True ) + Table("migrate_version", meta, autoload=True) except NoSuchTableError: # The database exists but is not yet under migrate version control, so init with version 1 - log.info( "Adding version control to existing database" ) + log.info("Adding version control to existing database") try: - Table( "metadata_file", meta, autoload=True ) - schema.ControlledSchema.create( engine, migrate_repository, version=2 ) + Table("metadata_file", meta, autoload=True) + schema.ControlledSchema.create(engine, migrate_repository, version=2) except NoSuchTableError: - schema.ControlledSchema.create( engine, migrate_repository, version=1 ) + schema.ControlledSchema.create(engine, migrate_repository, version=1) # Verify that the code and the DB are in sync - db_schema = schema.ControlledSchema( engine, migrate_repository ) + db_schema = schema.ControlledSchema(engine, migrate_repository) if migrate_repository.versions.latest != db_schema.version: config_arg = '' - if galaxy_config_file and os.path.abspath( os.path.join( os.getcwd(), 'config', 'galaxy.ini' ) ) != galaxy_config_file: - config_arg = ' -c %s' % galaxy_config_file.replace( os.path.abspath( os.getcwd() ), '.' ) - raise Exception( "Your database has version '%d' but this code expects version '%d'. Please backup your database and then migrate the schema by running 'sh manage_db.sh%s upgrade'." - % ( db_schema.version, migrate_repository.versions.latest, config_arg ) ) + if galaxy_config_file and os.path.abspath(os.path.join(os.getcwd(), 'config', 'galaxy.ini')) != galaxy_config_file: + config_arg = ' -c %s' % galaxy_config_file.replace(os.path.abspath(os.getcwd()), '.') + raise Exception("Your database has version '%d' but this code expects version '%d'. Please backup your database and then migrate the schema by running 'sh manage_db.sh%s upgrade'." + % (db_schema.version, migrate_repository.versions.latest, config_arg)) else: - log.info( "At database version %d" % db_schema.version ) + log.info("At database version %d" % db_schema.version) -def migrate_to_current_version( engine, schema ): +def migrate_to_current_version(engine, schema): # Changes to get to current version try: - changeset = schema.changeset( None ) + changeset = schema.changeset(None) except Exception as e: log.error("Problem determining migration changeset for engine [%s]" % engine) raise e for ver, change in changeset: nextver = ver + changeset.step - log.info( 'Migrating %s -> %s... ' % ( ver, nextver ) ) + log.info('Migrating %s -> %s... ' % (ver, nextver)) old_stdout = sys.stdout - class FakeStdout( object ): - def __init__( self ): + class FakeStdout(object): + def __init__(self): self.buffer = [] - def write( self, s ): - self.buffer.append( s ) + def write(self, s): + self.buffer.append(s) - def flush( self ): + def flush(self): pass sys.stdout = FakeStdout() try: - schema.runchange( ver, change, changeset.step ) + schema.runchange(ver, change, changeset.step) finally: - for message in "".join( sys.stdout.buffer ).split( "\n" ): - log.info( message ) + for message in "".join(sys.stdout.buffer).split("\n"): + log.info(message) sys.stdout = old_stdout diff --git a/lib/galaxy/model/migrate/versions/0001_initial_tables.py b/lib/galaxy/model/migrate/versions/0001_initial_tables.py index 82d5627394be..426cb5e17cfd 100644 --- a/lib/galaxy/model/migrate/versions/0001_initial_tables.py +++ b/lib/galaxy/model/migrate/versions/0001_initial_tables.py @@ -7,193 +7,193 @@ from galaxy.model.custom_types import JSONType, MetadataType, TrimmedString now = datetime.datetime.utcnow -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() # Tables as of changeset 1464:c7acaa1bb88f -User_table = Table( "galaxy_user", metadata, - Column( "id", Integer, primary_key=True), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "email", TrimmedString( 255 ), nullable=False ), - Column( "password", TrimmedString( 40 ), nullable=False ), - Column( "external", Boolean, default=False ) ) - -History_table = Table( "history", metadata, - Column( "id", Integer, primary_key=True), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, index=True, default=now, onupdate=now ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "name", TrimmedString( 255 ) ), - Column( "hid_counter", Integer, default=1 ), - Column( "deleted", Boolean, index=True, default=False ), - Column( "purged", Boolean, index=True, default=False ), - Column( "genome_build", TrimmedString( 40 ) ) ) - -HistoryDatasetAssociation_table = Table( "history_dataset_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ), - Column( "dataset_id", Integer, ForeignKey( "dataset.id" ), index=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "copied_from_history_dataset_association_id", Integer, ForeignKey( "history_dataset_association.id" ), nullable=True ), - Column( "hid", Integer ), - Column( "name", TrimmedString( 255 ) ), - Column( "info", TrimmedString( 255 ) ), - Column( "blurb", TrimmedString( 255 ) ), - Column( "peek", TEXT ), - Column( "extension", TrimmedString( 64 ) ), - Column( "metadata", MetadataType(), key="_metadata" ), - Column( "parent_id", Integer, ForeignKey( "history_dataset_association.id" ), nullable=True ), - Column( "designation", TrimmedString( 255 ) ), - Column( "deleted", Boolean, index=True, default=False ), - Column( "visible", Boolean ) ) - -Dataset_table = Table( "dataset", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, index=True, default=now, onupdate=now ), - Column( "state", TrimmedString( 64 ) ), - Column( "deleted", Boolean, index=True, default=False ), - Column( "purged", Boolean, index=True, default=False ), - Column( "purgable", Boolean, default=True ), - Column( "external_filename", TEXT ), - Column( "_extra_files_path", TEXT ), - Column( 'file_size', Numeric( 15, 0 ) ) ) - -ImplicitlyConvertedDatasetAssociation_table = Table( "implicitly_converted_dataset_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "hda_id", Integer, ForeignKey( "history_dataset_association.id" ), index=True, nullable=True ), - Column( "hda_parent_id", Integer, ForeignKey( "history_dataset_association.id" ), index=True ), - Column( "deleted", Boolean, index=True, default=False ), - Column( "metadata_safe", Boolean, index=True, default=True ), - Column( "type", TrimmedString( 255 ) ) ) - -ValidationError_table = Table( "validation_error", metadata, - Column( "id", Integer, primary_key=True ), - Column( "dataset_id", Integer, ForeignKey( "history_dataset_association.id" ), index=True ), - Column( "message", TrimmedString( 255 ) ), - Column( "err_type", TrimmedString( 64 ) ), - Column( "attributes", TEXT ) ) - -Job_table = Table( "job", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ), - Column( "tool_id", String( 255 ) ), - Column( "tool_version", TEXT, default="1.0.0" ), - Column( "state", String( 64 ) ), - Column( "info", TrimmedString( 255 ) ), - Column( "command_line", TEXT ), - Column( "param_filename", String( 1024 ) ), - Column( "runner_name", String( 255 ) ), - Column( "stdout", TEXT ), - Column( "stderr", TEXT ), - Column( "traceback", TEXT ), - Column( "session_id", Integer, ForeignKey( "galaxy_session.id" ), index=True, nullable=True ), - Column( "job_runner_name", String( 255 ) ), - Column( "job_runner_external_id", String( 255 ) ) ) - -JobParameter_table = Table( "job_parameter", metadata, - Column( "id", Integer, primary_key=True ), - Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ), - Column( "name", String(255) ), - Column( "value", TEXT ) ) - -JobToInputDatasetAssociation_table = Table( "job_to_input_dataset", metadata, - Column( "id", Integer, primary_key=True ), - Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ), - Column( "dataset_id", Integer, ForeignKey( "history_dataset_association.id" ), index=True ), - Column( "name", String(255) ) ) - -JobToOutputDatasetAssociation_table = Table( "job_to_output_dataset", metadata, - Column( "id", Integer, primary_key=True ), - Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ), - Column( "dataset_id", Integer, ForeignKey( "history_dataset_association.id" ), index=True ), - Column( "name", String(255) ) ) - -Event_table = Table( "event", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "history_id", Integer, ForeignKey( "history.id" ), index=True, nullable=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True, nullable=True ), - Column( "message", TrimmedString( 1024 ) ), - Column( "session_id", Integer, ForeignKey( "galaxy_session.id" ), index=True, nullable=True ), - Column( "tool_id", String( 255 ) ) ) - -GalaxySession_table = Table( "galaxy_session", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True, nullable=True ), - Column( "remote_host", String( 255 ) ), - Column( "remote_addr", String( 255 ) ), - Column( "referer", TEXT ), - Column( "current_history_id", Integer, ForeignKey( "history.id" ), nullable=True ), - Column( "session_key", TrimmedString( 255 ), index=True, unique=True ), - Column( "is_valid", Boolean, default=False ), - Column( "prev_session_id", Integer ) ) - -GalaxySessionToHistoryAssociation_table = Table( "galaxy_session_to_history", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "session_id", Integer, ForeignKey( "galaxy_session.id" ), index=True ), - Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ) ) - -StoredWorkflow_table = Table( "stored_workflow", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True, nullable=False ), - Column( "latest_workflow_id", Integer, - ForeignKey( "workflow.id", use_alter=True, name='stored_workflow_latest_workflow_id_fk' ), index=True ), - Column( "name", TEXT ), - Column( "deleted", Boolean, default=False ) ) - -Workflow_table = Table( "workflow", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "stored_workflow_id", Integer, ForeignKey( "stored_workflow.id" ), index=True, nullable=False ), - Column( "name", TEXT ), - Column( "has_cycles", Boolean ), - Column( "has_errors", Boolean ) ) - -WorkflowStep_table = Table( "workflow_step", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "workflow_id", Integer, ForeignKey( "workflow.id" ), index=True, nullable=False ), - Column( "type", String(64) ), - Column( "tool_id", TEXT ), - Column( "tool_version", TEXT ), - Column( "tool_inputs", JSONType ), - Column( "tool_errors", JSONType ), - Column( "position", JSONType ), - Column( "config", JSONType ), - Column( "order_index", Integer ) ) - -WorkflowStepConnection_table = Table( "workflow_step_connection", metadata, - Column( "id", Integer, primary_key=True ), - Column( "output_step_id", Integer, ForeignKey( "workflow_step.id" ), index=True ), - Column( "input_step_id", Integer, ForeignKey( "workflow_step.id" ), index=True ), - Column( "output_name", TEXT ), - Column( "input_name", TEXT) ) - -StoredWorkflowUserShareAssociation_table = Table( "stored_workflow_user_share_connection", metadata, - Column( "id", Integer, primary_key=True ), - Column( "stored_workflow_id", Integer, ForeignKey( "stored_workflow.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ) ) - -StoredWorkflowMenuEntry_table = Table( "stored_workflow_menu_entry", metadata, - Column( "id", Integer, primary_key=True ), - Column( "stored_workflow_id", Integer, ForeignKey( "stored_workflow.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "order_index", Integer ) ) +User_table = Table("galaxy_user", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("email", TrimmedString(255), nullable=False), + Column("password", TrimmedString(40), nullable=False), + Column("external", Boolean, default=False)) + +History_table = Table("history", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, index=True, default=now, onupdate=now), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("name", TrimmedString(255)), + Column("hid_counter", Integer, default=1), + Column("deleted", Boolean, index=True, default=False), + Column("purged", Boolean, index=True, default=False), + Column("genome_build", TrimmedString(40))) + +HistoryDatasetAssociation_table = Table("history_dataset_association", metadata, + Column("id", Integer, primary_key=True), + Column("history_id", Integer, ForeignKey("history.id"), index=True), + Column("dataset_id", Integer, ForeignKey("dataset.id"), index=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("copied_from_history_dataset_association_id", Integer, ForeignKey("history_dataset_association.id"), nullable=True), + Column("hid", Integer), + Column("name", TrimmedString(255)), + Column("info", TrimmedString(255)), + Column("blurb", TrimmedString(255)), + Column("peek", TEXT), + Column("extension", TrimmedString(64)), + Column("metadata", MetadataType(), key="_metadata"), + Column("parent_id", Integer, ForeignKey("history_dataset_association.id"), nullable=True), + Column("designation", TrimmedString(255)), + Column("deleted", Boolean, index=True, default=False), + Column("visible", Boolean)) + +Dataset_table = Table("dataset", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, index=True, default=now, onupdate=now), + Column("state", TrimmedString(64)), + Column("deleted", Boolean, index=True, default=False), + Column("purged", Boolean, index=True, default=False), + Column("purgable", Boolean, default=True), + Column("external_filename", TEXT), + Column("_extra_files_path", TEXT), + Column('file_size', Numeric(15, 0))) + +ImplicitlyConvertedDatasetAssociation_table = Table("implicitly_converted_dataset_association", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("hda_id", Integer, ForeignKey("history_dataset_association.id"), index=True, nullable=True), + Column("hda_parent_id", Integer, ForeignKey("history_dataset_association.id"), index=True), + Column("deleted", Boolean, index=True, default=False), + Column("metadata_safe", Boolean, index=True, default=True), + Column("type", TrimmedString(255))) + +ValidationError_table = Table("validation_error", metadata, + Column("id", Integer, primary_key=True), + Column("dataset_id", Integer, ForeignKey("history_dataset_association.id"), index=True), + Column("message", TrimmedString(255)), + Column("err_type", TrimmedString(64)), + Column("attributes", TEXT)) + +Job_table = Table("job", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("history_id", Integer, ForeignKey("history.id"), index=True), + Column("tool_id", String(255)), + Column("tool_version", TEXT, default="1.0.0"), + Column("state", String(64)), + Column("info", TrimmedString(255)), + Column("command_line", TEXT), + Column("param_filename", String(1024)), + Column("runner_name", String(255)), + Column("stdout", TEXT), + Column("stderr", TEXT), + Column("traceback", TEXT), + Column("session_id", Integer, ForeignKey("galaxy_session.id"), index=True, nullable=True), + Column("job_runner_name", String(255)), + Column("job_runner_external_id", String(255))) + +JobParameter_table = Table("job_parameter", metadata, + Column("id", Integer, primary_key=True), + Column("job_id", Integer, ForeignKey("job.id"), index=True), + Column("name", String(255)), + Column("value", TEXT)) + +JobToInputDatasetAssociation_table = Table("job_to_input_dataset", metadata, + Column("id", Integer, primary_key=True), + Column("job_id", Integer, ForeignKey("job.id"), index=True), + Column("dataset_id", Integer, ForeignKey("history_dataset_association.id"), index=True), + Column("name", String(255))) + +JobToOutputDatasetAssociation_table = Table("job_to_output_dataset", metadata, + Column("id", Integer, primary_key=True), + Column("job_id", Integer, ForeignKey("job.id"), index=True), + Column("dataset_id", Integer, ForeignKey("history_dataset_association.id"), index=True), + Column("name", String(255))) + +Event_table = Table("event", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("history_id", Integer, ForeignKey("history.id"), index=True, nullable=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True, nullable=True), + Column("message", TrimmedString(1024)), + Column("session_id", Integer, ForeignKey("galaxy_session.id"), index=True, nullable=True), + Column("tool_id", String(255))) + +GalaxySession_table = Table("galaxy_session", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True, nullable=True), + Column("remote_host", String(255)), + Column("remote_addr", String(255)), + Column("referer", TEXT), + Column("current_history_id", Integer, ForeignKey("history.id"), nullable=True), + Column("session_key", TrimmedString(255), index=True, unique=True), + Column("is_valid", Boolean, default=False), + Column("prev_session_id", Integer)) + +GalaxySessionToHistoryAssociation_table = Table("galaxy_session_to_history", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("session_id", Integer, ForeignKey("galaxy_session.id"), index=True), + Column("history_id", Integer, ForeignKey("history.id"), index=True)) + +StoredWorkflow_table = Table("stored_workflow", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True, nullable=False), + Column("latest_workflow_id", Integer, + ForeignKey("workflow.id", use_alter=True, name='stored_workflow_latest_workflow_id_fk'), index=True), + Column("name", TEXT), + Column("deleted", Boolean, default=False)) + +Workflow_table = Table("workflow", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("stored_workflow_id", Integer, ForeignKey("stored_workflow.id"), index=True, nullable=False), + Column("name", TEXT), + Column("has_cycles", Boolean), + Column("has_errors", Boolean)) + +WorkflowStep_table = Table("workflow_step", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("workflow_id", Integer, ForeignKey("workflow.id"), index=True, nullable=False), + Column("type", String(64)), + Column("tool_id", TEXT), + Column("tool_version", TEXT), + Column("tool_inputs", JSONType), + Column("tool_errors", JSONType), + Column("position", JSONType), + Column("config", JSONType), + Column("order_index", Integer)) + +WorkflowStepConnection_table = Table("workflow_step_connection", metadata, + Column("id", Integer, primary_key=True), + Column("output_step_id", Integer, ForeignKey("workflow_step.id"), index=True), + Column("input_step_id", Integer, ForeignKey("workflow_step.id"), index=True), + Column("output_name", TEXT), + Column("input_name", TEXT)) + +StoredWorkflowUserShareAssociation_table = Table("stored_workflow_user_share_connection", metadata, + Column("id", Integer, primary_key=True), + Column("stored_workflow_id", Integer, ForeignKey("stored_workflow.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True)) + +StoredWorkflowMenuEntry_table = Table("stored_workflow_menu_entry", metadata, + Column("id", Integer, primary_key=True), + Column("stored_workflow_id", Integer, ForeignKey("stored_workflow.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("order_index", Integer)) def upgrade(migrate_engine): diff --git a/lib/galaxy/model/migrate/versions/0002_metadata_file_table.py b/lib/galaxy/model/migrate/versions/0002_metadata_file_table.py index d1b20f0f2bf9..6d40b3a84340 100644 --- a/lib/galaxy/model/migrate/versions/0002_metadata_file_table.py +++ b/lib/galaxy/model/migrate/versions/0002_metadata_file_table.py @@ -4,18 +4,18 @@ from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, MetaData, Table, TEXT now = datetime.datetime.utcnow -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() # New table in changeset 1568:0b022adfdc34 -MetadataFile_table = Table( "metadata_file", metadata, - Column( "id", Integer, primary_key=True ), - Column( "name", TEXT ), - Column( "hda_id", Integer, ForeignKey( "history_dataset_association.id" ), index=True, nullable=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, index=True, default=now, onupdate=now ), - Column( "deleted", Boolean, index=True, default=False ), - Column( "purged", Boolean, index=True, default=False ) ) +MetadataFile_table = Table("metadata_file", metadata, + Column("id", Integer, primary_key=True), + Column("name", TEXT), + Column("hda_id", Integer, ForeignKey("history_dataset_association.id"), index=True, nullable=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, index=True, default=now, onupdate=now), + Column("deleted", Boolean, index=True, default=False), + Column("purged", Boolean, index=True, default=False)) def upgrade(migrate_engine): diff --git a/lib/galaxy/model/migrate/versions/0003_security_and_libraries.py b/lib/galaxy/model/migrate/versions/0003_security_and_libraries.py index 025769534afd..576ac169b2e1 100644 --- a/lib/galaxy/model/migrate/versions/0003_security_and_libraries.py +++ b/lib/galaxy/model/migrate/versions/0003_security_and_libraries.py @@ -10,298 +10,298 @@ from galaxy.model.custom_types import JSONType, MetadataType, TrimmedString now = datetime.datetime.utcnow -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) -handler = logging.StreamHandler( sys.stdout ) +handler = logging.StreamHandler(sys.stdout) format = "%(name)s %(levelname)s %(asctime)s %(message)s" -formatter = logging.Formatter( format ) -handler.setFormatter( formatter ) -log.addHandler( handler ) +formatter = logging.Formatter(format) +handler.setFormatter(formatter) +log.addHandler(handler) metadata = MetaData() # New tables as of changeset 2341:5498ac35eedd -Group_table = Table( "galaxy_group", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "name", String( 255 ), index=True, unique=True ), - Column( "deleted", Boolean, index=True, default=False ) ) - -UserGroupAssociation_table = Table( "user_group_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "group_id", Integer, ForeignKey( "galaxy_group.id" ), index=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ) ) - -UserRoleAssociation_table = Table( "user_role_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "role_id", Integer, ForeignKey( "role.id" ), index=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ) ) - -GroupRoleAssociation_table = Table( "group_role_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "group_id", Integer, ForeignKey( "galaxy_group.id" ), index=True ), - Column( "role_id", Integer, ForeignKey( "role.id" ), index=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ) ) - -Role_table = Table( "role", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "name", String( 255 ), index=True, unique=True ), - Column( "description", TEXT ), - Column( "type", String( 40 ), index=True ), - Column( "deleted", Boolean, index=True, default=False ) ) - -DatasetPermissions_table = Table( "dataset_permissions", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "action", TEXT ), - Column( "dataset_id", Integer, ForeignKey( "dataset.id" ), index=True ), - Column( "role_id", Integer, ForeignKey( "role.id" ), index=True ) ) - -LibraryPermissions_table = Table( "library_permissions", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "action", TEXT ), - Column( "library_id", Integer, ForeignKey( "library.id" ), nullable=True, index=True ), - Column( "role_id", Integer, ForeignKey( "role.id" ), index=True ) ) - -LibraryFolderPermissions_table = Table( "library_folder_permissions", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "action", TEXT ), - Column( "library_folder_id", Integer, ForeignKey( "library_folder.id" ), nullable=True, index=True ), - Column( "role_id", Integer, ForeignKey( "role.id" ), index=True ) ) - -LibraryDatasetPermissions_table = Table( "library_dataset_permissions", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "action", TEXT ), - Column( "library_dataset_id", Integer, ForeignKey( "library_dataset.id" ), nullable=True, index=True ), - Column( "role_id", Integer, ForeignKey( "role.id" ), index=True ) ) - -LibraryDatasetDatasetAssociationPermissions_table = Table( "library_dataset_dataset_association_permissions", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "action", TEXT ), - Column( "library_dataset_dataset_association_id", Integer, ForeignKey( "library_dataset_dataset_association.id" ), nullable=True ), - Column( "role_id", Integer, ForeignKey( "role.id" ), index=True ) ) -Index( "ix_lddap_library_dataset_dataset_association_id", LibraryDatasetDatasetAssociationPermissions_table.c.library_dataset_dataset_association_id ) - -LibraryItemInfoPermissions_table = Table( "library_item_info_permissions", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "action", TEXT ), - Column( "library_item_info_id", Integer, ForeignKey( "library_item_info.id" ), nullable=True, index=True ), - Column( "role_id", Integer, ForeignKey( "role.id" ), index=True ) ) - -LibraryItemInfoTemplatePermissions_table = Table( "library_item_info_template_permissions", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "action", TEXT ), - Column( "library_item_info_template_id", Integer, ForeignKey( "library_item_info_template.id" ), nullable=True ), - Column( "role_id", Integer, ForeignKey( "role.id" ), index=True ) ) -Index( "ix_liitp_library_item_info_template_id", LibraryItemInfoTemplatePermissions_table.c.library_item_info_template_id ) - -DefaultUserPermissions_table = Table( "default_user_permissions", metadata, - Column( "id", Integer, primary_key=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "action", TEXT ), - Column( "role_id", Integer, ForeignKey( "role.id" ), index=True ) ) - -DefaultHistoryPermissions_table = Table( "default_history_permissions", metadata, - Column( "id", Integer, primary_key=True ), - Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ), - Column( "action", TEXT ), - Column( "role_id", Integer, ForeignKey( "role.id" ), index=True ) ) - -LibraryDataset_table = Table( "library_dataset", metadata, - Column( "id", Integer, primary_key=True ), - Column( "library_dataset_dataset_association_id", Integer, ForeignKey( "library_dataset_dataset_association.id", use_alter=True, name="library_dataset_dataset_association_id_fk" ), nullable=True, index=True ), # current version of dataset, if null, there is not a current version selected - Column( "folder_id", Integer, ForeignKey( "library_folder.id" ), index=True ), - Column( "order_id", Integer ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "name", TrimmedString( 255 ), key="_name" ), # when not None/null this will supercede display in library (but not when imported into user's history?) - Column( "info", TrimmedString( 255 ), key="_info" ), # when not None/null this will supercede display in library (but not when imported into user's history?) - Column( "deleted", Boolean, index=True, default=False ) ) - -LibraryDatasetDatasetAssociation_table = Table( "library_dataset_dataset_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "library_dataset_id", Integer, ForeignKey( "library_dataset.id" ), index=True ), - Column( "dataset_id", Integer, ForeignKey( "dataset.id" ), index=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "copied_from_history_dataset_association_id", Integer, ForeignKey( "history_dataset_association.id", use_alter=True, name='history_dataset_association_dataset_id_fkey' ), nullable=True ), - Column( "copied_from_library_dataset_dataset_association_id", Integer, ForeignKey( "library_dataset_dataset_association.id", use_alter=True, name='library_dataset_dataset_association_id_fkey' ), nullable=True ), - Column( "name", TrimmedString( 255 ) ), - Column( "info", TrimmedString( 255 ) ), - Column( "blurb", TrimmedString( 255 ) ), - Column( "peek", TEXT ), - Column( "extension", TrimmedString( 64 ) ), - Column( "metadata", MetadataType(), key="_metadata" ), - Column( "parent_id", Integer, ForeignKey( "library_dataset_dataset_association.id" ), nullable=True ), - Column( "designation", TrimmedString( 255 ) ), - Column( "deleted", Boolean, index=True, default=False ), - Column( "visible", Boolean ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "message", TrimmedString( 255 ) ) ) - -Library_table = Table( "library", metadata, - Column( "id", Integer, primary_key=True ), - Column( "root_folder_id", Integer, ForeignKey( "library_folder.id" ), index=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "name", String( 255 ), index=True ), - Column( "deleted", Boolean, index=True, default=False ), - Column( "purged", Boolean, index=True, default=False ), - Column( "description", TEXT ) ) - -LibraryFolder_table = Table( "library_folder", metadata, - Column( "id", Integer, primary_key=True ), - Column( "parent_id", Integer, ForeignKey( "library_folder.id" ), nullable=True, index=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "name", TEXT ), - Column( "description", TEXT ), - Column( "order_id", Integer ), - Column( "item_count", Integer ), - Column( "deleted", Boolean, index=True, default=False ), - Column( "purged", Boolean, index=True, default=False ), - Column( "genome_build", TrimmedString( 40 ) ) ) - -LibraryItemInfoTemplateElement_table = Table( "library_item_info_template_element", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "optional", Boolean, index=True, default=True ), - Column( "deleted", Boolean, index=True, default=False ), - Column( "name", TEXT ), - Column( "description", TEXT ), - Column( "type", TEXT, default='string' ), - Column( "order_id", Integer ), - Column( "options", JSONType() ), - Column( "library_item_info_template_id", Integer, ForeignKey( "library_item_info_template.id" ) ) ) -Index( "ix_liite_library_item_info_template_id", LibraryItemInfoTemplateElement_table.c.library_item_info_template_id ) - -LibraryItemInfoTemplate_table = Table( "library_item_info_template", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "optional", Boolean, index=True, default=True ), - Column( "deleted", Boolean, index=True, default=False ), - Column( "name", TEXT ), - Column( "description", TEXT ), - Column( "item_count", Integer, default=0 ) ) - -LibraryInfoTemplateAssociation_table = Table( "library_info_template_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "library_id", Integer, ForeignKey( "library.id" ), nullable=True, index=True ), - Column( "library_item_info_template_id", Integer, ForeignKey( "library_item_info_template.id" ) ) ) -Index( "ix_lita_library_item_info_template_id", LibraryInfoTemplateAssociation_table.c.library_item_info_template_id ) - -LibraryFolderInfoTemplateAssociation_table = Table( "library_folder_info_template_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "library_folder_id", Integer, ForeignKey( "library_folder.id" ), nullable=True, index=True ), - Column( "library_item_info_template_id", Integer, ForeignKey( "library_item_info_template.id" ) ) ) -Index( "ix_lfita_library_item_info_template_id", LibraryFolderInfoTemplateAssociation_table.c.library_item_info_template_id ) - -LibraryDatasetInfoTemplateAssociation_table = Table( "library_dataset_info_template_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "library_dataset_id", Integer, ForeignKey( "library_dataset.id" ), nullable=True, index=True ), - Column( "library_item_info_template_id", Integer, ForeignKey( "library_item_info_template.id" ) ) ) -Index( "ix_ldita_library_item_info_template_id", LibraryDatasetInfoTemplateAssociation_table.c.library_item_info_template_id ) - -LibraryDatasetDatasetInfoTemplateAssociation_table = Table( "library_dataset_dataset_info_template_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "library_dataset_dataset_association_id", Integer, ForeignKey( "library_dataset_dataset_association.id" ), nullable=True ), - Column( "library_item_info_template_id", Integer, ForeignKey( "library_item_info_template.id" ) ) ) -Index( "ix_lddita_library_dataset_dataset_association_id", LibraryDatasetDatasetInfoTemplateAssociation_table.c.library_dataset_dataset_association_id ) -Index( "ix_lddita_library_item_info_template_id", LibraryDatasetDatasetInfoTemplateAssociation_table.c.library_item_info_template_id ) - -LibraryItemInfoElement_table = Table( "library_item_info_element", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "contents", JSONType() ), - Column( "library_item_info_id", Integer, ForeignKey( "library_item_info.id" ), index=True ), - Column( "library_item_info_template_element_id", Integer, ForeignKey( "library_item_info_template_element.id" ) ) ) -Index( "ix_liie_library_item_info_template_element_id", LibraryItemInfoElement_table.c.library_item_info_template_element_id ) - -LibraryItemInfo_table = Table( "library_item_info", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "deleted", Boolean, index=True, default=False ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), nullable=True, index=True ), - Column( "library_item_info_template_id", Integer, ForeignKey( "library_item_info_template.id" ), nullable=True, index=True ) ) - -LibraryInfoAssociation_table = Table( "library_info_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "library_id", Integer, ForeignKey( "library.id" ), nullable=True, index=True ), - Column( "library_item_info_id", Integer, ForeignKey( "library_item_info.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), nullable=True, index=True ) ) - -LibraryFolderInfoAssociation_table = Table( "library_folder_info_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "library_folder_id", Integer, ForeignKey( "library_folder.id" ), nullable=True, index=True ), - Column( "library_item_info_id", Integer, ForeignKey( "library_item_info.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), nullable=True, index=True ) ) - -LibraryDatasetInfoAssociation_table = Table( "library_dataset_info_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "library_dataset_id", Integer, ForeignKey( "library_dataset.id" ), nullable=True, index=True ), - Column( "library_item_info_id", Integer, ForeignKey( "library_item_info.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), nullable=True, index=True ) ) - -LibraryDatasetDatasetInfoAssociation_table = Table( "library_dataset_dataset_info_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "library_dataset_dataset_association_id", Integer, ForeignKey( "library_dataset_dataset_association.id" ), nullable=True ), - Column( "library_item_info_id", Integer, ForeignKey( "library_item_info.id" ) ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), nullable=True, index=True ) ) -Index( "ix_lddia_library_dataset_dataset_association_id", LibraryDatasetDatasetInfoAssociation_table.c.library_dataset_dataset_association_id ) -Index( "ix_lddia_library_item_info_id", LibraryDatasetDatasetInfoAssociation_table.c.library_item_info_id ) - -JobExternalOutputMetadata_table = Table( "job_external_output_metadata", metadata, - Column( "id", Integer, primary_key=True ), - Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ), - Column( "history_dataset_association_id", Integer, ForeignKey( "history_dataset_association.id" ), index=True, nullable=True ), - Column( "library_dataset_dataset_association_id", Integer, ForeignKey( "library_dataset_dataset_association.id" ), nullable=True ), - Column( "filename_in", String( 255 ) ), - Column( "filename_out", String( 255 ) ), - Column( "filename_results_code", String( 255 ) ), - Column( "filename_kwds", String( 255 ) ), - Column( "job_runner_external_pid", String( 255 ) ) ) -Index( "ix_jeom_library_dataset_dataset_association_id", JobExternalOutputMetadata_table.c.library_dataset_dataset_association_id ) +Group_table = Table("galaxy_group", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("name", String(255), index=True, unique=True), + Column("deleted", Boolean, index=True, default=False)) + +UserGroupAssociation_table = Table("user_group_association", metadata, + Column("id", Integer, primary_key=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("group_id", Integer, ForeignKey("galaxy_group.id"), index=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now)) + +UserRoleAssociation_table = Table("user_role_association", metadata, + Column("id", Integer, primary_key=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("role_id", Integer, ForeignKey("role.id"), index=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now)) + +GroupRoleAssociation_table = Table("group_role_association", metadata, + Column("id", Integer, primary_key=True), + Column("group_id", Integer, ForeignKey("galaxy_group.id"), index=True), + Column("role_id", Integer, ForeignKey("role.id"), index=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now)) + +Role_table = Table("role", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("name", String(255), index=True, unique=True), + Column("description", TEXT), + Column("type", String(40), index=True), + Column("deleted", Boolean, index=True, default=False)) + +DatasetPermissions_table = Table("dataset_permissions", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("action", TEXT), + Column("dataset_id", Integer, ForeignKey("dataset.id"), index=True), + Column("role_id", Integer, ForeignKey("role.id"), index=True)) + +LibraryPermissions_table = Table("library_permissions", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("action", TEXT), + Column("library_id", Integer, ForeignKey("library.id"), nullable=True, index=True), + Column("role_id", Integer, ForeignKey("role.id"), index=True)) + +LibraryFolderPermissions_table = Table("library_folder_permissions", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("action", TEXT), + Column("library_folder_id", Integer, ForeignKey("library_folder.id"), nullable=True, index=True), + Column("role_id", Integer, ForeignKey("role.id"), index=True)) + +LibraryDatasetPermissions_table = Table("library_dataset_permissions", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("action", TEXT), + Column("library_dataset_id", Integer, ForeignKey("library_dataset.id"), nullable=True, index=True), + Column("role_id", Integer, ForeignKey("role.id"), index=True)) + +LibraryDatasetDatasetAssociationPermissions_table = Table("library_dataset_dataset_association_permissions", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("action", TEXT), + Column("library_dataset_dataset_association_id", Integer, ForeignKey("library_dataset_dataset_association.id"), nullable=True), + Column("role_id", Integer, ForeignKey("role.id"), index=True)) +Index("ix_lddap_library_dataset_dataset_association_id", LibraryDatasetDatasetAssociationPermissions_table.c.library_dataset_dataset_association_id) + +LibraryItemInfoPermissions_table = Table("library_item_info_permissions", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("action", TEXT), + Column("library_item_info_id", Integer, ForeignKey("library_item_info.id"), nullable=True, index=True), + Column("role_id", Integer, ForeignKey("role.id"), index=True)) + +LibraryItemInfoTemplatePermissions_table = Table("library_item_info_template_permissions", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("action", TEXT), + Column("library_item_info_template_id", Integer, ForeignKey("library_item_info_template.id"), nullable=True), + Column("role_id", Integer, ForeignKey("role.id"), index=True)) +Index("ix_liitp_library_item_info_template_id", LibraryItemInfoTemplatePermissions_table.c.library_item_info_template_id) + +DefaultUserPermissions_table = Table("default_user_permissions", metadata, + Column("id", Integer, primary_key=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("action", TEXT), + Column("role_id", Integer, ForeignKey("role.id"), index=True)) + +DefaultHistoryPermissions_table = Table("default_history_permissions", metadata, + Column("id", Integer, primary_key=True), + Column("history_id", Integer, ForeignKey("history.id"), index=True), + Column("action", TEXT), + Column("role_id", Integer, ForeignKey("role.id"), index=True)) + +LibraryDataset_table = Table("library_dataset", metadata, + Column("id", Integer, primary_key=True), + Column("library_dataset_dataset_association_id", Integer, ForeignKey("library_dataset_dataset_association.id", use_alter=True, name="library_dataset_dataset_association_id_fk"), nullable=True, index=True), # current version of dataset, if null, there is not a current version selected + Column("folder_id", Integer, ForeignKey("library_folder.id"), index=True), + Column("order_id", Integer), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("name", TrimmedString(255), key="_name"), # when not None/null this will supercede display in library (but not when imported into user's history?) + Column("info", TrimmedString(255), key="_info"), # when not None/null this will supercede display in library (but not when imported into user's history?) + Column("deleted", Boolean, index=True, default=False)) + +LibraryDatasetDatasetAssociation_table = Table("library_dataset_dataset_association", metadata, + Column("id", Integer, primary_key=True), + Column("library_dataset_id", Integer, ForeignKey("library_dataset.id"), index=True), + Column("dataset_id", Integer, ForeignKey("dataset.id"), index=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("copied_from_history_dataset_association_id", Integer, ForeignKey("history_dataset_association.id", use_alter=True, name='history_dataset_association_dataset_id_fkey'), nullable=True), + Column("copied_from_library_dataset_dataset_association_id", Integer, ForeignKey("library_dataset_dataset_association.id", use_alter=True, name='library_dataset_dataset_association_id_fkey'), nullable=True), + Column("name", TrimmedString(255)), + Column("info", TrimmedString(255)), + Column("blurb", TrimmedString(255)), + Column("peek", TEXT), + Column("extension", TrimmedString(64)), + Column("metadata", MetadataType(), key="_metadata"), + Column("parent_id", Integer, ForeignKey("library_dataset_dataset_association.id"), nullable=True), + Column("designation", TrimmedString(255)), + Column("deleted", Boolean, index=True, default=False), + Column("visible", Boolean), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("message", TrimmedString(255))) + +Library_table = Table("library", metadata, + Column("id", Integer, primary_key=True), + Column("root_folder_id", Integer, ForeignKey("library_folder.id"), index=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("name", String(255), index=True), + Column("deleted", Boolean, index=True, default=False), + Column("purged", Boolean, index=True, default=False), + Column("description", TEXT)) + +LibraryFolder_table = Table("library_folder", metadata, + Column("id", Integer, primary_key=True), + Column("parent_id", Integer, ForeignKey("library_folder.id"), nullable=True, index=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("name", TEXT), + Column("description", TEXT), + Column("order_id", Integer), + Column("item_count", Integer), + Column("deleted", Boolean, index=True, default=False), + Column("purged", Boolean, index=True, default=False), + Column("genome_build", TrimmedString(40))) + +LibraryItemInfoTemplateElement_table = Table("library_item_info_template_element", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("optional", Boolean, index=True, default=True), + Column("deleted", Boolean, index=True, default=False), + Column("name", TEXT), + Column("description", TEXT), + Column("type", TEXT, default='string'), + Column("order_id", Integer), + Column("options", JSONType()), + Column("library_item_info_template_id", Integer, ForeignKey("library_item_info_template.id"))) +Index("ix_liite_library_item_info_template_id", LibraryItemInfoTemplateElement_table.c.library_item_info_template_id) + +LibraryItemInfoTemplate_table = Table("library_item_info_template", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("optional", Boolean, index=True, default=True), + Column("deleted", Boolean, index=True, default=False), + Column("name", TEXT), + Column("description", TEXT), + Column("item_count", Integer, default=0)) + +LibraryInfoTemplateAssociation_table = Table("library_info_template_association", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("library_id", Integer, ForeignKey("library.id"), nullable=True, index=True), + Column("library_item_info_template_id", Integer, ForeignKey("library_item_info_template.id"))) +Index("ix_lita_library_item_info_template_id", LibraryInfoTemplateAssociation_table.c.library_item_info_template_id) + +LibraryFolderInfoTemplateAssociation_table = Table("library_folder_info_template_association", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("library_folder_id", Integer, ForeignKey("library_folder.id"), nullable=True, index=True), + Column("library_item_info_template_id", Integer, ForeignKey("library_item_info_template.id"))) +Index("ix_lfita_library_item_info_template_id", LibraryFolderInfoTemplateAssociation_table.c.library_item_info_template_id) + +LibraryDatasetInfoTemplateAssociation_table = Table("library_dataset_info_template_association", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("library_dataset_id", Integer, ForeignKey("library_dataset.id"), nullable=True, index=True), + Column("library_item_info_template_id", Integer, ForeignKey("library_item_info_template.id"))) +Index("ix_ldita_library_item_info_template_id", LibraryDatasetInfoTemplateAssociation_table.c.library_item_info_template_id) + +LibraryDatasetDatasetInfoTemplateAssociation_table = Table("library_dataset_dataset_info_template_association", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("library_dataset_dataset_association_id", Integer, ForeignKey("library_dataset_dataset_association.id"), nullable=True), + Column("library_item_info_template_id", Integer, ForeignKey("library_item_info_template.id"))) +Index("ix_lddita_library_dataset_dataset_association_id", LibraryDatasetDatasetInfoTemplateAssociation_table.c.library_dataset_dataset_association_id) +Index("ix_lddita_library_item_info_template_id", LibraryDatasetDatasetInfoTemplateAssociation_table.c.library_item_info_template_id) + +LibraryItemInfoElement_table = Table("library_item_info_element", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("contents", JSONType()), + Column("library_item_info_id", Integer, ForeignKey("library_item_info.id"), index=True), + Column("library_item_info_template_element_id", Integer, ForeignKey("library_item_info_template_element.id"))) +Index("ix_liie_library_item_info_template_element_id", LibraryItemInfoElement_table.c.library_item_info_template_element_id) + +LibraryItemInfo_table = Table("library_item_info", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("deleted", Boolean, index=True, default=False), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), nullable=True, index=True), + Column("library_item_info_template_id", Integer, ForeignKey("library_item_info_template.id"), nullable=True, index=True)) + +LibraryInfoAssociation_table = Table("library_info_association", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("library_id", Integer, ForeignKey("library.id"), nullable=True, index=True), + Column("library_item_info_id", Integer, ForeignKey("library_item_info.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), nullable=True, index=True)) + +LibraryFolderInfoAssociation_table = Table("library_folder_info_association", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("library_folder_id", Integer, ForeignKey("library_folder.id"), nullable=True, index=True), + Column("library_item_info_id", Integer, ForeignKey("library_item_info.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), nullable=True, index=True)) + +LibraryDatasetInfoAssociation_table = Table("library_dataset_info_association", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("library_dataset_id", Integer, ForeignKey("library_dataset.id"), nullable=True, index=True), + Column("library_item_info_id", Integer, ForeignKey("library_item_info.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), nullable=True, index=True)) + +LibraryDatasetDatasetInfoAssociation_table = Table("library_dataset_dataset_info_association", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("library_dataset_dataset_association_id", Integer, ForeignKey("library_dataset_dataset_association.id"), nullable=True), + Column("library_item_info_id", Integer, ForeignKey("library_item_info.id")), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), nullable=True, index=True)) +Index("ix_lddia_library_dataset_dataset_association_id", LibraryDatasetDatasetInfoAssociation_table.c.library_dataset_dataset_association_id) +Index("ix_lddia_library_item_info_id", LibraryDatasetDatasetInfoAssociation_table.c.library_item_info_id) + +JobExternalOutputMetadata_table = Table("job_external_output_metadata", metadata, + Column("id", Integer, primary_key=True), + Column("job_id", Integer, ForeignKey("job.id"), index=True), + Column("history_dataset_association_id", Integer, ForeignKey("history_dataset_association.id"), index=True, nullable=True), + Column("library_dataset_dataset_association_id", Integer, ForeignKey("library_dataset_dataset_association.id"), nullable=True), + Column("filename_in", String(255)), + Column("filename_out", String(255)), + Column("filename_results_code", String(255)), + Column("filename_kwds", String(255)), + Column("job_runner_external_pid", String(255))) +Index("ix_jeom_library_dataset_dataset_association_id", JobExternalOutputMetadata_table.c.library_dataset_dataset_association_id) def engine_false(migrate_engine): @@ -318,13 +318,13 @@ def upgrade(migrate_engine): # Load existing tables metadata.reflect() - def nextval( table, col='id' ): + def nextval(table, col='id'): if migrate_engine.name in ['postgres', 'postgresql']: - return "nextval('%s_%s_seq')" % ( table, col ) + return "nextval('%s_%s_seq')" % (table, col) elif migrate_engine.name in ['mysql', 'sqlite']: return "null" else: - raise Exception( 'Unable to convert data for unknown database type: %s' % migrate_engine.name ) + raise Exception('Unable to convert data for unknown database type: %s' % migrate_engine.name) def localtimestamp(): if migrate_engine.name in ['mysql', 'postgres', 'postgresql']: @@ -332,78 +332,78 @@ def localtimestamp(): elif migrate_engine.name == 'sqlite': return "current_date || ' ' || current_time" else: - raise Exception( 'Unable to convert data for unknown database type: %s' % migrate_engine.name ) + raise Exception('Unable to convert data for unknown database type: %s' % migrate_engine.name) # Add 2 new columns to the galaxy_user table try: - User_table = Table( "galaxy_user", metadata, autoload=True ) + User_table = Table("galaxy_user", metadata, autoload=True) except NoSuchTableError: User_table = None - log.debug( "Failed loading table galaxy_user" ) + log.debug("Failed loading table galaxy_user") if User_table is not None: try: - col = Column( 'deleted', Boolean, index=True, default=False ) - col.create( User_table, index_name='ix_user_deleted') + col = Column('deleted', Boolean, index=True, default=False) + col.create(User_table, index_name='ix_user_deleted') assert col is User_table.c.deleted except Exception: log.exception("Adding column 'deleted' to galaxy_user table failed.") try: - col = Column( 'purged', Boolean, index=True, default=False ) - col.create( User_table, index_name='ix_user_purged') + col = Column('purged', Boolean, index=True, default=False) + col.create(User_table, index_name='ix_user_purged') assert col is User_table.c.purged except Exception: log.exception("Adding column 'purged' to galaxy_user table failed.") # Add 1 new column to the history_dataset_association table try: - HistoryDatasetAssociation_table = Table( "history_dataset_association", metadata, autoload=True ) + HistoryDatasetAssociation_table = Table("history_dataset_association", metadata, autoload=True) except NoSuchTableError: HistoryDatasetAssociation_table = None - log.debug( "Failed loading table history_dataset_association" ) + log.debug("Failed loading table history_dataset_association") if HistoryDatasetAssociation_table is not None: try: - col = Column( 'copied_from_library_dataset_dataset_association_id', Integer, nullable=True ) - col.create( HistoryDatasetAssociation_table) + col = Column('copied_from_library_dataset_dataset_association_id', Integer, nullable=True) + col.create(HistoryDatasetAssociation_table) assert col is HistoryDatasetAssociation_table.c.copied_from_library_dataset_dataset_association_id except Exception: log.exception("Adding column 'copied_from_library_dataset_dataset_association_id' to history_dataset_association table failed.") # Add 1 new column to the metadata_file table try: - MetadataFile_table = Table( "metadata_file", metadata, autoload=True ) + MetadataFile_table = Table("metadata_file", metadata, autoload=True) except NoSuchTableError: MetadataFile_table = None - log.debug( "Failed loading table metadata_file" ) + log.debug("Failed loading table metadata_file") if MetadataFile_table is not None: try: - col = Column( 'lda_id', Integer, index=True, nullable=True ) - col.create( MetadataFile_table, index_name='ix_metadata_file_lda_id') + col = Column('lda_id', Integer, index=True, nullable=True) + col.create(MetadataFile_table, index_name='ix_metadata_file_lda_id') assert col is MetadataFile_table.c.lda_id except Exception: log.exception("Adding column 'lda_id' to metadata_file table failed.") # Add 1 new column to the stored_workflow table - changeset 2328 try: - StoredWorkflow_table = Table( "stored_workflow", metadata, - Column( "latest_workflow_id", Integer, - ForeignKey( "workflow.id", use_alter=True, name='stored_workflow_latest_workflow_id_fk' ), index=True ), - autoload=True, extend_existing=True ) + StoredWorkflow_table = Table("stored_workflow", metadata, + Column("latest_workflow_id", Integer, + ForeignKey("workflow.id", use_alter=True, name='stored_workflow_latest_workflow_id_fk'), index=True), + autoload=True, extend_existing=True) except NoSuchTableError: StoredWorkflow_table = None - log.debug( "Failed loading table stored_workflow" ) + log.debug("Failed loading table stored_workflow") if StoredWorkflow_table is not None: try: - col = Column( 'importable', Boolean, default=False ) - col.create( StoredWorkflow_table ) + col = Column('importable', Boolean, default=False) + col.create(StoredWorkflow_table) assert col is StoredWorkflow_table.c.importable except Exception: log.exception("Adding column 'importable' to stored_workflow table failed.") # Create an index on the Job.state column - changeset 2192 try: - Job_table = Table( "job", metadata, autoload=True ) + Job_table = Table("job", metadata, autoload=True) except NoSuchTableError: Job_table = None - log.debug( "Failed loading table job" ) + log.debug("Failed loading table job") if Job_table is not None: try: - i = Index( 'ix_job_state', Job_table.c.state ) + i = Index('ix_job_state', Job_table.c.state) i.create() except Exception: log.exception("Adding index to job.state column failed.") @@ -411,52 +411,52 @@ def localtimestamp(): metadata.create_all() # Add 1 foreign key constraint to the history_dataset_association table try: - HistoryDatasetAssociation_table = Table( "history_dataset_association", metadata, autoload=True ) + HistoryDatasetAssociation_table = Table("history_dataset_association", metadata, autoload=True) except NoSuchTableError: HistoryDatasetAssociation_table = None - log.debug( "Failed loading table history_dataset_association" ) + log.debug("Failed loading table history_dataset_association") try: - LibraryDatasetDatasetAssociation_table = Table( "library_dataset_dataset_association", metadata, autoload=True ) + LibraryDatasetDatasetAssociation_table = Table("library_dataset_dataset_association", metadata, autoload=True) except NoSuchTableError: LibraryDatasetDatasetAssociation_table = None - log.debug( "Failed loading table library_dataset_dataset_association" ) + log.debug("Failed loading table library_dataset_dataset_association") if HistoryDatasetAssociation_table is not None and LibraryDatasetDatasetAssociation_table is not None: try: - cons = ForeignKeyConstraint( [HistoryDatasetAssociation_table.c.copied_from_library_dataset_dataset_association_id], - [LibraryDatasetDatasetAssociation_table.c.id], - name='history_dataset_association_copied_from_library_dataset_da_fkey' ) + cons = ForeignKeyConstraint([HistoryDatasetAssociation_table.c.copied_from_library_dataset_dataset_association_id], + [LibraryDatasetDatasetAssociation_table.c.id], + name='history_dataset_association_copied_from_library_dataset_da_fkey') # Create the constraint cons.create() except Exception: log.exception("Adding foreign key constraint 'history_dataset_association_copied_from_library_dataset_da_fkey' to table 'history_dataset_association' failed.") # Add 1 foreign key constraint to the metadata_file table try: - MetadataFile_table = Table( "metadata_file", metadata, autoload=True ) + MetadataFile_table = Table("metadata_file", metadata, autoload=True) except NoSuchTableError: MetadataFile_table = None - log.debug( "Failed loading table metadata_file" ) + log.debug("Failed loading table metadata_file") try: - LibraryDatasetDatasetAssociation_table = Table( "library_dataset_dataset_association", metadata, autoload=True ) + LibraryDatasetDatasetAssociation_table = Table("library_dataset_dataset_association", metadata, autoload=True) except NoSuchTableError: LibraryDatasetDatasetAssociation_table = None - log.debug( "Failed loading table library_dataset_dataset_association" ) + log.debug("Failed loading table library_dataset_dataset_association") if migrate_engine.name != 'sqlite': # Sqlite can't alter table add foreign key. if MetadataFile_table is not None and LibraryDatasetDatasetAssociation_table is not None: try: - cons = ForeignKeyConstraint( [MetadataFile_table.c.lda_id], - [LibraryDatasetDatasetAssociation_table.c.id], - name='metadata_file_lda_id_fkey' ) + cons = ForeignKeyConstraint([MetadataFile_table.c.lda_id], + [LibraryDatasetDatasetAssociation_table.c.id], + name='metadata_file_lda_id_fkey') # Create the constraint cons.create() except Exception: log.exception("Adding foreign key constraint 'metadata_file_lda_id_fkey' to table 'metadata_file' failed.") # Make sure we have at least 1 user cmd = "SELECT * FROM galaxy_user;" - users = migrate_engine.execute( cmd ).fetchall() + users = migrate_engine.execute(cmd).fetchall() if users: cmd = "SELECT * FROM role;" - roles = migrate_engine.execute( cmd ).fetchall() + roles = migrate_engine.execute(cmd).fetchall() if not roles: # Create private roles for each user - pass 1 cmd = \ @@ -470,14 +470,14 @@ def localtimestamp(): "%s AS deleted " + \ "FROM galaxy_user " + \ "ORDER BY id;" - cmd = cmd % ( nextval('role'), localtimestamp(), localtimestamp(), engine_false(migrate_engine) ) - migrate_engine.execute( cmd ) + cmd = cmd % (nextval('role'), localtimestamp(), localtimestamp(), engine_false(migrate_engine)) + migrate_engine.execute(cmd) # Create private roles for each user - pass 2 if migrate_engine.name in ['postgres', 'postgresql', 'sqlite']: cmd = "UPDATE role SET description = 'Private role for ' || description;" elif migrate_engine.name == 'mysql': cmd = "UPDATE role SET description = CONCAT( 'Private role for ', description );" - migrate_engine.execute( cmd ) + migrate_engine.execute(cmd) # Create private roles for each user - pass 3 cmd = \ "INSERT INTO user_role_association " + \ @@ -489,8 +489,8 @@ def localtimestamp(): "FROM galaxy_user, role " + \ "WHERE galaxy_user.email = role.name " + \ "ORDER BY galaxy_user.id;" - cmd = cmd % ( nextval('user_role_association'), localtimestamp(), localtimestamp() ) - migrate_engine.execute( cmd ) + cmd = cmd % (nextval('user_role_association'), localtimestamp(), localtimestamp()) + migrate_engine.execute(cmd) # Create default permissions for each user cmd = \ "INSERT INTO default_user_permissions " + \ @@ -502,7 +502,7 @@ def localtimestamp(): "JOIN user_role_association ON user_role_association.user_id = galaxy_user.id " + \ "ORDER BY galaxy_user.id;" cmd = cmd % nextval('default_user_permissions') - migrate_engine.execute( cmd ) + migrate_engine.execute(cmd) # Create default history permissions for each active history associated with a user cmd = \ @@ -514,8 +514,8 @@ def localtimestamp(): "FROM history " + \ "JOIN user_role_association ON user_role_association.user_id = history.user_id " + \ "WHERE history.purged = %s AND history.user_id IS NOT NULL;" - cmd = cmd % ( nextval('default_history_permissions'), engine_false(migrate_engine) ) - migrate_engine.execute( cmd ) + cmd = cmd % (nextval('default_history_permissions'), engine_false(migrate_engine)) + migrate_engine.execute(cmd) # Create "manage permissions" dataset_permissions for all activate-able datasets cmd = \ "INSERT INTO dataset_permissions " + \ @@ -530,8 +530,8 @@ def localtimestamp(): "JOIN dataset ON history_dataset_association.dataset_id = dataset.id " + \ "JOIN user_role_association ON user_role_association.user_id = history.user_id " + \ "WHERE dataset.purged = %s AND history.user_id IS NOT NULL;" - cmd = cmd % ( nextval('dataset_permissions'), localtimestamp(), localtimestamp(), engine_false(migrate_engine) ) - migrate_engine.execute( cmd ) + cmd = cmd % (nextval('dataset_permissions'), localtimestamp(), localtimestamp(), engine_false(migrate_engine)) + migrate_engine.execute(cmd) def downgrade(migrate_engine): @@ -541,40 +541,40 @@ def downgrade(migrate_engine): # NOTE: all new data added in the upgrade method is eliminated here via table drops # Drop 1 foreign key constraint from the metadata_file table try: - MetadataFile_table = Table( "metadata_file", metadata, autoload=True ) + MetadataFile_table = Table("metadata_file", metadata, autoload=True) except NoSuchTableError: MetadataFile_table = None - log.debug( "Failed loading table metadata_file" ) + log.debug("Failed loading table metadata_file") try: - LibraryDatasetDatasetAssociation_table = Table( "library_dataset_dataset_association", metadata, autoload=True ) + LibraryDatasetDatasetAssociation_table = Table("library_dataset_dataset_association", metadata, autoload=True) except NoSuchTableError: LibraryDatasetDatasetAssociation_table = None - log.debug( "Failed loading table library_dataset_dataset_association" ) + log.debug("Failed loading table library_dataset_dataset_association") if MetadataFile_table is not None and LibraryDatasetDatasetAssociation_table is not None: try: - cons = ForeignKeyConstraint( [MetadataFile_table.c.lda_id], - [LibraryDatasetDatasetAssociation_table.c.id], - name='metadata_file_lda_id_fkey' ) + cons = ForeignKeyConstraint([MetadataFile_table.c.lda_id], + [LibraryDatasetDatasetAssociation_table.c.id], + name='metadata_file_lda_id_fkey') # Drop the constraint cons.drop() except Exception: log.exception("Dropping foreign key constraint 'metadata_file_lda_id_fkey' from table 'metadata_file' failed.") # Drop 1 foreign key constraint from the history_dataset_association table try: - HistoryDatasetAssociation_table = Table( "history_dataset_association", metadata, autoload=True ) + HistoryDatasetAssociation_table = Table("history_dataset_association", metadata, autoload=True) except NoSuchTableError: HistoryDatasetAssociation_table = None - log.debug( "Failed loading table history_dataset_association" ) + log.debug("Failed loading table history_dataset_association") try: - LibraryDatasetDatasetAssociation_table = Table( "library_dataset_dataset_association", metadata, autoload=True ) + LibraryDatasetDatasetAssociation_table = Table("library_dataset_dataset_association", metadata, autoload=True) except NoSuchTableError: LibraryDatasetDatasetAssociation_table = None - log.debug( "Failed loading table library_dataset_dataset_association" ) + log.debug("Failed loading table library_dataset_dataset_association") if HistoryDatasetAssociation_table is not None and LibraryDatasetDatasetAssociation_table is not None: try: - cons = ForeignKeyConstraint( [HistoryDatasetAssociation_table.c.copied_from_library_dataset_dataset_association_id], - [LibraryDatasetDatasetAssociation_table.c.id], - name='history_dataset_association_copied_from_library_dataset_da_fkey' ) + cons = ForeignKeyConstraint([HistoryDatasetAssociation_table.c.copied_from_library_dataset_dataset_association_id], + [LibraryDatasetDatasetAssociation_table.c.id], + name='history_dataset_association_copied_from_library_dataset_da_fkey') # Drop the constraint cons.drop() except Exception: @@ -706,22 +706,22 @@ def downgrade(migrate_engine): log.exception("Dropping library_item_info_template table failed.") # Drop the index on the Job.state column - changeset 2192 try: - Job_table = Table( "job", metadata, autoload=True ) + Job_table = Table("job", metadata, autoload=True) except NoSuchTableError: Job_table = None - log.debug( "Failed loading table job" ) + log.debug("Failed loading table job") if Job_table is not None: try: - i = Index( 'ix_job_state', Job_table.c.state ) + i = Index('ix_job_state', Job_table.c.state) i.drop() except Exception: log.exception("Dropping index from job.state column failed.") # Drop 1 column from the stored_workflow table - changeset 2328 try: - StoredWorkflow_table = Table( "stored_workflow", metadata, autoload=True ) + StoredWorkflow_table = Table("stored_workflow", metadata, autoload=True) except NoSuchTableError: StoredWorkflow_table = None - log.debug( "Failed loading table stored_workflow" ) + log.debug("Failed loading table stored_workflow") if StoredWorkflow_table is not None: try: col = StoredWorkflow_table.c.importable @@ -730,10 +730,10 @@ def downgrade(migrate_engine): log.exception("Dropping column 'importable' from stored_workflow table failed.") # Drop 1 column from the metadata_file table try: - MetadataFile_table = Table( "metadata_file", metadata, autoload=True ) + MetadataFile_table = Table("metadata_file", metadata, autoload=True) except NoSuchTableError: MetadataFile_table = None - log.debug( "Failed loading table metadata_file" ) + log.debug("Failed loading table metadata_file") if MetadataFile_table is not None: try: col = MetadataFile_table.c.lda_id @@ -742,10 +742,10 @@ def downgrade(migrate_engine): log.exception("Dropping column 'lda_id' from metadata_file table failed.") # Drop 1 column from the history_dataset_association table try: - HistoryDatasetAssociation_table = Table( "history_dataset_association", metadata, autoload=True ) + HistoryDatasetAssociation_table = Table("history_dataset_association", metadata, autoload=True) except NoSuchTableError: HistoryDatasetAssociation_table = None - log.debug( "Failed loading table history_dataset_association" ) + log.debug("Failed loading table history_dataset_association") if HistoryDatasetAssociation_table is not None: try: col = HistoryDatasetAssociation_table.c.copied_from_library_dataset_dataset_association_id @@ -754,10 +754,10 @@ def downgrade(migrate_engine): log.exception("Dropping column 'copied_from_library_dataset_dataset_association_id' from history_dataset_association table failed.") # Drop 2 columns from the galaxy_user table try: - User_table = Table( "galaxy_user", metadata, autoload=True ) + User_table = Table("galaxy_user", metadata, autoload=True) except NoSuchTableError: User_table = None - log.debug( "Failed loading table galaxy_user" ) + log.debug("Failed loading table galaxy_user") if User_table is not None: try: col = User_table.c.deleted diff --git a/lib/galaxy/model/migrate/versions/0004_indexes_and_defaults.py b/lib/galaxy/model/migrate/versions/0004_indexes_and_defaults.py index a13222980e50..17876bbb4bcf 100644 --- a/lib/galaxy/model/migrate/versions/0004_indexes_and_defaults.py +++ b/lib/galaxy/model/migrate/versions/0004_indexes_and_defaults.py @@ -3,13 +3,13 @@ from sqlalchemy import Index, MetaData, Table -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) -handler = logging.StreamHandler( sys.stdout ) +handler = logging.StreamHandler(sys.stdout) format = "%(name)s %(levelname)s %(asctime)s %(message)s" -formatter = logging.Formatter( format ) -handler.setFormatter( formatter ) -log.addHandler( handler ) +formatter = logging.Formatter(format) +handler.setFormatter(formatter) +log.addHandler(handler) metadata = MetaData() @@ -25,17 +25,17 @@ def engine_false(migrate_engine): def upgrade(migrate_engine): metadata.bind = migrate_engine - User_table = Table( "galaxy_user", metadata, autoload=True ) - HistoryDatasetAssociation_table = Table( "history_dataset_association", metadata, autoload=True ) + User_table = Table("galaxy_user", metadata, autoload=True) + HistoryDatasetAssociation_table = Table("history_dataset_association", metadata, autoload=True) # Load existing tables metadata.reflect() # Add 2 indexes to the galaxy_user table - i = Index( 'ix_galaxy_user_deleted', User_table.c.deleted ) + i = Index('ix_galaxy_user_deleted', User_table.c.deleted) try: i.create() except Exception: log.exception("Adding index 'ix_galaxy_user_deleted' to galaxy_user table failed.") - i = Index( 'ix_galaxy_user_purged', User_table.c.purged ) + i = Index('ix_galaxy_user_purged', User_table.c.purged) try: i.create() except Exception: @@ -43,16 +43,16 @@ def upgrade(migrate_engine): # Set the default data in the galaxy_user table, but only for null values cmd = "UPDATE galaxy_user SET deleted = %s WHERE deleted is null" % engine_false(migrate_engine) try: - migrate_engine.execute( cmd ) + migrate_engine.execute(cmd) except Exception: log.exception("Setting default data for galaxy_user.deleted column failed.") cmd = "UPDATE galaxy_user SET purged = %s WHERE purged is null" % engine_false(migrate_engine) try: - migrate_engine.execute( cmd ) + migrate_engine.execute(cmd) except Exception: log.exception("Setting default data for galaxy_user.purged column failed.") # Add 1 index to the history_dataset_association table - i = Index( 'ix_hda_copied_from_library_dataset_dataset_association_id', HistoryDatasetAssociation_table.c.copied_from_library_dataset_dataset_association_id ) + i = Index('ix_hda_copied_from_library_dataset_dataset_association_id', HistoryDatasetAssociation_table.c.copied_from_library_dataset_dataset_association_id) try: i.create() except Exception: diff --git a/lib/galaxy/model/migrate/versions/0005_cleanup_datasets_fix.py b/lib/galaxy/model/migrate/versions/0005_cleanup_datasets_fix.py index 9218ee796351..485a9904d077 100644 --- a/lib/galaxy/model/migrate/versions/0005_cleanup_datasets_fix.py +++ b/lib/galaxy/model/migrate/versions/0005_cleanup_datasets_fix.py @@ -11,46 +11,46 @@ from galaxy.model.metadata import MetadataCollection from galaxy.util.bunch import Bunch -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) now = datetime.datetime.utcnow metadata = MetaData() -context = scoped_session( sessionmaker( autoflush=False, autocommit=True ) ) +context = scoped_session(sessionmaker(autoflush=False, autocommit=True)) # classes -def get_permitted_actions( **kwds ): +def get_permitted_actions(**kwds): return Bunch() -def directory_hash_id( id ): - s = str( id ) - l = len( s ) +def directory_hash_id(id): + s = str(id) + l = len(s) # Shortcut -- ids 0-999 go under ../000/ if l < 4: - return [ "000" ] + return ["000"] # Pad with zeros until a multiple of three - padded = ( ( 3 - len( s ) % 3 ) * "0" ) + s + padded = ((3 - len(s) % 3) * "0") + s # Drop the last three digits -- 1000 files per directory padded = padded[:-3] # Break into chunks of three - return [ padded[i * 3:(i + 1) * 3] for i in range( len( padded ) // 3 ) ] - - -class Dataset( object ): - states = Bunch( NEW='new', - UPLOAD='upload', - QUEUED='queued', - RUNNING='running', - OK='ok', - EMPTY='empty', - ERROR='error', - DISCARDED='discarded' ) - permitted_actions = get_permitted_actions( filter='DATASET' ) + return [padded[i * 3:(i + 1) * 3] for i in range(len(padded) // 3)] + + +class Dataset(object): + states = Bunch(NEW='new', + UPLOAD='upload', + QUEUED='queued', + RUNNING='running', + OK='ok', + EMPTY='empty', + ERROR='error', + DISCARDED='discarded') + permitted_actions = get_permitted_actions(filter='DATASET') file_path = "/tmp/" engine = None - def __init__( self, id=None, state=None, external_filename=None, extra_files_path=None, file_size=None, purgable=True ): + def __init__(self, id=None, state=None, external_filename=None, extra_files_path=None, file_size=None, purgable=True): self.id = id self.state = state self.deleted = False @@ -60,71 +60,71 @@ def __init__( self, id=None, state=None, external_filename=None, extra_files_pat self._extra_files_path = extra_files_path self.file_size = file_size - def get_file_name( self ): + def get_file_name(self): if not self.external_filename: assert self.id is not None, "ID must be set before filename used (commit the object)" # First try filename directly under file_path - filename = os.path.join( self.file_path, "dataset_%d.dat" % self.id ) + filename = os.path.join(self.file_path, "dataset_%d.dat" % self.id) # Only use that filename if it already exists (backward compatibility), # otherwise construct hashed path - if not os.path.exists( filename ): - dir = os.path.join( self.file_path, *directory_hash_id( self.id ) ) + if not os.path.exists(filename): + dir = os.path.join(self.file_path, *directory_hash_id(self.id)) # Create directory if it does not exist try: - os.makedirs( dir ) + os.makedirs(dir) except OSError as e: # File Exists is okay, otherwise reraise if e.errno != errno.EEXIST: raise # Return filename inside hashed directory - return os.path.abspath( os.path.join( dir, "dataset_%d.dat" % self.id ) ) + return os.path.abspath(os.path.join(dir, "dataset_%d.dat" % self.id)) else: filename = self.external_filename # Make filename absolute - return os.path.abspath( filename ) + return os.path.abspath(filename) - def set_file_name( self, filename ): + def set_file_name(self, filename): if not filename: self.external_filename = None else: self.external_filename = filename - file_name = property( get_file_name, set_file_name ) + file_name = property(get_file_name, set_file_name) @property - def extra_files_path( self ): + def extra_files_path(self): if self._extra_files_path: path = self._extra_files_path else: - path = os.path.join( self.file_path, "dataset_%d_files" % self.id ) + path = os.path.join(self.file_path, "dataset_%d_files" % self.id) # only use path directly under self.file_path if it exists - if not os.path.exists( path ): - path = os.path.join( os.path.join( self.file_path, *directory_hash_id( self.id ) ), "dataset_%d_files" % self.id ) + if not os.path.exists(path): + path = os.path.join(os.path.join(self.file_path, *directory_hash_id(self.id)), "dataset_%d_files" % self.id) # Make path absolute - return os.path.abspath( path ) + return os.path.abspath(path) - def get_size( self ): + def get_size(self): """Returns the size of the data on disk""" if self.file_size: return self.file_size else: try: - return os.path.getsize( self.file_name ) + return os.path.getsize(self.file_name) except OSError: return 0 - def set_size( self ): + def set_size(self): """Returns the size of the data on disk""" try: if not self.file_size: - self.file_size = os.path.getsize( self.file_name ) + self.file_size = os.path.getsize(self.file_name) except OSError: self.file_size = 0 - def has_data( self ): + def has_data(self): """Detects whether there is any data""" return self.get_size() > 0 - def mark_deleted( self, include_children=True ): + def mark_deleted(self, include_children=True): self.deleted = True # FIXME: sqlalchemy will replace this @@ -136,14 +136,14 @@ def _delete(self): log.critical('%s delete error %s' % (self.__class__.__name__, e)) -class DatasetInstance( object ): +class DatasetInstance(object): """A base class for all 'dataset instances', HDAs, LDAs, etc""" states = Dataset.states permitted_actions = Dataset.permitted_actions - def __init__( self, id=None, hid=None, name=None, info=None, blurb=None, peek=None, extension=None, - dbkey=None, metadata=None, history=None, dataset=None, deleted=False, designation=None, - parent_id=None, validation_errors=None, visible=True, create_dataset=False ): + def __init__(self, id=None, hid=None, name=None, info=None, blurb=None, peek=None, extension=None, + dbkey=None, metadata=None, history=None, dataset=None, deleted=False, designation=None, + parent_id=None, validation_errors=None, visible=True, create_dataset=False): self.name = name or "Unnamed dataset" self.id = id self.info = info @@ -158,50 +158,50 @@ def __init__( self, id=None, hid=None, name=None, info=None, blurb=None, peek=No self.visible = visible # Relationships if not dataset and create_dataset: - dataset = Dataset( state=Dataset.states.NEW ) - context.add( dataset ) + dataset = Dataset(state=Dataset.states.NEW) + context.add(dataset) context.flush() self.dataset = dataset self.parent_id = parent_id self.validation_errors = validation_errors @property - def ext( self ): + def ext(self): return self.extension - def get_dataset_state( self ): + def get_dataset_state(self): return self.dataset.state - def set_dataset_state( self, state ): + def set_dataset_state(self, state): self.dataset.state = state - context.add( self.dataset ) + context.add(self.dataset) context.flush() # flush here, because hda.flush() won't flush the Dataset object - state = property( get_dataset_state, set_dataset_state ) + state = property(get_dataset_state, set_dataset_state) - def get_file_name( self ): + def get_file_name(self): return self.dataset.get_file_name() def set_file_name(self, filename): - return self.dataset.set_file_name( filename ) - file_name = property( get_file_name, set_file_name ) + return self.dataset.set_file_name(filename) + file_name = property(get_file_name, set_file_name) @property - def extra_files_path( self ): + def extra_files_path(self): return self.dataset.extra_files_path - def get_metadata( self ): - if not hasattr( self, '_metadata_collection' ) or self._metadata_collection.parent != self: # using weakref to store parent (to prevent circ ref), does a context.clear() cause parent to be invalidated, while still copying over this non-database attribute? - self._metadata_collection = MetadataCollection( self ) + def get_metadata(self): + if not hasattr(self, '_metadata_collection') or self._metadata_collection.parent != self: # using weakref to store parent (to prevent circ ref), does a context.clear() cause parent to be invalidated, while still copying over this non-database attribute? + self._metadata_collection = MetadataCollection(self) return self._metadata_collection - def set_metadata( self, bunch ): + def set_metadata(self, bunch): # Needs to accept a MetadataCollection, a bunch, or a dict - self._metadata = self.metadata.make_dict_copy( bunch ) - metadata = property( get_metadata, set_metadata ) + self._metadata = self.metadata.make_dict_copy(bunch) + metadata = property(get_metadata, set_metadata) # This provide backwards compatibility with using the old dbkey # field in the database. That field now maps to "old_dbkey" (see mapping.py). - def get_dbkey( self ): + def get_dbkey(self): dbkey = self.metadata.dbkey if not isinstance(dbkey, list): dbkey = [dbkey] @@ -209,72 +209,72 @@ def get_dbkey( self ): return "?" return dbkey[0] - def set_dbkey( self, value ): + def set_dbkey(self, value): if "dbkey" in self.datatype.metadata_spec: if not isinstance(value, list): self.metadata.dbkey = [value] else: self.metadata.dbkey = value - dbkey = property( get_dbkey, set_dbkey ) + dbkey = property(get_dbkey, set_dbkey) - def get_size( self ): + def get_size(self): """Returns the size of the data on disk""" return self.dataset.get_size() - def set_size( self ): + def set_size(self): """Returns the size of the data on disk""" return self.dataset.set_size() - def has_data( self ): + def has_data(self): """Detects whether there is any data""" return self.dataset.has_data() - def get_raw_data( self ): + def get_raw_data(self): """Returns the full data. To stream it open the file_name and read/write as needed""" - return self.datatype.get_raw_data( self ) + return self.datatype.get_raw_data(self) - def write_from_stream( self, stream ): + def write_from_stream(self, stream): """Writes data from a stream""" self.datatype.write_from_stream(self, stream) - def set_raw_data( self, data ): + def set_raw_data(self, data): """Saves the data on the disc""" self.datatype.set_raw_data(self, data) - def set_peek( self, is_multi_byte=False ): - return self.datatype.set_peek( self, is_multi_byte=is_multi_byte ) + def set_peek(self, is_multi_byte=False): + return self.datatype.set_peek(self, is_multi_byte=is_multi_byte) - def init_meta( self, copy_from=None ): - return self.datatype.init_meta( self, copy_from=copy_from ) + def init_meta(self, copy_from=None): + return self.datatype.init_meta(self, copy_from=copy_from) - def set_meta( self, **kwd ): - self.clear_associated_files( metadata_safe=True ) - return self.datatype.set_meta( self, **kwd ) + def set_meta(self, **kwd): + self.clear_associated_files(metadata_safe=True) + return self.datatype.set_meta(self, **kwd) - def missing_meta( self, **kwd ): - return self.datatype.missing_meta( self, **kwd ) + def missing_meta(self, **kwd): + return self.datatype.missing_meta(self, **kwd) - def as_display_type( self, type, **kwd ): - return self.datatype.as_display_type( self, type, **kwd ) + def as_display_type(self, type, **kwd): + return self.datatype.as_display_type(self, type, **kwd) - def display_peek( self ): - return self.datatype.display_peek( self ) + def display_peek(self): + return self.datatype.display_peek(self) - def display_name( self ): - return self.datatype.display_name( self ) + def display_name(self): + return self.datatype.display_name(self) - def display_info( self ): - return self.datatype.display_info( self ) + def display_info(self): + return self.datatype.display_info(self) - def get_converted_files_by_type( self, file_type ): + def get_converted_files_by_type(self, file_type): valid = [] for assoc in self.implicitly_converted_datasets: if not assoc.deleted and assoc.type == file_type: - valid.append( assoc.dataset ) + valid.append(assoc.dataset) return valid - def clear_associated_files( self, metadata_safe=False, purge=False ): - raise Exception( 'Unimplemented' ) + def clear_associated_files(self, metadata_safe=False, purge=False): + raise Exception('Unimplemented') def get_child_by_designation(self, designation): for child in self.children: @@ -282,230 +282,230 @@ def get_child_by_designation(self, designation): return child return None - def add_validation_error( self, validation_error ): - self.validation_errors.append( validation_error ) + def add_validation_error(self, validation_error): + self.validation_errors.append(validation_error) - def extend_validation_errors( self, validation_errors ): + def extend_validation_errors(self, validation_errors): self.validation_errors.extend(validation_errors) - def mark_deleted( self, include_children=True ): + def mark_deleted(self, include_children=True): self.deleted = True if include_children: for child in self.children: child.mark_deleted() - def mark_undeleted( self, include_children=True ): + def mark_undeleted(self, include_children=True): self.deleted = False if include_children: for child in self.children: child.mark_undeleted() - def undeletable( self ): + def undeletable(self): if self.purged: return False return True @property - def source_library_dataset( self ): - def get_source( dataset ): - if isinstance( dataset, LibraryDatasetDatasetAssociation ): + def source_library_dataset(self): + def get_source(dataset): + if isinstance(dataset, LibraryDatasetDatasetAssociation): if dataset.library_dataset: - return ( dataset, dataset.library_dataset ) + return (dataset, dataset.library_dataset) if dataset.copied_from_library_dataset_dataset_association: - source = get_source( dataset.copied_from_library_dataset_dataset_association ) + source = get_source(dataset.copied_from_library_dataset_dataset_association) if source: return source if dataset.copied_from_history_dataset_association: - source = get_source( dataset.copied_from_history_dataset_association ) + source = get_source(dataset.copied_from_history_dataset_association) if source: return source - return ( None, None ) - return get_source( self ) - - -class HistoryDatasetAssociation( DatasetInstance ): - def __init__( self, - hid=None, - history=None, - copied_from_history_dataset_association=None, - copied_from_library_dataset_dataset_association=None, - **kwd ): - DatasetInstance.__init__( self, **kwd ) + return (None, None) + return get_source(self) + + +class HistoryDatasetAssociation(DatasetInstance): + def __init__(self, + hid=None, + history=None, + copied_from_history_dataset_association=None, + copied_from_library_dataset_dataset_association=None, + **kwd): + DatasetInstance.__init__(self, **kwd) self.hid = hid # Relationships self.history = history self.copied_from_history_dataset_association = copied_from_history_dataset_association self.copied_from_library_dataset_dataset_association = copied_from_library_dataset_dataset_association - def copy( self, copy_children=False, parent_id=None, target_history=None ): - hda = HistoryDatasetAssociation( hid=self.hid, - name=self.name, - info=self.info, - blurb=self.blurb, - peek=self.peek, - extension=self.extension, - dbkey=self.dbkey, - dataset=self.dataset, - visible=self.visible, - deleted=self.deleted, - parent_id=parent_id, - copied_from_history_dataset_association=self, - history=target_history ) - context.add( hda ) + def copy(self, copy_children=False, parent_id=None, target_history=None): + hda = HistoryDatasetAssociation(hid=self.hid, + name=self.name, + info=self.info, + blurb=self.blurb, + peek=self.peek, + extension=self.extension, + dbkey=self.dbkey, + dataset=self.dataset, + visible=self.visible, + deleted=self.deleted, + parent_id=parent_id, + copied_from_history_dataset_association=self, + history=target_history) + context.add(hda) context.flush() hda.set_size() # Need to set after flushed, as MetadataFiles require dataset.id hda.metadata = self.metadata if copy_children: for child in self.children: - child.copy( copy_children=copy_children, parent_id=hda.id ) + child.copy(copy_children=copy_children, parent_id=hda.id) if not self.datatype.copy_safe_peek: # In some instances peek relies on dataset_id, i.e. gmaj.zip for viewing MAFs hda.set_peek() context.flush() return hda - def to_library_dataset_dataset_association( self, target_folder, replace_dataset=None, parent_id=None ): + def to_library_dataset_dataset_association(self, target_folder, replace_dataset=None, parent_id=None): if replace_dataset: # The replace_dataset param ( when not None ) refers to a LibraryDataset that is being replaced with a new version. library_dataset = replace_dataset else: # If replace_dataset is None, the Library level permissions will be taken from the folder and applied to the new # LibraryDataset, and the current user's DefaultUserPermissions will be applied to the associated Dataset. - library_dataset = LibraryDataset( folder=target_folder, name=self.name, info=self.info ) - context.add( library_dataset ) + library_dataset = LibraryDataset(folder=target_folder, name=self.name, info=self.info) + context.add(library_dataset) context.flush() - ldda = LibraryDatasetDatasetAssociation( name=self.name, - info=self.info, - blurb=self.blurb, - peek=self.peek, - extension=self.extension, - dbkey=self.dbkey, - dataset=self.dataset, - library_dataset=library_dataset, - visible=self.visible, - deleted=self.deleted, - parent_id=parent_id, - copied_from_history_dataset_association=self, - user=self.history.user ) - context.add( ldda ) + ldda = LibraryDatasetDatasetAssociation(name=self.name, + info=self.info, + blurb=self.blurb, + peek=self.peek, + extension=self.extension, + dbkey=self.dbkey, + dataset=self.dataset, + library_dataset=library_dataset, + visible=self.visible, + deleted=self.deleted, + parent_id=parent_id, + copied_from_history_dataset_association=self, + user=self.history.user) + context.add(ldda) context.flush() # Permissions must be the same on the LibraryDatasetDatasetAssociation and the associated LibraryDataset # Must set metadata after ldda flushed, as MetadataFiles require ldda.id ldda.metadata = self.metadata if not replace_dataset: - target_folder.add_library_dataset( library_dataset, genome_build=ldda.dbkey ) - context.add( target_folder ) + target_folder.add_library_dataset(library_dataset, genome_build=ldda.dbkey) + context.add(target_folder) context.flush() library_dataset.library_dataset_dataset_association_id = ldda.id - context.add( library_dataset ) + context.add(library_dataset) context.flush() for child in self.children: - child.to_library_dataset_dataset_association( target_folder=target_folder, replace_dataset=replace_dataset, parent_id=ldda.id ) + child.to_library_dataset_dataset_association(target_folder=target_folder, replace_dataset=replace_dataset, parent_id=ldda.id) if not self.datatype.copy_safe_peek: # In some instances peek relies on dataset_id, i.e. gmaj.zip for viewing MAFs ldda.set_peek() context.flush() return ldda - def clear_associated_files( self, metadata_safe=False, purge=False ): + def clear_associated_files(self, metadata_safe=False, purge=False): # metadata_safe = True means to only clear when assoc.metadata_safe == False for assoc in self.implicitly_converted_datasets: if not metadata_safe or not assoc.metadata_safe: - assoc.clear( purge=purge ) + assoc.clear(purge=purge) -class LibraryDatasetDatasetAssociation( DatasetInstance ): - def __init__( self, - copied_from_history_dataset_association=None, - copied_from_library_dataset_dataset_association=None, - library_dataset=None, - user=None, - **kwd ): - DatasetInstance.__init__( self, **kwd ) +class LibraryDatasetDatasetAssociation(DatasetInstance): + def __init__(self, + copied_from_history_dataset_association=None, + copied_from_library_dataset_dataset_association=None, + library_dataset=None, + user=None, + **kwd): + DatasetInstance.__init__(self, **kwd) self.copied_from_history_dataset_association = copied_from_history_dataset_association self.copied_from_library_dataset_dataset_association = copied_from_library_dataset_dataset_association self.library_dataset = library_dataset self.user = user - def to_history_dataset_association( self, target_history, parent_id=None ): + def to_history_dataset_association(self, target_history, parent_id=None): hid = target_history._next_hid() - hda = HistoryDatasetAssociation( name=self.name, - info=self.info, - blurb=self.blurb, - peek=self.peek, - extension=self.extension, - dbkey=self.dbkey, - dataset=self.dataset, - visible=self.visible, - deleted=self.deleted, - parent_id=parent_id, - copied_from_library_dataset_dataset_association=self, - history=target_history, - hid=hid ) - context.add( hda ) + hda = HistoryDatasetAssociation(name=self.name, + info=self.info, + blurb=self.blurb, + peek=self.peek, + extension=self.extension, + dbkey=self.dbkey, + dataset=self.dataset, + visible=self.visible, + deleted=self.deleted, + parent_id=parent_id, + copied_from_library_dataset_dataset_association=self, + history=target_history, + hid=hid) + context.add(hda) context.flush() hda.metadata = self.metadata # need to set after flushed, as MetadataFiles require dataset.id for child in self.children: - child.to_history_dataset_association( target_history=target_history, parent_id=hda.id ) + child.to_history_dataset_association(target_history=target_history, parent_id=hda.id) if not self.datatype.copy_safe_peek: hda.set_peek() # in some instances peek relies on dataset_id, i.e. gmaj.zip for viewing MAFs context.flush() return hda - def copy( self, copy_children=False, parent_id=None, target_folder=None ): - ldda = LibraryDatasetDatasetAssociation( name=self.name, - info=self.info, - blurb=self.blurb, - peek=self.peek, - extension=self.extension, - dbkey=self.dbkey, - dataset=self.dataset, - visible=self.visible, - deleted=self.deleted, - parent_id=parent_id, - copied_from_library_dataset_dataset_association=self, - folder=target_folder ) - context.add( ldda ) + def copy(self, copy_children=False, parent_id=None, target_folder=None): + ldda = LibraryDatasetDatasetAssociation(name=self.name, + info=self.info, + blurb=self.blurb, + peek=self.peek, + extension=self.extension, + dbkey=self.dbkey, + dataset=self.dataset, + visible=self.visible, + deleted=self.deleted, + parent_id=parent_id, + copied_from_library_dataset_dataset_association=self, + folder=target_folder) + context.add(ldda) context.flush() # Need to set after flushed, as MetadataFiles require dataset.id ldda.metadata = self.metadata if copy_children: for child in self.children: - child.copy( copy_children=copy_children, parent_id=ldda.id ) + child.copy(copy_children=copy_children, parent_id=ldda.id) if not self.datatype.copy_safe_peek: # In some instances peek relies on dataset_id, i.e. gmaj.zip for viewing MAFs ldda.set_peek() context.flush() return ldda - def clear_associated_files( self, metadata_safe=False, purge=False ): + def clear_associated_files(self, metadata_safe=False, purge=False): return - def get_library_item_info_templates( self, template_list=[], restrict=False ): + def get_library_item_info_templates(self, template_list=[], restrict=False): # If restrict is True, we'll return only those templates directly associated with this LibraryDatasetDatasetAssociation if self.library_dataset_dataset_info_template_associations: - template_list.extend( [ lddita.library_item_info_template for lddita in self.library_dataset_dataset_info_template_associations if lddita.library_item_info_template not in template_list ] ) - self.library_dataset.get_library_item_info_templates( template_list, restrict ) + template_list.extend([lddita.library_item_info_template for lddita in self.library_dataset_dataset_info_template_associations if lddita.library_item_info_template not in template_list]) + self.library_dataset.get_library_item_info_templates(template_list, restrict) return template_list -class LibraryDataset( object ): +class LibraryDataset(object): # This class acts as a proxy to the currently selected LDDA - def __init__( self, folder=None, order_id=None, name=None, info=None, library_dataset_dataset_association=None, **kwd ): + def __init__(self, folder=None, order_id=None, name=None, info=None, library_dataset_dataset_association=None, **kwd): self.folder = folder self.order_id = order_id self.name = name self.info = info self.library_dataset_dataset_association = library_dataset_dataset_association - def set_library_dataset_dataset_association( self, ldda ): + def set_library_dataset_dataset_association(self, ldda): self.library_dataset_dataset_association = ldda ldda.library_dataset = self - context.add_all( ( self, ldda ) ) + context.add_all((self, ldda)) context.flush() - def get_info( self ): + def get_info(self): if self.library_dataset_dataset_association: return self.library_dataset_dataset_association.info elif self._info: @@ -513,11 +513,11 @@ def get_info( self ): else: return 'no info' - def set_info( self, info ): + def set_info(self, info): self._info = info - info = property( get_info, set_info ) + info = property(get_info, set_info) - def get_name( self ): + def get_name(self): if self.library_dataset_dataset_association: return self.library_dataset_dataset_association.name elif self._name: @@ -525,173 +525,173 @@ def get_name( self ): else: return 'Unnamed dataset' - def set_name( self, name ): + def set_name(self, name): self._name = name - name = property( get_name, set_name ) + name = property(get_name, set_name) - def display_name( self ): + def display_name(self): self.library_dataset_dataset_association.display_name() - def get_purged( self ): + def get_purged(self): return self.library_dataset_dataset_association.dataset.purged - def set_purged( self, purged ): + def set_purged(self, purged): if purged: - raise Exception( "Not implemented" ) + raise Exception("Not implemented") if not purged and self.purged: - raise Exception( "Cannot unpurge once purged" ) - purged = property( get_purged, set_purged ) + raise Exception("Cannot unpurge once purged") + purged = property(get_purged, set_purged) - def get_library_item_info_templates( self, template_list=[], restrict=False ): + def get_library_item_info_templates(self, template_list=[], restrict=False): # If restrict is True, we'll return only those templates directly associated with this LibraryDataset if self.library_dataset_info_template_associations: - template_list.extend( [ ldita.library_item_info_template for ldita in self.library_dataset_info_template_associations if ldita.library_item_info_template not in template_list ] ) - if restrict not in [ 'True', True ]: - self.folder.get_library_item_info_templates( template_list, restrict ) + template_list.extend([ldita.library_item_info_template for ldita in self.library_dataset_info_template_associations if ldita.library_item_info_template not in template_list]) + if restrict not in ['True', True]: + self.folder.get_library_item_info_templates(template_list, restrict) return template_list # tables -Dataset.table = Table( "dataset", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, index=True, default=now, onupdate=now ), - Column( "state", TrimmedString( 64 ) ), - Column( "deleted", Boolean, index=True, default=False ), - Column( "purged", Boolean, index=True, default=False ), - Column( "purgable", Boolean, default=True ), - Column( "external_filename", TEXT ), - Column( "_extra_files_path", TEXT ), - Column( 'file_size', Numeric( 15, 0 ) ) ) - - -HistoryDatasetAssociation.table = Table( "history_dataset_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "dataset_id", Integer, ForeignKey( "dataset.id" ), index=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "copied_from_history_dataset_association_id", Integer, ForeignKey( "history_dataset_association.id" ), nullable=True ), - Column( "copied_from_library_dataset_dataset_association_id", Integer, ForeignKey( "library_dataset_dataset_association.id" ), nullable=True ), - Column( "hid", Integer ), - Column( "name", TrimmedString( 255 ) ), - Column( "info", TrimmedString( 255 ) ), - Column( "blurb", TrimmedString( 255 ) ), - Column( "peek", TEXT ), - Column( "extension", TrimmedString( 64 ) ), - Column( "metadata", MetadataType(), key="_metadata" ), - Column( "parent_id", Integer, ForeignKey( "history_dataset_association.id" ), nullable=True ), - Column( "designation", TrimmedString( 255 ) ), - Column( "deleted", Boolean, index=True, default=False ), - Column( "visible", Boolean ) ) - - -LibraryDatasetDatasetAssociation.table = Table( "library_dataset_dataset_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "library_dataset_id", Integer, ForeignKey( "library_dataset.id" ), index=True ), - Column( "dataset_id", Integer, ForeignKey( "dataset.id" ), index=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "copied_from_history_dataset_association_id", Integer, ForeignKey( "history_dataset_association.id", use_alter=True, name='history_dataset_association_dataset_id_fkey' ), nullable=True ), - Column( "copied_from_library_dataset_dataset_association_id", Integer, ForeignKey( "library_dataset_dataset_association.id", use_alter=True, name='library_dataset_dataset_association_id_fkey' ), nullable=True ), - Column( "name", TrimmedString( 255 ) ), - Column( "info", TrimmedString( 255 ) ), - Column( "blurb", TrimmedString( 255 ) ), - Column( "peek", TEXT ), - Column( "extension", TrimmedString( 64 ) ), - Column( "metadata", MetadataType(), key="_metadata" ), - Column( "parent_id", Integer, ForeignKey( "library_dataset_dataset_association.id" ), nullable=True ), - Column( "designation", TrimmedString( 255 ) ), - Column( "deleted", Boolean, index=True, default=False ), - Column( "visible", Boolean ), - Column( "message", TrimmedString( 255 ) ) ) - -LibraryDataset.table = Table( "library_dataset", metadata, - Column( "id", Integer, primary_key=True ), - Column( "library_dataset_dataset_association_id", Integer, ForeignKey( "library_dataset_dataset_association.id", use_alter=True, name="library_dataset_dataset_association_id_fk" ), nullable=True, index=True ), # current version of dataset, if null, there is not a current version selected - Column( "order_id", Integer ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "name", TrimmedString( 255 ), key="_name" ), # when not None/null this will supercede display in library (but not when imported into user's history?) - Column( "info", TrimmedString( 255 ), key="_info" ), # when not None/null this will supercede display in library (but not when imported into user's history?) - Column( "deleted", Boolean, index=True, default=False ) ) +Dataset.table = Table("dataset", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, index=True, default=now, onupdate=now), + Column("state", TrimmedString(64)), + Column("deleted", Boolean, index=True, default=False), + Column("purged", Boolean, index=True, default=False), + Column("purgable", Boolean, default=True), + Column("external_filename", TEXT), + Column("_extra_files_path", TEXT), + Column('file_size', Numeric(15, 0))) + + +HistoryDatasetAssociation.table = Table("history_dataset_association", metadata, + Column("id", Integer, primary_key=True), + Column("dataset_id", Integer, ForeignKey("dataset.id"), index=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("copied_from_history_dataset_association_id", Integer, ForeignKey("history_dataset_association.id"), nullable=True), + Column("copied_from_library_dataset_dataset_association_id", Integer, ForeignKey("library_dataset_dataset_association.id"), nullable=True), + Column("hid", Integer), + Column("name", TrimmedString(255)), + Column("info", TrimmedString(255)), + Column("blurb", TrimmedString(255)), + Column("peek", TEXT), + Column("extension", TrimmedString(64)), + Column("metadata", MetadataType(), key="_metadata"), + Column("parent_id", Integer, ForeignKey("history_dataset_association.id"), nullable=True), + Column("designation", TrimmedString(255)), + Column("deleted", Boolean, index=True, default=False), + Column("visible", Boolean)) + + +LibraryDatasetDatasetAssociation.table = Table("library_dataset_dataset_association", metadata, + Column("id", Integer, primary_key=True), + Column("library_dataset_id", Integer, ForeignKey("library_dataset.id"), index=True), + Column("dataset_id", Integer, ForeignKey("dataset.id"), index=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("copied_from_history_dataset_association_id", Integer, ForeignKey("history_dataset_association.id", use_alter=True, name='history_dataset_association_dataset_id_fkey'), nullable=True), + Column("copied_from_library_dataset_dataset_association_id", Integer, ForeignKey("library_dataset_dataset_association.id", use_alter=True, name='library_dataset_dataset_association_id_fkey'), nullable=True), + Column("name", TrimmedString(255)), + Column("info", TrimmedString(255)), + Column("blurb", TrimmedString(255)), + Column("peek", TEXT), + Column("extension", TrimmedString(64)), + Column("metadata", MetadataType(), key="_metadata"), + Column("parent_id", Integer, ForeignKey("library_dataset_dataset_association.id"), nullable=True), + Column("designation", TrimmedString(255)), + Column("deleted", Boolean, index=True, default=False), + Column("visible", Boolean), + Column("message", TrimmedString(255))) + +LibraryDataset.table = Table("library_dataset", metadata, + Column("id", Integer, primary_key=True), + Column("library_dataset_dataset_association_id", Integer, ForeignKey("library_dataset_dataset_association.id", use_alter=True, name="library_dataset_dataset_association_id_fk"), nullable=True, index=True), # current version of dataset, if null, there is not a current version selected + Column("order_id", Integer), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("name", TrimmedString(255), key="_name"), # when not None/null this will supercede display in library (but not when imported into user's history?) + Column("info", TrimmedString(255), key="_info"), # when not None/null this will supercede display in library (but not when imported into user's history?) + Column("deleted", Boolean, index=True, default=False)) # mappers -mapper( Dataset, Dataset.table, +mapper(Dataset, Dataset.table, properties=dict( history_associations=relation( HistoryDatasetAssociation, - primaryjoin=( Dataset.table.c.id == HistoryDatasetAssociation.table.c.dataset_id ) ), + primaryjoin=(Dataset.table.c.id == HistoryDatasetAssociation.table.c.dataset_id)), active_history_associations=relation( HistoryDatasetAssociation, - primaryjoin=( ( Dataset.table.c.id == HistoryDatasetAssociation.table.c.dataset_id ) & ( HistoryDatasetAssociation.table.c.deleted == false() ) ) ), + primaryjoin=((Dataset.table.c.id == HistoryDatasetAssociation.table.c.dataset_id) & (HistoryDatasetAssociation.table.c.deleted == false()))), library_associations=relation( LibraryDatasetDatasetAssociation, - primaryjoin=( Dataset.table.c.id == LibraryDatasetDatasetAssociation.table.c.dataset_id ) ), + primaryjoin=(Dataset.table.c.id == LibraryDatasetDatasetAssociation.table.c.dataset_id)), active_library_associations=relation( LibraryDatasetDatasetAssociation, - primaryjoin=( ( Dataset.table.c.id == LibraryDatasetDatasetAssociation.table.c.dataset_id ) & ( LibraryDatasetDatasetAssociation.table.c.deleted == false() ) ) ) ) ) + primaryjoin=((Dataset.table.c.id == LibraryDatasetDatasetAssociation.table.c.dataset_id) & (LibraryDatasetDatasetAssociation.table.c.deleted == false()))))) -mapper( HistoryDatasetAssociation, HistoryDatasetAssociation.table, +mapper(HistoryDatasetAssociation, HistoryDatasetAssociation.table, properties=dict( dataset=relation( Dataset, - primaryjoin=( Dataset.table.c.id == HistoryDatasetAssociation.table.c.dataset_id ), lazy=False ), + primaryjoin=(Dataset.table.c.id == HistoryDatasetAssociation.table.c.dataset_id), lazy=False), # .history defined in History mapper copied_to_history_dataset_associations=relation( HistoryDatasetAssociation, - primaryjoin=( HistoryDatasetAssociation.table.c.copied_from_history_dataset_association_id == HistoryDatasetAssociation.table.c.id ), - backref=backref( "copied_from_history_dataset_association", primaryjoin=( HistoryDatasetAssociation.table.c.copied_from_history_dataset_association_id == HistoryDatasetAssociation.table.c.id ), remote_side=[HistoryDatasetAssociation.table.c.id], uselist=False ) ), + primaryjoin=(HistoryDatasetAssociation.table.c.copied_from_history_dataset_association_id == HistoryDatasetAssociation.table.c.id), + backref=backref("copied_from_history_dataset_association", primaryjoin=(HistoryDatasetAssociation.table.c.copied_from_history_dataset_association_id == HistoryDatasetAssociation.table.c.id), remote_side=[HistoryDatasetAssociation.table.c.id], uselist=False)), copied_to_library_dataset_dataset_associations=relation( LibraryDatasetDatasetAssociation, - primaryjoin=( HistoryDatasetAssociation.table.c.copied_from_library_dataset_dataset_association_id == LibraryDatasetDatasetAssociation.table.c.id ), - backref=backref( "copied_from_history_dataset_association", primaryjoin=( HistoryDatasetAssociation.table.c.copied_from_library_dataset_dataset_association_id == LibraryDatasetDatasetAssociation.table.c.id ), remote_side=[LibraryDatasetDatasetAssociation.table.c.id], uselist=False ) ), + primaryjoin=(HistoryDatasetAssociation.table.c.copied_from_library_dataset_dataset_association_id == LibraryDatasetDatasetAssociation.table.c.id), + backref=backref("copied_from_history_dataset_association", primaryjoin=(HistoryDatasetAssociation.table.c.copied_from_library_dataset_dataset_association_id == LibraryDatasetDatasetAssociation.table.c.id), remote_side=[LibraryDatasetDatasetAssociation.table.c.id], uselist=False)), children=relation( HistoryDatasetAssociation, - primaryjoin=( HistoryDatasetAssociation.table.c.parent_id == HistoryDatasetAssociation.table.c.id ), - backref=backref( "parent", primaryjoin=( HistoryDatasetAssociation.table.c.parent_id == HistoryDatasetAssociation.table.c.id ), remote_side=[HistoryDatasetAssociation.table.c.id], uselist=False ) ), + primaryjoin=(HistoryDatasetAssociation.table.c.parent_id == HistoryDatasetAssociation.table.c.id), + backref=backref("parent", primaryjoin=(HistoryDatasetAssociation.table.c.parent_id == HistoryDatasetAssociation.table.c.id), remote_side=[HistoryDatasetAssociation.table.c.id], uselist=False)), visible_children=relation( HistoryDatasetAssociation, - primaryjoin=( ( HistoryDatasetAssociation.table.c.parent_id == HistoryDatasetAssociation.table.c.id ) & ( HistoryDatasetAssociation.table.c.visible == true() ) ) ) ) ) + primaryjoin=((HistoryDatasetAssociation.table.c.parent_id == HistoryDatasetAssociation.table.c.id) & (HistoryDatasetAssociation.table.c.visible == true()))))) -mapper( LibraryDatasetDatasetAssociation, LibraryDatasetDatasetAssociation.table, +mapper(LibraryDatasetDatasetAssociation, LibraryDatasetDatasetAssociation.table, properties=dict( - dataset=relation( Dataset ), - library_dataset=relation( LibraryDataset, - primaryjoin=( LibraryDatasetDatasetAssociation.table.c.library_dataset_id == LibraryDataset.table.c.id ) ), + dataset=relation(Dataset), + library_dataset=relation(LibraryDataset, + primaryjoin=(LibraryDatasetDatasetAssociation.table.c.library_dataset_id == LibraryDataset.table.c.id)), copied_to_library_dataset_dataset_associations=relation( LibraryDatasetDatasetAssociation, - primaryjoin=( LibraryDatasetDatasetAssociation.table.c.copied_from_library_dataset_dataset_association_id == LibraryDatasetDatasetAssociation.table.c.id ), - backref=backref( "copied_from_library_dataset_dataset_association", primaryjoin=( LibraryDatasetDatasetAssociation.table.c.copied_from_library_dataset_dataset_association_id == LibraryDatasetDatasetAssociation.table.c.id ), remote_side=[LibraryDatasetDatasetAssociation.table.c.id] ) ), + primaryjoin=(LibraryDatasetDatasetAssociation.table.c.copied_from_library_dataset_dataset_association_id == LibraryDatasetDatasetAssociation.table.c.id), + backref=backref("copied_from_library_dataset_dataset_association", primaryjoin=(LibraryDatasetDatasetAssociation.table.c.copied_from_library_dataset_dataset_association_id == LibraryDatasetDatasetAssociation.table.c.id), remote_side=[LibraryDatasetDatasetAssociation.table.c.id])), copied_to_history_dataset_associations=relation( HistoryDatasetAssociation, - primaryjoin=( HistoryDatasetAssociation.table.c.copied_from_library_dataset_dataset_association_id == LibraryDatasetDatasetAssociation.table.c.id ), - backref=backref( "copied_from_library_dataset_dataset_association", primaryjoin=( HistoryDatasetAssociation.table.c.copied_from_library_dataset_dataset_association_id == LibraryDatasetDatasetAssociation.table.c.id ), remote_side=[LibraryDatasetDatasetAssociation.table.c.id], uselist=False ) ), + primaryjoin=(HistoryDatasetAssociation.table.c.copied_from_library_dataset_dataset_association_id == LibraryDatasetDatasetAssociation.table.c.id), + backref=backref("copied_from_library_dataset_dataset_association", primaryjoin=(HistoryDatasetAssociation.table.c.copied_from_library_dataset_dataset_association_id == LibraryDatasetDatasetAssociation.table.c.id), remote_side=[LibraryDatasetDatasetAssociation.table.c.id], uselist=False)), children=relation( LibraryDatasetDatasetAssociation, - primaryjoin=( LibraryDatasetDatasetAssociation.table.c.parent_id == LibraryDatasetDatasetAssociation.table.c.id ), - backref=backref( "parent", primaryjoin=( LibraryDatasetDatasetAssociation.table.c.parent_id == LibraryDatasetDatasetAssociation.table.c.id ), remote_side=[LibraryDatasetDatasetAssociation.table.c.id] ) ), + primaryjoin=(LibraryDatasetDatasetAssociation.table.c.parent_id == LibraryDatasetDatasetAssociation.table.c.id), + backref=backref("parent", primaryjoin=(LibraryDatasetDatasetAssociation.table.c.parent_id == LibraryDatasetDatasetAssociation.table.c.id), remote_side=[LibraryDatasetDatasetAssociation.table.c.id])), visible_children=relation( LibraryDatasetDatasetAssociation, - primaryjoin=( ( LibraryDatasetDatasetAssociation.table.c.parent_id == LibraryDatasetDatasetAssociation.table.c.id ) & ( LibraryDatasetDatasetAssociation.table.c.visible == true() ) ) ) ) ) + primaryjoin=((LibraryDatasetDatasetAssociation.table.c.parent_id == LibraryDatasetDatasetAssociation.table.c.id) & (LibraryDatasetDatasetAssociation.table.c.visible == true()))))) -mapper( LibraryDataset, LibraryDataset.table, +mapper(LibraryDataset, LibraryDataset.table, properties=dict( - library_dataset_dataset_association=relation( LibraryDatasetDatasetAssociation, primaryjoin=( LibraryDataset.table.c.library_dataset_dataset_association_id == LibraryDatasetDatasetAssociation.table.c.id ) ), - expired_datasets=relation( LibraryDatasetDatasetAssociation, foreign_keys=[LibraryDataset.table.c.id, LibraryDataset.table.c.library_dataset_dataset_association_id ], primaryjoin=( ( LibraryDataset.table.c.id == LibraryDatasetDatasetAssociation.table.c.library_dataset_id ) & ( not_( LibraryDataset.table.c.library_dataset_dataset_association_id == LibraryDatasetDatasetAssociation.table.c.id ) ) ), viewonly=True, uselist=True ) ) ) + library_dataset_dataset_association=relation(LibraryDatasetDatasetAssociation, primaryjoin=(LibraryDataset.table.c.library_dataset_dataset_association_id == LibraryDatasetDatasetAssociation.table.c.id)), + expired_datasets=relation(LibraryDatasetDatasetAssociation, foreign_keys=[LibraryDataset.table.c.id, LibraryDataset.table.c.library_dataset_dataset_association_id], primaryjoin=((LibraryDataset.table.c.id == LibraryDatasetDatasetAssociation.table.c.library_dataset_id) & (not_(LibraryDataset.table.c.library_dataset_dataset_association_id == LibraryDatasetDatasetAssociation.table.c.id))), viewonly=True, uselist=True))) -def __guess_dataset_by_filename( filename ): +def __guess_dataset_by_filename(filename): """Return a guessed dataset by filename""" try: - fields = os.path.split( filename ) + fields = os.path.split(filename) if fields: - if fields[-1].startswith( 'dataset_' ) and fields[-1].endswith( '.dat' ): # dataset_%d.dat - return Dataset.get( int( fields[-1][ len( 'dataset_' ): -len( '.dat' ) ] ) ) + if fields[-1].startswith('dataset_') and fields[-1].endswith('.dat'): # dataset_%d.dat + return Dataset.get(int(fields[-1][len('dataset_'): -len('.dat')])) except: pass # some parsing error, we can't guess Dataset return None @@ -699,34 +699,34 @@ def __guess_dataset_by_filename( filename ): def upgrade(migrate_engine): metadata.bind = migrate_engine - log.debug( "Fixing a discrepancy concerning deleted shared history items." ) + log.debug("Fixing a discrepancy concerning deleted shared history items.") affected_items = 0 start_time = time.time() - for dataset in context.query( Dataset ).filter( and_( Dataset.deleted == true(), Dataset.purged == false() ) ): + for dataset in context.query(Dataset).filter(and_(Dataset.deleted == true(), Dataset.purged == false())): for dataset_instance in dataset.history_associations + dataset.library_associations: if not dataset_instance.deleted: dataset.deleted = False - if dataset.file_size in [ None, 0 ]: + if dataset.file_size in [None, 0]: dataset.set_size() # Restore filesize affected_items += 1 break context.flush() - log.debug( "%i items affected, and restored." % ( affected_items ) ) - log.debug( "Time elapsed: %s" % ( time.time() - start_time ) ) + log.debug("%i items affected, and restored." % (affected_items)) + log.debug("Time elapsed: %s" % (time.time() - start_time)) # fix share before hda - log.debug( "Fixing a discrepancy concerning cleaning up deleted history items shared before HDAs." ) + log.debug("Fixing a discrepancy concerning cleaning up deleted history items shared before HDAs.") dataset_by_filename = {} changed_associations = 0 start_time = time.time() - for dataset in context.query( Dataset ).filter( Dataset.external_filename.like( '%dataset_%.dat' ) ): + for dataset in context.query(Dataset).filter(Dataset.external_filename.like('%dataset_%.dat')): if dataset.file_name in dataset_by_filename: - guessed_dataset = dataset_by_filename[ dataset.file_name ] + guessed_dataset = dataset_by_filename[dataset.file_name] else: - guessed_dataset = __guess_dataset_by_filename( dataset.file_name ) + guessed_dataset = __guess_dataset_by_filename(dataset.file_name) if guessed_dataset and dataset.file_name != guessed_dataset.file_name: # not os.path.samefile( dataset.file_name, guessed_dataset.file_name ): guessed_dataset = None - dataset_by_filename[ dataset.file_name ] = guessed_dataset + dataset_by_filename[dataset.file_name] = guessed_dataset if guessed_dataset is not None and guessed_dataset.id != dataset.id: # could we have a self referential dataset? for dataset_instance in dataset.history_associations + dataset.library_associations: @@ -734,13 +734,13 @@ def upgrade(migrate_engine): changed_associations += 1 # mark original Dataset as deleted and purged, it is no longer in use, but do not delete file_name contents dataset.deleted = True - dataset.external_filename = "Dataset was result of share before HDA, and has been replaced: %s mapped to Dataset %s" % ( dataset.external_filename, guessed_dataset.id ) + dataset.external_filename = "Dataset was result of share before HDA, and has been replaced: %s mapped to Dataset %s" % (dataset.external_filename, guessed_dataset.id) dataset.purged = True # we don't really purge the file here, but we mark it as purged, since this dataset is now defunct context.flush() - log.debug( "%i items affected, and restored." % ( changed_associations ) ) - log.debug( "Time elapsed: %s" % ( time.time() - start_time ) ) + log.debug("%i items affected, and restored." % (changed_associations)) + log.debug("Time elapsed: %s" % (time.time() - start_time)) def downgrade(migrate_engine): metadata.bind = migrate_engine - log.debug( "Downgrade is not possible." ) + log.debug("Downgrade is not possible.") diff --git a/lib/galaxy/model/migrate/versions/0006_change_qual_datatype.py b/lib/galaxy/model/migrate/versions/0006_change_qual_datatype.py index 82e1071f2389..4dcc9bc5713d 100644 --- a/lib/galaxy/model/migrate/versions/0006_change_qual_datatype.py +++ b/lib/galaxy/model/migrate/versions/0006_change_qual_datatype.py @@ -10,13 +10,13 @@ from sqlalchemy import Index, MetaData, Table from sqlalchemy.orm import scoped_session, sessionmaker -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) -handler = logging.StreamHandler( sys.stdout ) +handler = logging.StreamHandler(sys.stdout) format = "%(name)s %(levelname)s %(asctime)s %(message)s" -formatter = logging.Formatter( format ) -handler.setFormatter( formatter ) -log.addHandler( handler ) +formatter = logging.Formatter(format) +handler.setFormatter(formatter) +log.addHandler(handler) metadata = MetaData() @@ -24,12 +24,12 @@ def upgrade(migrate_engine): print(__doc__) metadata.bind = migrate_engine - db_session = scoped_session( sessionmaker( bind=migrate_engine, autoflush=False, autocommit=True ) ) - HistoryDatasetAssociation_table = Table( "history_dataset_association", metadata, autoload=True ) + db_session = scoped_session(sessionmaker(bind=migrate_engine, autoflush=False, autocommit=True)) + HistoryDatasetAssociation_table = Table("history_dataset_association", metadata, autoload=True) # Load existing tables metadata.reflect() # Add 2 indexes to the galaxy_user table - i = Index( 'ix_hda_extension', HistoryDatasetAssociation_table.c.extension ) + i = Index('ix_hda_extension', HistoryDatasetAssociation_table.c.extension) try: i.create() except Exception: @@ -38,12 +38,12 @@ def upgrade(migrate_engine): # Set the default data in the galaxy_user table, but only for null values cmd = "UPDATE history_dataset_association SET extension = 'qual454' WHERE extension = 'qual' and peek like \'>%%\'" try: - db_session.execute( cmd ) + db_session.execute(cmd) except Exception: log.exception("Resetting extension qual to qual454 in history_dataset_association failed.") cmd = "UPDATE history_dataset_association SET extension = 'qualsolexa' WHERE extension = 'qual' and peek not like \'>%%\'" try: - db_session.execute( cmd ) + db_session.execute(cmd) except Exception: log.exception("Resetting extension qual to qualsolexa in history_dataset_association failed.") # Add 1 index to the history_dataset_association table diff --git a/lib/galaxy/model/migrate/versions/0007_sharing_histories.py b/lib/galaxy/model/migrate/versions/0007_sharing_histories.py index 103d4b44b945..1d6d3051e437 100644 --- a/lib/galaxy/model/migrate/versions/0007_sharing_histories.py +++ b/lib/galaxy/model/migrate/versions/0007_sharing_histories.py @@ -11,20 +11,20 @@ from sqlalchemy import Boolean, Column, ForeignKey, Integer, MetaData, Table from sqlalchemy.exc import NoSuchTableError -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) -handler = logging.StreamHandler( sys.stdout ) +handler = logging.StreamHandler(sys.stdout) format = "%(name)s %(levelname)s %(asctime)s %(message)s" -formatter = logging.Formatter( format ) -handler.setFormatter( formatter ) -log.addHandler( handler ) +formatter = logging.Formatter(format) +handler.setFormatter(formatter) +log.addHandler(handler) metadata = MetaData() -HistoryUserShareAssociation_table = Table( "history_user_share_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ) ) +HistoryUserShareAssociation_table = Table("history_user_share_association", metadata, + Column("id", Integer, primary_key=True), + Column("history_id", Integer, ForeignKey("history.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True)) def upgrade(migrate_engine): @@ -39,14 +39,14 @@ def upgrade(migrate_engine): log.exception("Creating history_user_share_association table failed.") # Add 1 column to the history table try: - History_table = Table( "history", metadata, autoload=True ) + History_table = Table("history", metadata, autoload=True) except NoSuchTableError: History_table = None - log.debug( "Failed loading table history" ) + log.debug("Failed loading table history") if History_table is not None: try: - col = Column( 'importable', Boolean, index=True, default=False ) - col.create( History_table, index_name='ix_history_importable') + col = Column('importable', Boolean, index=True, default=False) + col.create(History_table, index_name='ix_history_importable') assert col is History_table.c.importable except Exception: log.exception("Adding column 'importable' to history table failed.") @@ -58,10 +58,10 @@ def downgrade(migrate_engine): metadata.reflect() # Drop 1 column from the history table try: - History_table = Table( "history", metadata, autoload=True ) + History_table = Table("history", metadata, autoload=True) except NoSuchTableError: History_table = None - log.debug( "Failed loading table history" ) + log.debug("Failed loading table history") if History_table is not None: try: col = History_table.c.importable diff --git a/lib/galaxy/model/migrate/versions/0008_galaxy_forms.py b/lib/galaxy/model/migrate/versions/0008_galaxy_forms.py index 855470e1ce5d..59d80835b4d3 100644 --- a/lib/galaxy/model/migrate/versions/0008_galaxy_forms.py +++ b/lib/galaxy/model/migrate/versions/0008_galaxy_forms.py @@ -22,84 +22,84 @@ from galaxy.model.custom_types import JSONType, TrimmedString now = datetime.datetime.utcnow -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) -handler = logging.StreamHandler( sys.stdout ) +handler = logging.StreamHandler(sys.stdout) format = "%(name)s %(levelname)s %(asctime)s %(message)s" -formatter = logging.Formatter( format ) -handler.setFormatter( formatter ) -log.addHandler( handler ) +formatter = logging.Formatter(format) +handler.setFormatter(formatter) +log.addHandler(handler) metadata = MetaData() FormDefinitionCurrent_table = Table('form_definition_current', metadata, - Column( "id", Integer, primary_key=True), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "latest_form_id", Integer, index=True ), - Column( "deleted", Boolean, index=True, default=False )) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("latest_form_id", Integer, index=True), + Column("deleted", Boolean, index=True, default=False)) FormDefinition_table = Table('form_definition', metadata, - Column( "id", Integer, primary_key=True), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "name", TrimmedString( 255 ), nullable=False ), - Column( "desc", TEXT ), - Column( "form_definition_current_id", Integer, ForeignKey( "form_definition_current.id" ), index=True, nullable=False ), - Column( "fields", JSONType()) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("name", TrimmedString(255), nullable=False), + Column("desc", TEXT), + Column("form_definition_current_id", Integer, ForeignKey("form_definition_current.id"), index=True, nullable=False), + Column("fields", JSONType())) FormValues_table = Table('form_values', metadata, - Column( "id", Integer, primary_key=True), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "form_definition_id", Integer, ForeignKey( "form_definition.id" ), index=True ), - Column( "content", JSONType()) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("form_definition_id", Integer, ForeignKey("form_definition.id"), index=True), + Column("content", JSONType())) RequestType_table = Table('request_type', metadata, - Column( "id", Integer, primary_key=True), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "name", TrimmedString( 255 ), nullable=False ), - Column( "desc", TEXT ), - Column( "request_form_id", Integer, ForeignKey( "form_definition.id" ), index=True ), - Column( "sample_form_id", Integer, ForeignKey( "form_definition.id" ), index=True ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("name", TrimmedString(255), nullable=False), + Column("desc", TEXT), + Column("request_form_id", Integer, ForeignKey("form_definition.id"), index=True), + Column("sample_form_id", Integer, ForeignKey("form_definition.id"), index=True)) Request_table = Table('request', metadata, - Column( "id", Integer, primary_key=True), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "name", TrimmedString( 255 ), nullable=False ), - Column( "desc", TEXT ), - Column( "form_values_id", Integer, ForeignKey( "form_values.id" ), index=True ), - Column( "request_type_id", Integer, ForeignKey( "request_type.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "library_id", Integer, ForeignKey( "library.id" ), index=True ), - Column( "deleted", Boolean, index=True, default=False ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("name", TrimmedString(255), nullable=False), + Column("desc", TEXT), + Column("form_values_id", Integer, ForeignKey("form_values.id"), index=True), + Column("request_type_id", Integer, ForeignKey("request_type.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("library_id", Integer, ForeignKey("library.id"), index=True), + Column("deleted", Boolean, index=True, default=False)) Sample_table = Table('sample', metadata, - Column( "id", Integer, primary_key=True), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "name", TrimmedString( 255 ), nullable=False ), - Column( "desc", TEXT ), - Column( "form_values_id", Integer, ForeignKey( "form_values.id" ), index=True ), - Column( "request_id", Integer, ForeignKey( "request.id" ), index=True ), - Column( "deleted", Boolean, index=True, default=False ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("name", TrimmedString(255), nullable=False), + Column("desc", TEXT), + Column("form_values_id", Integer, ForeignKey("form_values.id"), index=True), + Column("request_id", Integer, ForeignKey("request.id"), index=True), + Column("deleted", Boolean, index=True, default=False)) SampleState_table = Table('sample_state', metadata, - Column( "id", Integer, primary_key=True), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "name", TrimmedString( 255 ), nullable=False ), - Column( "desc", TEXT ), - Column( "request_type_id", Integer, ForeignKey( "request_type.id" ), index=True ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("name", TrimmedString(255), nullable=False), + Column("desc", TEXT), + Column("request_type_id", Integer, ForeignKey("request_type.id"), index=True)) SampleEvent_table = Table('sample_event', metadata, - Column( "id", Integer, primary_key=True), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "sample_id", Integer, ForeignKey( "sample.id" ), index=True ), - Column( "sample_state_id", Integer, ForeignKey( "sample_state.id" ), index=True ), - Column( "comment", TEXT ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("sample_id", Integer, ForeignKey("sample.id"), index=True), + Column("sample_state_id", Integer, ForeignKey("sample_state.id"), index=True), + Column("comment", TEXT)) def upgrade(migrate_engine): @@ -120,9 +120,9 @@ def upgrade(migrate_engine): # Add 1 foreign key constraint to the form_definition_current table if FormDefinitionCurrent_table is not None and FormDefinition_table is not None: try: - cons = ForeignKeyConstraint( [FormDefinitionCurrent_table.c.latest_form_id], - [FormDefinition_table.c.id], - name='form_definition_current_latest_form_id_fk' ) + cons = ForeignKeyConstraint([FormDefinitionCurrent_table.c.latest_form_id], + [FormDefinition_table.c.id], + name='form_definition_current_latest_form_id_fk') # Create the constraint cons.create() except Exception: diff --git a/lib/galaxy/model/migrate/versions/0009_request_table.py b/lib/galaxy/model/migrate/versions/0009_request_table.py index 72ec55c92a00..06b788e9283c 100644 --- a/lib/galaxy/model/migrate/versions/0009_request_table.py +++ b/lib/galaxy/model/migrate/versions/0009_request_table.py @@ -12,13 +12,13 @@ from galaxy.model.custom_types import TrimmedString -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) -handler = logging.StreamHandler( sys.stdout ) +handler = logging.StreamHandler(sys.stdout) format = "%(name)s %(levelname)s %(asctime)s %(message)s" -formatter = logging.Formatter( format ) -handler.setFormatter( formatter ) -log.addHandler( handler ) +formatter = logging.Formatter(format) +handler.setFormatter(formatter) +log.addHandler(handler) metadata = MetaData() @@ -27,14 +27,14 @@ def upgrade(migrate_engine): metadata.bind = migrate_engine print(__doc__) # Load existing tables - Request_table = Table( "request", metadata, autoload=True ) - Sample_table = Table( "sample", metadata, autoload=True ) + Request_table = Table("request", metadata, autoload=True) + Sample_table = Table("sample", metadata, autoload=True) metadata.reflect() # Add 1 column to the request table if Request_table is not None: try: - col = Column( 'submitted', Boolean, default=False ) - col.create( Request_table) + col = Column('submitted', Boolean, default=False) + col.create(Request_table) assert col is Request_table.c.submitted except Exception: log.exception("Adding column 'submitted' to request table failed.") @@ -42,8 +42,8 @@ def upgrade(migrate_engine): # Add 1 column to the sample table if Sample_table is not None: try: - col = Column( "bar_code", TrimmedString( 255 ), index=True ) - col.create( Sample_table, index_name='ix_sample_bar_code') + col = Column("bar_code", TrimmedString(255), index=True) + col.create(Sample_table, index_name='ix_sample_bar_code') assert col is Sample_table.c.bar_code except Exception: log.exception("Adding column 'bar_code' to sample table failed.") diff --git a/lib/galaxy/model/migrate/versions/0010_hda_display_at_authz_table.py b/lib/galaxy/model/migrate/versions/0010_hda_display_at_authz_table.py index 2a02e746de07..65d38a5d4a99 100644 --- a/lib/galaxy/model/migrate/versions/0010_hda_display_at_authz_table.py +++ b/lib/galaxy/model/migrate/versions/0010_hda_display_at_authz_table.py @@ -20,22 +20,22 @@ from galaxy.model.custom_types import TrimmedString now = datetime.datetime.utcnow -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) -handler = logging.StreamHandler( sys.stdout ) +handler = logging.StreamHandler(sys.stdout) format = "%(name)s %(levelname)s %(asctime)s %(message)s" -formatter = logging.Formatter( format ) -handler.setFormatter( formatter ) -log.addHandler( handler ) +formatter = logging.Formatter(format) +handler.setFormatter(formatter) +log.addHandler(handler) metadata = MetaData() -HistoryDatasetAssociationDisplayAtAuthorization_table = Table( "history_dataset_association_display_at_authorization", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, index=True, default=now, onupdate=now ), - Column( "history_dataset_association_id", Integer, ForeignKey( "history_dataset_association.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "site", TrimmedString( 255 ) ) ) +HistoryDatasetAssociationDisplayAtAuthorization_table = Table("history_dataset_association_display_at_authorization", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, index=True, default=now, onupdate=now), + Column("history_dataset_association_id", Integer, ForeignKey("history_dataset_association.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("site", TrimmedString(255))) def upgrade(migrate_engine): diff --git a/lib/galaxy/model/migrate/versions/0011_v0010_mysql_index_fix.py b/lib/galaxy/model/migrate/versions/0011_v0010_mysql_index_fix.py index 6541d1cb9b00..6f70f50794ee 100644 --- a/lib/galaxy/model/migrate/versions/0011_v0010_mysql_index_fix.py +++ b/lib/galaxy/model/migrate/versions/0011_v0010_mysql_index_fix.py @@ -16,22 +16,22 @@ from galaxy.model.custom_types import TrimmedString now = datetime.datetime.utcnow -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) -handler = logging.StreamHandler( sys.stdout ) +handler = logging.StreamHandler(sys.stdout) format = "%(name)s %(levelname)s %(asctime)s %(message)s" -formatter = logging.Formatter( format ) -handler.setFormatter( formatter ) -log.addHandler( handler ) +formatter = logging.Formatter(format) +handler.setFormatter(formatter) +log.addHandler(handler) metadata = MetaData() -HistoryDatasetAssociationDisplayAtAuthorization_table = Table( "history_dataset_association_display_at_authorization", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, index=True, default=now, onupdate=now ), - Column( "history_dataset_association_id", Integer, ForeignKey( "history_dataset_association.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "site", TrimmedString( 255 ) ) ) +HistoryDatasetAssociationDisplayAtAuthorization_table = Table("history_dataset_association_display_at_authorization", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, index=True, default=now, onupdate=now), + Column("history_dataset_association_id", Integer, ForeignKey("history_dataset_association.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("site", TrimmedString(255))) def upgrade(migrate_engine): @@ -40,7 +40,7 @@ def upgrade(migrate_engine): if migrate_engine.name == 'mysql': # Load existing tables metadata.reflect() - i = Index( "ix_hdadaa_history_dataset_association_id", HistoryDatasetAssociationDisplayAtAuthorization_table.c.history_dataset_association_id ) + i = Index("ix_hdadaa_history_dataset_association_id", HistoryDatasetAssociationDisplayAtAuthorization_table.c.history_dataset_association_id) try: i.create() except Exception: @@ -52,7 +52,7 @@ def downgrade(migrate_engine): if migrate_engine.name == 'mysql': # Load existing tables metadata.reflect() - i = Index( "ix_hdadaa_history_dataset_association_id", HistoryDatasetAssociationDisplayAtAuthorization_table.c.history_dataset_association_id ) + i = Index("ix_hdadaa_history_dataset_association_id", HistoryDatasetAssociationDisplayAtAuthorization_table.c.history_dataset_association_id) try: i.drop() except Exception: diff --git a/lib/galaxy/model/migrate/versions/0012_user_address.py b/lib/galaxy/model/migrate/versions/0012_user_address.py index 7ad20a5788d0..b3919c91d219 100644 --- a/lib/galaxy/model/migrate/versions/0012_user_address.py +++ b/lib/galaxy/model/migrate/versions/0012_user_address.py @@ -17,31 +17,31 @@ from galaxy.model.custom_types import TrimmedString now = datetime.datetime.utcnow -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) -handler = logging.StreamHandler( sys.stdout ) +handler = logging.StreamHandler(sys.stdout) format = "%(name)s %(levelname)s %(asctime)s %(message)s" -formatter = logging.Formatter( format ) -handler.setFormatter( formatter ) -log.addHandler( handler ) +formatter = logging.Formatter(format) +handler.setFormatter(formatter) +log.addHandler(handler) metadata = MetaData() -UserAddress_table = Table( "user_address", metadata, - Column( "id", Integer, primary_key=True), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "desc", TEXT), - Column( "name", TrimmedString( 255 ), nullable=False), - Column( "institution", TrimmedString( 255 )), - Column( "address", TrimmedString( 255 ), nullable=False), - Column( "city", TrimmedString( 255 ), nullable=False), - Column( "state", TrimmedString( 255 ), nullable=False), - Column( "postal_code", TrimmedString( 255 ), nullable=False), - Column( "country", TrimmedString( 255 ), nullable=False), - Column( "phone", TrimmedString( 255 )), - Column( "deleted", Boolean, index=True, default=False ), - Column( "purged", Boolean, index=True, default=False ) ) +UserAddress_table = Table("user_address", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("desc", TEXT), + Column("name", TrimmedString(255), nullable=False), + Column("institution", TrimmedString(255)), + Column("address", TrimmedString(255), nullable=False), + Column("city", TrimmedString(255), nullable=False), + Column("state", TrimmedString(255), nullable=False), + Column("postal_code", TrimmedString(255), nullable=False), + Column("country", TrimmedString(255), nullable=False), + Column("phone", TrimmedString(255)), + Column("deleted", Boolean, index=True, default=False), + Column("purged", Boolean, index=True, default=False)) def upgrade(migrate_engine): @@ -56,14 +56,14 @@ def upgrade(migrate_engine): log.exception("Creating user_address table failed.") # Add 1 column to the request_type table try: - RequestType_table = Table( "request_type", metadata, autoload=True ) + RequestType_table = Table("request_type", metadata, autoload=True) except NoSuchTableError: RequestType_table = None - log.debug( "Failed loading table request_type" ) + log.debug("Failed loading table request_type") if RequestType_table is not None: try: - col = Column( "deleted", Boolean, index=True, default=False ) - col.create( RequestType_table, index_name='ix_request_type_deleted') + col = Column("deleted", Boolean, index=True, default=False) + col.create(RequestType_table, index_name='ix_request_type_deleted') assert col is RequestType_table.c.deleted except Exception: log.exception("Adding column 'deleted' to request_type table failed.") @@ -71,16 +71,16 @@ def upgrade(migrate_engine): # Delete the submitted column # This fails for sqlite, so skip the drop -- no conflicts in the future try: - Request_table = Table( "request", metadata, autoload=True ) + Request_table = Table("request", metadata, autoload=True) except NoSuchTableError: Request_table = None - log.debug( "Failed loading table request" ) + log.debug("Failed loading table request") if Request_table is not None: if migrate_engine.name != 'sqlite': # DBTODO drop from table doesn't work in sqlite w/ sqlalchemy-migrate .6+ Request_table.c.submitted.drop() - col = Column( "state", TrimmedString( 255 ), index=True ) - col.create( Request_table, index_name='ix_request_state') + col = Column("state", TrimmedString(255), index=True) + col.create(Request_table, index_name='ix_request_state') assert col is Request_table.c.state diff --git a/lib/galaxy/model/migrate/versions/0013_change_lib_item_templates_to_forms.py b/lib/galaxy/model/migrate/versions/0013_change_lib_item_templates_to_forms.py index bd5403a27432..3e0733e2afa6 100644 --- a/lib/galaxy/model/migrate/versions/0013_change_lib_item_templates_to_forms.py +++ b/lib/galaxy/model/migrate/versions/0013_change_lib_item_templates_to_forms.py @@ -23,32 +23,32 @@ from sqlalchemy import Column, ForeignKey, Index, Integer, MetaData, Table from sqlalchemy.exc import NoSuchTableError -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) -handler = logging.StreamHandler( sys.stdout ) +handler = logging.StreamHandler(sys.stdout) format = "%(name)s %(levelname)s %(asctime)s %(message)s" -formatter = logging.Formatter( format ) -handler.setFormatter( formatter ) -log.addHandler( handler ) +formatter = logging.Formatter(format) +handler.setFormatter(formatter) +log.addHandler(handler) metadata = MetaData() -LibraryInfoAssociation_table = Table( 'library_info_association', metadata, - Column( "id", Integer, primary_key=True), - Column( "library_id", Integer, ForeignKey( "library.id" ), index=True ), - Column( "form_definition_id", Integer, ForeignKey( "form_definition.id" ), index=True ), - Column( "form_values_id", Integer, ForeignKey( "form_values.id" ), index=True ) ) +LibraryInfoAssociation_table = Table('library_info_association', metadata, + Column("id", Integer, primary_key=True), + Column("library_id", Integer, ForeignKey("library.id"), index=True), + Column("form_definition_id", Integer, ForeignKey("form_definition.id"), index=True), + Column("form_values_id", Integer, ForeignKey("form_values.id"), index=True)) -LibraryFolderInfoAssociation_table = Table( 'library_folder_info_association', metadata, - Column( "id", Integer, primary_key=True), - Column( "library_folder_id", Integer, ForeignKey( "library_folder.id" ), nullable=True, index=True ), - Column( "form_definition_id", Integer, ForeignKey( "form_definition.id" ), index=True ), - Column( "form_values_id", Integer, ForeignKey( "form_values.id" ), index=True ) ) +LibraryFolderInfoAssociation_table = Table('library_folder_info_association', metadata, + Column("id", Integer, primary_key=True), + Column("library_folder_id", Integer, ForeignKey("library_folder.id"), nullable=True, index=True), + Column("form_definition_id", Integer, ForeignKey("form_definition.id"), index=True), + Column("form_values_id", Integer, ForeignKey("form_values.id"), index=True)) -LibraryDatasetDatasetInfoAssociation_table = Table( 'library_dataset_dataset_info_association', metadata, - Column( "id", Integer, primary_key=True), - Column( "library_dataset_dataset_association_id", Integer, ForeignKey( "library_dataset_dataset_association.id" ), nullable=True, index=True ), - Column( "form_definition_id", Integer, ForeignKey( "form_definition.id" ), index=True ), - Column( "form_values_id", Integer, ForeignKey( "form_values.id" ), index=True ) ) +LibraryDatasetDatasetInfoAssociation_table = Table('library_dataset_dataset_info_association', metadata, + Column("id", Integer, primary_key=True), + Column("library_dataset_dataset_association_id", Integer, ForeignKey("library_dataset_dataset_association.id"), nullable=True, index=True), + Column("form_definition_id", Integer, ForeignKey("form_definition.id"), index=True), + Column("form_values_id", Integer, ForeignKey("form_values.id"), index=True)) def upgrade(migrate_engine): @@ -59,140 +59,140 @@ def upgrade(migrate_engine): # Drop all of the original library_item_info tables # NOTE: all existing library item into template data is eliminated here via table drops try: - LibraryItemInfoPermissions_table = Table( "library_item_info_permissions", metadata, autoload=True ) + LibraryItemInfoPermissions_table = Table("library_item_info_permissions", metadata, autoload=True) except NoSuchTableError: LibraryItemInfoPermissions_table = None - log.debug( "Failed loading table library_item_info_permissions" ) + log.debug("Failed loading table library_item_info_permissions") try: LibraryItemInfoPermissions_table.drop() except Exception: log.exception("Dropping library_item_info_permissions table failed.") try: - LibraryItemInfoTemplatePermissions_table = Table( "library_item_info_template_permissions", metadata, autoload=True ) + LibraryItemInfoTemplatePermissions_table = Table("library_item_info_template_permissions", metadata, autoload=True) except NoSuchTableError: LibraryItemInfoTemplatePermissions_table = None - log.debug( "Failed loading table library_item_info_template_permissions" ) + log.debug("Failed loading table library_item_info_template_permissions") try: LibraryItemInfoTemplatePermissions_table.drop() except Exception: log.exception("Dropping library_item_info_template_permissions table failed.") try: - LibraryItemInfoElement_table = Table( "library_item_info_element", metadata, autoload=True ) + LibraryItemInfoElement_table = Table("library_item_info_element", metadata, autoload=True) except NoSuchTableError: LibraryItemInfoElement_table = None - log.debug( "Failed loading table library_item_info_element" ) + log.debug("Failed loading table library_item_info_element") try: LibraryItemInfoElement_table.drop() except Exception: log.exception("Dropping library_item_info_element table failed.") try: - LibraryItemInfoTemplateElement_table = Table( "library_item_info_template_element", metadata, autoload=True ) + LibraryItemInfoTemplateElement_table = Table("library_item_info_template_element", metadata, autoload=True) except NoSuchTableError: LibraryItemInfoTemplateElement_table = None - log.debug( "Failed loading table library_item_info_template_element" ) + log.debug("Failed loading table library_item_info_template_element") try: LibraryItemInfoTemplateElement_table.drop() except Exception: log.exception("Dropping library_item_info_template_element table failed.") try: - LibraryInfoTemplateAssociation_table = Table( "library_info_template_association", metadata, autoload=True ) + LibraryInfoTemplateAssociation_table = Table("library_info_template_association", metadata, autoload=True) except NoSuchTableError: LibraryInfoTemplateAssociation_table = None - log.debug( "Failed loading table library_info_template_association" ) + log.debug("Failed loading table library_info_template_association") try: LibraryInfoTemplateAssociation_table.drop() except Exception: log.exception("Dropping library_info_template_association table failed.") try: - LibraryFolderInfoTemplateAssociation_table = Table( "library_folder_info_template_association", metadata, autoload=True ) + LibraryFolderInfoTemplateAssociation_table = Table("library_folder_info_template_association", metadata, autoload=True) except NoSuchTableError: LibraryFolderInfoTemplateAssociation_table = None - log.debug( "Failed loading table library_folder_info_template_association" ) + log.debug("Failed loading table library_folder_info_template_association") try: LibraryFolderInfoTemplateAssociation_table.drop() except Exception: log.exception("Dropping library_folder_info_template_association table failed.") try: - LibraryDatasetInfoTemplateAssociation_table = Table( "library_dataset_info_template_association", metadata, autoload=True ) + LibraryDatasetInfoTemplateAssociation_table = Table("library_dataset_info_template_association", metadata, autoload=True) except NoSuchTableError: LibraryDatasetInfoTemplateAssociation_table = None - log.debug( "Failed loading table library_dataset_info_template_association" ) + log.debug("Failed loading table library_dataset_info_template_association") try: LibraryDatasetInfoTemplateAssociation_table.drop() except Exception: log.exception("Dropping library_dataset_info_template_association table failed.") try: - LibraryDatasetDatasetInfoTemplateAssociation_table = Table( "library_dataset_dataset_info_template_association", metadata, autoload=True ) + LibraryDatasetDatasetInfoTemplateAssociation_table = Table("library_dataset_dataset_info_template_association", metadata, autoload=True) except NoSuchTableError: LibraryDatasetDatasetInfoTemplateAssociation_table = None - log.debug( "Failed loading table library_dataset_dataset_info_template_association" ) + log.debug("Failed loading table library_dataset_dataset_info_template_association") try: LibraryDatasetDatasetInfoTemplateAssociation_table.drop() except Exception: log.exception("Dropping library_dataset_dataset_info_template_association table failed.") try: - LibraryInfoAssociation_table = Table( "library_info_association", metadata, autoload=True ) + LibraryInfoAssociation_table = Table("library_info_association", metadata, autoload=True) except NoSuchTableError: LibraryInfoAssociation_table = None - log.debug( "Failed loading table library_info_association" ) + log.debug("Failed loading table library_info_association") try: LibraryInfoAssociation_table.drop() except Exception: log.exception("Dropping library_info_association table failed.") try: - LibraryFolderInfoAssociation_table = Table( "library_folder_info_association", metadata, autoload=True ) + LibraryFolderInfoAssociation_table = Table("library_folder_info_association", metadata, autoload=True) except NoSuchTableError: LibraryFolderInfoAssociation_table = None - log.debug( "Failed loading table library_folder_info_association" ) + log.debug("Failed loading table library_folder_info_association") try: LibraryFolderInfoAssociation_table.drop() except Exception: log.exception("Dropping library_folder_info_association table failed.") try: - LibraryDatasetInfoAssociation_table = Table( "library_dataset_info_association", metadata, autoload=True ) + LibraryDatasetInfoAssociation_table = Table("library_dataset_info_association", metadata, autoload=True) except NoSuchTableError: LibraryDatasetInfoAssociation_table = None - log.debug( "Failed loading table library_dataset_info_association" ) + log.debug("Failed loading table library_dataset_info_association") try: LibraryDatasetInfoAssociation_table.drop() except Exception: log.exception("Dropping library_dataset_info_association table failed.") try: - LibraryDatasetDatasetInfoAssociation_table = Table( "library_dataset_dataset_info_association", metadata, autoload=True ) + LibraryDatasetDatasetInfoAssociation_table = Table("library_dataset_dataset_info_association", metadata, autoload=True) except NoSuchTableError: LibraryDatasetDatasetInfoAssociation_table = None - log.debug( "Failed loading table library_dataset_dataset_info_association" ) + log.debug("Failed loading table library_dataset_dataset_info_association") try: LibraryDatasetDatasetInfoAssociation_table.drop() except Exception: log.exception("Dropping library_dataset_dataset_info_association table failed.") try: - LibraryItemInfo_table = Table( "library_item_info", metadata, autoload=True ) + LibraryItemInfo_table = Table("library_item_info", metadata, autoload=True) except NoSuchTableError: LibraryItemInfo_table = None - log.debug( "Failed loading table library_item_info" ) + log.debug("Failed loading table library_item_info") try: LibraryItemInfo_table.drop() except Exception: log.exception("Dropping library_item_info table failed.") try: - LibraryItemInfoTemplate_table = Table( "library_item_info_template", metadata, autoload=True ) + LibraryItemInfoTemplate_table = Table("library_item_info_template", metadata, autoload=True) except NoSuchTableError: LibraryItemInfoTemplate_table = None - log.debug( "Failed loading table library_item_info_template" ) + log.debug("Failed loading table library_item_info_template") try: LibraryItemInfoTemplate_table.drop() except Exception: @@ -215,7 +215,7 @@ def upgrade(migrate_engine): if migrate_engine.name == 'mysql': # Load existing tables metadata.reflect() - i = Index( "ix_lddaia_ldda_id", LibraryDatasetDatasetInfoAssociation_table.c.library_dataset_dataset_association_id ) + i = Index("ix_lddaia_ldda_id", LibraryDatasetDatasetInfoAssociation_table.c.library_dataset_dataset_association_id) try: i.create() except Exception: @@ -224,4 +224,4 @@ def upgrade(migrate_engine): def downgrade(migrate_engine): metadata.bind = migrate_engine - log.debug( "Downgrade is not possible." ) + log.debug("Downgrade is not possible.") diff --git a/lib/galaxy/model/migrate/versions/0014_pages.py b/lib/galaxy/model/migrate/versions/0014_pages.py index e3e3ca9c896d..728c22c7d535 100644 --- a/lib/galaxy/model/migrate/versions/0014_pages.py +++ b/lib/galaxy/model/migrate/versions/0014_pages.py @@ -11,26 +11,26 @@ from sqlalchemy import Column, DateTime, ForeignKey, Index, Integer, MetaData, String, Table, TEXT now = datetime.datetime.utcnow -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() -Page_table = Table( "page", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True, nullable=False ), - Column( "latest_revision_id", Integer, - ForeignKey( "page_revision.id", use_alter=True, name='page_latest_revision_id_fk' ), index=True ), - Column( "title", TEXT ), - Column( "slug", TEXT, unique=True, index=True ) ) +Page_table = Table("page", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True, nullable=False), + Column("latest_revision_id", Integer, + ForeignKey("page_revision.id", use_alter=True, name='page_latest_revision_id_fk'), index=True), + Column("title", TEXT), + Column("slug", TEXT, unique=True, index=True)) -PageRevision_table = Table( "page_revision", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "page_id", Integer, ForeignKey( "page.id" ), index=True, nullable=False ), - Column( "title", TEXT ), - Column( "content", TEXT ) ) +PageRevision_table = Table("page_revision", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("page_id", Integer, ForeignKey("page.id"), index=True, nullable=False), + Column("title", TEXT), + Column("content", TEXT)) def upgrade(migrate_engine): @@ -48,7 +48,7 @@ def upgrade(migrate_engine): Page_table.create() if migrate_engine.name == 'mysql': # Create slug index manually afterward. - i = Index( "ix_page_slug", Page_table.c.slug, mysql_length=200) + i = Index("ix_page_slug", Page_table.c.slug, mysql_length=200) i.create() except Exception: log.exception("Could not create page table") @@ -58,9 +58,9 @@ def upgrade(migrate_engine): log.exception("Could not create page_revision table") # Add 1 column to the user table - User_table = Table( "galaxy_user", metadata, autoload=True ) - col = Column( 'username', String(255), index=True, unique=True, default=False ) - col.create( User_table, index_name='ix_user_username', unique_name='username' ) + User_table = Table("galaxy_user", metadata, autoload=True) + col = Column('username', String(255), index=True, unique=True, default=False) + col.create(User_table, index_name='ix_user_username', unique_name='username') assert col is User_table.c.username @@ -69,5 +69,5 @@ def downgrade(migrate_engine): metadata.reflect() Page_table.drop() PageRevision_table.drop() - User_table = Table( "galaxy_user", metadata, autoload=True ) + User_table = Table("galaxy_user", metadata, autoload=True) User_table.c.username.drop() diff --git a/lib/galaxy/model/migrate/versions/0015_tagging.py b/lib/galaxy/model/migrate/versions/0015_tagging.py index e3ab06cf8d6c..6a4e6fb7a06f 100644 --- a/lib/galaxy/model/migrate/versions/0015_tagging.py +++ b/lib/galaxy/model/migrate/versions/0015_tagging.py @@ -18,37 +18,37 @@ # Need our custom types, but don't import anything else from model from galaxy.model.custom_types import TrimmedString -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() # New tables to support tagging of histories, datasets, and history-dataset associations. -Tag_table = Table( "tag", metadata, - Column( "id", Integer, primary_key=True ), - Column( "type", Integer ), - Column( "parent_id", Integer, ForeignKey( "tag.id" ) ), - Column( "name", TrimmedString(255) ), - UniqueConstraint( "name" ) ) +Tag_table = Table("tag", metadata, + Column("id", Integer, primary_key=True), + Column("type", Integer), + Column("parent_id", Integer, ForeignKey("tag.id")), + Column("name", TrimmedString(255)), + UniqueConstraint("name")) -HistoryTagAssociation_table = Table( "history_tag_association", metadata, - Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ), - Column( "tag_id", Integer, ForeignKey( "tag.id" ), index=True ), - Column( "user_tname", TrimmedString(255), index=True), - Column( "value", TrimmedString(255), index=True), - Column( "user_value", TrimmedString(255), index=True) ) +HistoryTagAssociation_table = Table("history_tag_association", metadata, + Column("history_id", Integer, ForeignKey("history.id"), index=True), + Column("tag_id", Integer, ForeignKey("tag.id"), index=True), + Column("user_tname", TrimmedString(255), index=True), + Column("value", TrimmedString(255), index=True), + Column("user_value", TrimmedString(255), index=True)) -DatasetTagAssociation_table = Table( "dataset_tag_association", metadata, - Column( "dataset_id", Integer, ForeignKey( "dataset.id" ), index=True ), - Column( "tag_id", Integer, ForeignKey( "tag.id" ), index=True ), - Column( "user_tname", TrimmedString(255), index=True), - Column( "value", TrimmedString(255), index=True), - Column( "user_value", TrimmedString(255), index=True) ) +DatasetTagAssociation_table = Table("dataset_tag_association", metadata, + Column("dataset_id", Integer, ForeignKey("dataset.id"), index=True), + Column("tag_id", Integer, ForeignKey("tag.id"), index=True), + Column("user_tname", TrimmedString(255), index=True), + Column("value", TrimmedString(255), index=True), + Column("user_value", TrimmedString(255), index=True)) -HistoryDatasetAssociationTagAssociation_table = Table( "history_dataset_association_tag_association", metadata, - Column( "history_dataset_association_id", Integer, ForeignKey( "history_dataset_association.id" ), index=True ), - Column( "tag_id", Integer, ForeignKey( "tag.id" ), index=True ), - Column( "user_tname", TrimmedString(255), index=True), - Column( "value", TrimmedString(255), index=True), - Column( "user_value", TrimmedString(255), index=True) ) +HistoryDatasetAssociationTagAssociation_table = Table("history_dataset_association_tag_association", metadata, + Column("history_dataset_association_id", Integer, ForeignKey("history_dataset_association.id"), index=True), + Column("tag_id", Integer, ForeignKey("tag.id"), index=True), + Column("user_tname", TrimmedString(255), index=True), + Column("value", TrimmedString(255), index=True), + Column("user_value", TrimmedString(255), index=True)) def upgrade(migrate_engine): diff --git a/lib/galaxy/model/migrate/versions/0016_v0015_mysql_index_fix.py b/lib/galaxy/model/migrate/versions/0016_v0015_mysql_index_fix.py index 9f01e347c33e..6f5214e07239 100644 --- a/lib/galaxy/model/migrate/versions/0016_v0015_mysql_index_fix.py +++ b/lib/galaxy/model/migrate/versions/0016_v0015_mysql_index_fix.py @@ -12,23 +12,23 @@ # Need our custom types, but don't import anything else from model from galaxy.model.custom_types import TrimmedString -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() -HistoryDatasetAssociationTagAssociation_table = Table( "history_dataset_association_tag_association", metadata, - Column( "history_dataset_association_id", Integer, ForeignKey( "history_dataset_association.id" ), index=True ), - Column( "tag_id", Integer, ForeignKey( "tag.id" ), index=True ), - Column( "user_tname", TrimmedString(255), index=True), - Column( "value", TrimmedString(255), index=True), - Column( "user_value", TrimmedString(255), index=True) ) +HistoryDatasetAssociationTagAssociation_table = Table("history_dataset_association_tag_association", metadata, + Column("history_dataset_association_id", Integer, ForeignKey("history_dataset_association.id"), index=True), + Column("tag_id", Integer, ForeignKey("tag.id"), index=True), + Column("user_tname", TrimmedString(255), index=True), + Column("value", TrimmedString(255), index=True), + Column("user_value", TrimmedString(255), index=True)) def upgrade(migrate_engine): metadata.bind = migrate_engine print(__doc__) metadata.reflect() - i = Index( "ix_hda_ta_history_dataset_association_id", HistoryDatasetAssociationTagAssociation_table.c.history_dataset_association_id ) + i = Index("ix_hda_ta_history_dataset_association_id", HistoryDatasetAssociationTagAssociation_table.c.history_dataset_association_id) try: i.create() except Exception: @@ -38,7 +38,7 @@ def upgrade(migrate_engine): def downgrade(migrate_engine): metadata.bind = migrate_engine metadata.reflect() - i = Index( "ix_hda_ta_history_dataset_association_id", HistoryDatasetAssociationTagAssociation_table.c.history_dataset_association_id ) + i = Index("ix_hda_ta_history_dataset_association_id", HistoryDatasetAssociationTagAssociation_table.c.history_dataset_association_id) try: i.drop() except Exception: diff --git a/lib/galaxy/model/migrate/versions/0017_library_item_indexes.py b/lib/galaxy/model/migrate/versions/0017_library_item_indexes.py index 609ffeccac13..668035132bca 100644 --- a/lib/galaxy/model/migrate/versions/0017_library_item_indexes.py +++ b/lib/galaxy/model/migrate/versions/0017_library_item_indexes.py @@ -9,38 +9,38 @@ from sqlalchemy import Index, MetaData, Table -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) -handler = logging.StreamHandler( sys.stdout ) +handler = logging.StreamHandler(sys.stdout) format = "%(name)s %(levelname)s %(asctime)s %(message)s" -formatter = logging.Formatter( format ) -handler.setFormatter( formatter ) -log.addHandler( handler ) +formatter = logging.Formatter(format) +handler.setFormatter(formatter) +log.addHandler(handler) metadata = MetaData() def upgrade(migrate_engine): metadata.bind = migrate_engine print(__doc__) - LibraryFolder_table = Table( "library_folder", metadata, autoload=True ) - LibraryDatasetDatasetAssociation_table = Table( "library_dataset_dataset_association", metadata, autoload=True ) - LibraryDataset_table = Table( "library_dataset", metadata, autoload=True ) + LibraryFolder_table = Table("library_folder", metadata, autoload=True) + LibraryDatasetDatasetAssociation_table = Table("library_dataset_dataset_association", metadata, autoload=True) + LibraryDataset_table = Table("library_dataset", metadata, autoload=True) # Load existing tables metadata.reflect() # Add 1 index to the library_folder table - i = Index( 'ix_library_folder_name', LibraryFolder_table.c.name, mysql_length=200 ) + i = Index('ix_library_folder_name', LibraryFolder_table.c.name, mysql_length=200) try: i.create() except Exception: log.exception("Adding index 'ix_library_folder_name' to library_folder table failed.") # Add 1 index to the library_dataset_dataset_association table - i = Index( 'ix_library_dataset_dataset_association_name', LibraryDatasetDatasetAssociation_table.c.name ) + i = Index('ix_library_dataset_dataset_association_name', LibraryDatasetDatasetAssociation_table.c.name) try: i.create() except Exception: log.exception("Adding index 'ix_library_dataset_dataset_association_name' to library_dataset_dataset_association table failed.") # Add 1 index to the library_dataset table - i = Index( 'ix_library_dataset_name', LibraryDataset_table.c.name ) + i = Index('ix_library_dataset_name', LibraryDataset_table.c.name) try: i.create() except Exception: @@ -49,4 +49,4 @@ def upgrade(migrate_engine): def downgrade(migrate_engine): metadata.bind = migrate_engine - log.debug( "Downgrade is not possible." ) + log.debug("Downgrade is not possible.") diff --git a/lib/galaxy/model/migrate/versions/0018_ordered_tags_and_page_tags.py b/lib/galaxy/model/migrate/versions/0018_ordered_tags_and_page_tags.py index 0b70a5244d96..7c5b4d614a0e 100644 --- a/lib/galaxy/model/migrate/versions/0018_ordered_tags_and_page_tags.py +++ b/lib/galaxy/model/migrate/versions/0018_ordered_tags_and_page_tags.py @@ -12,40 +12,40 @@ # Need our custom types, but don't import anything else from model from galaxy.model.custom_types import TrimmedString -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() -HistoryTagAssociation_table = Table( "history_tag_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ), - Column( "tag_id", Integer, ForeignKey( "tag.id" ), index=True ), - Column( "user_tname", TrimmedString(255), index=True), - Column( "value", TrimmedString(255), index=True), - Column( "user_value", TrimmedString(255), index=True) ) - -DatasetTagAssociation_table = Table( "dataset_tag_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "dataset_id", Integer, ForeignKey( "dataset.id" ), index=True ), - Column( "tag_id", Integer, ForeignKey( "tag.id" ), index=True ), - Column( "user_tname", TrimmedString(255), index=True), - Column( "value", TrimmedString(255), index=True), - Column( "user_value", TrimmedString(255), index=True) ) - -HistoryDatasetAssociationTagAssociation_table = Table( "history_dataset_association_tag_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "history_dataset_association_id", Integer, ForeignKey( "history_dataset_association.id" ), index=True ), - Column( "tag_id", Integer, ForeignKey( "tag.id" ), index=True ), - Column( "user_tname", TrimmedString(255), index=True), - Column( "value", TrimmedString(255), index=True), - Column( "user_value", TrimmedString(255), index=True) ) - -PageTagAssociation_table = Table( "page_tag_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "page_id", Integer, ForeignKey( "page.id" ), index=True ), - Column( "tag_id", Integer, ForeignKey( "tag.id" ), index=True ), - Column( "user_tname", TrimmedString(255), index=True), - Column( "value", TrimmedString(255), index=True), - Column( "user_value", TrimmedString(255), index=True) ) +HistoryTagAssociation_table = Table("history_tag_association", metadata, + Column("id", Integer, primary_key=True), + Column("history_id", Integer, ForeignKey("history.id"), index=True), + Column("tag_id", Integer, ForeignKey("tag.id"), index=True), + Column("user_tname", TrimmedString(255), index=True), + Column("value", TrimmedString(255), index=True), + Column("user_value", TrimmedString(255), index=True)) + +DatasetTagAssociation_table = Table("dataset_tag_association", metadata, + Column("id", Integer, primary_key=True), + Column("dataset_id", Integer, ForeignKey("dataset.id"), index=True), + Column("tag_id", Integer, ForeignKey("tag.id"), index=True), + Column("user_tname", TrimmedString(255), index=True), + Column("value", TrimmedString(255), index=True), + Column("user_value", TrimmedString(255), index=True)) + +HistoryDatasetAssociationTagAssociation_table = Table("history_dataset_association_tag_association", metadata, + Column("id", Integer, primary_key=True), + Column("history_dataset_association_id", Integer, ForeignKey("history_dataset_association.id"), index=True), + Column("tag_id", Integer, ForeignKey("tag.id"), index=True), + Column("user_tname", TrimmedString(255), index=True), + Column("value", TrimmedString(255), index=True), + Column("user_value", TrimmedString(255), index=True)) + +PageTagAssociation_table = Table("page_tag_association", metadata, + Column("id", Integer, primary_key=True), + Column("page_id", Integer, ForeignKey("page.id"), index=True), + Column("tag_id", Integer, ForeignKey("tag.id"), index=True), + Column("user_tname", TrimmedString(255), index=True), + Column("value", TrimmedString(255), index=True), + Column("user_value", TrimmedString(255), index=True)) def upgrade(migrate_engine): @@ -76,7 +76,7 @@ def upgrade(migrate_engine): # in MySQL. if str(e).find("CREATE INDEX") != -1: # Manually create index. - i = Index( "ix_hda_ta_history_dataset_association_id", HistoryDatasetAssociationTagAssociation_table.c.history_dataset_association_id ) + i = Index("ix_hda_ta_history_dataset_association_id", HistoryDatasetAssociationTagAssociation_table.c.history_dataset_association_id) try: i.create() except Exception: diff --git a/lib/galaxy/model/migrate/versions/0019_request_library_folder.py b/lib/galaxy/model/migrate/versions/0019_request_library_folder.py index d80f1d817cab..8033940322de 100644 --- a/lib/galaxy/model/migrate/versions/0019_request_library_folder.py +++ b/lib/galaxy/model/migrate/versions/0019_request_library_folder.py @@ -15,13 +15,13 @@ # Need our custom types, but don't import anything else from model from galaxy.model.custom_types import JSONType, TrimmedString -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) -handler = logging.StreamHandler( sys.stdout ) +handler = logging.StreamHandler(sys.stdout) format = "%(name)s %(levelname)s %(asctime)s %(message)s" -formatter = logging.Formatter( format ) -handler.setFormatter( formatter ) -log.addHandler( handler ) +formatter = logging.Formatter(format) +handler.setFormatter(formatter) +log.addHandler(handler) metadata = MetaData() @@ -32,48 +32,48 @@ def upgrade(migrate_engine): metadata.reflect() # Create the folder_id column try: - Request_table = Table( "request", metadata, autoload=True ) + Request_table = Table("request", metadata, autoload=True) except NoSuchTableError: Request_table = None - log.debug( "Failed loading table request" ) + log.debug("Failed loading table request") if Request_table is not None: try: - col = Column( "folder_id", Integer, index=True ) - col.create( Request_table, index_name='ix_request_folder_id') + col = Column("folder_id", Integer, index=True) + col.create(Request_table, index_name='ix_request_folder_id') assert col is Request_table.c.folder_id except Exception: log.exception("Adding column 'folder_id' to request table failed.") try: - LibraryFolder_table = Table( "library_folder", metadata, autoload=True ) + LibraryFolder_table = Table("library_folder", metadata, autoload=True) except NoSuchTableError: LibraryFolder_table = None - log.debug( "Failed loading table library_folder" ) + log.debug("Failed loading table library_folder") # Add 1 foreign key constraint to the library_folder table if migrate_engine.name != 'sqlite' and Request_table is not None and LibraryFolder_table is not None: try: - cons = ForeignKeyConstraint( [Request_table.c.folder_id], - [LibraryFolder_table.c.id], - name='request_folder_id_fk' ) + cons = ForeignKeyConstraint([Request_table.c.folder_id], + [LibraryFolder_table.c.id], + name='request_folder_id_fk') # Create the constraint cons.create() except Exception: log.exception("Adding foreign key constraint 'request_folder_id_fk' to table 'library_folder' failed.") # Create the type column in form_definition try: - FormDefinition_table = Table( "form_definition", metadata, autoload=True ) + FormDefinition_table = Table("form_definition", metadata, autoload=True) except NoSuchTableError: FormDefinition_table = None - log.debug( "Failed loading table form_definition" ) + log.debug("Failed loading table form_definition") if FormDefinition_table is not None: try: - col = Column( "type", TrimmedString( 255 ), index=True ) - col.create( FormDefinition_table, index_name='ix_form_definition_type') + col = Column("type", TrimmedString(255), index=True) + col.create(FormDefinition_table, index_name='ix_form_definition_type') assert col is FormDefinition_table.c.type except Exception: log.exception("Adding column 'type' to form_definition table failed.") try: - col = Column( "layout", JSONType()) - col.create( FormDefinition_table ) + col = Column("layout", JSONType()) + col.create(FormDefinition_table) assert col is FormDefinition_table.c.layout except Exception: log.exception("Adding column 'layout' to form_definition table failed.") diff --git a/lib/galaxy/model/migrate/versions/0020_library_upload_job.py b/lib/galaxy/model/migrate/versions/0020_library_upload_job.py index 747b49e3f0ac..ad8b07312c40 100644 --- a/lib/galaxy/model/migrate/versions/0020_library_upload_job.py +++ b/lib/galaxy/model/migrate/versions/0020_library_upload_job.py @@ -14,21 +14,21 @@ from sqlalchemy import Column, ForeignKey, Index, Integer, MetaData, String, Table from sqlalchemy.exc import NoSuchTableError -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) -handler = logging.StreamHandler( sys.stdout ) +handler = logging.StreamHandler(sys.stdout) format = "%(name)s %(levelname)s %(asctime)s %(message)s" -formatter = logging.Formatter( format ) -handler.setFormatter( formatter ) -log.addHandler( handler ) +formatter = logging.Formatter(format) +handler.setFormatter(formatter) +log.addHandler(handler) metadata = MetaData() -JobToOutputLibraryDatasetAssociation_table = Table( "job_to_output_library_dataset", metadata, - Column( "id", Integer, primary_key=True ), - Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ), - Column( "ldda_id", Integer, ForeignKey( "library_dataset_dataset_association.id" ), index=True ), - Column( "name", String(255) ) ) +JobToOutputLibraryDatasetAssociation_table = Table("job_to_output_library_dataset", metadata, + Column("id", Integer, primary_key=True), + Column("job_id", Integer, ForeignKey("job.id"), index=True), + Column("ldda_id", Integer, ForeignKey("library_dataset_dataset_association.id"), index=True), + Column("name", String(255))) def upgrade(migrate_engine): @@ -43,41 +43,41 @@ def upgrade(migrate_engine): log.exception("Creating job_to_output_library_dataset table failed.") # Create the library_folder_id column try: - Job_table = Table( "job", metadata, autoload=True ) + Job_table = Table("job", metadata, autoload=True) except NoSuchTableError: Job_table = None - log.debug( "Failed loading table job" ) + log.debug("Failed loading table job") if Job_table is not None: try: - col = Column( "library_folder_id", Integer, index=True ) - col.create( Job_table, index_name='ix_job_library_folder_id') + col = Column("library_folder_id", Integer, index=True) + col.create(Job_table, index_name='ix_job_library_folder_id') assert col is Job_table.c.library_folder_id except Exception: log.exception("Adding column 'library_folder_id' to job table failed.") try: - LibraryFolder_table = Table( "library_folder", metadata, autoload=True ) + LibraryFolder_table = Table("library_folder", metadata, autoload=True) except NoSuchTableError: LibraryFolder_table = None - log.debug( "Failed loading table library_folder" ) + log.debug("Failed loading table library_folder") # Add 1 foreign key constraint to the job table if migrate_engine.name != 'sqlite': # Sqlite can't alter-table-add-foreign-key if Job_table is not None and LibraryFolder_table is not None: try: - cons = ForeignKeyConstraint( [Job_table.c.library_folder_id], - [LibraryFolder_table.c.id], - name='job_library_folder_id_fk' ) + cons = ForeignKeyConstraint([Job_table.c.library_folder_id], + [LibraryFolder_table.c.id], + name='job_library_folder_id_fk') # Create the constraint cons.create() except Exception: log.exception("Adding foreign key constraint 'job_library_folder_id_fk' to table 'library_folder' failed.") # Create the ix_dataset_state index try: - Dataset_table = Table( "dataset", metadata, autoload=True ) + Dataset_table = Table("dataset", metadata, autoload=True) except NoSuchTableError: Dataset_table = None - log.debug( "Failed loading table dataset" ) - i = Index( "ix_dataset_state", Dataset_table.c.state ) + log.debug("Failed loading table dataset") + i = Index("ix_dataset_state", Dataset_table.c.state) try: i.create() except Exception: @@ -89,10 +89,10 @@ def downgrade(migrate_engine): metadata.reflect() # Drop the library_folder_id column try: - Job_table = Table( "job", metadata, autoload=True ) + Job_table = Table("job", metadata, autoload=True) except NoSuchTableError: Job_table = None - log.debug( "Failed loading table job" ) + log.debug("Failed loading table job") if Job_table is not None: try: col = Job_table.c.library_folder_id @@ -106,11 +106,11 @@ def downgrade(migrate_engine): log.exception("Dropping job_to_output_library_dataset table failed.") # Drop the ix_dataset_state index try: - Dataset_table = Table( "dataset", metadata, autoload=True ) + Dataset_table = Table("dataset", metadata, autoload=True) except NoSuchTableError: Dataset_table = None - log.debug( "Failed loading table dataset" ) - i = Index( "ix_dataset_state", Dataset_table.c.state ) + log.debug("Failed loading table dataset") + i = Index("ix_dataset_state", Dataset_table.c.state) try: i.drop() except Exception: diff --git a/lib/galaxy/model/migrate/versions/0021_user_prefs.py b/lib/galaxy/model/migrate/versions/0021_user_prefs.py index c40c5dd21f0d..56e16457f0c1 100644 --- a/lib/galaxy/model/migrate/versions/0021_user_prefs.py +++ b/lib/galaxy/model/migrate/versions/0021_user_prefs.py @@ -7,15 +7,15 @@ from sqlalchemy import Column, ForeignKey, Integer, MetaData, Table, Unicode -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() # New table to support user preferences. -UserPreference_table = Table( "user_preference", metadata, - Column( "id", Integer, primary_key=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "name", Unicode( 255 ), index=True), - Column( "value", Unicode( 1024 ) ) ) +UserPreference_table = Table("user_preference", metadata, + Column("id", Integer, primary_key=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("name", Unicode(255), index=True), + Column("value", Unicode(1024))) def upgrade(migrate_engine): diff --git a/lib/galaxy/model/migrate/versions/0022_visualization_tables.py b/lib/galaxy/model/migrate/versions/0022_visualization_tables.py index 964c7758a7c2..2e4ba13736c2 100644 --- a/lib/galaxy/model/migrate/versions/0022_visualization_tables.py +++ b/lib/galaxy/model/migrate/versions/0022_visualization_tables.py @@ -10,26 +10,26 @@ from sqlalchemy import Column, DateTime, ForeignKey, Integer, MetaData, Table, TEXT now = datetime.datetime.utcnow -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() -Visualization_table = Table( "visualization", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True, nullable=False ), - Column( "latest_revision_id", Integer, - ForeignKey( "visualization_revision.id", use_alter=True, name='visualization_latest_revision_id_fk' ), index=True ), - Column( "title", TEXT ), - Column( "type", TEXT ) ) - -VisualizationRevision_table = Table( "visualization_revision", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "visualization_id", Integer, ForeignKey( "visualization.id" ), index=True, nullable=False ), - Column( "title", TEXT ), - Column( "config", TEXT ) ) +Visualization_table = Table("visualization", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True, nullable=False), + Column("latest_revision_id", Integer, + ForeignKey("visualization_revision.id", use_alter=True, name='visualization_latest_revision_id_fk'), index=True), + Column("title", TEXT), + Column("type", TEXT)) + +VisualizationRevision_table = Table("visualization_revision", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("visualization_id", Integer, ForeignKey("visualization.id"), index=True, nullable=False), + Column("title", TEXT), + Column("config", TEXT)) def upgrade(migrate_engine): diff --git a/lib/galaxy/model/migrate/versions/0023_page_published_and_deleted_columns.py b/lib/galaxy/model/migrate/versions/0023_page_published_and_deleted_columns.py index 78fe1dc0d465..18cb2c54e4ea 100644 --- a/lib/galaxy/model/migrate/versions/0023_page_published_and_deleted_columns.py +++ b/lib/galaxy/model/migrate/versions/0023_page_published_and_deleted_columns.py @@ -8,7 +8,7 @@ from sqlalchemy import Boolean, Column, MetaData, Table -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() @@ -18,14 +18,14 @@ def upgrade(migrate_engine): print(__doc__) metadata.reflect() - Page_table = Table( "page", metadata, autoload=True ) + Page_table = Table("page", metadata, autoload=True) - c = Column( "published", Boolean, index=True, default=False ) - c.create( Page_table, index_name='ix_page_published' ) + c = Column("published", Boolean, index=True, default=False) + c.create(Page_table, index_name='ix_page_published') assert c is Page_table.c.published - c = Column( "deleted", Boolean, index=True, default=False ) - c.create( Page_table, index_name='ix_page_deleted') + c = Column("deleted", Boolean, index=True, default=False) + c.create(Page_table, index_name='ix_page_deleted') assert c is Page_table.c.deleted @@ -33,6 +33,6 @@ def downgrade(migrate_engine): metadata.bind = migrate_engine metadata.reflect() - Page_table = Table( "page", metadata, autoload=True ) + Page_table = Table("page", metadata, autoload=True) Page_table.c.published.drop() Page_table.c.deleted.drop() diff --git a/lib/galaxy/model/migrate/versions/0024_page_slug_unique_constraint.py b/lib/galaxy/model/migrate/versions/0024_page_slug_unique_constraint.py index 417e9393ea11..68c526937c8e 100644 --- a/lib/galaxy/model/migrate/versions/0024_page_slug_unique_constraint.py +++ b/lib/galaxy/model/migrate/versions/0024_page_slug_unique_constraint.py @@ -8,7 +8,7 @@ from sqlalchemy import Index, MetaData, Table -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() @@ -17,23 +17,23 @@ def upgrade(migrate_engine): print(__doc__) metadata.reflect() - Page_table = Table( "page", metadata, autoload=True ) + Page_table = Table("page", metadata, autoload=True) try: # Sqlite doesn't support .alter, so we need to drop an recreate - i = Index( "ix_page_slug", Page_table.c.slug ) + i = Index("ix_page_slug", Page_table.c.slug) i.drop() - i = Index( "ix_page_slug", Page_table.c.slug, unique=False ) + i = Index("ix_page_slug", Page_table.c.slug, unique=False) i.create() except: # Mysql doesn't have a named index, but alter should work - Page_table.c.slug.alter( unique=False ) + Page_table.c.slug.alter(unique=False) def downgrade(migrate_engine): diff --git a/lib/galaxy/model/migrate/versions/0025_user_info.py b/lib/galaxy/model/migrate/versions/0025_user_info.py index 22718743cf3b..001a3c687c93 100644 --- a/lib/galaxy/model/migrate/versions/0025_user_info.py +++ b/lib/galaxy/model/migrate/versions/0025_user_info.py @@ -10,13 +10,13 @@ from sqlalchemy import Column, Integer, MetaData, Table from sqlalchemy.exc import NoSuchTableError -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) -handler = logging.StreamHandler( sys.stdout ) +handler = logging.StreamHandler(sys.stdout) format = "%(name)s %(levelname)s %(asctime)s %(message)s" -formatter = logging.Formatter( format ) -handler.setFormatter( formatter ) -log.addHandler( handler ) +formatter = logging.Formatter(format) +handler.setFormatter(formatter) +log.addHandler(handler) metadata = MetaData() @@ -27,29 +27,29 @@ def upgrade(migrate_engine): # Load existing tables metadata.reflect() try: - User_table = Table( "galaxy_user", metadata, autoload=True ) + User_table = Table("galaxy_user", metadata, autoload=True) except NoSuchTableError: User_table = None - log.debug( "Failed loading table galaxy_user" ) + log.debug("Failed loading table galaxy_user") if User_table is not None: try: - col = Column( "form_values_id", Integer, index=True ) - col.create( User_table, index_name='ix_user_form_values_id') + col = Column("form_values_id", Integer, index=True) + col.create(User_table, index_name='ix_user_form_values_id') assert col is User_table.c.form_values_id except Exception: log.exception("Adding column 'form_values_id' to galaxy_user table failed.") try: - FormValues_table = Table( "form_values", metadata, autoload=True ) + FormValues_table = Table("form_values", metadata, autoload=True) except NoSuchTableError: FormValues_table = None - log.debug( "Failed loading table form_values" ) + log.debug("Failed loading table form_values") if migrate_engine.name != 'sqlite': # Add 1 foreign key constraint to the form_values table if User_table is not None and FormValues_table is not None: try: - cons = ForeignKeyConstraint( [User_table.c.form_values_id], - [FormValues_table.c.id], - name='user_form_values_id_fk' ) + cons = ForeignKeyConstraint([User_table.c.form_values_id], + [FormValues_table.c.id], + name='user_form_values_id_fk') # Create the constraint cons.create() except Exception: diff --git a/lib/galaxy/model/migrate/versions/0026_cloud_tables.py b/lib/galaxy/model/migrate/versions/0026_cloud_tables.py index e0b1290a0ccf..3331a27308ce 100644 --- a/lib/galaxy/model/migrate/versions/0026_cloud_tables.py +++ b/lib/galaxy/model/migrate/versions/0026_cloud_tables.py @@ -9,117 +9,117 @@ from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, MetaData, Table, TEXT now = datetime.datetime.utcnow -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() -CloudImage_table = Table( "cloud_image", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "provider_type", TEXT ), - Column( "image_id", TEXT, nullable=False ), - Column( "manifest", TEXT ), - Column( "state", TEXT ), - Column( "architecture", TEXT ), - Column( "deleted", Boolean, default=False ) ) +CloudImage_table = Table("cloud_image", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("provider_type", TEXT), + Column("image_id", TEXT, nullable=False), + Column("manifest", TEXT), + Column("state", TEXT), + Column("architecture", TEXT), + Column("deleted", Boolean, default=False)) """ UserConfiguredInstance (UCI) table """ -UCI_table = Table( "cloud_uci", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True, nullable=False ), - Column( "credentials_id", Integer, ForeignKey( "cloud_user_credentials.id" ), index=True ), - Column( "key_pair_name", TEXT ), - Column( "key_pair_material", TEXT ), - Column( "name", TEXT ), - Column( "state", TEXT ), - Column( "error", TEXT ), - Column( "total_size", Integer ), - Column( "launch_time", DateTime ), - Column( "deleted", Boolean, default=False ) ) +UCI_table = Table("cloud_uci", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True, nullable=False), + Column("credentials_id", Integer, ForeignKey("cloud_user_credentials.id"), index=True), + Column("key_pair_name", TEXT), + Column("key_pair_material", TEXT), + Column("name", TEXT), + Column("state", TEXT), + Column("error", TEXT), + Column("total_size", Integer), + Column("launch_time", DateTime), + Column("deleted", Boolean, default=False)) -CloudInstance_table = Table( "cloud_instance", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "launch_time", DateTime ), - Column( "stop_time", DateTime ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True, nullable=False ), - Column( "uci_id", Integer, ForeignKey( "cloud_uci.id" ), index=True ), - Column( "type", TEXT ), - Column( "reservation_id", TEXT ), - Column( "instance_id", TEXT ), - Column( "mi_id", Integer, ForeignKey( "cloud_image.id" ), index=True ), - Column( "state", TEXT ), - Column( "error", TEXT ), - Column( "public_dns", TEXT ), - Column( "private_dns", TEXT ), - Column( "security_group", TEXT ), - Column( "availability_zone", TEXT ) ) +CloudInstance_table = Table("cloud_instance", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("launch_time", DateTime), + Column("stop_time", DateTime), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True, nullable=False), + Column("uci_id", Integer, ForeignKey("cloud_uci.id"), index=True), + Column("type", TEXT), + Column("reservation_id", TEXT), + Column("instance_id", TEXT), + Column("mi_id", Integer, ForeignKey("cloud_image.id"), index=True), + Column("state", TEXT), + Column("error", TEXT), + Column("public_dns", TEXT), + Column("private_dns", TEXT), + Column("security_group", TEXT), + Column("availability_zone", TEXT)) -CloudStore_table = Table( "cloud_store", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "attach_time", DateTime ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True, nullable=False ), - Column( "uci_id", Integer, ForeignKey( "cloud_uci.id" ), index=True, nullable=False ), - Column( "volume_id", TEXT ), - Column( "size", Integer, nullable=False ), - Column( "availability_zone", TEXT ), - Column( "inst_id", Integer, ForeignKey( "cloud_instance.id" ) ), - Column( "status", TEXT ), - Column( "device", TEXT ), - Column( "space_consumed", Integer ), - Column( "error", TEXT ), - Column( "deleted", Boolean, default=False ) ) +CloudStore_table = Table("cloud_store", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("attach_time", DateTime), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True, nullable=False), + Column("uci_id", Integer, ForeignKey("cloud_uci.id"), index=True, nullable=False), + Column("volume_id", TEXT), + Column("size", Integer, nullable=False), + Column("availability_zone", TEXT), + Column("inst_id", Integer, ForeignKey("cloud_instance.id")), + Column("status", TEXT), + Column("device", TEXT), + Column("space_consumed", Integer), + Column("error", TEXT), + Column("deleted", Boolean, default=False)) -CloudSnapshot_table = Table( "cloud_snapshot", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True, nullable=False ), - Column( "uci_id", Integer, ForeignKey( "cloud_uci.id" ), index=True ), - Column( "store_id", Integer, ForeignKey( "cloud_store.id" ), index=True, nullable=False ), - Column( "snapshot_id", TEXT ), - Column( "status", TEXT ), - Column( "description", TEXT ), - Column( "error", TEXT ), - Column( "deleted", Boolean, default=False ) ) +CloudSnapshot_table = Table("cloud_snapshot", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True, nullable=False), + Column("uci_id", Integer, ForeignKey("cloud_uci.id"), index=True), + Column("store_id", Integer, ForeignKey("cloud_store.id"), index=True, nullable=False), + Column("snapshot_id", TEXT), + Column("status", TEXT), + Column("description", TEXT), + Column("error", TEXT), + Column("deleted", Boolean, default=False)) -CloudUserCredentials_table = Table( "cloud_user_credentials", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True, nullable=False ), - Column( "provider_id", Integer, ForeignKey( "cloud_provider.id" ), index=True, nullable=False ), - Column( "name", TEXT ), - Column( "access_key", TEXT ), - Column( "secret_key", TEXT ), - Column( "deleted", Boolean, default=False ) ) +CloudUserCredentials_table = Table("cloud_user_credentials", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True, nullable=False), + Column("provider_id", Integer, ForeignKey("cloud_provider.id"), index=True, nullable=False), + Column("name", TEXT), + Column("access_key", TEXT), + Column("secret_key", TEXT), + Column("deleted", Boolean, default=False)) -CloudProvider_table = Table( "cloud_provider", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True, nullable=False ), - Column( "type", TEXT, nullable=False ), - Column( "name", TEXT ), - Column( "region_connection", TEXT ), - Column( "region_name", TEXT ), - Column( "region_endpoint", TEXT ), - Column( "is_secure", Boolean ), - Column( "host", TEXT ), - Column( "port", Integer ), - Column( "proxy", TEXT ), - Column( "proxy_port", TEXT ), - Column( "proxy_user", TEXT ), - Column( "proxy_pass", TEXT ), - Column( "debug", Integer ), - Column( "https_connection_factory", TEXT ), - Column( "path", TEXT ), - Column( "deleted", Boolean, default=False ) ) +CloudProvider_table = Table("cloud_provider", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True, nullable=False), + Column("type", TEXT, nullable=False), + Column("name", TEXT), + Column("region_connection", TEXT), + Column("region_name", TEXT), + Column("region_endpoint", TEXT), + Column("is_secure", Boolean), + Column("host", TEXT), + Column("port", Integer), + Column("proxy", TEXT), + Column("proxy_port", TEXT), + Column("proxy_user", TEXT), + Column("proxy_pass", TEXT), + Column("debug", Integer), + Column("https_connection_factory", TEXT), + Column("path", TEXT), + Column("deleted", Boolean, default=False)) def upgrade(migrate_engine): diff --git a/lib/galaxy/model/migrate/versions/0027_request_events.py b/lib/galaxy/model/migrate/versions/0027_request_events.py index 831bb94e6109..4ed755d162cb 100644 --- a/lib/galaxy/model/migrate/versions/0027_request_events.py +++ b/lib/galaxy/model/migrate/versions/0027_request_events.py @@ -15,22 +15,22 @@ from galaxy.model.custom_types import TrimmedString now = datetime.datetime.utcnow -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) -handler = logging.StreamHandler( sys.stdout ) +handler = logging.StreamHandler(sys.stdout) format = "%(name)s %(levelname)s %(asctime)s %(message)s" -formatter = logging.Formatter( format ) -handler.setFormatter( formatter ) -log.addHandler( handler ) +formatter = logging.Formatter(format) +handler.setFormatter(formatter) +log.addHandler(handler) metadata = MetaData() RequestEvent_table = Table('request_event', metadata, - Column( "id", Integer, primary_key=True), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "request_id", Integer, ForeignKey( "request.id" ), index=True ), - Column( "state", TrimmedString( 255 ), index=True ), - Column( "comment", TEXT ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("request_id", Integer, ForeignKey("request.id"), index=True), + Column("state", TrimmedString(255), index=True), + Column("comment", TEXT)) def upgrade(migrate_engine): @@ -43,15 +43,15 @@ def localtimestamp(): elif migrate_engine.name == 'sqlite': return "current_date || ' ' || current_time" else: - raise Exception( 'Unable to convert data for unknown database type: %s' % migrate_engine.name ) + raise Exception('Unable to convert data for unknown database type: %s' % migrate_engine.name) - def nextval( table, col='id' ): + def nextval(table, col='id'): if migrate_engine.name in ['postgres', 'postgresql']: - return "nextval('%s_%s_seq')" % ( table, col ) + return "nextval('%s_%s_seq')" % (table, col) elif migrate_engine.name in ['mysql', 'sqlite']: return "null" else: - raise Exception( 'Unable to convert data for unknown database type: %s' % migrate_engine.name ) + raise Exception('Unable to convert data for unknown database type: %s' % migrate_engine.name) # Load existing tables metadata.reflect() # Add new request_event table @@ -70,16 +70,16 @@ def nextval( table, col='id' ): "request.state AS state," + \ "'%s' AS comment " + \ "FROM request;" - cmd = cmd % ( nextval('request_event'), localtimestamp(), localtimestamp(), 'Imported from request table') - migrate_engine.execute( cmd ) + cmd = cmd % (nextval('request_event'), localtimestamp(), localtimestamp(), 'Imported from request table') + migrate_engine.execute(cmd) if migrate_engine.name != 'sqlite': # Delete the state column try: - Request_table = Table( "request", metadata, autoload=True ) + Request_table = Table("request", metadata, autoload=True) except NoSuchTableError: Request_table = None - log.debug( "Failed loading table request" ) + log.debug("Failed loading table request") if Request_table is not None: try: Request_table.c.state.drop() diff --git a/lib/galaxy/model/migrate/versions/0028_external_metadata_file_override.py b/lib/galaxy/model/migrate/versions/0028_external_metadata_file_override.py index f870d06e23ab..3d7f6b8838ae 100644 --- a/lib/galaxy/model/migrate/versions/0028_external_metadata_file_override.py +++ b/lib/galaxy/model/migrate/versions/0028_external_metadata_file_override.py @@ -10,13 +10,13 @@ from sqlalchemy import Column, MetaData, String, Table -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) -handler = logging.StreamHandler( sys.stdout ) +handler = logging.StreamHandler(sys.stdout) format = "%(name)s %(levelname)s %(asctime)s %(message)s" -formatter = logging.Formatter( format ) -handler.setFormatter( formatter ) -log.addHandler( handler ) +formatter = logging.Formatter(format) +handler.setFormatter(formatter) +log.addHandler(handler) metadata = MetaData() @@ -27,9 +27,9 @@ def upgrade(migrate_engine): # Load existing tables metadata.reflect() try: - job_external_output_metadata = Table( "job_external_output_metadata", metadata, autoload=True ) - col = Column( "filename_override_metadata", String( 255 ) ) - col.create( job_external_output_metadata ) + job_external_output_metadata = Table("job_external_output_metadata", metadata, autoload=True) + col = Column("filename_override_metadata", String(255)) + col.create(job_external_output_metadata) assert col is job_external_output_metadata.c.filename_override_metadata except Exception: log.exception("Adding column 'filename_override_metadata' to job_external_output_metadata table failed.") diff --git a/lib/galaxy/model/migrate/versions/0029_user_actions.py b/lib/galaxy/model/migrate/versions/0029_user_actions.py index e62087b727ad..cf34cfe51118 100644 --- a/lib/galaxy/model/migrate/versions/0029_user_actions.py +++ b/lib/galaxy/model/migrate/versions/0029_user_actions.py @@ -9,18 +9,18 @@ from sqlalchemy import Column, DateTime, ForeignKey, Integer, MetaData, Table, Unicode now = datetime.datetime.utcnow -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() # New table to store user actions. -UserAction_table = Table( "user_action", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "session_id", Integer, ForeignKey( "galaxy_session.id" ), index=True ), - Column( "action", Unicode( 255 ) ), - Column( "context", Unicode( 512 ) ), - Column( "params", Unicode( 1024 ) ) ) +UserAction_table = Table("user_action", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("session_id", Integer, ForeignKey("galaxy_session.id"), index=True), + Column("action", Unicode(255)), + Column("context", Unicode(512)), + Column("params", Unicode(1024))) def upgrade(migrate_engine): diff --git a/lib/galaxy/model/migrate/versions/0030_history_slug_column.py b/lib/galaxy/model/migrate/versions/0030_history_slug_column.py index 11f05f40b99c..04564844e189 100644 --- a/lib/galaxy/model/migrate/versions/0030_history_slug_column.py +++ b/lib/galaxy/model/migrate/versions/0030_history_slug_column.py @@ -7,7 +7,7 @@ from sqlalchemy import Column, Index, MetaData, Table, TEXT -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() @@ -17,17 +17,17 @@ def upgrade(migrate_engine): print(__doc__) metadata.reflect() - History_table = Table( "history", metadata, autoload=True ) + History_table = Table("history", metadata, autoload=True) # Mysql needs manual index creation because of max length index. if migrate_engine.name != 'mysql': # Create slug column. - c = Column( "slug", TEXT, index=True ) - c.create( History_table, index_name='ix_history_slug') + c = Column("slug", TEXT, index=True) + c.create(History_table, index_name='ix_history_slug') else: - c = Column( "slug", TEXT ) - c.create( History_table, index_name='') - i = Index( "ix_history_slug", History_table.c.slug, mysql_length=200) + c = Column("slug", TEXT) + c.create(History_table, index_name='') + i = Index("ix_history_slug", History_table.c.slug, mysql_length=200) i.create() assert c is History_table.c.slug @@ -36,5 +36,5 @@ def downgrade(migrate_engine): metadata.bind = migrate_engine metadata.reflect() - History_table = Table( "history", metadata, autoload=True ) + History_table = Table("history", metadata, autoload=True) History_table.c.slug.drop() diff --git a/lib/galaxy/model/migrate/versions/0031_community_and_workflow_tags.py b/lib/galaxy/model/migrate/versions/0031_community_and_workflow_tags.py index 69f0c8d15194..96f031b8a4ab 100644 --- a/lib/galaxy/model/migrate/versions/0031_community_and_workflow_tags.py +++ b/lib/galaxy/model/migrate/versions/0031_community_and_workflow_tags.py @@ -8,26 +8,26 @@ from sqlalchemy import Column, ForeignKey, Integer, MetaData, Table, Unicode -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() -StoredWorkflowTagAssociation_table = Table( "stored_workflow_tag_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "stored_workflow_id", Integer, ForeignKey( "stored_workflow.id" ), index=True ), - Column( "tag_id", Integer, ForeignKey( "tag.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "user_tname", Unicode(255), index=True), - Column( "value", Unicode(255), index=True), - Column( "user_value", Unicode(255), index=True) ) - -WorkflowTagAssociation_table = Table( "workflow_tag_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "workflow_id", Integer, ForeignKey( "workflow.id" ), index=True ), - Column( "tag_id", Integer, ForeignKey( "tag.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "user_tname", Unicode(255), index=True), - Column( "value", Unicode(255), index=True), - Column( "user_value", Unicode(255), index=True) ) +StoredWorkflowTagAssociation_table = Table("stored_workflow_tag_association", metadata, + Column("id", Integer, primary_key=True), + Column("stored_workflow_id", Integer, ForeignKey("stored_workflow.id"), index=True), + Column("tag_id", Integer, ForeignKey("tag.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("user_tname", Unicode(255), index=True), + Column("value", Unicode(255), index=True), + Column("user_value", Unicode(255), index=True)) + +WorkflowTagAssociation_table = Table("workflow_tag_association", metadata, + Column("id", Integer, primary_key=True), + Column("workflow_id", Integer, ForeignKey("workflow.id"), index=True), + Column("tag_id", Integer, ForeignKey("tag.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("user_tname", Unicode(255), index=True), + Column("value", Unicode(255), index=True), + Column("user_value", Unicode(255), index=True)) def upgrade(migrate_engine): @@ -36,18 +36,18 @@ def upgrade(migrate_engine): metadata.reflect() # Create user_id column in history_tag_association table. - HistoryTagAssociation_table = Table( "history_tag_association", metadata, autoload=True ) + HistoryTagAssociation_table = Table("history_tag_association", metadata, autoload=True) if migrate_engine.name != 'sqlite': - c = Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ) + c = Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True) try: - c.create( HistoryTagAssociation_table, index_name='ix_history_tag_association_user_id') + c.create(HistoryTagAssociation_table, index_name='ix_history_tag_association_user_id') assert c is HistoryTagAssociation_table.c.user_id except Exception: log.exception("Adding user_id column to history_tag_association table failed.") else: - c = Column( "user_id", Integer) + c = Column("user_id", Integer) try: - c.create( HistoryTagAssociation_table) + c.create(HistoryTagAssociation_table) assert c is HistoryTagAssociation_table.c.user_id except Exception: log.exception("Adding user_id column to history_tag_association table failed.") @@ -55,24 +55,24 @@ def upgrade(migrate_engine): # Populate column so that user_id is the id of the user who owns the history (and, up to now, was the only person able to tag the history). if c is HistoryTagAssociation_table.c.user_id: migrate_engine.execute( - "UPDATE history_tag_association SET user_id=( SELECT user_id FROM history WHERE history_tag_association.history_id = history.id )" ) + "UPDATE history_tag_association SET user_id=( SELECT user_id FROM history WHERE history_tag_association.history_id = history.id )") if migrate_engine.name != 'sqlite': # Create user_id column in history_dataset_association_tag_association table. - HistoryDatasetAssociationTagAssociation_table = Table( "history_dataset_association_tag_association", metadata, autoload=True ) - c = Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ) + HistoryDatasetAssociationTagAssociation_table = Table("history_dataset_association_tag_association", metadata, autoload=True) + c = Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True) try: - c.create( HistoryDatasetAssociationTagAssociation_table, index_name='ix_history_dataset_association_tag_association_user_id') + c.create(HistoryDatasetAssociationTagAssociation_table, index_name='ix_history_dataset_association_tag_association_user_id') assert c is HistoryDatasetAssociationTagAssociation_table.c.user_id except Exception: log.exception("Adding user_id column to history_dataset_association_tag_association table failed.") else: # In sqlite, we can no longer quietly fail to add foreign key. # Create user_id column in history_dataset_association_tag_association table. - HistoryDatasetAssociationTagAssociation_table = Table( "history_dataset_association_tag_association", metadata, autoload=True ) - c = Column( "user_id", Integer) + HistoryDatasetAssociationTagAssociation_table = Table("history_dataset_association_tag_association", metadata, autoload=True) + c = Column("user_id", Integer) try: - c.create( HistoryDatasetAssociationTagAssociation_table) + c.create(HistoryDatasetAssociationTagAssociation_table) assert c is HistoryDatasetAssociationTagAssociation_table.c.user_id except Exception: log.exception("Adding user_id column to history_dataset_association_tag_association table failed.") @@ -80,22 +80,22 @@ def upgrade(migrate_engine): # Populate column so that user_id is the id of the user who owns the history_dataset_association (and, up to now, was the only person able to tag the page). if c is HistoryDatasetAssociationTagAssociation_table.c.user_id: migrate_engine.execute( - "UPDATE history_dataset_association_tag_association SET user_id=( SELECT history.user_id FROM history, history_dataset_association WHERE history_dataset_association.history_id = history.id AND history_dataset_association.id = history_dataset_association_tag_association.history_dataset_association_id)" ) + "UPDATE history_dataset_association_tag_association SET user_id=( SELECT history.user_id FROM history, history_dataset_association WHERE history_dataset_association.history_id = history.id AND history_dataset_association.id = history_dataset_association_tag_association.history_dataset_association_id)") if migrate_engine.name != 'sqlite': # Create user_id column in page_tag_association table. - PageTagAssociation_table = Table( "page_tag_association", metadata, autoload=True ) - c = Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ) + PageTagAssociation_table = Table("page_tag_association", metadata, autoload=True) + c = Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True) try: - c.create( PageTagAssociation_table, index_name='ix_page_tag_association_user_id') + c.create(PageTagAssociation_table, index_name='ix_page_tag_association_user_id') assert c is PageTagAssociation_table.c.user_id except Exception: log.exception("Adding user_id column to page_tag_association table failed.") else: # Create user_id column in page_tag_association table. - PageTagAssociation_table = Table( "page_tag_association", metadata, autoload=True ) - c = Column( "user_id", Integer ) + PageTagAssociation_table = Table("page_tag_association", metadata, autoload=True) + c = Column("user_id", Integer) try: - c.create( PageTagAssociation_table ) + c.create(PageTagAssociation_table) assert c is PageTagAssociation_table.c.user_id except Exception: log.exception("Adding user_id column to page_tag_association table failed.") @@ -103,7 +103,7 @@ def upgrade(migrate_engine): # Populate column so that user_id is the id of the user who owns the page (and, up to now, was the only person able to tag the page). if c is PageTagAssociation_table.c.user_id: migrate_engine.execute( - "UPDATE page_tag_association SET user_id=( SELECT user_id FROM page WHERE page_tag_association.page_id = page.id )" ) + "UPDATE page_tag_association SET user_id=( SELECT user_id FROM page WHERE page_tag_association.page_id = page.id )") # Create stored_workflow_tag_association table. try: @@ -123,21 +123,21 @@ def downgrade(migrate_engine): metadata.reflect() # Drop user_id column from history_tag_association table. - HistoryTagAssociation_table = Table( "history_tag_association", metadata, autoload=True ) + HistoryTagAssociation_table = Table("history_tag_association", metadata, autoload=True) try: HistoryTagAssociation_table.c.user_id.drop() except Exception: log.exception("Dropping column user_id from history_tag_association table failed.") # Drop user_id column from history_dataset_association_tag_association table. - HistoryDatasetAssociationTagAssociation_table = Table( "history_dataset_association_tag_association", metadata, autoload=True ) + HistoryDatasetAssociationTagAssociation_table = Table("history_dataset_association_tag_association", metadata, autoload=True) try: HistoryDatasetAssociationTagAssociation_table.c.user_id.drop() except Exception: log.exception("Dropping column user_id from history_dataset_association_tag_association table failed.") # Drop user_id column from page_tag_association table. - PageTagAssociation_table = Table( "page_tag_association", metadata, autoload=True ) + PageTagAssociation_table = Table("page_tag_association", metadata, autoload=True) try: PageTagAssociation_table.c.user_id.drop() except Exception: diff --git a/lib/galaxy/model/migrate/versions/0032_stored_workflow_slug_column.py b/lib/galaxy/model/migrate/versions/0032_stored_workflow_slug_column.py index 7f30dde6072c..a7ea25d6943b 100644 --- a/lib/galaxy/model/migrate/versions/0032_stored_workflow_slug_column.py +++ b/lib/galaxy/model/migrate/versions/0032_stored_workflow_slug_column.py @@ -7,7 +7,7 @@ from sqlalchemy import Column, Index, MetaData, Table, TEXT -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() @@ -17,27 +17,27 @@ def upgrade(migrate_engine): print(__doc__) metadata.reflect() - StoredWorkflow_table = Table( "stored_workflow", metadata, autoload=True ) + StoredWorkflow_table = Table("stored_workflow", metadata, autoload=True) # Create slug column. - c = Column( "slug", TEXT ) - c.create( StoredWorkflow_table ) + c = Column("slug", TEXT) + c.create(StoredWorkflow_table) assert c is StoredWorkflow_table.c.slug # Create slug index. if migrate_engine.name != 'sqlite': try: - i = Index( "ix_stored_workflow_slug", StoredWorkflow_table.c.slug, mysql_length=200 ) + i = Index("ix_stored_workflow_slug", StoredWorkflow_table.c.slug, mysql_length=200) i.create() except: # Mysql doesn't have a named index, but alter should work - StoredWorkflow_table.c.slug.alter( unique=False ) + StoredWorkflow_table.c.slug.alter(unique=False) def downgrade(migrate_engine): metadata.bind = migrate_engine metadata.reflect() - StoredWorkflow_table = Table( "stored_workflow", metadata, autoload=True ) + StoredWorkflow_table = Table("stored_workflow", metadata, autoload=True) StoredWorkflow_table.c.slug.drop() diff --git a/lib/galaxy/model/migrate/versions/0033_published_cols_for_histories_and_workflows.py b/lib/galaxy/model/migrate/versions/0033_published_cols_for_histories_and_workflows.py index fbe4f0030630..7e1f8e306c09 100644 --- a/lib/galaxy/model/migrate/versions/0033_published_cols_for_histories_and_workflows.py +++ b/lib/galaxy/model/migrate/versions/0033_published_cols_for_histories_and_workflows.py @@ -8,7 +8,7 @@ from sqlalchemy import Boolean, Column, Index, MetaData, Table -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() @@ -18,10 +18,10 @@ def upgrade(migrate_engine): metadata.reflect() # Create published column in history table. - History_table = Table( "history", metadata, autoload=True ) - c = Column( "published", Boolean, index=True ) + History_table = Table("history", metadata, autoload=True) + c = Column("published", Boolean, index=True) try: - c.create( History_table, index_name='ix_history_published') + c.create(History_table, index_name='ix_history_published') assert c is History_table.c.published except Exception: log.exception("Adding published column to history table failed.") @@ -29,17 +29,17 @@ def upgrade(migrate_engine): if migrate_engine.name != 'sqlite': # Create index for published column in history table. try: - i = Index( "ix_history_published", History_table.c.published ) + i = Index("ix_history_published", History_table.c.published) i.create() except: # Mysql doesn't have a named index, but alter should work - History_table.c.published.alter( unique=False ) + History_table.c.published.alter(unique=False) # Create published column in stored workflows table. - StoredWorkflow_table = Table( "stored_workflow", metadata, autoload=True ) - c = Column( "published", Boolean, index=True ) + StoredWorkflow_table = Table("stored_workflow", metadata, autoload=True) + c = Column("published", Boolean, index=True) try: - c.create( StoredWorkflow_table, index_name='ix_stored_workflow_published') + c.create(StoredWorkflow_table, index_name='ix_stored_workflow_published') assert c is StoredWorkflow_table.c.published except Exception: log.exception("Adding published column to stored_workflow table failed.") @@ -47,17 +47,17 @@ def upgrade(migrate_engine): if migrate_engine.name != 'sqlite': # Create index for published column in stored workflows table. try: - i = Index( "ix_stored_workflow_published", StoredWorkflow_table.c.published ) + i = Index("ix_stored_workflow_published", StoredWorkflow_table.c.published) i.create() except: # Mysql doesn't have a named index, but alter should work - StoredWorkflow_table.c.published.alter( unique=False ) + StoredWorkflow_table.c.published.alter(unique=False) # Create importable column in page table. - Page_table = Table( "page", metadata, autoload=True ) - c = Column( "importable", Boolean, index=True ) + Page_table = Table("page", metadata, autoload=True) + c = Column("importable", Boolean, index=True) try: - c.create( Page_table, index_name='ix_page_importable') + c.create(Page_table, index_name='ix_page_importable') assert c is Page_table.c.importable except Exception: log.exception("Adding importable column to page table failed.") @@ -65,11 +65,11 @@ def upgrade(migrate_engine): if migrate_engine.name != 'sqlite': # Create index for importable column in page table. try: - i = Index( "ix_page_importable", Page_table.c.importable ) + i = Index("ix_page_importable", Page_table.c.importable) i.create() except: # Mysql doesn't have a named index, but alter should work - Page_table.c.importable.alter( unique=False ) + Page_table.c.importable.alter(unique=False) def downgrade(migrate_engine): @@ -77,21 +77,21 @@ def downgrade(migrate_engine): metadata.reflect() # Drop published column from history table. - History_table = Table( "history", metadata, autoload=True ) + History_table = Table("history", metadata, autoload=True) try: History_table.c.published.drop() except Exception: log.exception("Dropping column published from history table failed.") # Drop published column from stored_workflow table. - StoredWorkflow_table = Table( "stored_workflow", metadata, autoload=True ) + StoredWorkflow_table = Table("stored_workflow", metadata, autoload=True) try: StoredWorkflow_table.c.published.drop() except Exception: log.exception("Dropping column published from stored_workflow table failed.") # Drop importable column from page table. - Page_table = Table( "page", metadata, autoload=True ) + Page_table = Table("page", metadata, autoload=True) try: Page_table.c.importable.drop() except Exception: diff --git a/lib/galaxy/model/migrate/versions/0034_page_user_share_association.py b/lib/galaxy/model/migrate/versions/0034_page_user_share_association.py index e8a78ce4c821..b949e724d732 100644 --- a/lib/galaxy/model/migrate/versions/0034_page_user_share_association.py +++ b/lib/galaxy/model/migrate/versions/0034_page_user_share_association.py @@ -7,13 +7,13 @@ from sqlalchemy import Column, ForeignKey, Integer, MetaData, Table -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() -PageUserShareAssociation_table = Table( "page_user_share_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "page_id", Integer, ForeignKey( "page.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ) ) +PageUserShareAssociation_table = Table("page_user_share_association", metadata, + Column("id", Integer, primary_key=True), + Column("page_id", Integer, ForeignKey("page.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True)) def upgrade(migrate_engine): diff --git a/lib/galaxy/model/migrate/versions/0035_item_annotations_and_workflow_step_tags.py b/lib/galaxy/model/migrate/versions/0035_item_annotations_and_workflow_step_tags.py index c55c18e7506f..2e675724e43e 100644 --- a/lib/galaxy/model/migrate/versions/0035_item_annotations_and_workflow_step_tags.py +++ b/lib/galaxy/model/migrate/versions/0035_item_annotations_and_workflow_step_tags.py @@ -7,45 +7,45 @@ from sqlalchemy import Column, ForeignKey, Index, Integer, MetaData, Table, TEXT, Unicode -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() # Annotation tables. -HistoryAnnotationAssociation_table = Table( "history_annotation_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "annotation", TEXT ) ) - -HistoryDatasetAssociationAnnotationAssociation_table = Table( "history_dataset_association_annotation_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "history_dataset_association_id", Integer, ForeignKey( "history_dataset_association.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "annotation", TEXT ) ) - -StoredWorkflowAnnotationAssociation_table = Table( "stored_workflow_annotation_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "stored_workflow_id", Integer, ForeignKey( "stored_workflow.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "annotation", TEXT ) ) - -WorkflowStepAnnotationAssociation_table = Table( "workflow_step_annotation_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "workflow_step_id", Integer, ForeignKey( "workflow_step.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "annotation", TEXT ) ) +HistoryAnnotationAssociation_table = Table("history_annotation_association", metadata, + Column("id", Integer, primary_key=True), + Column("history_id", Integer, ForeignKey("history.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("annotation", TEXT)) + +HistoryDatasetAssociationAnnotationAssociation_table = Table("history_dataset_association_annotation_association", metadata, + Column("id", Integer, primary_key=True), + Column("history_dataset_association_id", Integer, ForeignKey("history_dataset_association.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("annotation", TEXT)) + +StoredWorkflowAnnotationAssociation_table = Table("stored_workflow_annotation_association", metadata, + Column("id", Integer, primary_key=True), + Column("stored_workflow_id", Integer, ForeignKey("stored_workflow.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("annotation", TEXT)) + +WorkflowStepAnnotationAssociation_table = Table("workflow_step_annotation_association", metadata, + Column("id", Integer, primary_key=True), + Column("workflow_step_id", Integer, ForeignKey("workflow_step.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("annotation", TEXT)) # Tagging tables. -WorkflowStepTagAssociation_table = Table( "workflow_step_tag_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "workflow_step_id", Integer, ForeignKey( "workflow_step.id" ), index=True ), - Column( "tag_id", Integer, ForeignKey( "tag.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "user_tname", Unicode(255), index=True), - Column( "value", Unicode(255), index=True), - Column( "user_value", Unicode(255), index=True) ) +WorkflowStepTagAssociation_table = Table("workflow_step_tag_association", metadata, + Column("id", Integer, primary_key=True), + Column("workflow_step_id", Integer, ForeignKey("workflow_step.id"), index=True), + Column("tag_id", Integer, ForeignKey("tag.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("user_tname", Unicode(255), index=True), + Column("value", Unicode(255), index=True), + Column("user_value", Unicode(255), index=True)) def upgrade(migrate_engine): @@ -83,10 +83,10 @@ def upgrade(migrate_engine): except Exception: log.exception("Creating workflow_step_tag_association table failed.") - haaa = Index( "ix_history_anno_assoc_annotation", HistoryAnnotationAssociation_table.c.annotation, mysql_length=200) - hdaaa = Index( "ix_history_dataset_anno_assoc_annotation", HistoryDatasetAssociationAnnotationAssociation_table.c.annotation, mysql_length=200) - swaaa = Index( "ix_stored_workflow_ann_assoc_annotation", StoredWorkflowAnnotationAssociation_table.c.annotation, mysql_length=200) - wsaaa = Index( "ix_workflow_step_ann_assoc_annotation", WorkflowStepAnnotationAssociation_table.c.annotation, mysql_length=200) + haaa = Index("ix_history_anno_assoc_annotation", HistoryAnnotationAssociation_table.c.annotation, mysql_length=200) + hdaaa = Index("ix_history_dataset_anno_assoc_annotation", HistoryDatasetAssociationAnnotationAssociation_table.c.annotation, mysql_length=200) + swaaa = Index("ix_stored_workflow_ann_assoc_annotation", StoredWorkflowAnnotationAssociation_table.c.annotation, mysql_length=200) + wsaaa = Index("ix_workflow_step_ann_assoc_annotation", WorkflowStepAnnotationAssociation_table.c.annotation, mysql_length=200) try: haaa.create() diff --git a/lib/galaxy/model/migrate/versions/0036_add_deleted_column_to_library_template_assoc_tables.py b/lib/galaxy/model/migrate/versions/0036_add_deleted_column_to_library_template_assoc_tables.py index e340f74dd853..736fa4177161 100644 --- a/lib/galaxy/model/migrate/versions/0036_add_deleted_column_to_library_template_assoc_tables.py +++ b/lib/galaxy/model/migrate/versions/0036_add_deleted_column_to_library_template_assoc_tables.py @@ -8,7 +8,7 @@ from sqlalchemy import Boolean, Column, MetaData, Table -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() @@ -26,39 +26,39 @@ def upgrade(migrate_engine): print(__doc__) metadata.reflect() try: - LibraryInfoAssociation_table = Table( "library_info_association", metadata, autoload=True ) - c = Column( "deleted", Boolean, index=True, default=False ) - c.create( LibraryInfoAssociation_table, index_name='ix_library_info_association_deleted') + LibraryInfoAssociation_table = Table("library_info_association", metadata, autoload=True) + c = Column("deleted", Boolean, index=True, default=False) + c.create(LibraryInfoAssociation_table, index_name='ix_library_info_association_deleted') assert c is LibraryInfoAssociation_table.c.deleted except Exception: log.exception("Adding column 'deleted' to 'library_info_association' table failed.") cmd = "UPDATE library_info_association SET deleted = %s" % engine_false(migrate_engine) try: - migrate_engine.execute( cmd ) + migrate_engine.execute(cmd) except Exception: log.exception("deleted to false in library_info_association failed.") try: - LibraryFolderInfoAssociation_table = Table( "library_folder_info_association", metadata, autoload=True ) - c = Column( "deleted", Boolean, index=True, default=False ) - c.create( LibraryFolderInfoAssociation_table, index_name='ix_library_folder_info_association_deleted') + LibraryFolderInfoAssociation_table = Table("library_folder_info_association", metadata, autoload=True) + c = Column("deleted", Boolean, index=True, default=False) + c.create(LibraryFolderInfoAssociation_table, index_name='ix_library_folder_info_association_deleted') assert c is LibraryFolderInfoAssociation_table.c.deleted except Exception: log.exception("Adding column 'deleted' to 'library_folder_info_association' table failed.") cmd = "UPDATE library_folder_info_association SET deleted = %s" % engine_false(migrate_engine) try: - migrate_engine.execute( cmd ) + migrate_engine.execute(cmd) except Exception: log.exception("deleted to false in library_folder_info_association failed.") try: - LibraryDatasetDatasetInfoAssociation_table = Table( "library_dataset_dataset_info_association", metadata, autoload=True ) - c = Column( "deleted", Boolean, index=True, default=False ) - c.create( LibraryDatasetDatasetInfoAssociation_table, index_name='ix_library_dataset_dataset_info_association_deleted') + LibraryDatasetDatasetInfoAssociation_table = Table("library_dataset_dataset_info_association", metadata, autoload=True) + c = Column("deleted", Boolean, index=True, default=False) + c.create(LibraryDatasetDatasetInfoAssociation_table, index_name='ix_library_dataset_dataset_info_association_deleted') assert c is LibraryDatasetDatasetInfoAssociation_table.c.deleted except Exception: log.exception("Adding column 'deleted' to 'library_dataset_dataset_info_association' table failed.") cmd = "UPDATE library_dataset_dataset_info_association SET deleted = %s" % engine_false(migrate_engine) try: - migrate_engine.execute( cmd ) + migrate_engine.execute(cmd) except Exception: log.exception("deleted to false in library_dataset_dataset_info_association failed.") diff --git a/lib/galaxy/model/migrate/versions/0037_samples_library.py b/lib/galaxy/model/migrate/versions/0037_samples_library.py index e370f56a4c03..6d3f935a4f0c 100644 --- a/lib/galaxy/model/migrate/versions/0037_samples_library.py +++ b/lib/galaxy/model/migrate/versions/0037_samples_library.py @@ -16,13 +16,13 @@ from galaxy.model.custom_types import JSONType, TrimmedString now = datetime.datetime.utcnow -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) -handler = logging.StreamHandler( sys.stdout ) +handler = logging.StreamHandler(sys.stdout) format = "%(name)s %(levelname)s %(asctime)s %(message)s" -formatter = logging.Formatter( format ) -handler.setFormatter( formatter ) -log.addHandler( handler ) +formatter = logging.Formatter(format) +handler.setFormatter(formatter) +log.addHandler(handler) metadata = MetaData() @@ -34,40 +34,40 @@ def upgrade(migrate_engine): metadata.reflect() # retuest_type table try: - RequestType_table = Table( "request_type", metadata, autoload=True ) + RequestType_table = Table("request_type", metadata, autoload=True) except NoSuchTableError: RequestType_table = None - log.debug( "Failed loading table request_type" ) + log.debug("Failed loading table request_type") if RequestType_table is not None: # Add the datatx_info column in 'request_type' table try: - col = Column( "datatx_info", JSONType() ) - col.create( RequestType_table ) + col = Column("datatx_info", JSONType()) + col.create(RequestType_table) assert col is RequestType_table.c.datatx_info except Exception: log.exception("Adding column 'datatx_info' to request_type table failed.") # request table try: - Request_table = Table( "request", metadata, autoload=True ) + Request_table = Table("request", metadata, autoload=True) except NoSuchTableError: Request_table = None - log.debug( "Failed loading table request" ) + log.debug("Failed loading table request") if Request_table is not None: # Delete library_id & folder_id columns in the table 'request'. # if Galaxy is running on sqlite, the delete/recreate the table # otherwise remove the specific columns if migrate_engine.name == 'sqlite': # create a temporary table - RequestTemp_table = Table( 'request_temp', metadata, - Column( "id", Integer, primary_key=True), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "name", TrimmedString( 255 ), nullable=False ), - Column( "desc", TEXT ), - Column( "form_values_id", Integer, ForeignKey( "form_values.id" ), index=True ), - Column( "request_type_id", Integer, ForeignKey( "request_type.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "deleted", Boolean, index=True, default=False ) ) + RequestTemp_table = Table('request_temp', metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("name", TrimmedString(255), nullable=False), + Column("desc", TEXT), + Column("form_values_id", Integer, ForeignKey("form_values.id"), index=True), + Column("request_type_id", Integer, ForeignKey("request_type.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("deleted", Boolean, index=True, default=False)) try: RequestTemp_table.create() except Exception: @@ -76,7 +76,7 @@ def upgrade(migrate_engine): cmd = "INSERT INTO request_temp SELECT id, create_time, " + \ "update_time, name, desc, form_values_id, request_type_id, " + \ "user_id, deleted FROM request;" - migrate_engine.execute( cmd ) + migrate_engine.execute(cmd) # delete the 'request' table try: Request_table.drop() @@ -84,7 +84,7 @@ def upgrade(migrate_engine): log.exception("Dropping request table failed.") # rename table request_temp to request cmd = "ALTER TABLE request_temp RENAME TO request" - migrate_engine.execute( cmd ) + migrate_engine.execute(cmd) else: # Delete the library_id column in 'request' table try: @@ -98,49 +98,49 @@ def upgrade(migrate_engine): log.exception("Deleting column 'folder_id' to request table failed.") # sample table try: - Sample_table = Table( "sample", metadata, autoload=True ) + Sample_table = Table("sample", metadata, autoload=True) except NoSuchTableError: Sample_table = None - log.debug( "Failed loading table sample" ) + log.debug("Failed loading table sample") if Sample_table is not None: # Add the dataset_files column in 'sample' table try: - col = Column( "dataset_files", JSONType() ) - col.create( Sample_table ) + col = Column("dataset_files", JSONType()) + col.create(Sample_table) assert col is Sample_table.c.dataset_files except Exception: log.exception("Adding column 'dataset_files' to sample table failed.") # library table try: - Library_table = Table( "library", metadata, autoload=True ) + Library_table = Table("library", metadata, autoload=True) except NoSuchTableError: Library_table = None - log.debug( "Failed loading table library" ) + log.debug("Failed loading table library") if Library_table is not None: # Add the library_id column in 'sample' table try: if migrate_engine.name != 'sqlite': - col = Column( "library_id", Integer, ForeignKey( "library.id" ), index=True ) + col = Column("library_id", Integer, ForeignKey("library.id"), index=True) else: - col = Column( "library_id", Integer, index=True ) - col.create( Sample_table, index_name='ix_sample_library_id') + col = Column("library_id", Integer, index=True) + col.create(Sample_table, index_name='ix_sample_library_id') assert col is Sample_table.c.library_id except Exception: log.exception("Adding column 'library_id' to sample table failed.") # library_folder table try: - LibraryFolder_table = Table( "library_folder", metadata, autoload=True ) + LibraryFolder_table = Table("library_folder", metadata, autoload=True) except NoSuchTableError: LibraryFolder_table = None - log.debug( "Failed loading table library_folder" ) + log.debug("Failed loading table library_folder") if LibraryFolder_table is not None: # Add the library_id column in 'sample' table try: if migrate_engine.name != 'sqlite': - col = Column( "folder_id", Integer, ForeignKey( "library_folder.id" ), index=True ) + col = Column("folder_id", Integer, ForeignKey("library_folder.id"), index=True) else: - col = Column( "folder_id", Integer, index=True ) - col.create( Sample_table, index_name='ix_sample_library_folder_id') + col = Column("folder_id", Integer, index=True) + col.create(Sample_table, index_name='ix_sample_library_folder_id') assert col is Sample_table.c.folder_id except Exception: log.exception("Adding column 'folder_id' to sample table failed.") diff --git a/lib/galaxy/model/migrate/versions/0038_add_inheritable_column_to_library_template_assoc_tables.py b/lib/galaxy/model/migrate/versions/0038_add_inheritable_column_to_library_template_assoc_tables.py index 721872e6a773..6241708a9481 100644 --- a/lib/galaxy/model/migrate/versions/0038_add_inheritable_column_to_library_template_assoc_tables.py +++ b/lib/galaxy/model/migrate/versions/0038_add_inheritable_column_to_library_template_assoc_tables.py @@ -14,7 +14,7 @@ from galaxy.model.custom_types import TrimmedString now = datetime.datetime.utcnow -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() @@ -40,16 +40,16 @@ def upgrade(migrate_engine): # load the tables referenced in foreign keys metadata.reflect(only=['form_values', 'request_type', 'galaxy_user']) # create a temporary table - Request_table = Table( 'request', metadata, - Column( "id", Integer, primary_key=True), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "name", TrimmedString( 255 ), nullable=False ), - Column( "desc", TEXT ), - Column( "form_values_id", Integer, ForeignKey( "form_values.id" ), index=True ), - Column( "request_type_id", Integer, ForeignKey( "request_type.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "deleted", Boolean, index=True, default=False ) ) + Request_table = Table('request', metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("name", TrimmedString(255), nullable=False), + Column("desc", TEXT), + Column("form_values_id", Integer, ForeignKey("form_values.id"), index=True), + Column("request_type_id", Integer, ForeignKey("request_type.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("deleted", Boolean, index=True, default=False)) try: Request_table.create() except Exception: @@ -57,27 +57,27 @@ def upgrade(migrate_engine): metadata.reflect() try: - LibraryInfoAssociation_table = Table( "library_info_association", metadata, autoload=True ) - c = Column( "inheritable", Boolean, index=True, default=False ) - c.create( LibraryInfoAssociation_table, index_name='ix_library_info_association_inheritable') + LibraryInfoAssociation_table = Table("library_info_association", metadata, autoload=True) + c = Column("inheritable", Boolean, index=True, default=False) + c.create(LibraryInfoAssociation_table, index_name='ix_library_info_association_inheritable') assert c is LibraryInfoAssociation_table.c.inheritable except Exception: log.exception("Adding column 'inheritable' to 'library_info_association' table failed.") cmd = "UPDATE library_info_association SET inheritable = %s" % engine_false(migrate_engine) try: - migrate_engine.execute( cmd ) + migrate_engine.execute(cmd) except Exception: log.exception("Setting value of column inheritable to false in library_info_association failed.") try: - LibraryFolderInfoAssociation_table = Table( "library_folder_info_association", metadata, autoload=True ) - c = Column( "inheritable", Boolean, index=True, default=False ) - c.create( LibraryFolderInfoAssociation_table, index_name='ix_library_folder_info_association_inheritable') + LibraryFolderInfoAssociation_table = Table("library_folder_info_association", metadata, autoload=True) + c = Column("inheritable", Boolean, index=True, default=False) + c.create(LibraryFolderInfoAssociation_table, index_name='ix_library_folder_info_association_inheritable') assert c is LibraryFolderInfoAssociation_table.c.inheritable except Exception: log.exception("Adding column 'inheritable' to 'library_folder_info_association' table failed.") cmd = "UPDATE library_folder_info_association SET inheritable = %s" % engine_false(migrate_engine) try: - migrate_engine.execute( cmd ) + migrate_engine.execute(cmd) except Exception: log.exception("Setting value of column inheritable to false in library_folder_info_association failed.") diff --git a/lib/galaxy/model/migrate/versions/0039_add_synopsis_column_to_library_table.py b/lib/galaxy/model/migrate/versions/0039_add_synopsis_column_to_library_table.py index 0388eea05282..e0350905b705 100644 --- a/lib/galaxy/model/migrate/versions/0039_add_synopsis_column_to_library_table.py +++ b/lib/galaxy/model/migrate/versions/0039_add_synopsis_column_to_library_table.py @@ -7,7 +7,7 @@ from sqlalchemy import Column, MetaData, Table, TEXT -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() @@ -16,9 +16,9 @@ def upgrade(migrate_engine): print(__doc__) metadata.reflect() try: - Library_table = Table( "library", metadata, autoload=True ) - c = Column( "synopsis", TEXT ) - c.create( Library_table ) + Library_table = Table("library", metadata, autoload=True) + c = Column("synopsis", TEXT) + c.create(Library_table) assert c is Library_table.c.synopsis except Exception: log.exception("Adding column 'synopsis' to 'library' table failed.") @@ -28,7 +28,7 @@ def downgrade(migrate_engine): metadata.bind = migrate_engine metadata.reflect() try: - Library_table = Table( "library", metadata, autoload=True ) + Library_table = Table("library", metadata, autoload=True) Library_table.c.synopsis.drop() except Exception: log.exception("Dropping column 'synopsis' from 'library' table failed") diff --git a/lib/galaxy/model/migrate/versions/0040_page_annotations.py b/lib/galaxy/model/migrate/versions/0040_page_annotations.py index 81099455619b..000f772fd576 100644 --- a/lib/galaxy/model/migrate/versions/0040_page_annotations.py +++ b/lib/galaxy/model/migrate/versions/0040_page_annotations.py @@ -7,14 +7,14 @@ from sqlalchemy import Column, ForeignKey, Integer, MetaData, Table, TEXT -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() -PageAnnotationAssociation_table = Table( "page_annotation_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "page_id", Integer, ForeignKey( "page.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "annotation", TEXT, index=True) ) +PageAnnotationAssociation_table = Table("page_annotation_association", metadata, + Column("id", Integer, primary_key=True), + Column("page_id", Integer, ForeignKey("page.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("annotation", TEXT, index=True)) def upgrade(migrate_engine): diff --git a/lib/galaxy/model/migrate/versions/0041_workflow_invocation.py b/lib/galaxy/model/migrate/versions/0041_workflow_invocation.py index a81ad9b76000..1060e45e4458 100644 --- a/lib/galaxy/model/migrate/versions/0041_workflow_invocation.py +++ b/lib/galaxy/model/migrate/versions/0041_workflow_invocation.py @@ -8,26 +8,26 @@ from sqlalchemy import Column, DateTime, ForeignKey, Integer, MetaData, Table -logging.basicConfig( level=logging.DEBUG ) -log = logging.getLogger( __name__ ) +logging.basicConfig(level=logging.DEBUG) +log = logging.getLogger(__name__) now = datetime.datetime.utcnow metadata = MetaData() -WorkflowInvocation_table = Table( "workflow_invocation", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "workflow_id", Integer, ForeignKey( "workflow.id" ), index=True, nullable=False ) ) +WorkflowInvocation_table = Table("workflow_invocation", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("workflow_id", Integer, ForeignKey("workflow.id"), index=True, nullable=False)) -WorkflowInvocationStep_table = Table( "workflow_invocation_step", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "workflow_invocation_id", Integer, ForeignKey( "workflow_invocation.id" ), index=True, nullable=False ), - Column( "workflow_step_id", Integer, ForeignKey( "workflow_step.id" ), index=True, nullable=False ), - Column( "job_id", Integer, ForeignKey( "job.id" ), index=True, nullable=False ) ) +WorkflowInvocationStep_table = Table("workflow_invocation_step", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("workflow_invocation_id", Integer, ForeignKey("workflow_invocation.id"), index=True, nullable=False), + Column("workflow_step_id", Integer, ForeignKey("workflow_step.id"), index=True, nullable=False), + Column("job_id", Integer, ForeignKey("job.id"), index=True, nullable=False)) -tables = [ WorkflowInvocation_table, WorkflowInvocationStep_table ] +tables = [WorkflowInvocation_table, WorkflowInvocationStep_table] def upgrade(migrate_engine): @@ -39,7 +39,7 @@ def upgrade(migrate_engine): try: table.create() except: - log.warning( "Failed to create table '%s', ignoring (might result in wrong schema)" % table.name ) + log.warning("Failed to create table '%s', ignoring (might result in wrong schema)" % table.name) def downgrade(migrate_engine): diff --git a/lib/galaxy/model/migrate/versions/0042_workflow_invocation_fix.py b/lib/galaxy/model/migrate/versions/0042_workflow_invocation_fix.py index a5f0cf691ab7..88ae7c478db7 100644 --- a/lib/galaxy/model/migrate/versions/0042_workflow_invocation_fix.py +++ b/lib/galaxy/model/migrate/versions/0042_workflow_invocation_fix.py @@ -8,8 +8,8 @@ from sqlalchemy import Column, DateTime, ForeignKey, Integer, MetaData, Table -logging.basicConfig( level=logging.DEBUG ) -log = logging.getLogger( __name__ ) +logging.basicConfig(level=logging.DEBUG) +log = logging.getLogger(__name__) now = datetime.datetime.utcnow metadata = MetaData() @@ -20,34 +20,34 @@ def upgrade(migrate_engine): metadata.reflect() # 1) Drop - for table_name in [ "workflow_invocation_step", "workflow_invocation" ]: + for table_name in ["workflow_invocation_step", "workflow_invocation"]: try: - t = Table( table_name, metadata, autoload=True ) + t = Table(table_name, metadata, autoload=True) t.drop() metadata.remove(t) except: - log.exception( "Failed to drop table '%s', ignoring (might result in wrong schema)" % table_name ) + log.exception("Failed to drop table '%s', ignoring (might result in wrong schema)" % table_name) # 2) Readd - WorkflowInvocation_table = Table( "workflow_invocation", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "workflow_id", Integer, ForeignKey( "workflow.id" ), index=True, nullable=False ) ) - - WorkflowInvocationStep_table = Table( "workflow_invocation_step", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "workflow_invocation_id", Integer, ForeignKey( "workflow_invocation.id" ), index=True, nullable=False ), - Column( "workflow_step_id", Integer, ForeignKey( "workflow_step.id" ), index=True, nullable=False ), - Column( "job_id", Integer, ForeignKey( "job.id" ), index=True, nullable=True ) ) - - for table in [ WorkflowInvocation_table, WorkflowInvocationStep_table ]: + WorkflowInvocation_table = Table("workflow_invocation", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("workflow_id", Integer, ForeignKey("workflow.id"), index=True, nullable=False)) + + WorkflowInvocationStep_table = Table("workflow_invocation_step", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("workflow_invocation_id", Integer, ForeignKey("workflow_invocation.id"), index=True, nullable=False), + Column("workflow_step_id", Integer, ForeignKey("workflow_step.id"), index=True, nullable=False), + Column("job_id", Integer, ForeignKey("job.id"), index=True, nullable=True)) + + for table in [WorkflowInvocation_table, WorkflowInvocationStep_table]: try: table.create() except: - log.exception( "Failed to create table '%s', ignoring (might result in wrong schema)" % table.name ) + log.exception("Failed to create table '%s', ignoring (might result in wrong schema)" % table.name) def downgrade(migrate_engine): diff --git a/lib/galaxy/model/migrate/versions/0043_visualization_sharing_tagging_annotating.py b/lib/galaxy/model/migrate/versions/0043_visualization_sharing_tagging_annotating.py index 459c59751bda..a6937d0b9939 100644 --- a/lib/galaxy/model/migrate/versions/0043_visualization_sharing_tagging_annotating.py +++ b/lib/galaxy/model/migrate/versions/0043_visualization_sharing_tagging_annotating.py @@ -7,34 +7,34 @@ from sqlalchemy import Boolean, Column, ForeignKey, Index, Integer, MetaData, Table, TEXT, Unicode -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() # Sharing visualizations. -VisualizationUserShareAssociation_table = Table( "visualization_user_share_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "visualization_id", Integer, ForeignKey( "visualization.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ) ) +VisualizationUserShareAssociation_table = Table("visualization_user_share_association", metadata, + Column("id", Integer, primary_key=True), + Column("visualization_id", Integer, ForeignKey("visualization.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True)) # Tagging visualizations. -VisualizationTagAssociation_table = Table( "visualization_tag_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "visualization_id", Integer, ForeignKey( "visualization.id" ), index=True ), - Column( "tag_id", Integer, ForeignKey( "tag.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "user_tname", Unicode(255), index=True), - Column( "value", Unicode(255), index=True), - Column( "user_value", Unicode(255), index=True) ) +VisualizationTagAssociation_table = Table("visualization_tag_association", metadata, + Column("id", Integer, primary_key=True), + Column("visualization_id", Integer, ForeignKey("visualization.id"), index=True), + Column("tag_id", Integer, ForeignKey("tag.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("user_tname", Unicode(255), index=True), + Column("value", Unicode(255), index=True), + Column("user_value", Unicode(255), index=True)) # Annotating visualizations. -VisualizationAnnotationAssociation_table = Table( "visualization_annotation_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "visualization_id", Integer, ForeignKey( "visualization.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "annotation", TEXT, index=False ) ) +VisualizationAnnotationAssociation_table = Table("visualization_annotation_association", metadata, + Column("id", Integer, primary_key=True), + Column("visualization_id", Integer, ForeignKey("visualization.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("annotation", TEXT, index=False)) def engine_false(migrate_engine): @@ -51,7 +51,7 @@ def upgrade(migrate_engine): print(__doc__) metadata.reflect() - Visualiation_table = Table( "visualization", metadata, autoload=True ) + Visualiation_table = Table("visualization", metadata, autoload=True) # Create visualization_user_share_association table. try: VisualizationUserShareAssociation_table.create() @@ -59,35 +59,35 @@ def upgrade(migrate_engine): log.exception("Creating visualization_user_share_association table failed.") # Add columns & create indices for supporting sharing to visualization table. - deleted_column = Column( "deleted", Boolean, default=False, index=True ) - importable_column = Column( "importable", Boolean, default=False, index=True ) - slug_column = Column( "slug", TEXT ) - published_column = Column( "published", Boolean, index=True ) + deleted_column = Column("deleted", Boolean, default=False, index=True) + importable_column = Column("importable", Boolean, default=False, index=True) + slug_column = Column("slug", TEXT) + published_column = Column("published", Boolean, index=True) try: # Add column. - deleted_column.create( Visualiation_table, index_name="ix_visualization_deleted") + deleted_column.create(Visualiation_table, index_name="ix_visualization_deleted") assert deleted_column is Visualiation_table.c.deleted # Fill column with default value. cmd = "UPDATE visualization SET deleted = %s" % engine_false(migrate_engine) - migrate_engine.execute( cmd ) + migrate_engine.execute(cmd) except Exception: log.exception("Adding deleted column to visualization table failed.") try: # Add column. - importable_column.create( Visualiation_table, index_name='ix_visualization_importable') + importable_column.create(Visualiation_table, index_name='ix_visualization_importable') assert importable_column is Visualiation_table.c.importable # Fill column with default value. cmd = "UPDATE visualization SET importable = %s" % engine_false(migrate_engine) - migrate_engine.execute( cmd ) + migrate_engine.execute(cmd) except Exception: log.exception("Adding importable column to visualization table failed.") try: - slug_column.create( Visualiation_table ) + slug_column.create(Visualiation_table) assert slug_column is Visualiation_table.c.slug except Exception: log.exception("Adding slug column to visualization table failed.") @@ -96,21 +96,21 @@ def upgrade(migrate_engine): if migrate_engine.name == 'mysql': # Have to create index manually. cmd = "CREATE INDEX ix_visualization_slug ON visualization ( slug ( 100 ) )" - migrate_engine.execute( cmd ) + migrate_engine.execute(cmd) else: - i = Index( "ix_visualization_slug", Visualiation_table.c.slug ) + i = Index("ix_visualization_slug", Visualiation_table.c.slug) i.create() except Exception: log.exception("Adding index 'ix_visualization_slug' failed.") try: # Add column. - published_column.create( Visualiation_table, index_name='ix_visualization_published') + published_column.create(Visualiation_table, index_name='ix_visualization_published') assert published_column is Visualiation_table.c.published # Fill column with default value. cmd = "UPDATE visualization SET published = %s" % engine_false(migrate_engine) - migrate_engine.execute( cmd ) + migrate_engine.execute(cmd) except Exception: log.exception("Adding published column to visualization table failed.") @@ -131,9 +131,9 @@ def upgrade(migrate_engine): if migrate_engine.name == 'mysql': # Have to create index manually. cmd = "CREATE INDEX ix_visualization_annotation_association_annotation ON visualization_annotation_association ( annotation ( 100 ) )" - migrate_engine.execute( cmd ) + migrate_engine.execute(cmd) else: - i = Index( "ix_visualization_annotation_association_annotation", VisualizationAnnotationAssociation_table.c.annotation ) + i = Index("ix_visualization_annotation_association_annotation", VisualizationAnnotationAssociation_table.c.annotation) i.create() except Exception: log.exception("Adding index 'ix_visualization_annotation_association_annotation' failed.") @@ -143,7 +143,7 @@ def downgrade(migrate_engine): metadata.bind = migrate_engine metadata.reflect() - Visualiation_table = Table( "visualization", metadata, autoload=True ) + Visualiation_table = Table("visualization", metadata, autoload=True) # Drop visualization_user_share_association table. try: VisualizationUserShareAssociation_table.drop() diff --git a/lib/galaxy/model/migrate/versions/0044_add_notify_column_to_request_table.py b/lib/galaxy/model/migrate/versions/0044_add_notify_column_to_request_table.py index 1555ad8e4b78..a12e4e50cfa5 100644 --- a/lib/galaxy/model/migrate/versions/0044_add_notify_column_to_request_table.py +++ b/lib/galaxy/model/migrate/versions/0044_add_notify_column_to_request_table.py @@ -7,7 +7,7 @@ from sqlalchemy import Boolean, Column, MetaData, Table -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() @@ -16,9 +16,9 @@ def upgrade(migrate_engine): print(__doc__) metadata.reflect() try: - Request_table = Table( "request", metadata, autoload=True ) - c = Column( "notify", Boolean, default=False ) - c.create( Request_table ) + Request_table = Table("request", metadata, autoload=True) + c = Column("notify", Boolean, default=False) + c.create(Request_table) assert c is Request_table.c.notify except Exception: log.exception("Adding column 'notify' to 'request' table failed.") @@ -28,7 +28,7 @@ def downgrade(migrate_engine): metadata.bind = migrate_engine metadata.reflect() try: - Request_table = Table( "request", metadata, autoload=True ) + Request_table = Table("request", metadata, autoload=True) Request_table.c.notify.drop() except Exception: log.exception("Dropping column 'notify' from 'request' table failed.") diff --git a/lib/galaxy/model/migrate/versions/0045_request_type_permissions_table.py b/lib/galaxy/model/migrate/versions/0045_request_type_permissions_table.py index e3fefd543547..4d729e2a2ca8 100644 --- a/lib/galaxy/model/migrate/versions/0045_request_type_permissions_table.py +++ b/lib/galaxy/model/migrate/versions/0045_request_type_permissions_table.py @@ -9,16 +9,16 @@ from sqlalchemy import Column, DateTime, ForeignKey, Integer, MetaData, Table, TEXT now = datetime.datetime.utcnow -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() -RequestTypePermissions_table = Table( "request_type_permissions", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "action", TEXT ), - Column( "request_type_id", Integer, ForeignKey( "request_type.id" ), nullable=True, index=True ), - Column( "role_id", Integer, ForeignKey( "role.id" ), index=True ) ) +RequestTypePermissions_table = Table("request_type_permissions", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("action", TEXT), + Column("request_type_id", Integer, ForeignKey("request_type.id"), nullable=True, index=True), + Column("role_id", Integer, ForeignKey("role.id"), index=True)) def upgrade(migrate_engine): @@ -35,7 +35,7 @@ def downgrade(migrate_engine): metadata.bind = migrate_engine metadata.reflect() try: - RequestTypePermissions_table = Table( "request_type_permissions", metadata, autoload=True ) + RequestTypePermissions_table = Table("request_type_permissions", metadata, autoload=True) RequestTypePermissions_table.drop() except Exception: log.exception("Dropping 'request_type_permissions' table failed.") diff --git a/lib/galaxy/model/migrate/versions/0046_post_job_actions.py b/lib/galaxy/model/migrate/versions/0046_post_job_actions.py index 41f6aa6c7bcd..5cfa32a14a27 100644 --- a/lib/galaxy/model/migrate/versions/0046_post_job_actions.py +++ b/lib/galaxy/model/migrate/versions/0046_post_job_actions.py @@ -10,13 +10,13 @@ # Need our custom types, but don't import anything else from model from galaxy.model.custom_types import JSONType -logging.basicConfig( level=logging.DEBUG ) -log = logging.getLogger( __name__ ) +logging.basicConfig(level=logging.DEBUG) +log = logging.getLogger(__name__) metadata = MetaData() PostJobAction_table = Table("post_job_action", metadata, Column("id", Integer, primary_key=True), - Column("workflow_step_id", Integer, ForeignKey( "workflow_step.id" ), index=True, nullable=False), + Column("workflow_step_id", Integer, ForeignKey("workflow_step.id"), index=True, nullable=False), Column("action_type", String(255), nullable=False), Column("output_name", String(255), nullable=True), Column("action_arguments", JSONType, nullable=True)) diff --git a/lib/galaxy/model/migrate/versions/0047_job_table_user_id_column.py b/lib/galaxy/model/migrate/versions/0047_job_table_user_id_column.py index d8dd8fba4d48..cb0604690181 100644 --- a/lib/galaxy/model/migrate/versions/0047_job_table_user_id_column.py +++ b/lib/galaxy/model/migrate/versions/0047_job_table_user_id_column.py @@ -9,13 +9,13 @@ from sqlalchemy import Column, ForeignKey, Integer, MetaData, Table from sqlalchemy.exc import NoSuchTableError -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) -handler = logging.StreamHandler( sys.stdout ) +handler = logging.StreamHandler(sys.stdout) format = "%(name)s %(levelname)s %(asctime)s %(message)s" -formatter = logging.Formatter( format ) -handler.setFormatter( formatter ) -log.addHandler( handler ) +formatter = logging.Formatter(format) +handler.setFormatter(formatter) +log.addHandler(handler) metadata = MetaData() @@ -25,23 +25,23 @@ def upgrade(migrate_engine): print(__doc__) metadata.reflect() try: - Job_table = Table( "job", metadata, autoload=True ) + Job_table = Table("job", metadata, autoload=True) except NoSuchTableError: Job_table = None - log.debug( "Failed loading table job" ) + log.debug("Failed loading table job") if Job_table is not None: if migrate_engine.name != 'sqlite': try: - col = Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True, nullable=True ) - col.create( Job_table, index_name='ix_job_user_id' ) + col = Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True, nullable=True) + col.create(Job_table, index_name='ix_job_user_id') assert col is Job_table.c.user_id except Exception: log.exception("Adding column 'user_id' to job table failed.") else: try: - col = Column( "user_id", Integer, nullable=True) - col.create( Job_table ) + col = Column("user_id", Integer, nullable=True) + col.create(Job_table) assert col is Job_table.c.user_id except Exception: log.exception("Adding column 'user_id' to job table failed.") @@ -50,17 +50,17 @@ def upgrade(migrate_engine): + "galaxy_session.user_id AS galaxy_user_id " \ + "FROM job " \ + "JOIN galaxy_session ON job.session_id = galaxy_session.id;" - job_users = migrate_engine.execute( cmd ).fetchall() - print("Updating user_id column in job table for ", len( job_users ), " rows...") + job_users = migrate_engine.execute(cmd).fetchall() + print("Updating user_id column in job table for ", len(job_users), " rows...") print("") update_count = 0 for row in job_users: if row.galaxy_user_id: - cmd = "UPDATE job SET user_id = %d WHERE id = %d" % ( int( row.galaxy_user_id ), int( row.galaxy_job_id ) ) + cmd = "UPDATE job SET user_id = %d WHERE id = %d" % (int(row.galaxy_user_id), int(row.galaxy_job_id)) update_count += 1 - migrate_engine.execute( cmd ) + migrate_engine.execute(cmd) print("Updated the user_id column for ", update_count, " rows in the job table. ") - print(len( job_users ) - update_count, " rows have no user_id since the value was NULL in the galaxy_session table.") + print(len(job_users) - update_count, " rows have no user_id since the value was NULL in the galaxy_session table.") print("") except Exception: log.exception("Updating job.user_id column failed.") @@ -70,7 +70,7 @@ def downgrade(migrate_engine): metadata.bind = migrate_engine metadata.reflect() try: - Job_table = Table( "job", metadata, autoload=True ) + Job_table = Table("job", metadata, autoload=True) col = Job_table.c.user_id col.drop() except Exception: diff --git a/lib/galaxy/model/migrate/versions/0048_dataset_instance_state_column.py b/lib/galaxy/model/migrate/versions/0048_dataset_instance_state_column.py index 2d3026898200..73ff14c7d85b 100644 --- a/lib/galaxy/model/migrate/versions/0048_dataset_instance_state_column.py +++ b/lib/galaxy/model/migrate/versions/0048_dataset_instance_state_column.py @@ -11,17 +11,17 @@ from galaxy.model.custom_types import TrimmedString -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) -handler = logging.StreamHandler( sys.stdout ) +handler = logging.StreamHandler(sys.stdout) format = "%(name)s %(levelname)s %(asctime)s %(message)s" -formatter = logging.Formatter( format ) -handler.setFormatter( formatter ) -log.addHandler( handler ) +formatter = logging.Formatter(format) +handler.setFormatter(formatter) +log.addHandler(handler) metadata = MetaData() -DATASET_INSTANCE_TABLE_NAMES = [ 'history_dataset_association', 'library_dataset_dataset_association' ] +DATASET_INSTANCE_TABLE_NAMES = ['history_dataset_association', 'library_dataset_dataset_association'] def upgrade(migrate_engine): @@ -31,15 +31,15 @@ def upgrade(migrate_engine): dataset_instance_tables = [] for table_name in DATASET_INSTANCE_TABLE_NAMES: try: - dataset_instance_tables.append( ( table_name, Table( table_name, metadata, autoload=True ) ) ) + dataset_instance_tables.append((table_name, Table(table_name, metadata, autoload=True))) except NoSuchTableError: - log.debug( "Failed loading table %s" % table_name ) + log.debug("Failed loading table %s" % table_name) if dataset_instance_tables: for table_name, dataset_instance_table in dataset_instance_tables: index_name = "ix_%s_state" % table_name try: - col = Column( "state", TrimmedString( 64 ), index=True, nullable=True ) - col.create( dataset_instance_table, index_name=index_name) + col = Column("state", TrimmedString(64), index=True, nullable=True) + col.create(dataset_instance_table, index_name=index_name) assert col is dataset_instance_table.c.state except Exception: log.exception("Adding column 'state' to %s table failed.", table_name) @@ -51,9 +51,9 @@ def downgrade(migrate_engine): dataset_instance_tables = [] for table_name in DATASET_INSTANCE_TABLE_NAMES: try: - dataset_instance_tables.append( ( table_name, Table( table_name, metadata, autoload=True ) ) ) + dataset_instance_tables.append((table_name, Table(table_name, metadata, autoload=True))) except NoSuchTableError: - log.debug( "Failed loading table %s" % table_name ) + log.debug("Failed loading table %s" % table_name) if dataset_instance_tables: for table_name, dataset_instance_table in dataset_instance_tables: try: diff --git a/lib/galaxy/model/migrate/versions/0049_api_keys_table.py b/lib/galaxy/model/migrate/versions/0049_api_keys_table.py index eed3c69d85e2..f833b21ad6ce 100644 --- a/lib/galaxy/model/migrate/versions/0049_api_keys_table.py +++ b/lib/galaxy/model/migrate/versions/0049_api_keys_table.py @@ -11,14 +11,14 @@ from galaxy.model.custom_types import TrimmedString now = datetime.datetime.utcnow -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() -APIKeys_table = Table( "api_keys", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "key", TrimmedString( 32 ), index=True, unique=True ) ) +APIKeys_table = Table("api_keys", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("key", TrimmedString(32), index=True, unique=True)) def upgrade(migrate_engine): diff --git a/lib/galaxy/model/migrate/versions/0050_drop_cloud_tables.py b/lib/galaxy/model/migrate/versions/0050_drop_cloud_tables.py index e7c95ae649ba..61d1187e893d 100644 --- a/lib/galaxy/model/migrate/versions/0050_drop_cloud_tables.py +++ b/lib/galaxy/model/migrate/versions/0050_drop_cloud_tables.py @@ -9,117 +9,117 @@ from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, MetaData, Table, TEXT now = datetime.datetime.utcnow -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() -CloudImage_table = Table( "cloud_image", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "provider_type", TEXT ), - Column( "image_id", TEXT, nullable=False ), - Column( "manifest", TEXT ), - Column( "state", TEXT ), - Column( "architecture", TEXT ), - Column( "deleted", Boolean, default=False ) ) +CloudImage_table = Table("cloud_image", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("provider_type", TEXT), + Column("image_id", TEXT, nullable=False), + Column("manifest", TEXT), + Column("state", TEXT), + Column("architecture", TEXT), + Column("deleted", Boolean, default=False)) """ UserConfiguredInstance (UCI) table """ -UCI_table = Table( "cloud_uci", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True, nullable=False ), - Column( "credentials_id", Integer, ForeignKey( "cloud_user_credentials.id" ), index=True ), - Column( "key_pair_name", TEXT ), - Column( "key_pair_material", TEXT ), - Column( "name", TEXT ), - Column( "state", TEXT ), - Column( "error", TEXT ), - Column( "total_size", Integer ), - Column( "launch_time", DateTime ), - Column( "deleted", Boolean, default=False ) ) +UCI_table = Table("cloud_uci", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True, nullable=False), + Column("credentials_id", Integer, ForeignKey("cloud_user_credentials.id"), index=True), + Column("key_pair_name", TEXT), + Column("key_pair_material", TEXT), + Column("name", TEXT), + Column("state", TEXT), + Column("error", TEXT), + Column("total_size", Integer), + Column("launch_time", DateTime), + Column("deleted", Boolean, default=False)) -CloudInstance_table = Table( "cloud_instance", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "launch_time", DateTime ), - Column( "stop_time", DateTime ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True, nullable=False ), - Column( "uci_id", Integer, ForeignKey( "cloud_uci.id" ), index=True ), - Column( "type", TEXT ), - Column( "reservation_id", TEXT ), - Column( "instance_id", TEXT ), - Column( "mi_id", Integer, ForeignKey( "cloud_image.id" ), index=True ), - Column( "state", TEXT ), - Column( "error", TEXT ), - Column( "public_dns", TEXT ), - Column( "private_dns", TEXT ), - Column( "security_group", TEXT ), - Column( "availability_zone", TEXT ) ) +CloudInstance_table = Table("cloud_instance", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("launch_time", DateTime), + Column("stop_time", DateTime), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True, nullable=False), + Column("uci_id", Integer, ForeignKey("cloud_uci.id"), index=True), + Column("type", TEXT), + Column("reservation_id", TEXT), + Column("instance_id", TEXT), + Column("mi_id", Integer, ForeignKey("cloud_image.id"), index=True), + Column("state", TEXT), + Column("error", TEXT), + Column("public_dns", TEXT), + Column("private_dns", TEXT), + Column("security_group", TEXT), + Column("availability_zone", TEXT)) -CloudStore_table = Table( "cloud_store", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "attach_time", DateTime ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True, nullable=False ), - Column( "uci_id", Integer, ForeignKey( "cloud_uci.id" ), index=True, nullable=False ), - Column( "volume_id", TEXT ), - Column( "size", Integer, nullable=False ), - Column( "availability_zone", TEXT ), - Column( "inst_id", Integer, ForeignKey( "cloud_instance.id" ) ), - Column( "status", TEXT ), - Column( "device", TEXT ), - Column( "space_consumed", Integer ), - Column( "error", TEXT ), - Column( "deleted", Boolean, default=False ) ) +CloudStore_table = Table("cloud_store", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("attach_time", DateTime), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True, nullable=False), + Column("uci_id", Integer, ForeignKey("cloud_uci.id"), index=True, nullable=False), + Column("volume_id", TEXT), + Column("size", Integer, nullable=False), + Column("availability_zone", TEXT), + Column("inst_id", Integer, ForeignKey("cloud_instance.id")), + Column("status", TEXT), + Column("device", TEXT), + Column("space_consumed", Integer), + Column("error", TEXT), + Column("deleted", Boolean, default=False)) -CloudSnapshot_table = Table( "cloud_snapshot", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True, nullable=False ), - Column( "uci_id", Integer, ForeignKey( "cloud_uci.id" ), index=True ), - Column( "store_id", Integer, ForeignKey( "cloud_store.id" ), index=True, nullable=False ), - Column( "snapshot_id", TEXT ), - Column( "status", TEXT ), - Column( "description", TEXT ), - Column( "error", TEXT ), - Column( "deleted", Boolean, default=False ) ) +CloudSnapshot_table = Table("cloud_snapshot", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True, nullable=False), + Column("uci_id", Integer, ForeignKey("cloud_uci.id"), index=True), + Column("store_id", Integer, ForeignKey("cloud_store.id"), index=True, nullable=False), + Column("snapshot_id", TEXT), + Column("status", TEXT), + Column("description", TEXT), + Column("error", TEXT), + Column("deleted", Boolean, default=False)) -CloudUserCredentials_table = Table( "cloud_user_credentials", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True, nullable=False ), - Column( "provider_id", Integer, ForeignKey( "cloud_provider.id" ), index=True, nullable=False ), - Column( "name", TEXT ), - Column( "access_key", TEXT ), - Column( "secret_key", TEXT ), - Column( "deleted", Boolean, default=False ) ) +CloudUserCredentials_table = Table("cloud_user_credentials", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True, nullable=False), + Column("provider_id", Integer, ForeignKey("cloud_provider.id"), index=True, nullable=False), + Column("name", TEXT), + Column("access_key", TEXT), + Column("secret_key", TEXT), + Column("deleted", Boolean, default=False)) -CloudProvider_table = Table( "cloud_provider", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True, nullable=False ), - Column( "type", TEXT, nullable=False ), - Column( "name", TEXT ), - Column( "region_connection", TEXT ), - Column( "region_name", TEXT ), - Column( "region_endpoint", TEXT ), - Column( "is_secure", Boolean ), - Column( "host", TEXT ), - Column( "port", Integer ), - Column( "proxy", TEXT ), - Column( "proxy_port", TEXT ), - Column( "proxy_user", TEXT ), - Column( "proxy_pass", TEXT ), - Column( "debug", Integer ), - Column( "https_connection_factory", TEXT ), - Column( "path", TEXT ), - Column( "deleted", Boolean, default=False ) ) +CloudProvider_table = Table("cloud_provider", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True, nullable=False), + Column("type", TEXT, nullable=False), + Column("name", TEXT), + Column("region_connection", TEXT), + Column("region_name", TEXT), + Column("region_endpoint", TEXT), + Column("is_secure", Boolean), + Column("host", TEXT), + Column("port", Integer), + Column("proxy", TEXT), + Column("proxy_port", TEXT), + Column("proxy_user", TEXT), + Column("proxy_pass", TEXT), + Column("debug", Integer), + Column("https_connection_factory", TEXT), + Column("path", TEXT), + Column("deleted", Boolean, default=False)) def upgrade(migrate_engine): diff --git a/lib/galaxy/model/migrate/versions/0051_imported_col_for_jobs_table.py b/lib/galaxy/model/migrate/versions/0051_imported_col_for_jobs_table.py index e6f5ddb62417..ea490d88f70a 100644 --- a/lib/galaxy/model/migrate/versions/0051_imported_col_for_jobs_table.py +++ b/lib/galaxy/model/migrate/versions/0051_imported_col_for_jobs_table.py @@ -7,7 +7,7 @@ from sqlalchemy import Boolean, Column, MetaData, Table -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() @@ -26,14 +26,14 @@ def upgrade(migrate_engine): metadata.reflect() # Create and initialize imported column in job table. - Jobs_table = Table( "job", metadata, autoload=True ) - c = Column( "imported", Boolean, default=False, index=True ) + Jobs_table = Table("job", metadata, autoload=True) + c = Column("imported", Boolean, default=False, index=True) try: # Create - c.create( Jobs_table, index_name="ix_job_imported") + c.create(Jobs_table, index_name="ix_job_imported") assert c is Jobs_table.c.imported - migrate_engine.execute( "UPDATE job SET imported=%s" % engine_false(migrate_engine) ) + migrate_engine.execute("UPDATE job SET imported=%s" % engine_false(migrate_engine)) except Exception: log.exception("Adding imported column to job table failed.") @@ -43,7 +43,7 @@ def downgrade(migrate_engine): metadata.reflect() # Drop imported column from job table. - Jobs_table = Table( "job", metadata, autoload=True ) + Jobs_table = Table("job", metadata, autoload=True) try: Jobs_table.c.imported.drop() except Exception: diff --git a/lib/galaxy/model/migrate/versions/0052_sample_dataset_table.py b/lib/galaxy/model/migrate/versions/0052_sample_dataset_table.py index 01122889ac1d..64253415fd0c 100644 --- a/lib/galaxy/model/migrate/versions/0052_sample_dataset_table.py +++ b/lib/galaxy/model/migrate/versions/0052_sample_dataset_table.py @@ -14,17 +14,17 @@ from galaxy.model.custom_types import TrimmedString now = datetime.datetime.utcnow -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() -def nextval( migrate_engine, table, col='id' ): +def nextval(migrate_engine, table, col='id'): if migrate_engine.name in ['postgres', 'postgresql']: - return "nextval('%s_%s_seq')" % ( table, col ) + return "nextval('%s_%s_seq')" % (table, col) elif migrate_engine.name in ['mysql', 'sqlite']: return "null" else: - raise Exception( 'Unable to convert data for unknown database type: %s' % migrate_engine.name ) + raise Exception('Unable to convert data for unknown database type: %s' % migrate_engine.name) def localtimestamp(migrate_engine): @@ -33,19 +33,19 @@ def localtimestamp(migrate_engine): elif migrate_engine.name == 'sqlite': return "current_date || ' ' || current_time" else: - raise Exception( 'Unable to convert data for unknown database type: %s' % migrate_engine.name ) + raise Exception('Unable to convert data for unknown database type: %s' % migrate_engine.name) SampleDataset_table = Table('sample_dataset', metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "sample_id", Integer, ForeignKey( "sample.id" ), index=True ), - Column( "name", TrimmedString( 255 ), nullable=False ), - Column( "file_path", TrimmedString( 255 ), nullable=False ), - Column( "status", TrimmedString( 255 ), nullable=False ), - Column( "error_msg", TEXT ), - Column( "size", TrimmedString( 255 ) ) ) + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("sample_id", Integer, ForeignKey("sample.id"), index=True), + Column("name", TrimmedString(255), nullable=False), + Column("file_path", TrimmedString(255), nullable=False), + Column("status", TrimmedString(255), nullable=False), + Column("error_msg", TEXT), + Column("size", TrimmedString(255))) def upgrade(migrate_engine): @@ -58,7 +58,7 @@ def upgrade(migrate_engine): log.exception("Creating sample_dataset table failed.") cmd = "SELECT id, dataset_files FROM sample" - result = migrate_engine.execute( cmd ) + result = migrate_engine.execute(cmd) for r in result: sample_id = r[0] if r[1]: @@ -66,23 +66,23 @@ def upgrade(migrate_engine): for df in dataset_files: if isinstance(df, dict): cmd = "INSERT INTO sample_dataset VALUES (%s, %s, %s, %s, '%s', '%s', '%s', '%s', '%s')" - cmd = cmd % ( nextval(migrate_engine, 'sample_dataset'), - localtimestamp(migrate_engine), - localtimestamp(migrate_engine), - str(sample_id), - df.get('name', ''), - df.get('filepath', ''), - df.get('status', '').replace('"', '').replace("'", ""), - "", - df.get('size', '').replace('"', '').replace("'", "").replace(df.get('filepath', ''), '').strip() ) - migrate_engine.execute( cmd ) + cmd = cmd % (nextval(migrate_engine, 'sample_dataset'), + localtimestamp(migrate_engine), + localtimestamp(migrate_engine), + str(sample_id), + df.get('name', ''), + df.get('filepath', ''), + df.get('status', '').replace('"', '').replace("'", ""), + "", + df.get('size', '').replace('"', '').replace("'", "").replace(df.get('filepath', ''), '').strip()) + migrate_engine.execute(cmd) # Delete the dataset_files column in the Sample table try: - Sample_table = Table( "sample", metadata, autoload=True ) + Sample_table = Table("sample", metadata, autoload=True) except NoSuchTableError: Sample_table = None - log.debug( "Failed loading table sample" ) + log.debug("Failed loading table sample") if Sample_table is not None: try: Sample_table.c.dataset_files.drop() diff --git a/lib/galaxy/model/migrate/versions/0053_item_ratings.py b/lib/galaxy/model/migrate/versions/0053_item_ratings.py index c44a805ec970..3b0c21123cd4 100644 --- a/lib/galaxy/model/migrate/versions/0053_item_ratings.py +++ b/lib/galaxy/model/migrate/versions/0053_item_ratings.py @@ -7,40 +7,40 @@ from sqlalchemy import Column, ForeignKey, Index, Integer, MetaData, Table -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() # Rating tables. -HistoryRatingAssociation_table = Table( "history_rating_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "rating", Integer, index=True) ) - -HistoryDatasetAssociationRatingAssociation_table = Table( "history_dataset_association_rating_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "history_dataset_association_id", Integer, ForeignKey( "history_dataset_association.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "rating", Integer, index=True) ) - -StoredWorkflowRatingAssociation_table = Table( "stored_workflow_rating_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "stored_workflow_id", Integer, ForeignKey( "stored_workflow.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "rating", Integer, index=True) ) - -PageRatingAssociation_table = Table( "page_rating_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "page_id", Integer, ForeignKey( "page.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "rating", Integer, index=True) ) - -VisualizationRatingAssociation_table = Table( "visualization_rating_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "visualization_id", Integer, ForeignKey( "visualization.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "rating", Integer, index=True) ) +HistoryRatingAssociation_table = Table("history_rating_association", metadata, + Column("id", Integer, primary_key=True), + Column("history_id", Integer, ForeignKey("history.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("rating", Integer, index=True)) + +HistoryDatasetAssociationRatingAssociation_table = Table("history_dataset_association_rating_association", metadata, + Column("id", Integer, primary_key=True), + Column("history_dataset_association_id", Integer, ForeignKey("history_dataset_association.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("rating", Integer, index=True)) + +StoredWorkflowRatingAssociation_table = Table("stored_workflow_rating_association", metadata, + Column("id", Integer, primary_key=True), + Column("stored_workflow_id", Integer, ForeignKey("stored_workflow.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("rating", Integer, index=True)) + +PageRatingAssociation_table = Table("page_rating_association", metadata, + Column("id", Integer, primary_key=True), + Column("page_id", Integer, ForeignKey("page.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("rating", Integer, index=True)) + +VisualizationRatingAssociation_table = Table("visualization_rating_association", metadata, + Column("id", Integer, primary_key=True), + Column("visualization_id", Integer, ForeignKey("visualization.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("rating", Integer, index=True)) def upgrade(migrate_engine): @@ -61,7 +61,7 @@ def upgrade(migrate_engine): # MySQL cannot handle long index names; when we see this error, create the index name manually. if migrate_engine.name == 'mysql' and \ str(e).lower().find("identifier name 'ix_history_dataset_association_rating_association_history_dataset_association_id' is too long"): - i = Index( "ix_hda_rating_association_hda_id", HistoryDatasetAssociationRatingAssociation_table.c.history_dataset_association_id ) + i = Index("ix_hda_rating_association_hda_id", HistoryDatasetAssociationRatingAssociation_table.c.history_dataset_association_id) try: i.create() except Exception: diff --git a/lib/galaxy/model/migrate/versions/0054_visualization_dbkey.py b/lib/galaxy/model/migrate/versions/0054_visualization_dbkey.py index 3cfe92d9cb5e..9c66c8c59046 100644 --- a/lib/galaxy/model/migrate/versions/0054_visualization_dbkey.py +++ b/lib/galaxy/model/migrate/versions/0054_visualization_dbkey.py @@ -8,7 +8,7 @@ from sqlalchemy import Column, Index, MetaData, Table, TEXT -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() @@ -18,24 +18,24 @@ def upgrade(migrate_engine): print(__doc__) metadata.reflect() - Visualization_table = Table( "visualization", metadata, autoload=True ) - Visualization_revision_table = Table( "visualization_revision", metadata, autoload=True ) + Visualization_table = Table("visualization", metadata, autoload=True) + Visualization_revision_table = Table("visualization_revision", metadata, autoload=True) # Create dbkey columns. - x = Column( "dbkey", TEXT ) - y = Column( "dbkey", TEXT ) - x.create( Visualization_table ) - y.create( Visualization_revision_table ) + x = Column("dbkey", TEXT) + y = Column("dbkey", TEXT) + x.create(Visualization_table) + y.create(Visualization_revision_table) # Manually create indexes for compatability w/ mysql_length. - xi = Index( "ix_visualization_dbkey", Visualization_table.c.dbkey, mysql_length=200) + xi = Index("ix_visualization_dbkey", Visualization_table.c.dbkey, mysql_length=200) xi.create() - yi = Index( "ix_visualization_revision_dbkey", Visualization_revision_table.c.dbkey, mysql_length=200) + yi = Index("ix_visualization_revision_dbkey", Visualization_revision_table.c.dbkey, mysql_length=200) yi.create() assert x is Visualization_table.c.dbkey assert y is Visualization_revision_table.c.dbkey - all_viz = migrate_engine.execute( "SELECT visualization.id as viz_id, visualization_revision.id as viz_rev_id, visualization_revision.config FROM visualization_revision \ - LEFT JOIN visualization ON visualization.id=visualization_revision.visualization_id" ) + all_viz = migrate_engine.execute("SELECT visualization.id as viz_id, visualization_revision.id as viz_rev_id, visualization_revision.config FROM visualization_revision \ + LEFT JOIN visualization ON visualization.id=visualization_revision.visualization_id") for viz in all_viz: viz_id = viz['viz_id'] viz_rev_id = viz['viz_rev_id'] @@ -49,8 +49,8 @@ def downgrade(migrate_engine): metadata.bind = migrate_engine metadata.reflect() - Visualization_table = Table( "visualization", metadata, autoload=True ) - Visualization_revision_table = Table( "visualization_revision", metadata, autoload=True ) + Visualization_table = Table("visualization", metadata, autoload=True) + Visualization_revision_table = Table("visualization_revision", metadata, autoload=True) Visualization_table.c.dbkey.drop() Visualization_revision_table.c.dbkey.drop() diff --git a/lib/galaxy/model/migrate/versions/0055_add_pja_assoc_for_jobs.py b/lib/galaxy/model/migrate/versions/0055_add_pja_assoc_for_jobs.py index caad47f09615..afc4f504d761 100644 --- a/lib/galaxy/model/migrate/versions/0055_add_pja_assoc_for_jobs.py +++ b/lib/galaxy/model/migrate/versions/0055_add_pja_assoc_for_jobs.py @@ -7,7 +7,7 @@ from sqlalchemy import Column, ForeignKey, Integer, MetaData, Table -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() PostJobActionAssociation_table = Table("post_job_action_association", metadata, diff --git a/lib/galaxy/model/migrate/versions/0056_workflow_outputs.py b/lib/galaxy/model/migrate/versions/0056_workflow_outputs.py index d0a0f7e886f4..9b354080ec20 100644 --- a/lib/galaxy/model/migrate/versions/0056_workflow_outputs.py +++ b/lib/galaxy/model/migrate/versions/0056_workflow_outputs.py @@ -7,15 +7,15 @@ from sqlalchemy import Column, ForeignKey, Integer, MetaData, String, Table -logging.basicConfig( level=logging.DEBUG ) -log = logging.getLogger( __name__ ) +logging.basicConfig(level=logging.DEBUG) +log = logging.getLogger(__name__) metadata = MetaData() -WorkflowOutput_table = Table( "workflow_output", metadata, - Column( "id", Integer, primary_key=True ), - Column( "workflow_step_id", Integer, ForeignKey("workflow_step.id"), index=True, nullable=False), - Column( "output_name", String(255), nullable=True)) +WorkflowOutput_table = Table("workflow_output", metadata, + Column("id", Integer, primary_key=True), + Column("workflow_step_id", Integer, ForeignKey("workflow_step.id"), index=True, nullable=False), + Column("output_name", String(255), nullable=True)) tables = [WorkflowOutput_table] @@ -28,7 +28,7 @@ def upgrade(migrate_engine): try: table.create() except: - log.warning( "Failed to create table '%s', ignoring (might result in wrong schema)" % table.name ) + log.warning("Failed to create table '%s', ignoring (might result in wrong schema)" % table.name) def downgrade(migrate_engine): diff --git a/lib/galaxy/model/migrate/versions/0057_request_notify.py b/lib/galaxy/model/migrate/versions/0057_request_notify.py index 4a2d1b351024..4f973fe8ffed 100644 --- a/lib/galaxy/model/migrate/versions/0057_request_notify.py +++ b/lib/galaxy/model/migrate/versions/0057_request_notify.py @@ -12,7 +12,7 @@ from galaxy.model.custom_types import JSONType -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() @@ -21,27 +21,27 @@ def upgrade(migrate_engine): print(__doc__) metadata.reflect() try: - Request_table = Table( "request", metadata, autoload=True ) + Request_table = Table("request", metadata, autoload=True) except NoSuchTableError: Request_table = None - log.debug( "Failed loading table 'request'" ) + log.debug("Failed loading table 'request'") if Request_table is not None: # create the column again as JSONType try: - col = Column( "notification", JSONType() ) - col.create( Request_table ) + col = Column("notification", JSONType()) + col.create(Request_table) assert col is Request_table.c.notification except Exception: log.exception("Creating column 'notification' in the 'request' table failed.") cmd = "SELECT id, user_id, notify FROM request" - result = migrate_engine.execute( cmd ) + result = migrate_engine.execute(cmd) for r in result: id = int(r[0]) notify_new = dict(email=[], sample_states=[], body='', subject='') cmd = "UPDATE request SET notification='%s' WHERE id=%i" % (dumps(notify_new), id) - migrate_engine.execute( cmd ) + migrate_engine.execute(cmd) # remove the 'notify' column for non-sqlite databases. if migrate_engine.name != 'sqlite': @@ -55,7 +55,7 @@ def downgrade(migrate_engine): metadata.bind = migrate_engine metadata.reflect() try: - Request_table = Table( "request", metadata, autoload=True ) + Request_table = Table("request", metadata, autoload=True) Request_table.c.notification.drop() except Exception: log.exception("Dropping column 'notification' from 'request' table failed.") diff --git a/lib/galaxy/model/migrate/versions/0058_history_import_export.py b/lib/galaxy/model/migrate/versions/0058_history_import_export.py index e62c4164d16c..a4d457bbe330 100644 --- a/lib/galaxy/model/migrate/versions/0058_history_import_export.py +++ b/lib/galaxy/model/migrate/versions/0058_history_import_export.py @@ -7,20 +7,20 @@ from sqlalchemy import Boolean, Column, ForeignKey, Integer, MetaData, Table, TEXT -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() # Table to add. -JobExportHistoryArchive_table = Table( "job_export_history_archive", metadata, - Column( "id", Integer, primary_key=True ), - Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ), - Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ), - Column( "dataset_id", Integer, ForeignKey( "dataset.id" ), index=True ), - Column( "compressed", Boolean, index=True, default=False ), - Column( "history_attrs_filename", TEXT ), - Column( "datasets_attrs_filename", TEXT ), - Column( "jobs_attrs_filename", TEXT ) ) +JobExportHistoryArchive_table = Table("job_export_history_archive", metadata, + Column("id", Integer, primary_key=True), + Column("job_id", Integer, ForeignKey("job.id"), index=True), + Column("history_id", Integer, ForeignKey("history.id"), index=True), + Column("dataset_id", Integer, ForeignKey("dataset.id"), index=True), + Column("compressed", Boolean, index=True, default=False), + Column("history_attrs_filename", TEXT), + Column("datasets_attrs_filename", TEXT), + Column("jobs_attrs_filename", TEXT)) def upgrade(migrate_engine): diff --git a/lib/galaxy/model/migrate/versions/0059_sample_dataset_file_path.py b/lib/galaxy/model/migrate/versions/0059_sample_dataset_file_path.py index 926514a63045..6c6390276d90 100644 --- a/lib/galaxy/model/migrate/versions/0059_sample_dataset_file_path.py +++ b/lib/galaxy/model/migrate/versions/0059_sample_dataset_file_path.py @@ -9,7 +9,7 @@ from sqlalchemy import Column, MetaData, Table, TEXT from sqlalchemy.exc import NoSuchTableError -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() @@ -18,14 +18,14 @@ def upgrade(migrate_engine): print(__doc__) metadata.reflect() try: - SampleDataset_table = Table( "sample_dataset", metadata, autoload=True ) + SampleDataset_table = Table("sample_dataset", metadata, autoload=True) except NoSuchTableError: SampleDataset_table = None - log.debug( "Failed loading table 'sample_dataset'" ) + log.debug("Failed loading table 'sample_dataset'") if SampleDataset_table is not None: cmd = "SELECT id, file_path FROM sample_dataset" - result = migrate_engine.execute( cmd ) + result = migrate_engine.execute(cmd) filepath_dict = {} for r in result: id = int(r[0]) @@ -37,15 +37,15 @@ def upgrade(migrate_engine): log.exception("Deleting column 'file_path' from the 'sample_dataset' table failed.") # create the column again try: - col = Column( "file_path", TEXT ) - col.create( SampleDataset_table ) + col = Column("file_path", TEXT) + col.create(SampleDataset_table) assert col is SampleDataset_table.c.file_path except Exception: log.exception("Creating column 'file_path' in the 'sample_dataset' table failed.") for id, file_path in filepath_dict.items(): cmd = "update sample_dataset set file_path='%s' where id=%i" % (file_path, id) - migrate_engine.execute( cmd ) + migrate_engine.execute(cmd) def downgrade(migrate_engine): diff --git a/lib/galaxy/model/migrate/versions/0060_history_archive_import.py b/lib/galaxy/model/migrate/versions/0060_history_archive_import.py index 9ee87446a6db..27cae402d9c6 100644 --- a/lib/galaxy/model/migrate/versions/0060_history_archive_import.py +++ b/lib/galaxy/model/migrate/versions/0060_history_archive_import.py @@ -8,21 +8,21 @@ from sqlalchemy import Boolean, Column, ForeignKey, Integer, MetaData, Table, TEXT -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() # Columns to add. -importing_col = Column( "importing", Boolean, index=True, default=False ) -ldda_parent_col = Column( "ldda_parent_id", Integer, ForeignKey( "library_dataset_dataset_association.id" ), index=True ) +importing_col = Column("importing", Boolean, index=True, default=False) +ldda_parent_col = Column("ldda_parent_id", Integer, ForeignKey("library_dataset_dataset_association.id"), index=True) # Table to add. -JobImportHistoryArchive_table = Table( "job_import_history_archive", metadata, - Column( "id", Integer, primary_key=True ), - Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ), - Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ), - Column( "archive_dir", TEXT ) ) +JobImportHistoryArchive_table = Table("job_import_history_archive", metadata, + Column("id", Integer, primary_key=True), + Column("job_id", Integer, ForeignKey("job.id"), index=True), + Column("history_id", Integer, ForeignKey("history.id"), index=True), + Column("archive_dir", TEXT)) def engine_false(migrate_engine): @@ -41,12 +41,12 @@ def upgrade(migrate_engine): # Add column to history table and initialize. try: - History_table = Table( "history", metadata, autoload=True ) - importing_col.create( History_table, index_name="ix_history_importing") + History_table = Table("history", metadata, autoload=True) + importing_col.create(History_table, index_name="ix_history_importing") assert importing_col is History_table.c.importing # Initialize column to false. - migrate_engine.execute( "UPDATE history SET importing=%s" % engine_false(migrate_engine) ) + migrate_engine.execute("UPDATE history SET importing=%s" % engine_false(migrate_engine)) except Exception: log.exception("Adding column 'importing' to history table failed.") @@ -63,7 +63,7 @@ def downgrade(migrate_engine): # Drop 'importing' column from history table. try: - History_table = Table( "history", metadata, autoload=True ) + History_table = Table("history", metadata, autoload=True) importing_col = History_table.c.importing importing_col.drop() except Exception: diff --git a/lib/galaxy/model/migrate/versions/0061_tasks.py b/lib/galaxy/model/migrate/versions/0061_tasks.py index b83d64baf647..6a067ee0ca08 100644 --- a/lib/galaxy/model/migrate/versions/0061_tasks.py +++ b/lib/galaxy/model/migrate/versions/0061_tasks.py @@ -8,26 +8,26 @@ from sqlalchemy import Column, DateTime, ForeignKey, Integer, MetaData, String, Table, TEXT -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() now = datetime.datetime.utcnow -Task_table = Table( "task", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "execution_time", DateTime ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "state", String( 64 ), index=True ), - Column( "command_line", TEXT ), - Column( "param_filename", String( 1024 ) ), - Column( "runner_name", String( 255 ) ), - Column( "stdout", TEXT ), - Column( "stderr", TEXT ), - Column( "traceback", TEXT ), - Column( "job_id", Integer, ForeignKey( "job.id" ), index=True, nullable=False ), - Column( "part_file", String(1024)), - Column( "task_runner_name", String( 255 ) ), - Column( "task_runner_external_id", String( 255 ) ) ) +Task_table = Table("task", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("execution_time", DateTime), + Column("update_time", DateTime, default=now, onupdate=now), + Column("state", String(64), index=True), + Column("command_line", TEXT), + Column("param_filename", String(1024)), + Column("runner_name", String(255)), + Column("stdout", TEXT), + Column("stderr", TEXT), + Column("traceback", TEXT), + Column("job_id", Integer, ForeignKey("job.id"), index=True, nullable=False), + Column("part_file", String(1024)), + Column("task_runner_name", String(255)), + Column("task_runner_external_id", String(255))) tables = [Task_table] @@ -40,7 +40,7 @@ def upgrade(migrate_engine): try: table.create() except: - log.warning( "Failed to create table '%s', ignoring (might result in wrong schema)" % table.name ) + log.warning("Failed to create table '%s', ignoring (might result in wrong schema)" % table.name) def downgrade(migrate_engine): diff --git a/lib/galaxy/model/migrate/versions/0062_user_openid_table.py b/lib/galaxy/model/migrate/versions/0062_user_openid_table.py index 54a5aa846623..83378235f532 100644 --- a/lib/galaxy/model/migrate/versions/0062_user_openid_table.py +++ b/lib/galaxy/model/migrate/versions/0062_user_openid_table.py @@ -10,18 +10,18 @@ from sqlalchemy import Column, DateTime, ForeignKey, Index, Integer, MetaData, Table, TEXT now = datetime.datetime.utcnow -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() # Table to add -UserOpenID_table = Table( "galaxy_user_openid", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, index=True, default=now, onupdate=now ), - Column( "session_id", Integer, ForeignKey( "galaxy_session.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "openid", TEXT ) ) +UserOpenID_table = Table("galaxy_user_openid", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, index=True, default=now, onupdate=now), + Column("session_id", Integer, ForeignKey("galaxy_session.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("openid", TEXT)) def upgrade(migrate_engine): @@ -38,9 +38,9 @@ def upgrade(migrate_engine): ix_name = 'ix_galaxy_user_openid_openid' if migrate_engine.name == 'mysql': i = "ALTER TABLE galaxy_user_openid ADD UNIQUE INDEX ( openid( 255 ) )" - migrate_engine.execute( i ) + migrate_engine.execute(i) else: - i = Index( ix_name, UserOpenID_table.c.openid, unique=True ) + i = Index(ix_name, UserOpenID_table.c.openid, unique=True) try: i.create() except Exception: diff --git a/lib/galaxy/model/migrate/versions/0063_sequencer_table.py b/lib/galaxy/model/migrate/versions/0063_sequencer_table.py index b552667a9830..12581b7de890 100644 --- a/lib/galaxy/model/migrate/versions/0063_sequencer_table.py +++ b/lib/galaxy/model/migrate/versions/0063_sequencer_table.py @@ -11,21 +11,21 @@ from galaxy.model.custom_types import TrimmedString now = datetime.datetime.utcnow -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() # Table to add -Sequencer_table = Table( 'sequencer', metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "name", TrimmedString( 255 ), nullable=False ), - Column( "description", TEXT ), - Column( "sequencer_type_id", TrimmedString( 255 ), nullable=False ), - Column( "version", TrimmedString( 255 ) ), - Column( "form_definition_id", Integer, ForeignKey( "form_definition.id" ), index=True ), - Column( "form_values_id", Integer, ForeignKey( "form_values.id" ), index=True ), - Column( "deleted", Boolean, index=True, default=False ) ) +Sequencer_table = Table('sequencer', metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("name", TrimmedString(255), nullable=False), + Column("description", TEXT), + Column("sequencer_type_id", TrimmedString(255), nullable=False), + Column("version", TrimmedString(255)), + Column("form_definition_id", Integer, ForeignKey("form_definition.id"), index=True), + Column("form_values_id", Integer, ForeignKey("form_values.id"), index=True), + Column("deleted", Boolean, index=True, default=False)) def upgrade(migrate_engine): @@ -44,7 +44,7 @@ def downgrade(migrate_engine): metadata.reflect() # delete sequencer table try: - Sequencer_table = Table( "sequencer", metadata, autoload=True ) + Sequencer_table = Table("sequencer", metadata, autoload=True) Sequencer_table.drop() except Exception: log.exception("Deleting 'sequencer' table failed.") diff --git a/lib/galaxy/model/migrate/versions/0064_add_run_and_sample_run_association_tables.py b/lib/galaxy/model/migrate/versions/0064_add_run_and_sample_run_association_tables.py index 7cbbaa15d8fb..c045295caddd 100644 --- a/lib/galaxy/model/migrate/versions/0064_add_run_and_sample_run_association_tables.py +++ b/lib/galaxy/model/migrate/versions/0064_add_run_and_sample_run_association_tables.py @@ -9,26 +9,26 @@ from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, MetaData, Table now = datetime.datetime.utcnow -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() -Run_table = Table( "run", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "form_definition_id", Integer, ForeignKey( "form_definition.id" ), index=True ), - Column( "form_values_id", Integer, ForeignKey( "form_values.id" ), index=True ), - Column( "deleted", Boolean, index=True, default=False ) ) +Run_table = Table("run", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("form_definition_id", Integer, ForeignKey("form_definition.id"), index=True), + Column("form_values_id", Integer, ForeignKey("form_values.id"), index=True), + Column("deleted", Boolean, index=True, default=False)) -RequestTypeRunAssociation_table = Table( "request_type_run_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "request_type_id", Integer, ForeignKey( "request_type.id" ), index=True, nullable=False ), - Column( "run_id", Integer, ForeignKey( "run.id" ), index=True, nullable=False ) ) +RequestTypeRunAssociation_table = Table("request_type_run_association", metadata, + Column("id", Integer, primary_key=True), + Column("request_type_id", Integer, ForeignKey("request_type.id"), index=True, nullable=False), + Column("run_id", Integer, ForeignKey("run.id"), index=True, nullable=False)) -SampleRunAssociation_table = Table( "sample_run_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "sample_id", Integer, ForeignKey( "sample.id" ), index=True, nullable=False ), - Column( "run_id", Integer, ForeignKey( "run.id" ), index=True, nullable=False ) ) +SampleRunAssociation_table = Table("sample_run_association", metadata, + Column("id", Integer, primary_key=True), + Column("sample_id", Integer, ForeignKey("sample.id"), index=True, nullable=False), + Column("run_id", Integer, ForeignKey("run.id"), index=True, nullable=False)) def upgrade(migrate_engine): diff --git a/lib/galaxy/model/migrate/versions/0065_add_name_to_form_fields_and_values.py b/lib/galaxy/model/migrate/versions/0065_add_name_to_form_fields_and_values.py index db16bdcf2187..0de0c6c3da54 100644 --- a/lib/galaxy/model/migrate/versions/0065_add_name_to_form_fields_and_values.py +++ b/lib/galaxy/model/migrate/versions/0065_add_name_to_form_fields_and_values.py @@ -13,13 +13,13 @@ from galaxy.model.custom_types import _sniffnfix_pg9_hex -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) -handler = logging.StreamHandler( sys.stdout ) +handler = logging.StreamHandler(sys.stdout) format = "%(name)s %(levelname)s %(asctime)s %(message)s" -formatter = logging.Formatter( format ) -handler.setFormatter( formatter ) -log.addHandler( handler ) +formatter = logging.Formatter(format) +handler.setFormatter(formatter) +log.addHandler(handler) metadata = MetaData() @@ -28,11 +28,11 @@ def upgrade(migrate_engine): print(__doc__) metadata.reflect() try: - Table( "form_definition", metadata, autoload=True ) + Table("form_definition", metadata, autoload=True) except Exception: log.exception("Loading 'form_definition' table failed.") try: - Table( "form_values", metadata, autoload=True ) + Table("form_values", metadata, autoload=True) except Exception: log.exception("Loading 'form_values' table failed.") @@ -44,57 +44,57 @@ def get_value(lst, index): # Go through the entire table and add a 'name' attribute for each field # in the list of fields for each form definition cmd = "SELECT f.id, f.fields FROM form_definition AS f" - result = migrate_engine.execute( cmd ) + result = migrate_engine.execute(cmd) for row in result: form_definition_id = row[0] - fields = str( row[1] ) + fields = str(row[1]) if not fields.strip(): continue - fields_list = loads( _sniffnfix_pg9_hex( fields ) ) - if len( fields_list ): - for index, field in enumerate( fields_list ): - field[ 'name' ] = 'field_%i' % index - field[ 'helptext' ] = field[ 'helptext' ].replace("'", "''").replace('"', "") - field[ 'label' ] = field[ 'label' ].replace("'", "''") - fields_json = dumps( fields_list ) + fields_list = loads(_sniffnfix_pg9_hex(fields)) + if len(fields_list): + for index, field in enumerate(fields_list): + field['name'] = 'field_%i' % index + field['helptext'] = field['helptext'].replace("'", "''").replace('"', "") + field['label'] = field['label'].replace("'", "''") + fields_json = dumps(fields_list) if migrate_engine.name == 'mysql': - cmd = "UPDATE form_definition AS f SET f.fields='%s' WHERE f.id=%i" % ( fields_json, form_definition_id ) + cmd = "UPDATE form_definition AS f SET f.fields='%s' WHERE f.id=%i" % (fields_json, form_definition_id) else: - cmd = "UPDATE form_definition SET fields='%s' WHERE id=%i" % ( fields_json, form_definition_id ) - migrate_engine.execute( cmd ) + cmd = "UPDATE form_definition SET fields='%s' WHERE id=%i" % (fields_json, form_definition_id) + migrate_engine.execute(cmd) # replace the values list in the content field of the form_values table with a name:value dict cmd = "SELECT form_values.id, form_values.content, form_definition.fields" \ " FROM form_values, form_definition" \ " WHERE form_values.form_definition_id=form_definition.id" \ " ORDER BY form_values.id ASC" - result = migrate_engine.execute( cmd ) + result = migrate_engine.execute(cmd) for row in result: - form_values_id = int( row[0] ) - if not str( row[1] ).strip(): + form_values_id = int(row[0]) + if not str(row[1]).strip(): continue row1 = str(row[1]).replace('\n', '').replace('\r', '') - values_list = loads( str( row1 ).strip() ) - if not str( row[2] ).strip(): + values_list = loads(str(row1).strip()) + if not str(row[2]).strip(): continue - fields_list = loads( str( row[2] ).strip() ) + fields_list = loads(str(row[2]).strip()) if fields_list and isinstance(values_list, list): values_dict = {} - for field_index, field in enumerate( fields_list ): - field_name = field[ 'name' ] - values_dict[ field_name ] = get_value(values_list, field_index ) - cmd = "UPDATE form_values SET content='%s' WHERE id=%i" % ( dumps( values_dict ), form_values_id ) - migrate_engine.execute( cmd ) + for field_index, field in enumerate(fields_list): + field_name = field['name'] + values_dict[field_name] = get_value(values_list, field_index) + cmd = "UPDATE form_values SET content='%s' WHERE id=%i" % (dumps(values_dict), form_values_id) + migrate_engine.execute(cmd) def downgrade(migrate_engine): metadata.bind = migrate_engine metadata.reflect() try: - Table( "form_definition", metadata, autoload=True ) + Table("form_definition", metadata, autoload=True) except Exception: log.exception("Loading 'form_definition' table failed.") try: - Table( "form_values", metadata, autoload=True ) + Table("form_values", metadata, autoload=True) except Exception: log.exception("Loading 'form_values' table failed.") # remove the name attribute in the content column JSON dict in the form_values table @@ -103,38 +103,38 @@ def downgrade(migrate_engine): " FROM form_values, form_definition" \ " WHERE form_values.form_definition_id=form_definition.id" \ " ORDER BY form_values.id ASC" - result = migrate_engine.execute( cmd ) + result = migrate_engine.execute(cmd) for row in result: - form_values_id = int( row[0] ) - if not str( row[1] ).strip(): + form_values_id = int(row[0]) + if not str(row[1]).strip(): continue - values_dict = loads( str( row[1] ) ) - if not str( row[2] ).strip(): + values_dict = loads(str(row[1])) + if not str(row[2]).strip(): continue - fields_list = loads( str( row[2] ) ) + fields_list = loads(str(row[2])) if fields_list: values_list = [] - for field_index, field in enumerate( fields_list ): - field_name = field[ 'name' ] - field_value = values_dict[ field_name ] - values_list.append( field_value ) - cmd = "UPDATE form_values SET content='%s' WHERE id=%i" % ( dumps( values_list ), form_values_id ) - migrate_engine.execute( cmd ) + for field_index, field in enumerate(fields_list): + field_name = field['name'] + field_value = values_dict[field_name] + values_list.append(field_value) + cmd = "UPDATE form_values SET content='%s' WHERE id=%i" % (dumps(values_list), form_values_id) + migrate_engine.execute(cmd) # remove name attribute from the field column of the form_definition table cmd = "SELECT f.id, f.fields FROM form_definition AS f" - result = migrate_engine.execute( cmd ) + result = migrate_engine.execute(cmd) for row in result: form_definition_id = row[0] - fields = str( row[1] ) + fields = str(row[1]) if not fields.strip(): continue - fields_list = loads( _sniffnfix_pg9_hex( fields ) ) - if len( fields_list ): - for index, field in enumerate( fields_list ): + fields_list = loads(_sniffnfix_pg9_hex(fields)) + if len(fields_list): + for index, field in enumerate(fields_list): if 'name' in field: - del field[ 'name' ] + del field['name'] if migrate_engine.name == 'mysql': - cmd = "UPDATE form_definition AS f SET f.fields='%s' WHERE f.id=%i" % ( dumps( fields_list ), form_definition_id ) + cmd = "UPDATE form_definition AS f SET f.fields='%s' WHERE f.id=%i" % (dumps(fields_list), form_definition_id) else: - cmd = "UPDATE form_definition SET fields='%s' WHERE id=%i" % ( dumps( fields_list ), form_definition_id ) - migrate_engine.execute( cmd ) + cmd = "UPDATE form_definition SET fields='%s' WHERE id=%i" % (dumps(fields_list), form_definition_id) + migrate_engine.execute(cmd) diff --git a/lib/galaxy/model/migrate/versions/0066_deferred_job_and_transfer_job_tables.py b/lib/galaxy/model/migrate/versions/0066_deferred_job_and_transfer_job_tables.py index 7361358d6828..7ffb81b08f51 100644 --- a/lib/galaxy/model/migrate/versions/0066_deferred_job_and_transfer_job_tables.py +++ b/lib/galaxy/model/migrate/versions/0066_deferred_job_and_transfer_job_tables.py @@ -12,26 +12,26 @@ from galaxy.model.custom_types import JSONType now = datetime.datetime.utcnow -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() # Table to add -DeferredJob_table = Table( "deferred_job", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "state", String( 64 ), index=True ), - Column( "plugin", String( 128 ), index=True ), - Column( "params", JSONType ) ) +DeferredJob_table = Table("deferred_job", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("state", String(64), index=True), + Column("plugin", String(128), index=True), + Column("params", JSONType)) -TransferJob_table = Table( "transfer_job", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "state", String( 64 ), index=True ), - Column( "path", String( 1024 ) ), - Column( "params", JSONType ) ) +TransferJob_table = Table("transfer_job", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("state", String(64), index=True), + Column("path", String(1024)), + Column("params", JSONType)) def upgrade(migrate_engine): diff --git a/lib/galaxy/model/migrate/versions/0067_populate_sequencer_table.py b/lib/galaxy/model/migrate/versions/0067_populate_sequencer_table.py index 90969a2750e9..0ec730b2deb6 100644 --- a/lib/galaxy/model/migrate/versions/0067_populate_sequencer_table.py +++ b/lib/galaxy/model/migrate/versions/0067_populate_sequencer_table.py @@ -14,49 +14,49 @@ from galaxy.model.custom_types import JSONType -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() -def nextval( migrate_engine, table, col='id' ): +def nextval(migrate_engine, table, col='id'): if migrate_engine.name in ['postgres', 'postgresql']: - return "nextval('%s_%s_seq')" % ( table, col ) + return "nextval('%s_%s_seq')" % (table, col) elif migrate_engine.name in ['mysql', 'sqlite']: return "null" else: - raise Exception( 'Unable to convert data for unknown database type: %s' % migrate_engine.name ) + raise Exception('Unable to convert data for unknown database type: %s' % migrate_engine.name) -def localtimestamp( migrate_engine ): +def localtimestamp(migrate_engine): if migrate_engine.name in ['mysql', 'postgres', 'postgresql']: return "LOCALTIMESTAMP" elif migrate_engine.name == 'sqlite': return "current_date || ' ' || current_time" else: - raise Exception( 'Unable to convert data for unknown database type: %s' % migrate_engine.name ) + raise Exception('Unable to convert data for unknown database type: %s' % migrate_engine.name) -def get_latest_id( migrate_engine, table ): - result = migrate_engine.execute( "select id from %s order by id desc" % table ) +def get_latest_id(migrate_engine, table): + result = migrate_engine.execute("select id from %s order by id desc" % table) row = result.fetchone() if row: return row[0] else: - raise Exception( 'Unable to get the latest id in the %s table.' % table ) + raise Exception('Unable to get the latest id in the %s table.' % table) -def boolean( migrate_engine, value ): +def boolean(migrate_engine, value): if migrate_engine.name in ['mysql', 'postgres', 'postgresql']: return value elif migrate_engine.name == 'sqlite': - if value in [ 'True', 'true' ]: + if value in ['True', 'true']: return 1 return 0 else: - raise Exception( 'Unable to convert data for unknown database type: %s' % migrate_engine.name ) + raise Exception('Unable to convert data for unknown database type: %s' % migrate_engine.name) -def create_sequencer_form_definition( migrate_engine ): +def create_sequencer_form_definition(migrate_engine): ''' Create a new form_definition containing 5 fields (host, username, password, data_dir & rename_datasets) which described the existing datatx_info json @@ -64,127 +64,127 @@ def create_sequencer_form_definition( migrate_engine ): ''' # create new form_definition_current in the db cmd = "INSERT INTO form_definition_current VALUES ( %s, %s, %s, %s, %s )" - cmd = cmd % ( nextval( migrate_engine, 'form_definition_current' ), - localtimestamp( migrate_engine ), - localtimestamp( migrate_engine ), - 'NULL', - boolean( migrate_engine, 'false' ) ) - migrate_engine.execute( cmd ) + cmd = cmd % (nextval(migrate_engine, 'form_definition_current'), + localtimestamp(migrate_engine), + localtimestamp(migrate_engine), + 'NULL', + boolean(migrate_engine, 'false')) + migrate_engine.execute(cmd) # get this form_definition_current id - form_definition_current_id = get_latest_id( migrate_engine, 'form_definition_current' ) + form_definition_current_id = get_latest_id(migrate_engine, 'form_definition_current') # create new form_definition in the db form_definition_name = 'Generic sequencer form' form_definition_desc = '' form_definition_fields = [] - fields = [ ( 'Host', 'TextField' ), - ( 'User name', 'TextField' ), - ( 'Password', 'PasswordField' ), - ( 'Data directory', 'TextField' ) ] - for index, ( label, field_type ) in enumerate( fields ): - form_definition_fields.append( { 'name': 'field_%i' % index, - 'label': label, - 'helptext': '', - 'visible': True, - 'required': False, - 'type': field_type, - 'selectlist': [], - 'layout': 'none', - 'default': '' } ) - form_definition_fields.append( { 'name': 'field_%i' % len( fields ), - 'label': 'Prepend the experiment name and sample name to the dataset name?', - 'helptext': 'Galaxy datasets are renamed by prepending the experiment name and sample name to the dataset name, ensuring dataset names remain unique in Galaxy even when multiple datasets have the same name on the sequencer.', - 'visible': True, - 'required': False, - 'type': 'SelectField', - 'selectlist': [ 'Do not rename', - 'Preprend sample name', - 'Prepend experiment name', - 'Prepend experiment and sample name' ], - 'layout': 'none', - 'default': '' } ) + fields = [('Host', 'TextField'), + ('User name', 'TextField'), + ('Password', 'PasswordField'), + ('Data directory', 'TextField')] + for index, (label, field_type) in enumerate(fields): + form_definition_fields.append({'name': 'field_%i' % index, + 'label': label, + 'helptext': '', + 'visible': True, + 'required': False, + 'type': field_type, + 'selectlist': [], + 'layout': 'none', + 'default': ''}) + form_definition_fields.append({'name': 'field_%i' % len(fields), + 'label': 'Prepend the experiment name and sample name to the dataset name?', + 'helptext': 'Galaxy datasets are renamed by prepending the experiment name and sample name to the dataset name, ensuring dataset names remain unique in Galaxy even when multiple datasets have the same name on the sequencer.', + 'visible': True, + 'required': False, + 'type': 'SelectField', + 'selectlist': ['Do not rename', + 'Preprend sample name', + 'Prepend experiment name', + 'Prepend experiment and sample name'], + 'layout': 'none', + 'default': ''}) form_definition_type = 'Sequencer Information Form' form_definition_layout = dumps('[]') cmd = "INSERT INTO form_definition VALUES ( %s, %s, %s, '%s', '%s', %s, '%s', '%s', '%s' )" - cmd = cmd % ( nextval( migrate_engine, 'form_definition' ), - localtimestamp( migrate_engine ), - localtimestamp( migrate_engine ), - form_definition_name, - form_definition_desc, - form_definition_current_id, - dumps( form_definition_fields ), - form_definition_type, - form_definition_layout ) - migrate_engine.execute( cmd ) + cmd = cmd % (nextval(migrate_engine, 'form_definition'), + localtimestamp(migrate_engine), + localtimestamp(migrate_engine), + form_definition_name, + form_definition_desc, + form_definition_current_id, + dumps(form_definition_fields), + form_definition_type, + form_definition_layout) + migrate_engine.execute(cmd) # get this form_definition id - form_definition_id = get_latest_id( migrate_engine, 'form_definition' ) + form_definition_id = get_latest_id(migrate_engine, 'form_definition') # update the form_definition_id column in form_definition_current - cmd = "UPDATE form_definition_current SET latest_form_id=%i WHERE id=%i" % ( form_definition_id, form_definition_current_id ) - migrate_engine.execute( cmd ) + cmd = "UPDATE form_definition_current SET latest_form_id=%i WHERE id=%i" % (form_definition_id, form_definition_current_id) + migrate_engine.execute(cmd) return form_definition_id -def get_sequencer_id( migrate_engine, sequencer_info ): +def get_sequencer_id(migrate_engine, sequencer_info): '''Get the sequencer id corresponding to the sequencer information''' # Check if there is any existing sequencer which have the same sequencer # information fields & values cmd = "SELECT sequencer.id, form_values.content FROM sequencer, form_values WHERE sequencer.form_values_id=form_values.id" - result = migrate_engine.execute( cmd ) + result = migrate_engine.execute(cmd) for row in result: sequencer_id = row[0] - values = str( row[1] ) + values = str(row[1]) if not values.strip(): continue - values = loads( values ) + values = loads(values) # proceed only if sequencer_info is a valid list if values and isinstance(values, dict): - if sequencer_info.get( 'host', '' ) == values.get( 'field_0', '' ) \ - and sequencer_info.get( 'username', '' ) == values.get( 'field_1', '' ) \ - and sequencer_info.get( 'password', '' ) == values.get( 'field_2', '' ) \ - and sequencer_info.get( 'data_dir', '' ) == values.get( 'field_3', '' ) \ - and sequencer_info.get( 'rename_dataset', '' ) == values.get( 'field_4', '' ): + if sequencer_info.get('host', '') == values.get('field_0', '') \ + and sequencer_info.get('username', '') == values.get('field_1', '') \ + and sequencer_info.get('password', '') == values.get('field_2', '') \ + and sequencer_info.get('data_dir', '') == values.get('field_3', '') \ + and sequencer_info.get('rename_dataset', '') == values.get('field_4', ''): return sequencer_id return None -def add_sequencer( migrate_engine, sequencer_index, sequencer_form_definition_id, sequencer_info ): +def add_sequencer(migrate_engine, sequencer_index, sequencer_form_definition_id, sequencer_info): '''Adds a new sequencer to the sequencer table along with its form values.''' # Create a new form values record with the supplied sequencer information - values = dumps( { 'field_0': sequencer_info.get( 'host', '' ), - 'field_1': sequencer_info.get( 'username', '' ), - 'field_2': sequencer_info.get( 'password', '' ), - 'field_3': sequencer_info.get( 'data_dir', '' ), - 'field_4': sequencer_info.get( 'rename_dataset', '' ) } ) - cmd = "INSERT INTO form_values VALUES ( %s, %s, %s, %s, '%s' )" % ( nextval( migrate_engine, 'form_values' ), - localtimestamp( migrate_engine ), - localtimestamp( migrate_engine ), - sequencer_form_definition_id, - values ) + values = dumps({'field_0': sequencer_info.get('host', ''), + 'field_1': sequencer_info.get('username', ''), + 'field_2': sequencer_info.get('password', ''), + 'field_3': sequencer_info.get('data_dir', ''), + 'field_4': sequencer_info.get('rename_dataset', '')}) + cmd = "INSERT INTO form_values VALUES ( %s, %s, %s, %s, '%s' )" % (nextval(migrate_engine, 'form_values'), + localtimestamp(migrate_engine), + localtimestamp(migrate_engine), + sequencer_form_definition_id, + values) migrate_engine.execute(cmd) - sequencer_form_values_id = get_latest_id( migrate_engine, 'form_values' ) + sequencer_form_values_id = get_latest_id(migrate_engine, 'form_values') # Create a new sequencer record with reference to the form value created above. name = 'Sequencer_%i' % sequencer_index desc = '' version = '' sequencer_type_id = 'simple_unknown_sequencer' cmd = "INSERT INTO sequencer VALUES ( %s, %s, %s, '%s', '%s', '%s', '%s', %s, %s, %s )" - cmd = cmd % ( nextval( migrate_engine, 'sequencer'), - localtimestamp( migrate_engine ), - localtimestamp( migrate_engine ), - name, - desc, - sequencer_type_id, - version, - sequencer_form_definition_id, - sequencer_form_values_id, - boolean( migrate_engine, 'false' ) ) + cmd = cmd % (nextval(migrate_engine, 'sequencer'), + localtimestamp(migrate_engine), + localtimestamp(migrate_engine), + name, + desc, + sequencer_type_id, + version, + sequencer_form_definition_id, + sequencer_form_values_id, + boolean(migrate_engine, 'false')) migrate_engine.execute(cmd) - return get_latest_id( migrate_engine, 'sequencer' ) + return get_latest_id(migrate_engine, 'sequencer') -def update_sequencer_id_in_request_type( migrate_engine, request_type_id, sequencer_id ): +def update_sequencer_id_in_request_type(migrate_engine, request_type_id, sequencer_id): '''Update the foreign key to the sequencer table in the request_type table''' - cmd = "UPDATE request_type SET sequencer_id=%i WHERE id=%i" % ( sequencer_id, request_type_id ) - migrate_engine.execute( cmd ) + cmd = "UPDATE request_type SET sequencer_id=%i WHERE id=%i" % (sequencer_id, request_type_id) + migrate_engine.execute(cmd) def upgrade(migrate_engine): @@ -192,58 +192,58 @@ def upgrade(migrate_engine): print(__doc__) metadata.reflect() try: - RequestType_table = Table( "request_type", metadata, autoload=True ) + RequestType_table = Table("request_type", metadata, autoload=True) except NoSuchTableError: RequestType_table = None - log.debug( "Failed loading table 'request_type'" ) + log.debug("Failed loading table 'request_type'") if RequestType_table is None: return # load the sequencer table try: - Sequencer_table = Table( "sequencer", metadata, autoload=True ) + Sequencer_table = Table("sequencer", metadata, autoload=True) except NoSuchTableError: Sequencer_table = None - log.debug( "Failed loading table 'sequencer'" ) + log.debug("Failed loading table 'sequencer'") if Sequencer_table is None: return # create foreign key field to the sequencer table in the request_type table try: - col = Column( "sequencer_id", Integer, ForeignKey( "sequencer.id" ), nullable=True ) - col.create( RequestType_table ) + col = Column("sequencer_id", Integer, ForeignKey("sequencer.id"), nullable=True) + col.create(RequestType_table) assert col is RequestType_table.c.sequencer_id except Exception: log.exception("Creating column 'sequencer_id' in the 'request_type' table failed.") # copy the sequencer information contained in the 'datatx_info' column # of the request_type table to the form values referenced in the sequencer table cmd = "SELECT id, name, datatx_info FROM request_type ORDER BY id ASC" - result = migrate_engine.execute( cmd ) + result = migrate_engine.execute(cmd) results_list = result.fetchall() # Proceed only if request_types exists - if len( results_list ): + if len(results_list): # In this migration script the all the contents of the datatx_info are stored as form_values # with a pointer to the sequencer table. This way the sequencer information can be customized # by the admin and is no longer restricted to host, username, password, data directory. # For the existing request_types in the database, we add a new form_definition # with these 4 fields. Then we populate the sequencer table with unique datatx_info # column from the existing request_types. - sequencer_form_definition_id = create_sequencer_form_definition( migrate_engine ) + sequencer_form_definition_id = create_sequencer_form_definition(migrate_engine) sequencer_index = 1 for row in results_list: request_type_id = row[0] - sequencer_info = str( row[2] ) # datatx_info column + sequencer_info = str(row[2]) # datatx_info column # skip if sequencer_info is empty if not sequencer_info.strip() or sequencer_info in ['None', 'null']: continue - sequencer_info = loads( sequencer_info.strip() ) + sequencer_info = loads(sequencer_info.strip()) # proceed only if sequencer_info is a valid dict if sequencer_info and isinstance(sequencer_info, dict): # check if this sequencer has already been added to the sequencer table - sequencer_id = get_sequencer_id( migrate_engine, sequencer_info ) + sequencer_id = get_sequencer_id(migrate_engine, sequencer_info) if not sequencer_id: # add to the sequencer table - sequencer_id = add_sequencer( migrate_engine, sequencer_index, sequencer_form_definition_id, sequencer_info ) + sequencer_id = add_sequencer(migrate_engine, sequencer_index, sequencer_form_definition_id, sequencer_info) # now update the sequencer_id column in request_type table - update_sequencer_id_in_request_type( migrate_engine, request_type_id, sequencer_id ) + update_sequencer_id_in_request_type(migrate_engine, request_type_id, sequencer_id) sequencer_index = sequencer_index + 1 # Finally delete the 'datatx_info' column from the request_type table @@ -257,15 +257,15 @@ def downgrade(migrate_engine): metadata.bind = migrate_engine metadata.reflect() try: - RequestType_table = Table( "request_type", metadata, autoload=True ) + RequestType_table = Table("request_type", metadata, autoload=True) except NoSuchTableError: RequestType_table = None - log.debug( "Failed loading table 'request_type'" ) + log.debug("Failed loading table 'request_type'") if RequestType_table is not None: # create the 'datatx_info' column try: - col = Column( "datatx_info", JSONType() ) - col.create( RequestType_table ) + col = Column("datatx_info", JSONType()) + col.create(RequestType_table) assert col is RequestType_table.c.datatx_info except Exception: log.exception("Creating column 'datatx_info' in the 'request_type' table failed.") @@ -275,19 +275,19 @@ def downgrade(migrate_engine): + " FROM request_type, sequencer, form_values "\ + " WHERE request_type.sequencer_id=sequencer.id AND sequencer.form_values_id=form_values.id "\ + " ORDER BY request_type.id ASC" - result = migrate_engine.execute( cmd ) + result = migrate_engine.execute(cmd) for row in result: request_type_id = row[0] - seq_values = loads( str( row[1] ) ) + seq_values = loads(str(row[1])) # create the datatx_info json dict - datatx_info = dumps( dict( host=seq_values.get( 'field_0', '' ), - username=seq_values.get( 'field_1', '' ), - password=seq_values.get( 'field_2', '' ), - data_dir=seq_values.get( 'field_3', '' ), - rename_dataset=seq_values.get( 'field_4', '' ) ) ) + datatx_info = dumps(dict(host=seq_values.get('field_0', ''), + username=seq_values.get('field_1', ''), + password=seq_values.get('field_2', ''), + data_dir=seq_values.get('field_3', ''), + rename_dataset=seq_values.get('field_4', ''))) # update the column - cmd = "UPDATE request_type SET datatx_info='%s' WHERE id=%i" % ( datatx_info, request_type_id ) - migrate_engine.execute( cmd ) + cmd = "UPDATE request_type SET datatx_info='%s' WHERE id=%i" % (datatx_info, request_type_id) + migrate_engine.execute(cmd) # delete foreign key field to the sequencer table in the request_type table try: RequestType_table.c.sequencer_id.drop() diff --git a/lib/galaxy/model/migrate/versions/0068_rename_sequencer_to_external_services.py b/lib/galaxy/model/migrate/versions/0068_rename_sequencer_to_external_services.py index 84fd3b4492cb..1514c96691a3 100644 --- a/lib/galaxy/model/migrate/versions/0068_rename_sequencer_to_external_services.py +++ b/lib/galaxy/model/migrate/versions/0068_rename_sequencer_to_external_services.py @@ -19,17 +19,17 @@ from galaxy.model.custom_types import TrimmedString now = datetime.datetime.utcnow -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() -def nextval( migrate_engine, table, col='id' ): +def nextval(migrate_engine, table, col='id'): if migrate_engine.name in ['postgres', 'postgresql']: - return "nextval('%s_%s_seq')" % ( table, col ) + return "nextval('%s_%s_seq')" % (table, col) elif migrate_engine.name in ['mysql', 'sqlite']: return "null" else: - raise Exception( 'Unable to convert data for unknown database type: %s' % migrate_engine.name ) + raise Exception('Unable to convert data for unknown database type: %s' % migrate_engine.name) def upgrade(migrate_engine): @@ -40,19 +40,19 @@ def upgrade(migrate_engine): # create the column. Call it external_services_id as the table 'sequencer' is # going to be renamed to 'external_service' try: - SampleDataset_table = Table( "sample_dataset", metadata, autoload=True ) - col = Column( "external_service_id", Integer, index=True ) - col.create( SampleDataset_table, index_name="ix_sample_dataset_external_service_id" ) + SampleDataset_table = Table("sample_dataset", metadata, autoload=True) + col = Column("external_service_id", Integer, index=True) + col.create(SampleDataset_table, index_name="ix_sample_dataset_external_service_id") assert col is SampleDataset_table.c.external_service_id except Exception: log.exception("Creating column 'external_service_id' in the 'sample_dataset' table failed.") if migrate_engine.name != 'sqlite': # Add a foreign key to the external_service table in the sample_dataset table try: - Sequencer_table = Table( "sequencer", metadata, autoload=True ) - cons = ForeignKeyConstraint( [SampleDataset_table.c.external_service_id], - [Sequencer_table.c.id], - name='sample_dataset_external_services_id_fk' ) + Sequencer_table = Table("sequencer", metadata, autoload=True) + cons = ForeignKeyConstraint([SampleDataset_table.c.external_service_id], + [Sequencer_table.c.id], + name='sample_dataset_external_services_id_fk') # Create the constraint cons.create() except Exception: @@ -63,73 +63,73 @@ def upgrade(migrate_engine): + " WHERE sample.id=sample_dataset.sample_id and request.id=sample.request_id and request.request_type_id=request_type.id " \ + " ORDER BY sample_dataset.id" try: - result = migrate_engine.execute( cmd ) + result = migrate_engine.execute(cmd) for r in result: sample_dataset_id = int(r[0]) sequencer_id = int(r[1]) - cmd = "UPDATE sample_dataset SET external_service_id='%i' where id=%i" % ( sequencer_id, sample_dataset_id ) - migrate_engine.execute( cmd ) + cmd = "UPDATE sample_dataset SET external_service_id='%i' where id=%i" % (sequencer_id, sample_dataset_id) + migrate_engine.execute(cmd) # rename 'sequencer' table to 'external_service' cmd = "ALTER TABLE sequencer RENAME TO external_service" - migrate_engine.execute( cmd ) + migrate_engine.execute(cmd) except Exception: log.exception("Exception executing SQL command: %s", cmd) # if running postgres then rename the primary key sequence too if migrate_engine.name in ['postgres', 'postgresql']: cmd = "ALTER TABLE sequencer_id_seq RENAME TO external_service_id_seq" - migrate_engine.execute( cmd ) + migrate_engine.execute(cmd) # rename 'sequencer_type_id' column to 'external_service_type_id' in the table 'external_service' # create the column as 'external_service_type_id' try: - ExternalServices_table = Table( "external_service", metadata, autoload=True ) - col = Column( "external_service_type_id", TrimmedString( 255 ) ) - col.create( ExternalServices_table ) + ExternalServices_table = Table("external_service", metadata, autoload=True) + col = Column("external_service_type_id", TrimmedString(255)) + col.create(ExternalServices_table) assert col is ExternalServices_table.c.external_service_type_id except Exception: log.exception("Creating column 'external_service_type_id' in the 'external_service' table failed.") # populate this new column cmd = "UPDATE external_service SET external_service_type_id=sequencer_type_id" - migrate_engine.execute( cmd ) + migrate_engine.execute(cmd) # remove the 'sequencer_type_id' column try: ExternalServices_table.c.sequencer_type_id.drop() except Exception: log.exception("Deleting column 'sequencer_type_id' from the 'external_service' table failed.") # create 'request_type_external_service_association' table - RequestTypeExternalServiceAssociation_table = Table( "request_type_external_service_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "request_type_id", Integer, ForeignKey( "request_type.id" ), index=True ), - Column( "external_service_id", Integer, ForeignKey( "external_service.id" ), index=True ) ) + RequestTypeExternalServiceAssociation_table = Table("request_type_external_service_association", metadata, + Column("id", Integer, primary_key=True), + Column("request_type_id", Integer, ForeignKey("request_type.id"), index=True), + Column("external_service_id", Integer, ForeignKey("external_service.id"), index=True)) try: RequestTypeExternalServiceAssociation_table.create() except Exception: log.exception("Creating request_type_external_service_association table failed.") try: - RequestTypeExternalServiceAssociation_table = Table( "request_type_external_service_association", metadata, autoload=True ) + RequestTypeExternalServiceAssociation_table = Table("request_type_external_service_association", metadata, autoload=True) except NoSuchTableError: RequestTypeExternalServiceAssociation_table = None - log.debug( "Failed loading table request_type_external_service_association" ) + log.debug("Failed loading table request_type_external_service_association") if RequestTypeExternalServiceAssociation_table is None: return # populate 'request_type_external_service_association' table cmd = "SELECT id, sequencer_id FROM request_type ORDER BY id ASC" - result = migrate_engine.execute( cmd ) + result = migrate_engine.execute(cmd) results_list = result.fetchall() # Proceed only if request_types exists - if len( results_list ): + if len(results_list): for row in results_list: request_type_id = row[0] sequencer_id = row[1] if not sequencer_id: sequencer_id = 'null' cmd = "INSERT INTO request_type_external_service_association VALUES ( %s, %s, %s )" - cmd = cmd % ( nextval( migrate_engine, 'request_type_external_service_association' ), - request_type_id, - sequencer_id ) - migrate_engine.execute( cmd ) + cmd = cmd % (nextval(migrate_engine, 'request_type_external_service_association'), + request_type_id, + sequencer_id) + migrate_engine.execute(cmd) # drop the 'sequencer_id' column in the 'request_type' table try: - RequestType_table = Table( "request_type", metadata, autoload=True ) + RequestType_table = Table("request_type", metadata, autoload=True) RequestType_table.c.sequencer_id.drop() except Exception: log.exception("Deleting column 'sequencer_id' from the 'request_type' table failed.") @@ -141,43 +141,43 @@ def downgrade(migrate_engine): metadata.reflect() # load sequencer & request_type table try: - RequestType_table = Table( "request_type", metadata, autoload=True ) + RequestType_table = Table("request_type", metadata, autoload=True) except NoSuchTableError: RequestType_table = None - log.debug( "Failed loading table request_type" ) + log.debug("Failed loading table request_type") if RequestType_table is None: return try: - ExternalServices_table = Table( "external_service", metadata, autoload=True ) + ExternalServices_table = Table("external_service", metadata, autoload=True) except NoSuchTableError: ExternalServices_table = None - log.debug( "Failed loading table 'external_service'" ) + log.debug("Failed loading table 'external_service'") if ExternalServices_table is None: return try: - RequestTypeExternalServiceAssociation_table = Table( "request_type_external_service_association", metadata, autoload=True ) + RequestTypeExternalServiceAssociation_table = Table("request_type_external_service_association", metadata, autoload=True) except NoSuchTableError: RequestTypeExternalServiceAssociation_table = None - log.debug( "Failed loading table request_type_external_service_association" ) + log.debug("Failed loading table request_type_external_service_association") # create the 'sequencer_id' column in the 'request_type' table try: - col = Column( "sequencer_id", Integer, ForeignKey( "external_service.id" ), nullable=True ) - col.create( RequestType_table ) + col = Column("sequencer_id", Integer, ForeignKey("external_service.id"), nullable=True) + col.create(RequestType_table) assert col is RequestType_table.c.sequencer_id except Exception: log.exception("Creating column 'sequencer_id' in the 'request_type' table failed.") # populate 'sequencer_id' column in the 'request_type' table from the # 'request_type_external_service_association' table cmd = "SELECT request_type_id, external_service_id FROM request_type_external_service_association ORDER BY id ASC" - result = migrate_engine.execute( cmd ) + result = migrate_engine.execute(cmd) results_list = result.fetchall() # Proceed only if request_types exists - if len( results_list ): + if len(results_list): for row in results_list: request_type_id = row[0] external_service_id = row[1] - cmd = "UPDATE request_type SET sequencer_id=%i WHERE id=%i" % ( external_service_id, request_type_id ) - migrate_engine.execute( cmd ) + cmd = "UPDATE request_type SET sequencer_id=%i WHERE id=%i" % (external_service_id, request_type_id) + migrate_engine.execute(cmd) # remove the 'request_type_external_service_association' table if RequestTypeExternalServiceAssociation_table is not None: try: @@ -187,14 +187,14 @@ def downgrade(migrate_engine): # rename 'external_service_type_id' column to 'sequencer_type_id' in the table 'external_service' # create the column 'sequencer_type_id' try: - col = Column( "sequencer_type_id", TrimmedString( 255 ) ) - col.create( ExternalServices_table ) + col = Column("sequencer_type_id", TrimmedString(255)) + col.create(ExternalServices_table) assert col is ExternalServices_table.c.sequencer_type_id except Exception: log.exception("Creating column 'sequencer_type_id' in the 'external_service' table failed.") # populate this new column cmd = "UPDATE external_service SET sequencer_type_id=external_service_type_id" - migrate_engine.execute( cmd ) + migrate_engine.execute(cmd) # remove the 'external_service_type_id' column try: ExternalServices_table.c.external_service_type_id.drop() @@ -202,17 +202,17 @@ def downgrade(migrate_engine): log.exception("Deleting column 'external_service_type_id' from the 'external_service' table failed.") # rename the 'external_service' table to 'sequencer' cmd = "ALTER TABLE external_service RENAME TO sequencer" - migrate_engine.execute( cmd ) + migrate_engine.execute(cmd) # if running postgres then rename the primary key sequence too if migrate_engine.name in ['postgres', 'postgresql']: cmd = "ALTER SEQUENCE external_service_id_seq RENAME TO sequencer_id_seq" - migrate_engine.execute( cmd ) + migrate_engine.execute(cmd) # drop the 'external_service_id' column in the 'sample_dataset' table try: - SampleDataset_table = Table( "sample_dataset", metadata, autoload=True ) + SampleDataset_table = Table("sample_dataset", metadata, autoload=True) except NoSuchTableError: SampleDataset_table = None - log.debug( "Failed loading table 'sample_dataset'" ) + log.debug("Failed loading table 'sample_dataset'") if SampleDataset_table is None: return try: diff --git a/lib/galaxy/model/migrate/versions/0069_rename_sequencer_form_type.py b/lib/galaxy/model/migrate/versions/0069_rename_sequencer_form_type.py index 817020404d1b..bf66ce14f36d 100644 --- a/lib/galaxy/model/migrate/versions/0069_rename_sequencer_form_type.py +++ b/lib/galaxy/model/migrate/versions/0069_rename_sequencer_form_type.py @@ -7,7 +7,7 @@ from sqlalchemy import MetaData -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() @@ -17,8 +17,8 @@ def upgrade(migrate_engine): metadata.reflect() current_form_type = 'Sequencer Information Form' new_form_type = "External Service Information Form" - cmd = "update form_definition set type='%s' where type='%s'" % ( new_form_type, current_form_type ) - migrate_engine.execute( cmd ) + cmd = "update form_definition set type='%s' where type='%s'" % (new_form_type, current_form_type) + migrate_engine.execute(cmd) def downgrade(migrate_engine): @@ -26,5 +26,5 @@ def downgrade(migrate_engine): metadata.reflect() new_form_type = 'Sequencer Information Form' current_form_type = "External Service Information Form" - cmd = "update form_definition set type='%s' where type='%s'" % ( new_form_type, current_form_type ) - migrate_engine.execute( cmd ) + cmd = "update form_definition set type='%s' where type='%s'" % (new_form_type, current_form_type) + migrate_engine.execute(cmd) diff --git a/lib/galaxy/model/migrate/versions/0070_add_info_column_to_deferred_job_table.py b/lib/galaxy/model/migrate/versions/0070_add_info_column_to_deferred_job_table.py index a4c8318c7e15..c45cc66b59c3 100644 --- a/lib/galaxy/model/migrate/versions/0070_add_info_column_to_deferred_job_table.py +++ b/lib/galaxy/model/migrate/versions/0070_add_info_column_to_deferred_job_table.py @@ -7,7 +7,7 @@ from sqlalchemy import Column, MetaData, Table, TEXT -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() @@ -16,9 +16,9 @@ def upgrade(migrate_engine): print(__doc__) metadata.reflect() try: - TransferJob_table = Table( "transfer_job", metadata, autoload=True ) - c = Column( "info", TEXT ) - c.create( TransferJob_table ) + TransferJob_table = Table("transfer_job", metadata, autoload=True) + c = Column("info", TEXT) + c.create(TransferJob_table) assert c is TransferJob_table.c.info except Exception: log.exception("Adding info column to transfer_job table failed.") @@ -28,7 +28,7 @@ def downgrade(migrate_engine): metadata.bind = migrate_engine metadata.reflect() try: - TransferJob_table = Table( "transfer_job", metadata, autoload=True ) + TransferJob_table = Table("transfer_job", metadata, autoload=True) TransferJob_table.c.info.drop() except Exception: log.exception("Dropping info column from transfer_job table failed.") diff --git a/lib/galaxy/model/migrate/versions/0071_add_history_and_workflow_to_sample.py b/lib/galaxy/model/migrate/versions/0071_add_history_and_workflow_to_sample.py index 08dd1a1689e4..8cfc26f1e8ab 100644 --- a/lib/galaxy/model/migrate/versions/0071_add_history_and_workflow_to_sample.py +++ b/lib/galaxy/model/migrate/versions/0071_add_history_and_workflow_to_sample.py @@ -9,7 +9,7 @@ from galaxy.model.custom_types import JSONType -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() @@ -18,11 +18,11 @@ def upgrade(migrate_engine): print(__doc__) metadata.reflect() try: - Sample_table = Table( "sample", metadata, autoload=True ) - c1 = Column( "workflow", JSONType, nullable=True ) - c2 = Column( "history_id", Integer, ForeignKey( "history.id" ), nullable=True) - c1.create( Sample_table ) - c2.create( Sample_table ) + Sample_table = Table("sample", metadata, autoload=True) + c1 = Column("workflow", JSONType, nullable=True) + c2 = Column("history_id", Integer, ForeignKey("history.id"), nullable=True) + c1.create(Sample_table) + c2.create(Sample_table) assert c1 is Sample_table.c.workflow assert c2 is Sample_table.c.history_id except Exception: @@ -33,12 +33,12 @@ def downgrade(migrate_engine): metadata.bind = migrate_engine metadata.reflect() try: - Sample_table = Table( "sample", metadata, autoload=True ) + Sample_table = Table("sample", metadata, autoload=True) Sample_table.c.workflow.drop() except Exception: log.exception("Dropping workflow column from sample table failed.") try: - Sample_table = Table( "sample", metadata, autoload=True ) + Sample_table = Table("sample", metadata, autoload=True) Sample_table.c.history_id.drop() except Exception: log.exception("Dropping history column from sample table failed.") diff --git a/lib/galaxy/model/migrate/versions/0072_add_pid_and_socket_columns_to_transfer_job_table.py b/lib/galaxy/model/migrate/versions/0072_add_pid_and_socket_columns_to_transfer_job_table.py index 8e6756c3829f..554637c5fd6d 100644 --- a/lib/galaxy/model/migrate/versions/0072_add_pid_and_socket_columns_to_transfer_job_table.py +++ b/lib/galaxy/model/migrate/versions/0072_add_pid_and_socket_columns_to_transfer_job_table.py @@ -7,7 +7,7 @@ from sqlalchemy import Column, Integer, MetaData, Table -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() @@ -16,12 +16,12 @@ def upgrade(migrate_engine): print(__doc__) metadata.reflect() try: - TransferJob_table = Table( "transfer_job", metadata, autoload=True ) - c = Column( "pid", Integer ) - c.create( TransferJob_table ) + TransferJob_table = Table("transfer_job", metadata, autoload=True) + c = Column("pid", Integer) + c.create(TransferJob_table) assert c is TransferJob_table.c.pid - c = Column( "socket", Integer ) - c.create( TransferJob_table ) + c = Column("socket", Integer) + c.create(TransferJob_table) assert c is TransferJob_table.c.socket except Exception: log.exception("Adding columns to transfer_job table failed.") @@ -31,7 +31,7 @@ def downgrade(migrate_engine): metadata.bind = migrate_engine metadata.reflect() try: - TransferJob_table = Table( "transfer_job", metadata, autoload=True ) + TransferJob_table = Table("transfer_job", metadata, autoload=True) TransferJob_table.c.pid.drop() TransferJob_table.c.socket.drop() except Exception: diff --git a/lib/galaxy/model/migrate/versions/0073_add_ldda_to_implicit_conversion_table.py b/lib/galaxy/model/migrate/versions/0073_add_ldda_to_implicit_conversion_table.py index ede1ac77f66d..e1b4f77902f8 100644 --- a/lib/galaxy/model/migrate/versions/0073_add_ldda_to_implicit_conversion_table.py +++ b/lib/galaxy/model/migrate/versions/0073_add_ldda_to_implicit_conversion_table.py @@ -7,7 +7,7 @@ from sqlalchemy import Column, ForeignKey, Integer, MetaData, Table -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() @@ -16,13 +16,13 @@ def upgrade(migrate_engine): print(__doc__) metadata.reflect() try: - Implicitly_converted_table = Table( "implicitly_converted_dataset_association", metadata, autoload=True ) + Implicitly_converted_table = Table("implicitly_converted_dataset_association", metadata, autoload=True) if migrate_engine.name != 'sqlite': - c = Column( "ldda_parent_id", Integer, ForeignKey( "library_dataset_dataset_association.id" ), index=True, nullable=True ) + c = Column("ldda_parent_id", Integer, ForeignKey("library_dataset_dataset_association.id"), index=True, nullable=True) else: # Can't use the ForeignKey in sqlite. - c = Column( "ldda_parent_id", Integer, index=True, nullable=True ) - c.create( Implicitly_converted_table, index_name="ix_implicitly_converted_dataset_assoc_ldda_parent_id") + c = Column("ldda_parent_id", Integer, index=True, nullable=True) + c.create(Implicitly_converted_table, index_name="ix_implicitly_converted_dataset_assoc_ldda_parent_id") assert c is Implicitly_converted_table.c.ldda_parent_id except Exception: log.exception("Adding ldda_parent_id column to implicitly_converted_dataset_association table failed.") @@ -32,7 +32,7 @@ def downgrade(migrate_engine): metadata.bind = migrate_engine metadata.reflect() try: - Implicitly_converted_table = Table( "implicitly_converted_dataset_association", metadata, autoload=True ) + Implicitly_converted_table = Table("implicitly_converted_dataset_association", metadata, autoload=True) Implicitly_converted_table.c.ldda_parent_id.drop() except Exception: log.exception("Dropping ldda_parent_id column from implicitly_converted_dataset_association table failed.") diff --git a/lib/galaxy/model/migrate/versions/0074_add_purged_column_to_library_dataset_table.py b/lib/galaxy/model/migrate/versions/0074_add_purged_column_to_library_dataset_table.py index 53f01963885e..69d8ff056af4 100644 --- a/lib/galaxy/model/migrate/versions/0074_add_purged_column_to_library_dataset_table.py +++ b/lib/galaxy/model/migrate/versions/0074_add_purged_column_to_library_dataset_table.py @@ -7,7 +7,7 @@ from sqlalchemy import Boolean, Column, MetaData, Table -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() @@ -34,37 +34,37 @@ def upgrade(migrate_engine): print(__doc__) metadata.reflect() try: - LibraryDataset_table = Table( "library_dataset", metadata, autoload=True ) - c = Column( "purged", Boolean, index=True, default=False ) - c.create( LibraryDataset_table, index_name='ix_library_dataset_purged') + LibraryDataset_table = Table("library_dataset", metadata, autoload=True) + c = Column("purged", Boolean, index=True, default=False) + c.create(LibraryDataset_table, index_name='ix_library_dataset_purged') assert c is LibraryDataset_table.c.purged except Exception: log.exception("Adding purged column to library_dataset table failed.") # Update the purged flag to the default False cmd = "UPDATE library_dataset SET purged = %s;" % engine_false(migrate_engine) try: - migrate_engine.execute( cmd ) + migrate_engine.execute(cmd) except Exception: log.exception("Setting default data for library_dataset.purged column failed.") # Update the purged flag for those LibaryDatasets whose purged flag should be True. This happens # when the LibraryDataset has no active LibraryDatasetDatasetAssociations. cmd = "SELECT * FROM library_dataset WHERE deleted = %s;" % engine_true(migrate_engine) - deleted_lds = migrate_engine.execute( cmd ).fetchall() + deleted_lds = migrate_engine.execute(cmd).fetchall() for row in deleted_lds: - cmd = "SELECT * FROM library_dataset_dataset_association WHERE library_dataset_id = %d AND library_dataset_dataset_association.deleted = %s;" % ( int( row.id ), engine_false(migrate_engine) ) - active_lddas = migrate_engine.execute( cmd ).fetchall() + cmd = "SELECT * FROM library_dataset_dataset_association WHERE library_dataset_id = %d AND library_dataset_dataset_association.deleted = %s;" % (int(row.id), engine_false(migrate_engine)) + active_lddas = migrate_engine.execute(cmd).fetchall() if not active_lddas: - print("Updating purged column to True for LibraryDataset id : ", int( row.id )) - cmd = "UPDATE library_dataset SET purged = %s WHERE id = %d;" % ( engine_true(migrate_engine), int( row.id ) ) - migrate_engine.execute( cmd ) + print("Updating purged column to True for LibraryDataset id : ", int(row.id)) + cmd = "UPDATE library_dataset SET purged = %s WHERE id = %d;" % (engine_true(migrate_engine), int(row.id)) + migrate_engine.execute(cmd) def downgrade(migrate_engine): metadata.bind = migrate_engine metadata.reflect() try: - LibraryDataset_table = Table( "library_dataset", metadata, autoload=True ) + LibraryDataset_table = Table("library_dataset", metadata, autoload=True) LibraryDataset_table.c.purged.drop() except Exception: log.exception("Dropping purged column from library_dataset table failed.") diff --git a/lib/galaxy/model/migrate/versions/0075_add_subindex_column_to_run_table.py b/lib/galaxy/model/migrate/versions/0075_add_subindex_column_to_run_table.py index 5054ce2852fb..7c99ef61a087 100644 --- a/lib/galaxy/model/migrate/versions/0075_add_subindex_column_to_run_table.py +++ b/lib/galaxy/model/migrate/versions/0075_add_subindex_column_to_run_table.py @@ -9,7 +9,7 @@ from galaxy.model.custom_types import TrimmedString -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() @@ -18,9 +18,9 @@ def upgrade(migrate_engine): print(__doc__) metadata.reflect() try: - Run_table = Table( "run", metadata, autoload=True ) - c = Column( "subindex", TrimmedString( 255 ), index=True ) - c.create( Run_table, index_name="ix_run_subindex") + Run_table = Table("run", metadata, autoload=True) + c = Column("subindex", TrimmedString(255), index=True) + c.create(Run_table, index_name="ix_run_subindex") assert c is Run_table.c.subindex except Exception: log.exception("Adding the subindex column to the run table failed.") @@ -30,7 +30,7 @@ def downgrade(migrate_engine): metadata.bind = migrate_engine metadata.reflect() try: - Run_table = Table( "run", metadata, autoload=True ) + Run_table = Table("run", metadata, autoload=True) Run_table.c.subindex.drop() except Exception: log.exception("Dropping the subindex column from run table failed.") diff --git a/lib/galaxy/model/migrate/versions/0076_fix_form_values_data_corruption.py b/lib/galaxy/model/migrate/versions/0076_fix_form_values_data_corruption.py index d17f38192fae..2b689e12ec92 100644 --- a/lib/galaxy/model/migrate/versions/0076_fix_form_values_data_corruption.py +++ b/lib/galaxy/model/migrate/versions/0076_fix_form_values_data_corruption.py @@ -11,7 +11,7 @@ from galaxy.model.custom_types import _sniffnfix_pg9_hex -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() @@ -23,18 +23,18 @@ def upgrade(migrate_engine): + " FROM form_definition, form_values " \ + " WHERE form_values.form_definition_id=form_definition.id " \ + " ORDER BY form_values.id" - result = migrate_engine.execute( cmd ) + result = migrate_engine.execute(cmd) corrupted_rows = 0 for row in result: # first check if loading the dict from the json succeeds # if that fails, it means that the content field is corrupted. try: - field_values_dict = loads( _sniffnfix_pg9_hex( str( row['field_values'] ) ) ) + field_values_dict = loads(_sniffnfix_pg9_hex(str(row['field_values']))) except Exception: corrupted_rows = corrupted_rows + 1 # content field is corrupted - fields_list = loads( _sniffnfix_pg9_hex( str( row['fdfields'] ) ) ) - field_values_str = _sniffnfix_pg9_hex( str( row['field_values'] ) ) + fields_list = loads(_sniffnfix_pg9_hex(str(row['fdfields']))) + field_values_str = _sniffnfix_pg9_hex(str(row['field_values'])) try: # Encoding errors? Just to be safe. print("Attempting to fix row %s" % row['id']) @@ -43,38 +43,38 @@ def upgrade(migrate_engine): pass field_values_dict = {} # look for each field name in the values and extract its value (string) - for index in range( len(fields_list) ): + for index in range(len(fields_list)): field = fields_list[index] field_name_key = '"%s": "' % field['name'] - field_index = field_values_str.find( field_name_key ) + field_index = field_values_str.find(field_name_key) if field_index == -1: # if the field name is not present the field values dict then # inform the admin that this form values cannot be fixed - print("The 'content' field of row 'id' %i does not have the field '%s' in the 'form_values' table and could not be fixed by this migration script." % ( int( field['id'] ), field['name'] )) + print("The 'content' field of row 'id' %i does not have the field '%s' in the 'form_values' table and could not be fixed by this migration script." % (int(field['id']), field['name'])) else: # check if this is the last field - if index == len( fields_list ) - 1: + if index == len(fields_list) - 1: # since this is the last field, the value string lies between the # field name and the '"}' string at the end, hence len(field_values_str) - 2 - value = field_values_str[ field_index + len( field_name_key ):len( field_values_str ) - 2 ] + value = field_values_str[field_index + len(field_name_key):len(field_values_str) - 2] else: # if this is not the last field then the value string lies between # this field name and the next field name next_field = fields_list[index + 1] - next_field_index = field_values_str.find( '", "%s": "' % next_field['name'] ) - value = field_values_str[ field_index + len( field_name_key ):next_field_index ] + next_field_index = field_values_str.find('", "%s": "' % next_field['name']) + value = field_values_str[field_index + len(field_name_key):next_field_index] # clean up the value string, escape the required quoutes and newline characters - value = value.replace( "'", "\''" )\ - .replace( '"', '\\\\"' )\ - .replace( '\r', "\\\\r" )\ - .replace( '\n', "\\\\n" )\ - .replace( '\t', "\\\\t" ) + value = value.replace("'", "\''")\ + .replace('"', '\\\\"')\ + .replace('\r', "\\\\r")\ + .replace('\n', "\\\\n")\ + .replace('\t', "\\\\t") # add to the new values dict - field_values_dict[ field['name'] ] = value + field_values_dict[field['name']] = value # update the db json_values = dumps(field_values_dict) - cmd = "UPDATE form_values SET content='%s' WHERE id=%i" % ( json_values, int( row['id'] ) ) - migrate_engine.execute( cmd ) + cmd = "UPDATE form_values SET content='%s' WHERE id=%i" % (json_values, int(row['id'])) + migrate_engine.execute(cmd) try: print("Post replacement: %s" % json_values) except: diff --git a/lib/galaxy/model/migrate/versions/0077_create_tool_tag_association_table.py b/lib/galaxy/model/migrate/versions/0077_create_tool_tag_association_table.py index d01ce498ff5d..c500e027d5e8 100644 --- a/lib/galaxy/model/migrate/versions/0077_create_tool_tag_association_table.py +++ b/lib/galaxy/model/migrate/versions/0077_create_tool_tag_association_table.py @@ -9,19 +9,19 @@ from galaxy.model.custom_types import TrimmedString -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() # Table to add -ToolTagAssociation_table = Table( "tool_tag_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "tool_id", TrimmedString(255), index=True ), - Column( "tag_id", Integer, ForeignKey( "tag.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "user_tname", TrimmedString(255), index=True), - Column( "value", TrimmedString(255), index=True), - Column( "user_value", TrimmedString(255), index=True) ) +ToolTagAssociation_table = Table("tool_tag_association", metadata, + Column("id", Integer, primary_key=True), + Column("tool_id", TrimmedString(255), index=True), + Column("tag_id", Integer, ForeignKey("tag.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("user_tname", TrimmedString(255), index=True), + Column("value", TrimmedString(255), index=True), + Column("user_value", TrimmedString(255), index=True)) def upgrade(migrate_engine): diff --git a/lib/galaxy/model/migrate/versions/0078_add_columns_for_disk_usage_accounting.py b/lib/galaxy/model/migrate/versions/0078_add_columns_for_disk_usage_accounting.py index 2aad0c60c97b..c0fc4b7b90ba 100644 --- a/lib/galaxy/model/migrate/versions/0078_add_columns_for_disk_usage_accounting.py +++ b/lib/galaxy/model/migrate/versions/0078_add_columns_for_disk_usage_accounting.py @@ -9,7 +9,7 @@ from sqlalchemy import Boolean, Column, MetaData, Numeric, Table -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() @@ -19,34 +19,34 @@ def upgrade(migrate_engine): metadata.reflect() try: - Dataset_table = Table( "dataset", metadata, autoload=True ) - c = Column( 'total_size', Numeric( 15, 0 ) ) - c.create( Dataset_table ) + Dataset_table = Table("dataset", metadata, autoload=True) + c = Column('total_size', Numeric(15, 0)) + c.create(Dataset_table) assert c is Dataset_table.c.total_size except Exception: log.exception("Adding total_size column to dataset table failed.") try: - HistoryDatasetAssociation_table = Table( "history_dataset_association", metadata, autoload=True ) - c = Column( "purged", Boolean, index=True, default=False ) - c.create( HistoryDatasetAssociation_table, index_name="ix_history_dataset_association_purged") + HistoryDatasetAssociation_table = Table("history_dataset_association", metadata, autoload=True) + c = Column("purged", Boolean, index=True, default=False) + c.create(HistoryDatasetAssociation_table, index_name="ix_history_dataset_association_purged") assert c is HistoryDatasetAssociation_table.c.purged migrate_engine.execute(HistoryDatasetAssociation_table.update().values(purged=False)) except Exception: log.exception("Adding purged column to history_dataset_association table failed.") try: - User_table = Table( "galaxy_user", metadata, autoload=True ) - c = Column( 'disk_usage', Numeric( 15, 0 ), index=True ) - c.create( User_table, index_name="ix_galaxy_user_disk_usage") + User_table = Table("galaxy_user", metadata, autoload=True) + c = Column('disk_usage', Numeric(15, 0), index=True) + c.create(User_table, index_name="ix_galaxy_user_disk_usage") assert c is User_table.c.disk_usage except Exception: log.exception("Adding disk_usage column to galaxy_user table failed.") try: - GalaxySession_table = Table( "galaxy_session", metadata, autoload=True ) - c = Column( 'disk_usage', Numeric( 15, 0 ), index=True ) - c.create( GalaxySession_table, index_name="ix_galaxy_session_disk_usage") + GalaxySession_table = Table("galaxy_session", metadata, autoload=True) + c = Column('disk_usage', Numeric(15, 0), index=True) + c.create(GalaxySession_table, index_name="ix_galaxy_session_disk_usage") assert c is GalaxySession_table.c.disk_usage except Exception: log.exception("Adding disk_usage column to galaxy_session table failed.") @@ -56,25 +56,25 @@ def downgrade(migrate_engine): metadata.bind = migrate_engine metadata.reflect() try: - Dataset_table = Table( "dataset", metadata, autoload=True ) + Dataset_table = Table("dataset", metadata, autoload=True) Dataset_table.c.total_size.drop() except Exception: log.exception("Dropping total_size column from dataset table failed.") try: - HistoryDatasetAssociation_table = Table( "history_dataset_association", metadata, autoload=True ) + HistoryDatasetAssociation_table = Table("history_dataset_association", metadata, autoload=True) HistoryDatasetAssociation_table.c.purged.drop() except Exception: log.exception("Dropping purged column from history_dataset_association table failed.") try: - User_table = Table( "galaxy_user", metadata, autoload=True ) + User_table = Table("galaxy_user", metadata, autoload=True) User_table.c.disk_usage.drop() except Exception: log.exception("Dropping disk_usage column from galaxy_user table failed.") try: - GalaxySession_table = Table( "galaxy_session", metadata, autoload=True ) + GalaxySession_table = Table("galaxy_session", metadata, autoload=True) GalaxySession_table.c.disk_usage.drop() except Exception: log.exception("Dropping disk_usage column from galaxy_session table failed.") diff --git a/lib/galaxy/model/migrate/versions/0079_input_library_to_job_table.py b/lib/galaxy/model/migrate/versions/0079_input_library_to_job_table.py index 26b7deb6f903..ea3f86ab2de3 100644 --- a/lib/galaxy/model/migrate/versions/0079_input_library_to_job_table.py +++ b/lib/galaxy/model/migrate/versions/0079_input_library_to_job_table.py @@ -7,15 +7,15 @@ from sqlalchemy import Column, ForeignKey, Integer, MetaData, String, Table -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() -JobToInputLibraryDatasetAssociation_table = Table( "job_to_input_library_dataset", metadata, - Column( "id", Integer, primary_key=True ), - Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ), - Column( "ldda_id", Integer, ForeignKey( "library_dataset_dataset_association.id" ), index=True ), - Column( "name", String(255) ) ) +JobToInputLibraryDatasetAssociation_table = Table("job_to_input_library_dataset", metadata, + Column("id", Integer, primary_key=True), + Column("job_id", Integer, ForeignKey("job.id"), index=True), + Column("ldda_id", Integer, ForeignKey("library_dataset_dataset_association.id"), index=True), + Column("name", String(255))) def upgrade(migrate_engine): diff --git a/lib/galaxy/model/migrate/versions/0080_quota_tables.py b/lib/galaxy/model/migrate/versions/0080_quota_tables.py index f93993228d79..6f67964b7fd7 100644 --- a/lib/galaxy/model/migrate/versions/0080_quota_tables.py +++ b/lib/galaxy/model/migrate/versions/0080_quota_tables.py @@ -9,41 +9,41 @@ from sqlalchemy import BigInteger, Boolean, Column, DateTime, ForeignKey, Integer, MetaData, String, Table, TEXT now = datetime.datetime.utcnow -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() # Tables to add -Quota_table = Table( "quota", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "name", String( 255 ), index=True, unique=True ), - Column( "description", TEXT ), - Column( "bytes", BigInteger ), - Column( "operation", String( 8 ) ), - Column( "deleted", Boolean, index=True, default=False ) ) - -UserQuotaAssociation_table = Table( "user_quota_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "quota_id", Integer, ForeignKey( "quota.id" ), index=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ) ) - -GroupQuotaAssociation_table = Table( "group_quota_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "group_id", Integer, ForeignKey( "galaxy_group.id" ), index=True ), - Column( "quota_id", Integer, ForeignKey( "quota.id" ), index=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ) ) - -DefaultQuotaAssociation_table = Table( "default_quota_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "type", String( 32 ), index=True, unique=True ), - Column( "quota_id", Integer, ForeignKey( "quota.id" ), index=True ) ) +Quota_table = Table("quota", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("name", String(255), index=True, unique=True), + Column("description", TEXT), + Column("bytes", BigInteger), + Column("operation", String(8)), + Column("deleted", Boolean, index=True, default=False)) + +UserQuotaAssociation_table = Table("user_quota_association", metadata, + Column("id", Integer, primary_key=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("quota_id", Integer, ForeignKey("quota.id"), index=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now)) + +GroupQuotaAssociation_table = Table("group_quota_association", metadata, + Column("id", Integer, primary_key=True), + Column("group_id", Integer, ForeignKey("galaxy_group.id"), index=True), + Column("quota_id", Integer, ForeignKey("quota.id"), index=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now)) + +DefaultQuotaAssociation_table = Table("default_quota_association", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("type", String(32), index=True, unique=True), + Column("quota_id", Integer, ForeignKey("quota.id"), index=True)) def upgrade(migrate_engine): diff --git a/lib/galaxy/model/migrate/versions/0081_add_tool_version_to_hda_ldda.py b/lib/galaxy/model/migrate/versions/0081_add_tool_version_to_hda_ldda.py index 556988be5451..58eae4cf8108 100644 --- a/lib/galaxy/model/migrate/versions/0081_add_tool_version_to_hda_ldda.py +++ b/lib/galaxy/model/migrate/versions/0081_add_tool_version_to_hda_ldda.py @@ -7,7 +7,7 @@ from sqlalchemy import Column, MetaData, Table, TEXT -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() @@ -16,14 +16,14 @@ def upgrade(migrate_engine): print(__doc__) metadata.reflect() try: - hda_table = Table( "history_dataset_association", metadata, autoload=True ) - c = Column( "tool_version", TEXT ) - c.create( hda_table ) + hda_table = Table("history_dataset_association", metadata, autoload=True) + c = Column("tool_version", TEXT) + c.create(hda_table) assert c is hda_table.c.tool_version - ldda_table = Table( "library_dataset_dataset_association", metadata, autoload=True ) - c = Column( "tool_version", TEXT ) - c.create( ldda_table ) + ldda_table = Table("library_dataset_dataset_association", metadata, autoload=True) + c = Column("tool_version", TEXT) + c.create(ldda_table) assert c is ldda_table.c.tool_version except Exception: @@ -34,10 +34,10 @@ def downgrade(migrate_engine): metadata.bind = migrate_engine metadata.reflect() try: - hda_table = Table( "history_dataset_association", metadata, autoload=True ) + hda_table = Table("history_dataset_association", metadata, autoload=True) hda_table.c.tool_version.drop() - ldda_table = Table( "library_dataset_dataset_association", metadata, autoload=True ) + ldda_table = Table("library_dataset_dataset_association", metadata, autoload=True) ldda_table.c.tool_version.drop() except Exception: log.exception("Dropping the tool_version column from hda/ldda table failed.") diff --git a/lib/galaxy/model/migrate/versions/0082_add_tool_shed_repository_table.py b/lib/galaxy/model/migrate/versions/0082_add_tool_shed_repository_table.py index 30a3f06280ac..de7160de87eb 100644 --- a/lib/galaxy/model/migrate/versions/0082_add_tool_shed_repository_table.py +++ b/lib/galaxy/model/migrate/versions/0082_add_tool_shed_repository_table.py @@ -13,27 +13,27 @@ from galaxy.model.custom_types import TrimmedString now = datetime.datetime.utcnow -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) -handler = logging.StreamHandler( sys.stdout ) +handler = logging.StreamHandler(sys.stdout) format = "%(name)s %(levelname)s %(asctime)s %(message)s" -formatter = logging.Formatter( format ) -handler.setFormatter( formatter ) -log.addHandler( handler ) +formatter = logging.Formatter(format) +handler.setFormatter(formatter) +log.addHandler(handler) metadata = MetaData() # New table to store information about cloned tool shed repositories. -ToolShedRepository_table = Table( "tool_shed_repository", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "tool_shed", TrimmedString( 255 ), index=True ), - Column( "name", TrimmedString( 255 ), index=True ), - Column( "description", TEXT ), - Column( "owner", TrimmedString( 255 ), index=True ), - Column( "changeset_revision", TrimmedString( 255 ), index=True ), - Column( "deleted", Boolean, index=True, default=False ) ) +ToolShedRepository_table = Table("tool_shed_repository", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("tool_shed", TrimmedString(255), index=True), + Column("name", TrimmedString(255), index=True), + Column("description", TEXT), + Column("owner", TrimmedString(255), index=True), + Column("changeset_revision", TrimmedString(255), index=True), + Column("deleted", Boolean, index=True, default=False)) def upgrade(migrate_engine): diff --git a/lib/galaxy/model/migrate/versions/0083_add_prepare_files_to_task.py b/lib/galaxy/model/migrate/versions/0083_add_prepare_files_to_task.py index 93662bbc2fd7..9c5f50388553 100644 --- a/lib/galaxy/model/migrate/versions/0083_add_prepare_files_to_task.py +++ b/lib/galaxy/model/migrate/versions/0083_add_prepare_files_to_task.py @@ -7,7 +7,7 @@ from sqlalchemy import Column, MetaData, String, Table, TEXT -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() @@ -16,16 +16,16 @@ def upgrade(migrate_engine): print(__doc__) metadata.reflect() try: - task_table = Table( "task", metadata, autoload=True ) - c = Column( "prepare_input_files_cmd", TEXT, nullable=True ) - c.create( task_table ) + task_table = Table("task", metadata, autoload=True) + c = Column("prepare_input_files_cmd", TEXT, nullable=True) + c.create(task_table) assert c is task_table.c.prepare_input_files_cmd except Exception: log.exception("Adding prepare_input_files_cmd column to task table failed.") try: - task_table = Table( "task", metadata, autoload=True ) - c = Column( "working_directory", String( 1024 ), nullable=True ) - c.create( task_table ) + task_table = Table("task", metadata, autoload=True) + c = Column("working_directory", String(1024), nullable=True) + c.create(task_table) assert c is task_table.c.working_directory except Exception: log.exception("Adding working_directory column to task table failed.") @@ -41,19 +41,19 @@ def downgrade(migrate_engine): metadata.bind = migrate_engine metadata.reflect() try: - task_table = Table( "task", metadata, autoload=True ) + task_table = Table("task", metadata, autoload=True) task_table.c.prepare_input_files_cmd.drop() except Exception: log.exception("Dropping prepare_input_files_cmd column from task table failed.") try: - task_table = Table( "task", metadata, autoload=True ) + task_table = Table("task", metadata, autoload=True) task_table.c.working_directory.drop() except Exception: log.exception("Dropping working_directory column from task table failed.") try: - task_table = Table( "task", metadata, autoload=True ) - c = Column( "part_file", String( 1024 ), nullable=True ) - c.create( task_table ) + task_table = Table("task", metadata, autoload=True) + c = Column("part_file", String(1024), nullable=True) + c.create(task_table) assert c is task_table.c.part_file except Exception: log.exception("Adding part_file column to task table failed.") diff --git a/lib/galaxy/model/migrate/versions/0084_add_ldda_id_to_implicit_conversion_table.py b/lib/galaxy/model/migrate/versions/0084_add_ldda_id_to_implicit_conversion_table.py index e5602dc314a5..c111aa02f590 100644 --- a/lib/galaxy/model/migrate/versions/0084_add_ldda_id_to_implicit_conversion_table.py +++ b/lib/galaxy/model/migrate/versions/0084_add_ldda_id_to_implicit_conversion_table.py @@ -7,7 +7,7 @@ from sqlalchemy import Column, ForeignKey, Integer, MetaData, Table -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() @@ -16,12 +16,12 @@ def upgrade(migrate_engine): print(__doc__) metadata.reflect() try: - Implicitly_converted_table = Table( "implicitly_converted_dataset_association", metadata, autoload=True ) + Implicitly_converted_table = Table("implicitly_converted_dataset_association", metadata, autoload=True) if migrate_engine.name != 'sqlite': - c = Column( "ldda_id", Integer, ForeignKey( "library_dataset_dataset_association.id" ), index=True, nullable=True ) + c = Column("ldda_id", Integer, ForeignKey("library_dataset_dataset_association.id"), index=True, nullable=True) else: - c = Column( "ldda_id", Integer, index=True, nullable=True ) - c.create( Implicitly_converted_table, index_name="ix_implicitly_converted_ds_assoc_ldda_id") + c = Column("ldda_id", Integer, index=True, nullable=True) + c.create(Implicitly_converted_table, index_name="ix_implicitly_converted_ds_assoc_ldda_id") assert c is Implicitly_converted_table.c.ldda_id except Exception: log.exception("Adding ldda_id column to implicitly_converted_dataset_association table failed.") @@ -31,7 +31,7 @@ def downgrade(migrate_engine): metadata.bind = migrate_engine metadata.reflect() try: - Implicitly_converted_table = Table( "implicitly_converted_dataset_association", metadata, autoload=True ) + Implicitly_converted_table = Table("implicitly_converted_dataset_association", metadata, autoload=True) Implicitly_converted_table.c.ldda_id.drop() except Exception: log.exception("Dropping ldda_id column from implicitly_converted_dataset_association table failed.") diff --git a/lib/galaxy/model/migrate/versions/0085_add_task_info.py b/lib/galaxy/model/migrate/versions/0085_add_task_info.py index 2a7307309bd1..c54daac808f2 100644 --- a/lib/galaxy/model/migrate/versions/0085_add_task_info.py +++ b/lib/galaxy/model/migrate/versions/0085_add_task_info.py @@ -9,7 +9,7 @@ from galaxy.model.custom_types import TrimmedString -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() @@ -18,9 +18,9 @@ def upgrade(migrate_engine): print(__doc__) metadata.reflect() try: - task_table = Table( "task", metadata, autoload=True ) - c = Column( "info", TrimmedString(255), nullable=True ) - c.create( task_table ) + task_table = Table("task", metadata, autoload=True) + c = Column("info", TrimmedString(255), nullable=True) + c.create(task_table) assert c is task_table.c.info except Exception: log.exception("Adding info column to task table failed.") @@ -30,7 +30,7 @@ def downgrade(migrate_engine): metadata.bind = migrate_engine metadata.reflect() try: - task_table = Table( "task", metadata, autoload=True ) + task_table = Table("task", metadata, autoload=True) task_table.c.info.drop() except Exception: log.exception("Dropping info column from task table failed.") diff --git a/lib/galaxy/model/migrate/versions/0086_add_tool_shed_repository_table_columns.py b/lib/galaxy/model/migrate/versions/0086_add_tool_shed_repository_table_columns.py index 5bd1e01d2c6c..c77f51a8b93f 100644 --- a/lib/galaxy/model/migrate/versions/0086_add_tool_shed_repository_table_columns.py +++ b/lib/galaxy/model/migrate/versions/0086_add_tool_shed_repository_table_columns.py @@ -11,13 +11,13 @@ # Need our custom types, but don't import anything else from model from galaxy.model.custom_types import JSONType -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) -handler = logging.StreamHandler( sys.stdout ) +handler = logging.StreamHandler(sys.stdout) format = "%(name)s %(levelname)s %(asctime)s %(message)s" -formatter = logging.Formatter( format ) -handler.setFormatter( formatter ) -log.addHandler( handler ) +formatter = logging.Formatter(format) +handler.setFormatter(formatter) +log.addHandler(handler) metadata = MetaData() @@ -35,25 +35,25 @@ def upgrade(migrate_engine): metadata.bind = migrate_engine print(__doc__) metadata.reflect() - ToolShedRepository_table = Table( "tool_shed_repository", metadata, autoload=True ) - c = Column( "metadata", JSONType(), nullable=True ) + ToolShedRepository_table = Table("tool_shed_repository", metadata, autoload=True) + c = Column("metadata", JSONType(), nullable=True) try: - c.create( ToolShedRepository_table ) + c.create(ToolShedRepository_table) assert c is ToolShedRepository_table.c.metadata except Exception: log.exception("Adding metadata column to the tool_shed_repository table failed.") - c = Column( "includes_datatypes", Boolean, index=True, default=False ) + c = Column("includes_datatypes", Boolean, index=True, default=False) try: - c.create( ToolShedRepository_table, index_name="ix_tool_shed_repository_includes_datatypes") + c.create(ToolShedRepository_table, index_name="ix_tool_shed_repository_includes_datatypes") assert c is ToolShedRepository_table.c.includes_datatypes - migrate_engine.execute( "UPDATE tool_shed_repository SET includes_datatypes=%s" % engine_false(migrate_engine)) + migrate_engine.execute("UPDATE tool_shed_repository SET includes_datatypes=%s" % engine_false(migrate_engine)) except Exception: log.exception("Adding includes_datatypes column to the tool_shed_repository table failed.") - c = Column( "update_available", Boolean, default=False ) + c = Column("update_available", Boolean, default=False) try: - c.create( ToolShedRepository_table ) + c.create(ToolShedRepository_table) assert c is ToolShedRepository_table.c.update_available - migrate_engine.execute( "UPDATE tool_shed_repository SET update_available=%s" % engine_false(migrate_engine)) + migrate_engine.execute("UPDATE tool_shed_repository SET update_available=%s" % engine_false(migrate_engine)) except Exception: log.exception("Adding update_available column to the tool_shed_repository table failed.") @@ -61,7 +61,7 @@ def upgrade(migrate_engine): def downgrade(migrate_engine): metadata.bind = migrate_engine metadata.reflect() - ToolShedRepository_table = Table( "tool_shed_repository", metadata, autoload=True ) + ToolShedRepository_table = Table("tool_shed_repository", metadata, autoload=True) try: ToolShedRepository_table.c.metadata.drop() except Exception: diff --git a/lib/galaxy/model/migrate/versions/0087_tool_id_guid_map_table.py b/lib/galaxy/model/migrate/versions/0087_tool_id_guid_map_table.py index b9d04d5e1983..01cde978ad83 100644 --- a/lib/galaxy/model/migrate/versions/0087_tool_id_guid_map_table.py +++ b/lib/galaxy/model/migrate/versions/0087_tool_id_guid_map_table.py @@ -13,26 +13,26 @@ from galaxy.model.custom_types import TrimmedString now = datetime.datetime.utcnow -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) -handler = logging.StreamHandler( sys.stdout ) +handler = logging.StreamHandler(sys.stdout) format = "%(name)s %(levelname)s %(asctime)s %(message)s" -formatter = logging.Formatter( format ) -handler.setFormatter( formatter ) -log.addHandler( handler ) +formatter = logging.Formatter(format) +handler.setFormatter(formatter) +log.addHandler(handler) metadata = MetaData() -ToolIdGuidMap_table = Table( "tool_id_guid_map", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "tool_id", String( 255 ) ), - Column( "tool_version", TEXT ), - Column( "tool_shed", TrimmedString( 255 ) ), - Column( "repository_owner", TrimmedString( 255 ) ), - Column( "repository_name", TrimmedString( 255 ) ), - Column( "guid", TEXT, index=True, unique=True ) ) +ToolIdGuidMap_table = Table("tool_id_guid_map", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("tool_id", String(255)), + Column("tool_version", TEXT), + Column("tool_shed", TrimmedString(255)), + Column("repository_owner", TrimmedString(255)), + Column("repository_name", TrimmedString(255)), + Column("guid", TEXT, index=True, unique=True)) def upgrade(migrate_engine): diff --git a/lib/galaxy/model/migrate/versions/0088_add_installed_changeset_revison_column.py b/lib/galaxy/model/migrate/versions/0088_add_installed_changeset_revison_column.py index 5efb1dbbe9ab..fb8a853632e1 100644 --- a/lib/galaxy/model/migrate/versions/0088_add_installed_changeset_revison_column.py +++ b/lib/galaxy/model/migrate/versions/0088_add_installed_changeset_revison_column.py @@ -11,13 +11,13 @@ # Need our custom types, but don't import anything else from model from galaxy.model.custom_types import TrimmedString -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) -handler = logging.StreamHandler( sys.stdout ) +handler = logging.StreamHandler(sys.stdout) format = "%(name)s %(levelname)s %(asctime)s %(message)s" -formatter = logging.Formatter( format ) -handler.setFormatter( formatter ) -log.addHandler( handler ) +formatter = logging.Formatter(format) +handler.setFormatter(formatter) +log.addHandler(handler) metadata = MetaData() @@ -26,10 +26,10 @@ def upgrade(migrate_engine): metadata.bind = migrate_engine print(__doc__) metadata.reflect() - ToolShedRepository_table = Table( "tool_shed_repository", metadata, autoload=True ) - col = Column( "installed_changeset_revision", TrimmedString( 255 ) ) + ToolShedRepository_table = Table("tool_shed_repository", metadata, autoload=True) + col = Column("installed_changeset_revision", TrimmedString(255)) try: - col.create( ToolShedRepository_table ) + col.create(ToolShedRepository_table) assert col is ToolShedRepository_table.c.installed_changeset_revision except Exception: log.exception("Adding installed_changeset_revision column to the tool_shed_repository table failed.") @@ -42,13 +42,13 @@ def upgrade(migrate_engine): + "installed_changeset_revision AS installed_changeset_revision, " \ + "changeset_revision AS changeset_revision " \ + "FROM tool_shed_repository;" - tool_shed_repositories = migrate_engine.execute( cmd ).fetchall() + tool_shed_repositories = migrate_engine.execute(cmd).fetchall() update_count = 0 for row in tool_shed_repositories: cmd = "UPDATE tool_shed_repository " \ + "SET installed_changeset_revision = '%s' " % row.changeset_revision \ + "WHERE changeset_revision = '%s';" % row.changeset_revision - migrate_engine.execute( cmd ) + migrate_engine.execute(cmd) update_count += 1 print("Updated the installed_changeset_revision column for ", update_count, " rows in the tool_shed_repository table. ") @@ -56,7 +56,7 @@ def upgrade(migrate_engine): def downgrade(migrate_engine): metadata.bind = migrate_engine metadata.reflect() - ToolShedRepository_table = Table( "tool_shed_repository", metadata, autoload=True ) + ToolShedRepository_table = Table("tool_shed_repository", metadata, autoload=True) try: ToolShedRepository_table.c.installed_changeset_revision.drop() except Exception: diff --git a/lib/galaxy/model/migrate/versions/0089_add_object_store_id_columns.py b/lib/galaxy/model/migrate/versions/0089_add_object_store_id_columns.py index eab2c8f88b94..b99e12cdc34c 100644 --- a/lib/galaxy/model/migrate/versions/0089_add_object_store_id_columns.py +++ b/lib/galaxy/model/migrate/versions/0089_add_object_store_id_columns.py @@ -9,7 +9,7 @@ from galaxy.model.custom_types import TrimmedString -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() @@ -17,11 +17,11 @@ def upgrade(migrate_engine): metadata.bind = migrate_engine print(__doc__) metadata.reflect() - for t_name in ( 'dataset', 'job', 'metadata_file' ): - t = Table( t_name, metadata, autoload=True ) - c = Column( "object_store_id", TrimmedString( 255 ), index=True ) + for t_name in ('dataset', 'job', 'metadata_file'): + t = Table(t_name, metadata, autoload=True) + c = Column("object_store_id", TrimmedString(255), index=True) try: - c.create( t, index_name="ix_%s_object_store_id" % t_name) + c.create(t, index_name="ix_%s_object_store_id" % t_name) assert c is t.c.object_store_id except Exception: log.exception("Adding object_store_id column to %s table failed.", t_name) @@ -30,8 +30,8 @@ def upgrade(migrate_engine): def downgrade(migrate_engine): metadata.bind = migrate_engine metadata.reflect() - for t_name in ( 'dataset', 'job', 'metadata_file' ): - t = Table( t_name, metadata, autoload=True ) + for t_name in ('dataset', 'job', 'metadata_file'): + t = Table(t_name, metadata, autoload=True) try: t.c.object_store_id.drop() except Exception: diff --git a/lib/galaxy/model/migrate/versions/0090_add_tool_shed_repository_table_columns.py b/lib/galaxy/model/migrate/versions/0090_add_tool_shed_repository_table_columns.py index a8f80620875b..05deb99d8eff 100644 --- a/lib/galaxy/model/migrate/versions/0090_add_tool_shed_repository_table_columns.py +++ b/lib/galaxy/model/migrate/versions/0090_add_tool_shed_repository_table_columns.py @@ -8,13 +8,13 @@ from sqlalchemy import Boolean, Column, MetaData, Table -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) -handler = logging.StreamHandler( sys.stdout ) +handler = logging.StreamHandler(sys.stdout) format = "%(name)s %(levelname)s %(asctime)s %(message)s" -formatter = logging.Formatter( format ) -handler.setFormatter( formatter ) -log.addHandler( handler ) +formatter = logging.Formatter(format) +handler.setFormatter(formatter) +log.addHandler(handler) metadata = MetaData() @@ -32,19 +32,19 @@ def upgrade(migrate_engine): metadata.bind = migrate_engine print(__doc__) metadata.reflect() - ToolShedRepository_table = Table( "tool_shed_repository", metadata, autoload=True ) - c = Column( "uninstalled", Boolean, default=False ) + ToolShedRepository_table = Table("tool_shed_repository", metadata, autoload=True) + c = Column("uninstalled", Boolean, default=False) try: - c.create( ToolShedRepository_table ) + c.create(ToolShedRepository_table) assert c is ToolShedRepository_table.c.uninstalled - migrate_engine.execute( "UPDATE tool_shed_repository SET uninstalled=%s" % engine_false(migrate_engine) ) + migrate_engine.execute("UPDATE tool_shed_repository SET uninstalled=%s" % engine_false(migrate_engine)) except Exception: log.exception("Adding uninstalled column to the tool_shed_repository table failed.") - c = Column( "dist_to_shed", Boolean, default=False ) + c = Column("dist_to_shed", Boolean, default=False) try: - c.create( ToolShedRepository_table ) + c.create(ToolShedRepository_table) assert c is ToolShedRepository_table.c.dist_to_shed - migrate_engine.execute( "UPDATE tool_shed_repository SET dist_to_shed=%s" % engine_false(migrate_engine) ) + migrate_engine.execute("UPDATE tool_shed_repository SET dist_to_shed=%s" % engine_false(migrate_engine)) except Exception: log.exception("Adding dist_to_shed column to the tool_shed_repository table failed.") @@ -52,7 +52,7 @@ def upgrade(migrate_engine): def downgrade(migrate_engine): metadata.bind = migrate_engine metadata.reflect() - ToolShedRepository_table = Table( "tool_shed_repository", metadata, autoload=True ) + ToolShedRepository_table = Table("tool_shed_repository", metadata, autoload=True) # SQLAlchemy Migrate has a bug when dropping a boolean column in SQLite if migrate_engine.name != 'sqlite': try: diff --git a/lib/galaxy/model/migrate/versions/0091_add_tool_version_tables.py b/lib/galaxy/model/migrate/versions/0091_add_tool_version_tables.py index 2be6b3a723fe..4dcecbaaf897 100644 --- a/lib/galaxy/model/migrate/versions/0091_add_tool_version_tables.py +++ b/lib/galaxy/model/migrate/versions/0091_add_tool_version_tables.py @@ -14,24 +14,24 @@ from galaxy.model.custom_types import _sniffnfix_pg9_hex, TrimmedString now = datetime.datetime.utcnow -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) -handler = logging.StreamHandler( sys.stdout ) +handler = logging.StreamHandler(sys.stdout) format = "%(name)s %(levelname)s %(asctime)s %(message)s" -formatter = logging.Formatter( format ) -handler.setFormatter( formatter ) -log.addHandler( handler ) +formatter = logging.Formatter(format) +handler.setFormatter(formatter) +log.addHandler(handler) metadata = MetaData() -def nextval( migrate_engine, table, col='id' ): +def nextval(migrate_engine, table, col='id'): if migrate_engine.name in ['postgres', 'postgresql']: - return "nextval('%s_%s_seq')" % ( table, col ) + return "nextval('%s_%s_seq')" % (table, col) elif migrate_engine.name in ['mysql', 'sqlite']: return "null" else: - raise Exception( 'Unable to convert data for unknown database type: %s' % migrate_engine.name ) + raise Exception('Unable to convert data for unknown database type: %s' % migrate_engine.name) def localtimestamp(migrate_engine): @@ -40,27 +40,27 @@ def localtimestamp(migrate_engine): elif migrate_engine.name == 'sqlite': return "current_date || ' ' || current_time" else: - raise Exception( 'Unable to convert data for unknown database type: %s' % migrate_engine.name ) + raise Exception('Unable to convert data for unknown database type: %s' % migrate_engine.name) -ToolVersion_table = Table( "tool_version", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "tool_id", String( 255 ) ), - Column( "tool_shed_repository_id", Integer, ForeignKey( "tool_shed_repository.id" ), index=True, nullable=True ) ) +ToolVersion_table = Table("tool_version", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("tool_id", String(255)), + Column("tool_shed_repository_id", Integer, ForeignKey("tool_shed_repository.id"), index=True, nullable=True)) -ToolVersionAssociation_table = Table( "tool_version_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "tool_id", Integer, ForeignKey( "tool_version.id" ), index=True, nullable=False ), - Column( "parent_id", Integer, ForeignKey( "tool_version.id" ), index=True, nullable=False ) ) +ToolVersionAssociation_table = Table("tool_version_association", metadata, + Column("id", Integer, primary_key=True), + Column("tool_id", Integer, ForeignKey("tool_version.id"), index=True, nullable=False), + Column("parent_id", Integer, ForeignKey("tool_version.id"), index=True, nullable=False)) def upgrade(migrate_engine): metadata.bind = migrate_engine print(__doc__) - ToolIdGuidMap_table = Table( "tool_id_guid_map", metadata, autoload=True ) + ToolIdGuidMap_table = Table("tool_id_guid_map", metadata, autoload=True) metadata.reflect() # Create the tables. @@ -74,19 +74,19 @@ def upgrade(migrate_engine): log.exception("Creating tool_version_association table failed.") # Populate the tool table with tools included in installed tool shed repositories. cmd = "SELECT id, metadata FROM tool_shed_repository" - result = migrate_engine.execute( cmd ) + result = migrate_engine.execute(cmd) count = 0 for row in result: if row[1]: tool_shed_repository_id = row[0] - repository_metadata = loads( _sniffnfix_pg9_hex( str( row[1] ) ) ) + repository_metadata = loads(_sniffnfix_pg9_hex(str(row[1]))) # Create a new row in the tool table for each tool included in repository. We will NOT # handle tool_version_associaions because we do not have the information we need to do so. - tools = repository_metadata.get( 'tools', [] ) + tools = repository_metadata.get('tools', []) for tool_dict in tools: cmd = "INSERT INTO tool_version VALUES (%s, %s, %s, '%s', %s)" % \ - ( nextval( migrate_engine, 'tool_version' ), localtimestamp( migrate_engine ), localtimestamp( migrate_engine ), tool_dict[ 'guid' ], tool_shed_repository_id ) - migrate_engine.execute( cmd ) + (nextval(migrate_engine, 'tool_version'), localtimestamp(migrate_engine), localtimestamp(migrate_engine), tool_dict['guid'], tool_shed_repository_id) + migrate_engine.execute(cmd) count += 1 print("Added %d rows to the new tool_version table." % count) # Drop the tool_id_guid_map table since the 2 new tables render it unnecessary. @@ -99,16 +99,16 @@ def upgrade(migrate_engine): def downgrade(migrate_engine): metadata.bind = migrate_engine - ToolIdGuidMap_table = Table( "tool_id_guid_map", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "tool_id", String( 255 ) ), - Column( "tool_version", TEXT ), - Column( "tool_shed", TrimmedString( 255 ) ), - Column( "repository_owner", TrimmedString( 255 ) ), - Column( "repository_name", TrimmedString( 255 ) ), - Column( "guid", TEXT, index=True, unique=True ) ) + ToolIdGuidMap_table = Table("tool_id_guid_map", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("tool_id", String(255)), + Column("tool_version", TEXT), + Column("tool_shed", TrimmedString(255)), + Column("repository_owner", TrimmedString(255)), + Column("repository_name", TrimmedString(255)), + Column("guid", TEXT, index=True, unique=True)) metadata.reflect() try: diff --git a/lib/galaxy/model/migrate/versions/0092_add_migrate_tools_table.py b/lib/galaxy/model/migrate/versions/0092_add_migrate_tools_table.py index be546b3445b4..7419ec0ea728 100644 --- a/lib/galaxy/model/migrate/versions/0092_add_migrate_tools_table.py +++ b/lib/galaxy/model/migrate/versions/0092_add_migrate_tools_table.py @@ -11,20 +11,20 @@ # Need our custom types, but don't import anything else from model from galaxy.model.custom_types import TrimmedString -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) -handler = logging.StreamHandler( sys.stdout ) +handler = logging.StreamHandler(sys.stdout) format = "%(name)s %(levelname)s %(asctime)s %(message)s" -formatter = logging.Formatter( format ) -handler.setFormatter( formatter ) -log.addHandler( handler ) +formatter = logging.Formatter(format) +handler.setFormatter(formatter) +log.addHandler(handler) metadata = MetaData() -MigrateTools_table = Table( "migrate_tools", metadata, - Column( "repository_id", TrimmedString( 255 ) ), - Column( "repository_path", TEXT ), - Column( "version", Integer ) ) +MigrateTools_table = Table("migrate_tools", metadata, + Column("repository_id", TrimmedString(255)), + Column("repository_path", TEXT), + Column("version", Integer)) def upgrade(migrate_engine): @@ -36,7 +36,7 @@ def upgrade(migrate_engine): try: MigrateTools_table.create() cmd = "INSERT INTO migrate_tools VALUES ('GalaxyTools', 'lib/galaxy/tool_shed/migrate', %d)" % 1 - migrate_engine.execute( cmd ) + migrate_engine.execute(cmd) except Exception: log.exception("Creating migrate_tools table failed.") diff --git a/lib/galaxy/model/migrate/versions/0093_add_job_params_col.py b/lib/galaxy/model/migrate/versions/0093_add_job_params_col.py index 79acf1af7835..a201a0c538bd 100644 --- a/lib/galaxy/model/migrate/versions/0093_add_job_params_col.py +++ b/lib/galaxy/model/migrate/versions/0093_add_job_params_col.py @@ -10,11 +10,11 @@ # Need our custom types, but don't import anything else from model from galaxy.model.custom_types import TrimmedString -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() # Column to add. -params_col = Column( "params", TrimmedString(255), index=True ) +params_col = Column("params", TrimmedString(255), index=True) def upgrade(migrate_engine): @@ -24,8 +24,8 @@ def upgrade(migrate_engine): # Add column to Job table. try: - Job_table = Table( "job", metadata, autoload=True ) - params_col.create( Job_table, index_name="ix_job_params") + Job_table = Table("job", metadata, autoload=True) + params_col.create(Job_table, index_name="ix_job_params") assert params_col is Job_table.c.params except Exception: @@ -38,7 +38,7 @@ def downgrade(migrate_engine): # Drop column from Job table. try: - Job_table = Table( "job", metadata, autoload=True ) + Job_table = Table("job", metadata, autoload=True) params_col = Job_table.c.params params_col.drop() except Exception: diff --git a/lib/galaxy/model/migrate/versions/0094_add_job_handler_col.py b/lib/galaxy/model/migrate/versions/0094_add_job_handler_col.py index 2ac6d3e4a094..a93a56c4ec7b 100644 --- a/lib/galaxy/model/migrate/versions/0094_add_job_handler_col.py +++ b/lib/galaxy/model/migrate/versions/0094_add_job_handler_col.py @@ -10,11 +10,11 @@ # Need our custom types, but don't import anything else from model from galaxy.model.custom_types import TrimmedString -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() # Column to add. -handler_col = Column( "handler", TrimmedString(255), index=True ) +handler_col = Column("handler", TrimmedString(255), index=True) def upgrade(migrate_engine): @@ -24,8 +24,8 @@ def upgrade(migrate_engine): # Add column to Job table. try: - Job_table = Table( "job", metadata, autoload=True ) - handler_col.create( Job_table, index_name="ix_job_handler" ) + Job_table = Table("job", metadata, autoload=True) + handler_col.create(Job_table, index_name="ix_job_handler") assert handler_col is Job_table.c.handler except Exception: @@ -38,7 +38,7 @@ def downgrade(migrate_engine): # Drop column from Job table. try: - Job_table = Table( "job", metadata, autoload=True ) + Job_table = Table("job", metadata, autoload=True) handler_col = Job_table.c.handler handler_col.drop() except Exception: diff --git a/lib/galaxy/model/migrate/versions/0095_hda_subsets.py b/lib/galaxy/model/migrate/versions/0095_hda_subsets.py index 33dbf2aa47aa..0126903bfef4 100644 --- a/lib/galaxy/model/migrate/versions/0095_hda_subsets.py +++ b/lib/galaxy/model/migrate/versions/0095_hda_subsets.py @@ -7,16 +7,16 @@ from sqlalchemy import Column, ForeignKey, Index, Integer, MetaData, Table, Unicode -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() # Table to add. -HistoryDatasetAssociationSubset_table = Table( "history_dataset_association_subset", metadata, - Column( "id", Integer, primary_key=True ), - Column( "history_dataset_association_id", Integer, ForeignKey( "history_dataset_association.id" ) ), - Column( "history_dataset_association_subset_id", Integer, ForeignKey( "history_dataset_association.id" ) ), - Column( "location", Unicode(255), index=True) ) +HistoryDatasetAssociationSubset_table = Table("history_dataset_association_subset", metadata, + Column("id", Integer, primary_key=True), + Column("history_dataset_association_id", Integer, ForeignKey("history_dataset_association.id")), + Column("history_dataset_association_subset_id", Integer, ForeignKey("history_dataset_association.id")), + Column("location", Unicode(255), index=True)) def upgrade(migrate_engine): @@ -31,8 +31,8 @@ def upgrade(migrate_engine): log.exception("Creating history_dataset_association_subset table failed.") # Manually create indexes because they are too long for MySQL databases. - i1 = Index( "ix_hda_id", HistoryDatasetAssociationSubset_table.c.history_dataset_association_id ) - i2 = Index( "ix_hda_subset_id", HistoryDatasetAssociationSubset_table.c.history_dataset_association_subset_id ) + i1 = Index("ix_hda_id", HistoryDatasetAssociationSubset_table.c.history_dataset_association_id) + i2 = Index("ix_hda_subset_id", HistoryDatasetAssociationSubset_table.c.history_dataset_association_subset_id) try: i1.create() i2.create() diff --git a/lib/galaxy/model/migrate/versions/0096_openid_provider.py b/lib/galaxy/model/migrate/versions/0096_openid_provider.py index 5c250daa064a..99efce16bc3b 100644 --- a/lib/galaxy/model/migrate/versions/0096_openid_provider.py +++ b/lib/galaxy/model/migrate/versions/0096_openid_provider.py @@ -10,7 +10,7 @@ from galaxy.model.custom_types import TrimmedString -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) BAD_IDENTIFIER = 'https://identity.genomespace.org/identityServer/xrd.jsp' metadata = MetaData() @@ -21,16 +21,16 @@ def upgrade(migrate_engine): metadata.reflect() try: - OpenID_table = Table( "galaxy_user_openid", metadata, autoload=True ) - c = Column( "provider", TrimmedString( 255 ) ) - c.create( OpenID_table ) + OpenID_table = Table("galaxy_user_openid", metadata, autoload=True) + c = Column("provider", TrimmedString(255)) + c.create(OpenID_table) assert c is OpenID_table.c.provider except Exception: log.exception("Adding provider column to galaxy_user_openid table failed.") try: - cmd = "DELETE FROM galaxy_user_openid WHERE openid='%s'" % ( BAD_IDENTIFIER ) - migrate_engine.execute( cmd ) + cmd = "DELETE FROM galaxy_user_openid WHERE openid='%s'" % (BAD_IDENTIFIER) + migrate_engine.execute(cmd) except Exception: log.exception("Deleting bad Identifiers from galaxy_user_openid failed.") @@ -39,7 +39,7 @@ def downgrade(migrate_engine): metadata.bind = migrate_engine metadata.reflect() try: - OpenID_table = Table( "galaxy_user_openid", metadata, autoload=True ) + OpenID_table = Table("galaxy_user_openid", metadata, autoload=True) OpenID_table.c.provider.drop() except Exception: log.exception("Dropping provider column from galaxy_user_openid table failed.") diff --git a/lib/galaxy/model/migrate/versions/0097_add_ctx_rev_column.py b/lib/galaxy/model/migrate/versions/0097_add_ctx_rev_column.py index 46c04b568857..c736a138ee96 100644 --- a/lib/galaxy/model/migrate/versions/0097_add_ctx_rev_column.py +++ b/lib/galaxy/model/migrate/versions/0097_add_ctx_rev_column.py @@ -11,13 +11,13 @@ # Need our custom types, but don't import anything else from model from galaxy.model.custom_types import TrimmedString -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) -handler = logging.StreamHandler( sys.stdout ) +handler = logging.StreamHandler(sys.stdout) format = "%(name)s %(levelname)s %(asctime)s %(message)s" -formatter = logging.Formatter( format ) -handler.setFormatter( formatter ) -log.addHandler( handler ) +formatter = logging.Formatter(format) +handler.setFormatter(formatter) +log.addHandler(handler) metadata = MetaData() @@ -26,10 +26,10 @@ def upgrade(migrate_engine): metadata.bind = migrate_engine print(__doc__) metadata.reflect() - ToolShedRepository_table = Table( "tool_shed_repository", metadata, autoload=True ) - col = Column( "ctx_rev", TrimmedString( 10 ) ) + ToolShedRepository_table = Table("tool_shed_repository", metadata, autoload=True) + col = Column("ctx_rev", TrimmedString(10)) try: - col.create( ToolShedRepository_table ) + col.create(ToolShedRepository_table) assert col is ToolShedRepository_table.c.ctx_rev except Exception: log.exception("Adding ctx_rev column to the tool_shed_repository table failed.") @@ -38,7 +38,7 @@ def upgrade(migrate_engine): def downgrade(migrate_engine): metadata.bind = migrate_engine metadata.reflect() - ToolShedRepository_table = Table( "tool_shed_repository", metadata, autoload=True ) + ToolShedRepository_table = Table("tool_shed_repository", metadata, autoload=True) try: ToolShedRepository_table.c.ctx_rev.drop() except Exception: diff --git a/lib/galaxy/model/migrate/versions/0098_genome_index_tool_data_table.py b/lib/galaxy/model/migrate/versions/0098_genome_index_tool_data_table.py index 6971c9217e71..d83d0fa022be 100644 --- a/lib/galaxy/model/migrate/versions/0098_genome_index_tool_data_table.py +++ b/lib/galaxy/model/migrate/versions/0098_genome_index_tool_data_table.py @@ -10,28 +10,28 @@ from sqlalchemy import Column, DateTime, ForeignKey, Integer, MetaData, String, Table now = datetime.datetime.utcnow -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) -handler = logging.StreamHandler( sys.stdout ) +handler = logging.StreamHandler(sys.stdout) format = "%(name)s %(levelname)s %(asctime)s %(message)s" -formatter = logging.Formatter( format ) -handler.setFormatter( formatter ) -log.addHandler( handler ) +formatter = logging.Formatter(format) +handler.setFormatter(formatter) +log.addHandler(handler) metadata = MetaData() # New table in changeset TODO:TODO -GenomeIndexToolData_table = Table( "genome_index_tool_data", metadata, - Column( "id", Integer, primary_key=True ), - Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ), - Column( "dataset_id", Integer, ForeignKey( "dataset.id" ), index=True ), - Column( "deferred_job_id", Integer, ForeignKey( "deferred_job.id" ), index=True ), - Column( "transfer_job_id", Integer, ForeignKey( "transfer_job.id" ), index=True ), - Column( "fasta_path", String( 255 ) ), - Column( "created_time", DateTime, default=now ), - Column( "modified_time", DateTime, default=now, onupdate=now ), - Column( "indexer", String( 64 ) ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ) ) +GenomeIndexToolData_table = Table("genome_index_tool_data", metadata, + Column("id", Integer, primary_key=True), + Column("job_id", Integer, ForeignKey("job.id"), index=True), + Column("dataset_id", Integer, ForeignKey("dataset.id"), index=True), + Column("deferred_job_id", Integer, ForeignKey("deferred_job.id"), index=True), + Column("transfer_job_id", Integer, ForeignKey("transfer_job.id"), index=True), + Column("fasta_path", String(255)), + Column("created_time", DateTime, default=now), + Column("modified_time", DateTime, default=now, onupdate=now), + Column("indexer", String(64)), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True)) def upgrade(migrate_engine): diff --git a/lib/galaxy/model/migrate/versions/0099_add_tool_dependency_table.py b/lib/galaxy/model/migrate/versions/0099_add_tool_dependency_table.py index 4bb82c0a32f3..23c951e34e70 100644 --- a/lib/galaxy/model/migrate/versions/0099_add_tool_dependency_table.py +++ b/lib/galaxy/model/migrate/versions/0099_add_tool_dependency_table.py @@ -12,27 +12,27 @@ from galaxy.model.custom_types import TrimmedString now = datetime.datetime.utcnow -log = logging.getLogger( __name__ ) -log.setLevel( logging.DEBUG ) -handler = logging.StreamHandler( sys.stdout ) +log = logging.getLogger(__name__) +log.setLevel(logging.DEBUG) +handler = logging.StreamHandler(sys.stdout) format = "%(name)s %(levelname)s %(asctime)s %(message)s" -formatter = logging.Formatter( format ) -handler.setFormatter( formatter ) -log.addHandler( handler ) +formatter = logging.Formatter(format) +handler.setFormatter(formatter) +log.addHandler(handler) metadata = MetaData() # New table to store information about cloned tool shed repositories. -ToolDependency_table = Table( "tool_dependency", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "tool_shed_repository_id", Integer, ForeignKey( "tool_shed_repository.id" ), index=True, nullable=False ), - Column( "installed_changeset_revision", TrimmedString( 255 ) ), - Column( "name", TrimmedString( 255 ) ), - Column( "version", TrimmedString( 40 ) ), - Column( "type", TrimmedString( 40 ) ), - Column( "uninstalled", Boolean, default=False ) ) +ToolDependency_table = Table("tool_dependency", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("tool_shed_repository_id", Integer, ForeignKey("tool_shed_repository.id"), index=True, nullable=False), + Column("installed_changeset_revision", TrimmedString(255)), + Column("name", TrimmedString(255)), + Column("version", TrimmedString(40)), + Column("type", TrimmedString(40)), + Column("uninstalled", Boolean, default=False)) def upgrade(migrate_engine): diff --git a/lib/galaxy/model/migrate/versions/0100_alter_tool_dependency_table_version_column.py b/lib/galaxy/model/migrate/versions/0100_alter_tool_dependency_table_version_column.py index dc9400f9d265..1085aaa11847 100644 --- a/lib/galaxy/model/migrate/versions/0100_alter_tool_dependency_table_version_column.py +++ b/lib/galaxy/model/migrate/versions/0100_alter_tool_dependency_table_version_column.py @@ -8,13 +8,13 @@ from sqlalchemy import MetaData, Table -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) -handler = logging.StreamHandler( sys.stdout ) +handler = logging.StreamHandler(sys.stdout) format = "%(name)s %(levelname)s %(asctime)s %(message)s" -formatter = logging.Formatter( format ) -handler.setFormatter( formatter ) -log.addHandler( handler ) +formatter = logging.Formatter(format) +handler.setFormatter(formatter) +log.addHandler(handler) metadata = MetaData() @@ -22,7 +22,7 @@ def upgrade(migrate_engine): metadata.bind = migrate_engine print(__doc__) metadata.reflect() - Table( "tool_dependency", metadata, autoload=True ) + Table("tool_dependency", metadata, autoload=True) # Change the tool_dependency table's version column from TrimmedString to Text. if migrate_engine.name in ['postgres', 'postgresql']: cmd = "ALTER TABLE tool_dependency ALTER COLUMN version TYPE Text;" @@ -40,7 +40,7 @@ def upgrade(migrate_engine): cmd = None if cmd: try: - migrate_engine.execute( cmd ) + migrate_engine.execute(cmd) except Exception: log.exception("Altering tool_dependency.version column from TrimmedString(40) to Text failed.") diff --git a/lib/galaxy/model/migrate/versions/0101_drop_installed_changeset_revision_column.py b/lib/galaxy/model/migrate/versions/0101_drop_installed_changeset_revision_column.py index 30cf17cea223..e9fb8feb899e 100644 --- a/lib/galaxy/model/migrate/versions/0101_drop_installed_changeset_revision_column.py +++ b/lib/galaxy/model/migrate/versions/0101_drop_installed_changeset_revision_column.py @@ -9,13 +9,13 @@ from sqlalchemy import MetaData, Table from sqlalchemy.exc import NoSuchTableError -log = logging.getLogger( __name__ ) -log.setLevel( logging.DEBUG ) -handler = logging.StreamHandler( sys.stdout ) +log = logging.getLogger(__name__) +log.setLevel(logging.DEBUG) +handler = logging.StreamHandler(sys.stdout) format = "%(name)s %(levelname)s %(asctime)s %(message)s" -formatter = logging.Formatter( format ) -handler.setFormatter( formatter ) -log.addHandler( handler ) +formatter = logging.Formatter(format) +handler.setFormatter(formatter) +log.addHandler(handler) metadata = MetaData() @@ -25,10 +25,10 @@ def upgrade(migrate_engine): print(__doc__) metadata.reflect() try: - ToolDependency_table = Table( "tool_dependency", metadata, autoload=True ) + ToolDependency_table = Table("tool_dependency", metadata, autoload=True) except NoSuchTableError: ToolDependency_table = None - log.debug( "Failed loading table tool_dependency" ) + log.debug("Failed loading table tool_dependency") if ToolDependency_table is not None: try: col = ToolDependency_table.c.installed_changeset_revision diff --git a/lib/galaxy/model/migrate/versions/0102_add_tool_dependency_status_columns.py b/lib/galaxy/model/migrate/versions/0102_add_tool_dependency_status_columns.py index 5ac41c2cee2c..43cf8d775b0f 100644 --- a/lib/galaxy/model/migrate/versions/0102_add_tool_dependency_status_columns.py +++ b/lib/galaxy/model/migrate/versions/0102_add_tool_dependency_status_columns.py @@ -11,13 +11,13 @@ # Need our custom types, but don't import anything else from model from galaxy.model.custom_types import TrimmedString -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) -handler = logging.StreamHandler( sys.stdout ) +handler = logging.StreamHandler(sys.stdout) format = "%(name)s %(levelname)s %(asctime)s %(message)s" -formatter = logging.Formatter( format ) -handler.setFormatter( formatter ) -log.addHandler( handler ) +formatter = logging.Formatter(format) +handler.setFormatter(formatter) +log.addHandler(handler) metadata = MetaData() @@ -26,19 +26,19 @@ def upgrade(migrate_engine): metadata.bind = migrate_engine print(__doc__) metadata.reflect() - ToolDependency_table = Table( "tool_dependency", metadata, autoload=True ) + ToolDependency_table = Table("tool_dependency", metadata, autoload=True) if migrate_engine.name == 'sqlite': - col = Column( "status", TrimmedString( 255 )) + col = Column("status", TrimmedString(255)) else: - col = Column( "status", TrimmedString( 255 ), nullable=False) + col = Column("status", TrimmedString(255), nullable=False) try: - col.create( ToolDependency_table ) + col.create(ToolDependency_table) assert col is ToolDependency_table.c.status except Exception: log.exception("Adding status column to the tool_dependency table failed.") - col = Column( "error_message", TEXT ) + col = Column("error_message", TEXT) try: - col.create( ToolDependency_table ) + col.create(ToolDependency_table) assert col is ToolDependency_table.c.error_message except Exception: log.exception("Adding error_message column to the tool_dependency table failed.") @@ -55,7 +55,7 @@ def upgrade(migrate_engine): def downgrade(migrate_engine): metadata.bind = migrate_engine metadata.reflect() - ToolDependency_table = Table( "tool_dependency", metadata, autoload=True ) + ToolDependency_table = Table("tool_dependency", metadata, autoload=True) try: ToolDependency_table.c.status.drop() except Exception: @@ -64,9 +64,9 @@ def downgrade(migrate_engine): ToolDependency_table.c.error_message.drop() except Exception: log.exception("Dropping column error_message from the tool_dependency table failed.") - col = Column( "uninstalled", Boolean, default=False ) + col = Column("uninstalled", Boolean, default=False) try: - col.create( ToolDependency_table ) + col.create(ToolDependency_table) assert col is ToolDependency_table.c.uninstalled except Exception: log.exception("Adding uninstalled column to the tool_dependency table failed.") diff --git a/lib/galaxy/model/migrate/versions/0103_add_tool_shed_repository_status_columns.py b/lib/galaxy/model/migrate/versions/0103_add_tool_shed_repository_status_columns.py index 20f5b4682e95..b549fe2c927e 100644 --- a/lib/galaxy/model/migrate/versions/0103_add_tool_shed_repository_status_columns.py +++ b/lib/galaxy/model/migrate/versions/0103_add_tool_shed_repository_status_columns.py @@ -8,7 +8,7 @@ # Need our custom types, but don't import anything else from model from galaxy.model.custom_types import TrimmedString -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() @@ -16,37 +16,37 @@ def upgrade(migrate_engine): metadata.bind = migrate_engine print(__doc__) metadata.reflect() - ToolShedRepository_table = Table( "tool_shed_repository", metadata, autoload=True ) + ToolShedRepository_table = Table("tool_shed_repository", metadata, autoload=True) # Add the status column to the tool_shed_repository table. - col = Column( "status", TrimmedString( 255 ) ) + col = Column("status", TrimmedString(255)) try: - col.create( ToolShedRepository_table ) + col.create(ToolShedRepository_table) assert col is ToolShedRepository_table.c.status except Exception: log.exception("Adding status column to the tool_shed_repository table failed.") # Add the error_message column to the tool_shed_repository table. - col = Column( "error_message", TEXT ) + col = Column("error_message", TEXT) try: - col.create( ToolShedRepository_table ) + col.create(ToolShedRepository_table) assert col is ToolShedRepository_table.c.error_message except Exception: log.exception("Adding error_message column to the tool_shed_repository table failed.") # Update the status column value for tool_shed_repositories to the default value 'Installed'. cmd = "UPDATE tool_shed_repository SET status = 'Installed';" try: - migrate_engine.execute( cmd ) + migrate_engine.execute(cmd) except Exception: log.exception("Exception executing SQL command: %s", cmd) # Update the status column for tool_shed_repositories that have been uninstalled. cmd = "UPDATE tool_shed_repository SET status = 'Uninstalled' WHERE uninstalled;" try: - migrate_engine.execute( cmd ) + migrate_engine.execute(cmd) except Exception: log.exception("Exception executing SQL command: %s", cmd) # Update the status column for tool_shed_repositories that have been deactivated. cmd = "UPDATE tool_shed_repository SET status = 'Deactivated' where deleted and not uninstalled;" try: - migrate_engine.execute( cmd ) + migrate_engine.execute(cmd) except Exception: log.exception("Exception executing SQL command: %s", cmd) @@ -54,7 +54,7 @@ def upgrade(migrate_engine): def downgrade(migrate_engine): metadata.bind = migrate_engine metadata.reflect() - ToolShedRepository_table = Table( "tool_shed_repository", metadata, autoload=True ) + ToolShedRepository_table = Table("tool_shed_repository", metadata, autoload=True) try: ToolShedRepository_table.c.status.drop() except Exception: diff --git a/lib/galaxy/model/migrate/versions/0104_update_genome_downloader_job_parameters.py b/lib/galaxy/model/migrate/versions/0104_update_genome_downloader_job_parameters.py index f6e06f7265bc..ad02c4c68788 100644 --- a/lib/galaxy/model/migrate/versions/0104_update_genome_downloader_job_parameters.py +++ b/lib/galaxy/model/migrate/versions/0104_update_genome_downloader_job_parameters.py @@ -13,41 +13,41 @@ from galaxy.util.bunch import Bunch now = datetime.datetime.utcnow -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) -handler = logging.StreamHandler( sys.stdout ) +handler = logging.StreamHandler(sys.stdout) format = "%(name)s %(levelname)s %(asctime)s %(message)s" -formatter = logging.Formatter( format ) -handler.setFormatter( formatter ) -log.addHandler( handler ) +formatter = logging.Formatter(format) +handler.setFormatter(formatter) +log.addHandler(handler) metadata = MetaData() -context = scoped_session( sessionmaker( autoflush=False, autocommit=True ) ) +context = scoped_session(sessionmaker(autoflush=False, autocommit=True)) -class DeferredJob( object ): - states = Bunch( NEW='new', - WAITING='waiting', - QUEUED='queued', - RUNNING='running', - OK='ok', - ERROR='error' ) +class DeferredJob(object): + states = Bunch(NEW='new', + WAITING='waiting', + QUEUED='queued', + RUNNING='running', + OK='ok', + ERROR='error') - def __init__( self, state=None, plugin=None, params=None ): + def __init__(self, state=None, plugin=None, params=None): self.state = state self.plugin = plugin self.params = params -DeferredJob.table = Table( "deferred_job", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "state", String( 64 ), index=True ), - Column( "plugin", String( 128 ), index=True ), - Column( "params", JSONType ) ) +DeferredJob.table = Table("deferred_job", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("state", String(64), index=True), + Column("plugin", String(128), index=True), + Column("params", JSONType)) -mapper( DeferredJob, DeferredJob.table, properties={} ) +mapper(DeferredJob, DeferredJob.table, properties={}) def upgrade(migrate_engine): @@ -55,17 +55,17 @@ def upgrade(migrate_engine): liftoverjobs = dict() - jobs = context.query( DeferredJob ).filter_by( plugin='LiftOverTransferPlugin' ).all() + jobs = context.query(DeferredJob).filter_by(plugin='LiftOverTransferPlugin').all() for job in jobs: - if job.params[ 'parentjob' ] not in liftoverjobs: - liftoverjobs[ job.params[ 'parentjob' ] ] = [] - liftoverjobs[ job.params[ 'parentjob'] ].append( job.id ) + if job.params['parentjob'] not in liftoverjobs: + liftoverjobs[job.params['parentjob']] = [] + liftoverjobs[job.params['parentjob']].append(job.id) for parent in liftoverjobs: - lifts = liftoverjobs[ parent ] - deferred = context.query( DeferredJob ).filter_by( id=parent ).first() - deferred.params[ 'liftover' ] = lifts + lifts = liftoverjobs[parent] + deferred = context.query(DeferredJob).filter_by(id=parent).first() + deferred.params['liftover'] = lifts context.flush() @@ -73,15 +73,15 @@ def upgrade(migrate_engine): def downgrade(migrate_engine): metadata.bind = migrate_engine - jobs = context.query( DeferredJob ).filter_by( plugin='GenomeTransferPlugin' ).all() + jobs = context.query(DeferredJob).filter_by(plugin='GenomeTransferPlugin').all() for job in jobs: - if len( job.params[ 'liftover' ] ) == 0: + if len(job.params['liftover']) == 0: continue transfers = [] - for lift in job.params[ 'liftover' ]: - liftoverjob = context.query( DeferredJob ).filter_by( id=lift ).first() - transfers.append( liftoverjob.params[ 'transfer_job_id' ] ) - job.params[ 'liftover' ] = transfers + for lift in job.params['liftover']: + liftoverjob = context.query(DeferredJob).filter_by(id=lift).first() + transfers.append(liftoverjob.params['transfer_job_id']) + job.params['liftover'] = transfers context.flush() diff --git a/lib/galaxy/model/migrate/versions/0105_add_cleanup_event_table.py b/lib/galaxy/model/migrate/versions/0105_add_cleanup_event_table.py index d5469076dede..c2caaf7fb80c 100644 --- a/lib/galaxy/model/migrate/versions/0105_add_cleanup_event_table.py +++ b/lib/galaxy/model/migrate/versions/0105_add_cleanup_event_table.py @@ -12,75 +12,75 @@ from galaxy.model.custom_types import TrimmedString now = datetime.datetime.utcnow -log = logging.getLogger( __name__ ) -log.setLevel( logging.DEBUG ) -handler = logging.StreamHandler( sys.stdout ) +log = logging.getLogger(__name__) +log.setLevel(logging.DEBUG) +handler = logging.StreamHandler(sys.stdout) format = "%(name)s %(levelname)s %(asctime)s %(message)s" -formatter = logging.Formatter( format ) -handler.setFormatter( formatter ) -log.addHandler( handler ) +formatter = logging.Formatter(format) +handler.setFormatter(formatter) +log.addHandler(handler) metadata = MetaData() # New table to log cleanup events -CleanupEvent_table = Table( "cleanup_event", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "message", TrimmedString( 1024 ) ) ) - -CleanupEventDatasetAssociation_table = Table( "cleanup_event_dataset_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "cleanup_event_id", Integer, ForeignKey( "cleanup_event.id" ), index=True, nullable=True ), - Column( "dataset_id", Integer, ForeignKey( "dataset.id" ), index=True ) ) - -CleanupEventMetadataFileAssociation_table = Table( "cleanup_event_metadata_file_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "cleanup_event_id", Integer, ForeignKey( "cleanup_event.id" ), index=True, nullable=True ), - Column( "metadata_file_id", Integer, ForeignKey( "metadata_file.id" ), index=True ) ) - -CleanupEventHistoryAssociation_table = Table( "cleanup_event_history_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "cleanup_event_id", Integer, ForeignKey( "cleanup_event.id" ), index=True, nullable=True ), - Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ) ) - -CleanupEventHistoryDatasetAssociationAssociation_table = Table( "cleanup_event_hda_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "cleanup_event_id", Integer, ForeignKey( "cleanup_event.id" ), index=True, nullable=True ), - Column( "hda_id", Integer, ForeignKey( "history_dataset_association.id" ), index=True ) ) - -CleanupEventLibraryAssociation_table = Table( "cleanup_event_library_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "cleanup_event_id", Integer, ForeignKey( "cleanup_event.id" ), index=True, nullable=True ), - Column( "library_id", Integer, ForeignKey( "library.id" ), index=True ) ) - -CleanupEventLibraryFolderAssociation_table = Table( "cleanup_event_library_folder_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "cleanup_event_id", Integer, ForeignKey( "cleanup_event.id" ), index=True, nullable=True ), - Column( "library_folder_id", Integer, ForeignKey( "library_folder.id" ), index=True ) ) - -CleanupEventLibraryDatasetAssociation_table = Table( "cleanup_event_library_dataset_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "cleanup_event_id", Integer, ForeignKey( "cleanup_event.id" ), index=True, nullable=True ), - Column( "library_dataset_id", Integer, ForeignKey( "library_dataset.id" ), index=True ) ) - -CleanupEventLibraryDatasetDatasetAssociationAssociation_table = Table( "cleanup_event_ldda_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "cleanup_event_id", Integer, ForeignKey( "cleanup_event.id" ), index=True, nullable=True ), - Column( "ldda_id", Integer, ForeignKey( "library_dataset_dataset_association.id" ), index=True ) ) - -CleanupEventImplicitlyConvertedDatasetAssociationAssociation_table = Table( "cleanup_event_icda_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "cleanup_event_id", Integer, ForeignKey( "cleanup_event.id" ), index=True, nullable=True ), - Column( "icda_id", Integer, ForeignKey( "implicitly_converted_dataset_association.id" ), index=True ) ) +CleanupEvent_table = Table("cleanup_event", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("message", TrimmedString(1024))) + +CleanupEventDatasetAssociation_table = Table("cleanup_event_dataset_association", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("cleanup_event_id", Integer, ForeignKey("cleanup_event.id"), index=True, nullable=True), + Column("dataset_id", Integer, ForeignKey("dataset.id"), index=True)) + +CleanupEventMetadataFileAssociation_table = Table("cleanup_event_metadata_file_association", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("cleanup_event_id", Integer, ForeignKey("cleanup_event.id"), index=True, nullable=True), + Column("metadata_file_id", Integer, ForeignKey("metadata_file.id"), index=True)) + +CleanupEventHistoryAssociation_table = Table("cleanup_event_history_association", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("cleanup_event_id", Integer, ForeignKey("cleanup_event.id"), index=True, nullable=True), + Column("history_id", Integer, ForeignKey("history.id"), index=True)) + +CleanupEventHistoryDatasetAssociationAssociation_table = Table("cleanup_event_hda_association", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("cleanup_event_id", Integer, ForeignKey("cleanup_event.id"), index=True, nullable=True), + Column("hda_id", Integer, ForeignKey("history_dataset_association.id"), index=True)) + +CleanupEventLibraryAssociation_table = Table("cleanup_event_library_association", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("cleanup_event_id", Integer, ForeignKey("cleanup_event.id"), index=True, nullable=True), + Column("library_id", Integer, ForeignKey("library.id"), index=True)) + +CleanupEventLibraryFolderAssociation_table = Table("cleanup_event_library_folder_association", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("cleanup_event_id", Integer, ForeignKey("cleanup_event.id"), index=True, nullable=True), + Column("library_folder_id", Integer, ForeignKey("library_folder.id"), index=True)) + +CleanupEventLibraryDatasetAssociation_table = Table("cleanup_event_library_dataset_association", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("cleanup_event_id", Integer, ForeignKey("cleanup_event.id"), index=True, nullable=True), + Column("library_dataset_id", Integer, ForeignKey("library_dataset.id"), index=True)) + +CleanupEventLibraryDatasetDatasetAssociationAssociation_table = Table("cleanup_event_ldda_association", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("cleanup_event_id", Integer, ForeignKey("cleanup_event.id"), index=True, nullable=True), + Column("ldda_id", Integer, ForeignKey("library_dataset_dataset_association.id"), index=True)) + +CleanupEventImplicitlyConvertedDatasetAssociationAssociation_table = Table("cleanup_event_icda_association", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("cleanup_event_id", Integer, ForeignKey("cleanup_event.id"), index=True, nullable=True), + Column("icda_id", Integer, ForeignKey("implicitly_converted_dataset_association.id"), index=True)) def upgrade(migrate_engine): diff --git a/lib/galaxy/model/migrate/versions/0106_add_missing_indexes.py b/lib/galaxy/model/migrate/versions/0106_add_missing_indexes.py index a872e2d697e3..946be8e433a5 100644 --- a/lib/galaxy/model/migrate/versions/0106_add_missing_indexes.py +++ b/lib/galaxy/model/migrate/versions/0106_add_missing_indexes.py @@ -8,45 +8,45 @@ from sqlalchemy import Index, MetaData, Table from sqlalchemy.engine import reflection -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) metadata = MetaData() indexes = ( - ( "ix_metadata_file_lda_id", 'metadata_file', 'lda_id' ), # 0003 - ( "ix_history_importable", 'history', 'importable' ), # 0007 - ( "ix_sample_bar_code", 'sample', 'bar_code' ), # 0009 - ( "ix_request_type_deleted", 'request_type', 'deleted' ), # 0012 - ( "ix_galaxy_user_username", 'galaxy_user', 'username' ), # 0014 - ( "ix_form_definition_type", 'form_definition', 'type' ), # 0019 - ( "ix_form_definition_layout", 'form_definition', 'layout' ), # 0019 - ( "ix_job_library_folder_id", 'job', 'library_folder_id' ), # 0020 - ( "ix_page_published", 'page', 'published' ), # 0023 - ( "ix_page_deleted", 'page', 'deleted' ), # 0023 - ( "ix_galaxy_user_form_values_id", 'galaxy_user', 'form_values_id' ), # 0025 - ( "ix_lia_deleted", 'library_info_association', 'deleted' ), # 0036 - ( "ix_lfia_deleted", 'library_folder_info_association', 'deleted' ), # 0036 - ( "ix_lddia_deleted", 'library_dataset_dataset_info_association', 'deleted' ), # 0036 - ( "ix_sample_library_id", 'sample', 'library_id' ), # 0037 - ( "ix_sample_folder_id", 'sample', 'folder_id' ), # 0037 - ( "ix_lia_inheritable", 'library_info_association', 'inheritable' ), # 0038 - ( "ix_lfia_inheritable", 'library_folder_info_association', 'inheritable' ), # 0038 - ( "ix_job_imported", 'job', 'imported' ), # 0051 - ( "ix_request_notification", 'request', 'notification' ), # 0057 - ( "ix_sd_external_service_id", 'sample_dataset', 'external_service_id' ), # 0068 - ( "ix_icda_ldda_parent_id", 'implicitly_converted_dataset_association', 'ldda_parent_id' ), # 0073 - ( "ix_library_dataset_purged", 'library_dataset', 'purged' ), # 0074 - ( "ix_run_subindex", 'run', 'subindex' ), # 0075 - ( "ix_history_dataset_association_purged", 'history_dataset_association', 'purged' ), # 0078 - ( "ix_galaxy_user_disk_usage", 'galaxy_user', 'disk_usage' ), # 0078 - ( "ix_galaxy_session_disk_usage", 'galaxy_session', 'disk_usage' ), # 0078 - ( "ix_icda_ldda_id", 'implicitly_converted_dataset_association', 'ldda_id' ), # 0084 - ( "ix_tsr_includes_datatypes", 'tool_shed_repository', 'includes_datatypes' ), # 0086 - ( "ix_dataset_object_store_id", 'dataset', 'object_store_id' ), # 0089 - ( "ix_job_object_store_id", 'job', 'object_store_id' ), # 0089 - ( "ix_metadata_file_object_store_id", 'metadata_file', 'object_store_id' ), # 0089 - ( "ix_job_handler", 'job', 'handler' ), # 0094 - ( "ix_galaxy_user_email", 'galaxy_user', 'email' ) # 0106 + ("ix_metadata_file_lda_id", 'metadata_file', 'lda_id'), # 0003 + ("ix_history_importable", 'history', 'importable'), # 0007 + ("ix_sample_bar_code", 'sample', 'bar_code'), # 0009 + ("ix_request_type_deleted", 'request_type', 'deleted'), # 0012 + ("ix_galaxy_user_username", 'galaxy_user', 'username'), # 0014 + ("ix_form_definition_type", 'form_definition', 'type'), # 0019 + ("ix_form_definition_layout", 'form_definition', 'layout'), # 0019 + ("ix_job_library_folder_id", 'job', 'library_folder_id'), # 0020 + ("ix_page_published", 'page', 'published'), # 0023 + ("ix_page_deleted", 'page', 'deleted'), # 0023 + ("ix_galaxy_user_form_values_id", 'galaxy_user', 'form_values_id'), # 0025 + ("ix_lia_deleted", 'library_info_association', 'deleted'), # 0036 + ("ix_lfia_deleted", 'library_folder_info_association', 'deleted'), # 0036 + ("ix_lddia_deleted", 'library_dataset_dataset_info_association', 'deleted'), # 0036 + ("ix_sample_library_id", 'sample', 'library_id'), # 0037 + ("ix_sample_folder_id", 'sample', 'folder_id'), # 0037 + ("ix_lia_inheritable", 'library_info_association', 'inheritable'), # 0038 + ("ix_lfia_inheritable", 'library_folder_info_association', 'inheritable'), # 0038 + ("ix_job_imported", 'job', 'imported'), # 0051 + ("ix_request_notification", 'request', 'notification'), # 0057 + ("ix_sd_external_service_id", 'sample_dataset', 'external_service_id'), # 0068 + ("ix_icda_ldda_parent_id", 'implicitly_converted_dataset_association', 'ldda_parent_id'), # 0073 + ("ix_library_dataset_purged", 'library_dataset', 'purged'), # 0074 + ("ix_run_subindex", 'run', 'subindex'), # 0075 + ("ix_history_dataset_association_purged", 'history_dataset_association', 'purged'), # 0078 + ("ix_galaxy_user_disk_usage", 'galaxy_user', 'disk_usage'), # 0078 + ("ix_galaxy_session_disk_usage", 'galaxy_session', 'disk_usage'), # 0078 + ("ix_icda_ldda_id", 'implicitly_converted_dataset_association', 'ldda_id'), # 0084 + ("ix_tsr_includes_datatypes", 'tool_shed_repository', 'includes_datatypes'), # 0086 + ("ix_dataset_object_store_id", 'dataset', 'object_store_id'), # 0089 + ("ix_job_object_store_id", 'job', 'object_store_id'), # 0089 + ("ix_metadata_file_object_store_id", 'metadata_file', 'object_store_id'), # 0089 + ("ix_job_handler", 'job', 'handler'), # 0094 + ("ix_galaxy_user_email", 'galaxy_user', 'email') # 0106 ) @@ -59,9 +59,9 @@ def upgrade(migrate_engine): for ix, table, col in indexes: try: log.debug("Creating index '%s' on column '%s' in table '%s'" % (ix, col, table)) - t = Table( table, metadata, autoload=True ) + t = Table(table, metadata, autoload=True) if ix not in [ins_ix.get('name', None) for ins_ix in insp.get_indexes(table)]: - Index( ix, t.c[col] ).create() + Index(ix, t.c[col]).create() else: pass # Index already exists, don't recreate. except Exception: @@ -75,7 +75,7 @@ def downgrade(migrate_engine): # Drop indexes for ix, table, col in indexes: try: - t = Table( table, metadata, autoload=True ) - Index( ix, t.c[col] ).drop() + t = Table(table, metadata, autoload=True) + Index(ix, t.c[col]).drop() except Exception: log.exception("Unable to drop index '%s'.", ix) diff --git a/lib/galaxy/model/migrate/versions/0107_add_exit_code_to_job_and_task.py b/lib/galaxy/model/migrate/versions/0107_add_exit_code_to_job_and_task.py index d91a0686a587..81eaaf5dc0ee 100644 --- a/lib/galaxy/model/migrate/versions/0107_add_exit_code_to_job_and_task.py +++ b/lib/galaxy/model/migrate/versions/0107_add_exit_code_to_job_and_task.py @@ -7,13 +7,13 @@ from sqlalchemy import Column, Integer, MetaData, Table -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() # There was a bug when only one column was used for both tables, # so create separate columns. -exit_code_job_col = Column( "exit_code", Integer, nullable=True ) -exit_code_task_col = Column( "exit_code", Integer, nullable=True ) +exit_code_job_col = Column("exit_code", Integer, nullable=True) +exit_code_task_col = Column("exit_code", Integer, nullable=True) def upgrade(migrate_engine): @@ -23,16 +23,16 @@ def upgrade(migrate_engine): # Add the exit_code column to the Job table. try: - job_table = Table( "job", metadata, autoload=True ) - exit_code_job_col.create( job_table ) + job_table = Table("job", metadata, autoload=True) + exit_code_job_col.create(job_table) assert exit_code_job_col is job_table.c.exit_code except Exception: log.exception("Adding column 'exit_code' to job table failed.") # Add the exit_code column to the Task table. try: - task_table = Table( "task", metadata, autoload=True ) - exit_code_task_col.create( task_table ) + task_table = Table("task", metadata, autoload=True) + exit_code_task_col.create(task_table) assert exit_code_task_col is task_table.c.exit_code except Exception: log.exception("Adding column 'exit_code' to task table failed.") @@ -44,7 +44,7 @@ def downgrade(migrate_engine): # Drop the Job table's exit_code column. try: - job_table = Table( "job", metadata, autoload=True ) + job_table = Table("job", metadata, autoload=True) exit_code_col = job_table.c.exit_code exit_code_col.drop() except Exception: @@ -52,7 +52,7 @@ def downgrade(migrate_engine): # Drop the Job table's exit_code column. try: - task_table = Table( "task", metadata, autoload=True ) + task_table = Table("task", metadata, autoload=True) exit_code_col = task_table.c.exit_code exit_code_col.drop() except Exception: diff --git a/lib/galaxy/model/migrate/versions/0108_add_extended_metadata.py b/lib/galaxy/model/migrate/versions/0108_add_extended_metadata.py index 3debc9d7ebe7..3ed755860932 100644 --- a/lib/galaxy/model/migrate/versions/0108_add_extended_metadata.py +++ b/lib/galaxy/model/migrate/versions/0108_add_extended_metadata.py @@ -9,23 +9,23 @@ from galaxy.model.custom_types import JSONType -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() ExtendedMetadata_table = Table("extended_metadata", metadata, - Column( "id", Integer, primary_key=True ), - Column( "data", JSONType ) ) + Column("id", Integer, primary_key=True), + Column("data", JSONType)) ExtendedMetadataIndex_table = Table("extended_metadata_index", metadata, - Column( "id", Integer, primary_key=True ), - Column( "extended_metadata_id", Integer, ForeignKey("extended_metadata.id", - onupdate="CASCADE", - ondelete="CASCADE" ), - index=True ), - Column( "path", String( 255 )), - Column( "value", TEXT)) + Column("id", Integer, primary_key=True), + Column("extended_metadata_id", Integer, ForeignKey("extended_metadata.id", + onupdate="CASCADE", + ondelete="CASCADE"), + index=True), + Column("path", String(255)), + Column("value", TEXT)) -extended_metadata_ldda_col = Column( "extended_metadata_id", Integer, ForeignKey("extended_metadata.id"), nullable=True ) +extended_metadata_ldda_col = Column("extended_metadata_id", Integer, ForeignKey("extended_metadata.id"), nullable=True) def upgrade(migrate_engine): @@ -42,8 +42,8 @@ def upgrade(migrate_engine): log.exception("Could not create ExtendedMetadataIndex Table.") # Add the extended_metadata_id to the ldda table try: - ldda_table = Table( "library_dataset_dataset_association", metadata, autoload=True ) - extended_metadata_ldda_col.create( ldda_table ) + ldda_table = Table("library_dataset_dataset_association", metadata, autoload=True) + extended_metadata_ldda_col.create(ldda_table) assert extended_metadata_ldda_col is ldda_table.c.extended_metadata_id except Exception: log.exception("Adding column 'extended_metadata_id' to library_dataset_dataset_association table failed.") @@ -54,7 +54,7 @@ def downgrade(migrate_engine): metadata.reflect() # Drop the LDDA table's extended metadata ID column. try: - ldda_table = Table( "library_dataset_dataset_association", metadata, autoload=True ) + ldda_table = Table("library_dataset_dataset_association", metadata, autoload=True) extended_metadata_id = ldda_table.c.extended_metadata_id extended_metadata_id.drop() except Exception: diff --git a/lib/galaxy/model/migrate/versions/0109_add_repository_dependency_tables.py b/lib/galaxy/model/migrate/versions/0109_add_repository_dependency_tables.py index a3c16e6d6836..bd0a9c8f60c4 100644 --- a/lib/galaxy/model/migrate/versions/0109_add_repository_dependency_tables.py +++ b/lib/galaxy/model/migrate/versions/0109_add_repository_dependency_tables.py @@ -10,28 +10,28 @@ from sqlalchemy import Column, DateTime, ForeignKey, Integer, MetaData, Table now = datetime.datetime.utcnow -log = logging.getLogger( __name__ ) -log.setLevel( logging.DEBUG ) -handler = logging.StreamHandler( sys.stdout ) +log = logging.getLogger(__name__) +log.setLevel(logging.DEBUG) +handler = logging.StreamHandler(sys.stdout) format = "%(name)s %(levelname)s %(asctime)s %(message)s" -formatter = logging.Formatter( format ) -handler.setFormatter( formatter ) -log.addHandler( handler ) +formatter = logging.Formatter(format) +handler.setFormatter(formatter) +log.addHandler(handler) metadata = MetaData() -RepositoryDependency_table = Table( "repository_dependency", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "tool_shed_repository_id", Integer, ForeignKey( "tool_shed_repository.id" ), index=True, nullable=False ) ) - -RepositoryRepositoryDependencyAssociation_table = Table( "repository_repository_dependency_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "tool_shed_repository_id", Integer, ForeignKey( "tool_shed_repository.id" ), index=True ), - Column( "repository_dependency_id", Integer, ForeignKey( "repository_dependency.id" ), index=True ) ) +RepositoryDependency_table = Table("repository_dependency", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("tool_shed_repository_id", Integer, ForeignKey("tool_shed_repository.id"), index=True, nullable=False)) + +RepositoryRepositoryDependencyAssociation_table = Table("repository_repository_dependency_association", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("tool_shed_repository_id", Integer, ForeignKey("tool_shed_repository.id"), index=True), + Column("repository_dependency_id", Integer, ForeignKey("repository_dependency.id"), index=True)) def upgrade(migrate_engine): diff --git a/lib/galaxy/model/migrate/versions/0110_add_dataset_uuid.py b/lib/galaxy/model/migrate/versions/0110_add_dataset_uuid.py index e82ec0235577..0932fef81226 100644 --- a/lib/galaxy/model/migrate/versions/0110_add_dataset_uuid.py +++ b/lib/galaxy/model/migrate/versions/0110_add_dataset_uuid.py @@ -9,8 +9,8 @@ from galaxy.model.custom_types import UUIDType -log = logging.getLogger( __name__ ) -dataset_uuid_column = Column( "uuid", UUIDType, nullable=True ) +log = logging.getLogger(__name__) +dataset_uuid_column = Column("uuid", UUIDType, nullable=True) def upgrade(migrate_engine): @@ -21,8 +21,8 @@ def upgrade(migrate_engine): # Add the uuid colum to the dataset table try: - dataset_table = Table( "dataset", metadata, autoload=True ) - dataset_uuid_column.create( dataset_table ) + dataset_table = Table("dataset", metadata, autoload=True) + dataset_uuid_column.create(dataset_table) assert dataset_uuid_column is dataset_table.c.uuid except Exception: log.exception("Adding column 'uuid' to dataset table failed.") @@ -35,7 +35,7 @@ def downgrade(migrate_engine): # Drop the dataset table's uuid column. try: - dataset_table = Table( "dataset", metadata, autoload=True ) + dataset_table = Table("dataset", metadata, autoload=True) dataset_uuid = dataset_table.c.uuid dataset_uuid.drop() except Exception: diff --git a/lib/galaxy/model/migrate/versions/0111_add_job_destinations.py b/lib/galaxy/model/migrate/versions/0111_add_job_destinations.py index c3f2f1d93813..b3127bf99b2c 100644 --- a/lib/galaxy/model/migrate/versions/0111_add_job_destinations.py +++ b/lib/galaxy/model/migrate/versions/0111_add_job_destinations.py @@ -9,7 +9,7 @@ from galaxy.model.custom_types import JSONType -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) def upgrade(migrate_engine): @@ -17,18 +17,18 @@ def upgrade(migrate_engine): metadata = MetaData() metadata.bind = migrate_engine metadata.reflect() - Job_table = Table( "job", metadata, autoload=True ) + Job_table = Table("job", metadata, autoload=True) - c = Column( "destination_id", String( 255 ), nullable=True ) + c = Column("destination_id", String(255), nullable=True) try: - c.create( Job_table ) + c.create(Job_table) assert c is Job_table.c.destination_id except Exception: log.exception("Adding column 'destination_id' to job table failed.") - c = Column( "destination_params", JSONType, nullable=True ) + c = Column("destination_params", JSONType, nullable=True) try: - c.create( Job_table ) + c.create(Job_table) assert c is Job_table.c.destination_params except Exception: log.exception("Adding column 'destination_params' to job table failed.") @@ -38,7 +38,7 @@ def downgrade(migrate_engine): metadata = MetaData() metadata.bind = migrate_engine metadata.reflect() - Job_table = Table( "job", metadata, autoload=True ) + Job_table = Table("job", metadata, autoload=True) try: Job_table.c.destination_params.drop() diff --git a/lib/galaxy/model/migrate/versions/0112_add_data_manager_history_association_and_data_manager_job_association_tables.py b/lib/galaxy/model/migrate/versions/0112_add_data_manager_history_association_and_data_manager_job_association_tables.py index f613eff3eb72..9babafdfab80 100644 --- a/lib/galaxy/model/migrate/versions/0112_add_data_manager_history_association_and_data_manager_job_association_tables.py +++ b/lib/galaxy/model/migrate/versions/0112_add_data_manager_history_association_and_data_manager_job_association_tables.py @@ -9,23 +9,23 @@ from sqlalchemy import Column, DateTime, ForeignKey, Integer, MetaData, Table, TEXT now = datetime.datetime.utcnow -log = logging.getLogger( __name__ ) -log.setLevel( logging.DEBUG ) +log = logging.getLogger(__name__) +log.setLevel(logging.DEBUG) metadata = MetaData() -DataManagerHistoryAssociation_table = Table( "data_manager_history_association", metadata, - Column( "id", Integer, primary_key=True), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, index=True, default=now, onupdate=now ), - Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ) ) +DataManagerHistoryAssociation_table = Table("data_manager_history_association", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, index=True, default=now, onupdate=now), + Column("history_id", Integer, ForeignKey("history.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True)) -DataManagerJobAssociation_table = Table( "data_manager_job_association", metadata, - Column( "id", Integer, primary_key=True), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, index=True, default=now, onupdate=now ), - Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ), - Column( "data_manager_id", TEXT, index=True ) ) +DataManagerJobAssociation_table = Table("data_manager_job_association", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, index=True, default=now, onupdate=now), + Column("job_id", Integer, ForeignKey("job.id"), index=True), + Column("data_manager_id", TEXT, index=True)) def upgrade(migrate_engine): @@ -34,12 +34,12 @@ def upgrade(migrate_engine): metadata.reflect() try: DataManagerHistoryAssociation_table.create() - log.debug( "Created data_manager_history_association table" ) + log.debug("Created data_manager_history_association table") except Exception: log.exception("Creating data_manager_history_association table failed.") try: DataManagerJobAssociation_table.create() - log.debug( "Created data_manager_job_association table" ) + log.debug("Created data_manager_job_association table") except Exception: log.exception("Creating data_manager_job_association table failed.") @@ -49,11 +49,11 @@ def downgrade(migrate_engine): metadata.reflect() try: DataManagerHistoryAssociation_table.drop() - log.debug( "Dropped data_manager_history_association table" ) + log.debug("Dropped data_manager_history_association table") except Exception: log.exception("Dropping data_manager_history_association table failed.") try: DataManagerJobAssociation_table.drop() - log.debug( "Dropped data_manager_job_association table" ) + log.debug("Dropped data_manager_job_association table") except Exception: log.exception("Dropping data_manager_job_association table failed.") diff --git a/lib/galaxy/model/migrate/versions/0113_update_migrate_tools_table.py b/lib/galaxy/model/migrate/versions/0113_update_migrate_tools_table.py index 8dea87bbb9e5..794e1273e3b7 100644 --- a/lib/galaxy/model/migrate/versions/0113_update_migrate_tools_table.py +++ b/lib/galaxy/model/migrate/versions/0113_update_migrate_tools_table.py @@ -6,13 +6,13 @@ import logging import sys -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) -handler = logging.StreamHandler( sys.stdout ) +handler = logging.StreamHandler(sys.stdout) format = "%(name)s %(levelname)s %(asctime)s %(message)s" -formatter = logging.Formatter( format ) -handler.setFormatter( formatter ) -log.addHandler( handler ) +formatter = logging.Formatter(format) +handler.setFormatter(formatter) +log.addHandler(handler) def upgrade(migrate_engine): @@ -20,7 +20,7 @@ def upgrade(migrate_engine): # Create the table. try: cmd = "UPDATE migrate_tools set repository_path='lib/galaxy/tool_shed/migrate';" - migrate_engine.execute( cmd ) + migrate_engine.execute(cmd) except Exception: log.exception("Updating migrate_tools.repository_path column to point to the new location lib/tool_shed/galaxy_install/migrate failed.") @@ -28,6 +28,6 @@ def upgrade(migrate_engine): def downgrade(migrate_engine): try: cmd = "UPDATE migrate_tools set repository_path='lib/galaxy/tool_shed/migrate';" - migrate_engine.execute( cmd ) + migrate_engine.execute(cmd) except Exception: log.exception("Updating migrate_tools.repository_path column to point to the old location lib/galaxy/tool_shed/migrate failed.") diff --git a/lib/galaxy/model/migrate/versions/0114_update_migrate_tools_table_again.py b/lib/galaxy/model/migrate/versions/0114_update_migrate_tools_table_again.py index 92822862545a..4124dcd507cc 100644 --- a/lib/galaxy/model/migrate/versions/0114_update_migrate_tools_table_again.py +++ b/lib/galaxy/model/migrate/versions/0114_update_migrate_tools_table_again.py @@ -6,13 +6,13 @@ import logging import sys -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) -handler = logging.StreamHandler( sys.stdout ) +handler = logging.StreamHandler(sys.stdout) format = "%(name)s %(levelname)s %(asctime)s %(message)s" -formatter = logging.Formatter( format ) -handler.setFormatter( formatter ) -log.addHandler( handler ) +formatter = logging.Formatter(format) +handler.setFormatter(formatter) +log.addHandler(handler) def upgrade(migrate_engine): @@ -20,7 +20,7 @@ def upgrade(migrate_engine): # Create the table. try: cmd = "UPDATE migrate_tools set repository_path='lib/tool_shed/galaxy_install/migrate';" - migrate_engine.execute( cmd ) + migrate_engine.execute(cmd) except Exception: log.exception("Updating migrate_tools.repository_path column to point to the new location lib/tool_shed/galaxy_install/migrate failed.") @@ -28,6 +28,6 @@ def upgrade(migrate_engine): def downgrade(migrate_engine): try: cmd = "UPDATE migrate_tools set repository_path='lib/galaxy/tool_shed/migrate';" - migrate_engine.execute( cmd ) + migrate_engine.execute(cmd) except Exception: log.exception("Updating migrate_tools.repository_path column to point to the old location lib/galaxy/tool_shed/migrate failed.") diff --git a/lib/galaxy/model/migrate/versions/0115_longer_user_password_field.py b/lib/galaxy/model/migrate/versions/0115_longer_user_password_field.py index e2b1bdaf897f..9e6f25a424cc 100644 --- a/lib/galaxy/model/migrate/versions/0115_longer_user_password_field.py +++ b/lib/galaxy/model/migrate/versions/0115_longer_user_password_field.py @@ -5,16 +5,16 @@ from sqlalchemy import MetaData, String, Table -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -def upgrade( migrate_engine ): - meta = MetaData( bind=migrate_engine ) - user = Table( 'galaxy_user', meta, autoload=True ) +def upgrade(migrate_engine): + meta = MetaData(bind=migrate_engine) + user = Table('galaxy_user', meta, autoload=True) try: user.c.password.alter(type=String(255)) except: - log.exception( "Altering password column failed" ) + log.exception("Altering password column failed") def downgrade(migrate_engine): diff --git a/lib/galaxy/model/migrate/versions/0116_drop_update_available_col_add_tool_shed_status_col.py b/lib/galaxy/model/migrate/versions/0116_drop_update_available_col_add_tool_shed_status_col.py index 05a9d9088b97..efa6c840388d 100644 --- a/lib/galaxy/model/migrate/versions/0116_drop_update_available_col_add_tool_shed_status_col.py +++ b/lib/galaxy/model/migrate/versions/0116_drop_update_available_col_add_tool_shed_status_col.py @@ -11,13 +11,13 @@ from galaxy.model.custom_types import JSONType -log = logging.getLogger( __name__ ) -log.setLevel( logging.DEBUG ) -handler = logging.StreamHandler( sys.stdout ) +log = logging.getLogger(__name__) +log.setLevel(logging.DEBUG) +handler = logging.StreamHandler(sys.stdout) format = "%(name)s %(levelname)s %(asctime)s %(message)s" -formatter = logging.Formatter( format ) -handler.setFormatter( formatter ) -log.addHandler( handler ) +formatter = logging.Formatter(format) +handler.setFormatter(formatter) +log.addHandler(handler) metadata = MetaData() @@ -31,15 +31,15 @@ def engine_false(migrate_engine): raise Exception('Unknown database type: %s' % migrate_engine.name) -def upgrade( migrate_engine ): +def upgrade(migrate_engine): metadata.bind = migrate_engine print(__doc__) metadata.reflect() try: - ToolShedRepository_table = Table( "tool_shed_repository", metadata, autoload=True ) + ToolShedRepository_table = Table("tool_shed_repository", metadata, autoload=True) except NoSuchTableError: ToolShedRepository_table = None - log.debug( "Failed loading table tool_shed_repository" ) + log.debug("Failed loading table tool_shed_repository") if ToolShedRepository_table is not None: # For some unknown reason it is no longer possible to drop a column in a migration script if using the sqlite database. if migrate_engine.name != 'sqlite': @@ -48,22 +48,22 @@ def upgrade( migrate_engine ): col.drop() except Exception: log.exception("Dropping column update_available from the tool_shed_repository table failed.") - c = Column( "tool_shed_status", JSONType, nullable=True ) + c = Column("tool_shed_status", JSONType, nullable=True) try: - c.create( ToolShedRepository_table ) + c.create(ToolShedRepository_table) assert c is ToolShedRepository_table.c.tool_shed_status except Exception: log.exception("Adding tool_shed_status column to the tool_shed_repository table failed.") -def downgrade( migrate_engine ): +def downgrade(migrate_engine): metadata.bind = migrate_engine metadata.reflect() try: - ToolShedRepository_table = Table( "tool_shed_repository", metadata, autoload=True ) + ToolShedRepository_table = Table("tool_shed_repository", metadata, autoload=True) except NoSuchTableError: ToolShedRepository_table = None - log.debug( "Failed loading table tool_shed_repository" ) + log.debug("Failed loading table tool_shed_repository") if ToolShedRepository_table is not None: # For some unknown reason it is no longer possible to drop a column in a migration script if using the sqlite database. if migrate_engine.name != 'sqlite': @@ -72,10 +72,10 @@ def downgrade( migrate_engine ): col.drop() except Exception: log.exception("Dropping column tool_shed_status from the tool_shed_repository table failed.") - c = Column( "update_available", Boolean, default=False ) + c = Column("update_available", Boolean, default=False) try: - c.create( ToolShedRepository_table ) + c.create(ToolShedRepository_table) assert c is ToolShedRepository_table.c.update_available - migrate_engine.execute( "UPDATE tool_shed_repository SET update_available=%s" % engine_false( migrate_engine ) ) + migrate_engine.execute("UPDATE tool_shed_repository SET update_available=%s" % engine_false(migrate_engine)) except Exception: log.exception("Adding column update_available to the tool_shed_repository table failed.") diff --git a/lib/galaxy/model/migrate/versions/0117_add_user_activation.py b/lib/galaxy/model/migrate/versions/0117_add_user_activation.py index 148b1dd1eb37..70513201fa28 100644 --- a/lib/galaxy/model/migrate/versions/0117_add_user_activation.py +++ b/lib/galaxy/model/migrate/versions/0117_add_user_activation.py @@ -9,9 +9,9 @@ from galaxy.model.custom_types import TrimmedString -log = logging.getLogger( __name__ ) -user_active_column = Column( "active", Boolean, default=True, nullable=True ) -user_activation_token_column = Column( "activation_token", TrimmedString( 64 ), nullable=True ) +log = logging.getLogger(__name__) +user_active_column = Column("active", Boolean, default=True, nullable=True) +user_activation_token_column = Column("activation_token", TrimmedString(64), nullable=True) def upgrade(migrate_engine): @@ -22,10 +22,10 @@ def upgrade(migrate_engine): # Add the active and activation_token columns to the user table in one try because the depend on each other. try: - user_table = Table( "galaxy_user", metadata, autoload=True ) - user_activation_token_column.create( table=user_table ) + user_table = Table("galaxy_user", metadata, autoload=True) + user_activation_token_column.create(table=user_table) assert user_activation_token_column is user_table.c.activation_token - user_active_column.create( table=user_table, populate_default=True) + user_active_column.create(table=user_table, populate_default=True) assert user_active_column is user_table.c.active except Exception: log.exception("Adding columns 'active' and 'activation_token' to galaxy_user table failed.") @@ -38,7 +38,7 @@ def downgrade(migrate_engine): # Drop the user table's active and activation_token columns in one try because the depend on each other. try: - user_table = Table( "galaxy_user", metadata, autoload=True ) + user_table = Table("galaxy_user", metadata, autoload=True) # SQLAlchemy Migrate has a bug when dropping a boolean column in SQLite if migrate_engine.name != 'sqlite': user_active = user_table.c.active diff --git a/lib/galaxy/model/migrate/versions/0118_add_hda_extended_metadata.py b/lib/galaxy/model/migrate/versions/0118_add_hda_extended_metadata.py index bd40ea8de0a0..4913150f6804 100644 --- a/lib/galaxy/model/migrate/versions/0118_add_hda_extended_metadata.py +++ b/lib/galaxy/model/migrate/versions/0118_add_hda_extended_metadata.py @@ -7,9 +7,9 @@ from sqlalchemy import Column, ForeignKey, Integer, MetaData, Table -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() -extended_metadata_hda_col = Column( "extended_metadata_id", Integer, ForeignKey("extended_metadata.id"), nullable=True ) +extended_metadata_hda_col = Column("extended_metadata_id", Integer, ForeignKey("extended_metadata.id"), nullable=True) def upgrade(migrate_engine): @@ -18,8 +18,8 @@ def upgrade(migrate_engine): metadata.reflect() try: - hda_table = Table( "history_dataset_association", metadata, autoload=True ) - extended_metadata_hda_col.create( hda_table ) + hda_table = Table("history_dataset_association", metadata, autoload=True) + extended_metadata_hda_col.create(hda_table) assert extended_metadata_hda_col is hda_table.c.extended_metadata_id except Exception: log.exception("Adding column 'extended_metadata_id' to history_dataset_association table failed.") @@ -31,7 +31,7 @@ def downgrade(migrate_engine): # Drop the HDA table's extended metadata ID column. try: - hda_table = Table( "history_dataset_association", metadata, autoload=True ) + hda_table = Table("history_dataset_association", metadata, autoload=True) extended_metadata_id = hda_table.c.extended_metadata_id extended_metadata_id.drop() except Exception: diff --git a/lib/galaxy/model/migrate/versions/0119_job_metrics.py b/lib/galaxy/model/migrate/versions/0119_job_metrics.py index ebce56c9b6ea..b2242b603fcc 100644 --- a/lib/galaxy/model/migrate/versions/0119_job_metrics.py +++ b/lib/galaxy/model/migrate/versions/0119_job_metrics.py @@ -7,7 +7,7 @@ from sqlalchemy import Column, ForeignKey, Integer, MetaData, Numeric, Table, Unicode -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() TEXT_METRIC_MAX_LENGTH = 1023 @@ -15,44 +15,44 @@ JobMetricText_table = Table( "job_metric_text", metadata, - Column( "id", Integer, primary_key=True ), - Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ), - Column( "plugin", Unicode(255), ), - Column( "metric_name", Unicode(255), ), - Column( "metric_value", Unicode(TEXT_METRIC_MAX_LENGTH), ), + Column("id", Integer, primary_key=True), + Column("job_id", Integer, ForeignKey("job.id"), index=True), + Column("plugin", Unicode(255), ), + Column("metric_name", Unicode(255), ), + Column("metric_value", Unicode(TEXT_METRIC_MAX_LENGTH), ), ) TaskMetricText_table = Table( "task_metric_text", metadata, - Column( "id", Integer, primary_key=True ), - Column( "task_id", Integer, ForeignKey( "task.id" ), index=True ), - Column( "plugin", Unicode(255), ), - Column( "metric_name", Unicode(255), ), - Column( "metric_value", Unicode(TEXT_METRIC_MAX_LENGTH), ), + Column("id", Integer, primary_key=True), + Column("task_id", Integer, ForeignKey("task.id"), index=True), + Column("plugin", Unicode(255), ), + Column("metric_name", Unicode(255), ), + Column("metric_value", Unicode(TEXT_METRIC_MAX_LENGTH), ), ) JobMetricNumeric_table = Table( "job_metric_numeric", metadata, - Column( "id", Integer, primary_key=True ), - Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ), - Column( "plugin", Unicode(255), ), - Column( "metric_name", Unicode(255), ), - Column( "metric_value", Numeric( 22, 7 ), ), + Column("id", Integer, primary_key=True), + Column("job_id", Integer, ForeignKey("job.id"), index=True), + Column("plugin", Unicode(255), ), + Column("metric_name", Unicode(255), ), + Column("metric_value", Numeric(22, 7), ), ) TaskMetricNumeric_table = Table( "task_metric_numeric", metadata, - Column( "id", Integer, primary_key=True ), - Column( "task_id", Integer, ForeignKey( "task.id" ), index=True ), - Column( "plugin", Unicode(255), ), - Column( "metric_name", Unicode(255), ), - Column( "metric_value", Numeric( 22, 7 ), ), + Column("id", Integer, primary_key=True), + Column("task_id", Integer, ForeignKey("task.id"), index=True), + Column("plugin", Unicode(255), ), + Column("metric_name", Unicode(255), ), + Column("metric_value", Numeric(22, 7), ), ) @@ -64,7 +64,7 @@ ] -def upgrade( migrate_engine ): +def upgrade(migrate_engine): metadata.bind = migrate_engine print(__doc__) metadata.reflect() diff --git a/lib/galaxy/model/migrate/versions/0120_dataset_collections.py b/lib/galaxy/model/migrate/versions/0120_dataset_collections.py index b19174f3fd72..0cffb880a33b 100644 --- a/lib/galaxy/model/migrate/versions/0120_dataset_collections.py +++ b/lib/galaxy/model/migrate/versions/0120_dataset_collections.py @@ -11,104 +11,104 @@ from galaxy.model.custom_types import TrimmedString now = datetime.datetime.utcnow -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() -DatasetCollection_table = Table( "dataset_collection", metadata, - Column( "id", Integer, primary_key=True ), - Column( "collection_type", Unicode(255), nullable=False, ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ) ) - -HistoryDatasetCollectionAssociation_table = Table( "history_dataset_collection_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "collection_id", Integer, ForeignKey( "dataset_collection.id" ), index=True ), - Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ), - Column( "hid", Integer ), - Column( "name", TrimmedString( 255 ) ), - Column( "deleted", Boolean, default=False ), - Column( "visible", Boolean, default=True ), - Column( "copied_from_history_dataset_collection_association_id", Integer, ForeignKey( "history_dataset_collection_association.id" ), nullable=True ), - Column( "implicit_output_name", Unicode(255), nullable=True ) ) - -LibraryDatasetCollectionAssociation_table = Table( "library_dataset_collection_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "collection_id", Integer, ForeignKey( "dataset_collection.id" ), index=True ), - Column( "name", TrimmedString( 255 ) ), - Column( "deleted", Boolean, default=False ), - Column( "folder_id", Integer, ForeignKey( "library_folder.id" ), index=True ) ) - -DatasetCollectionElement_table = Table( "dataset_collection_element", metadata, - Column( "id", Integer, primary_key=True ), - Column( "dataset_collection_id", Integer, ForeignKey( "dataset_collection.id" ), index=True, nullable=False ), - Column( "hda_id", Integer, ForeignKey( "history_dataset_association.id" ), index=True, nullable=True ), - Column( "ldda_id", Integer, ForeignKey( "library_dataset_dataset_association.id" ), index=True, nullable=True ), - Column( "child_collection_id", Integer, ForeignKey( "dataset_collection.id" ), index=True, nullable=True ), - Column( "element_index", Integer, nullable=False ), - Column( "element_identifier", Unicode(255), nullable=False ) ) - -HistoryDatasetCollectionAnnotationAssociation_table = Table( "history_dataset_collection_annotation_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "history_dataset_collection_id", Integer, ForeignKey( "history_dataset_collection_association.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "annotation", TEXT ) ) - -LibraryDatasetCollectionAnnotationAssociation_table = Table( "library_dataset_collection_annotation_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "library_dataset_collection_id", Integer, ForeignKey( "library_dataset_collection_association.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "annotation", TEXT ) ) - -HistoryDatasetCollectionRatingAssociation_table = Table( "history_dataset_collection_rating_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "history_dataset_collection_id", Integer, ForeignKey( "history_dataset_collection_association.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "rating", Integer, index=True) ) - -LibraryDatasetCollectionRatingAssociation_table = Table( "library_dataset_collection_rating_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "library_dataset_collection_id", Integer, ForeignKey( "library_dataset_collection_association.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "rating", Integer, index=True) ) - -HistoryDatasetCollectionTagAssociation_table = Table( "history_dataset_collection_tag_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "history_dataset_collection_id", Integer, ForeignKey( "history_dataset_collection_association.id" ), index=True ), - Column( "tag_id", Integer, ForeignKey( "tag.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "user_tname", Unicode(255), index=True), - Column( "value", Unicode(255), index=True), - Column( "user_value", Unicode(255), index=True) ) - -LibraryDatasetCollectionTagAssociation_table = Table( "library_dataset_collection_tag_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "library_dataset_collection_id", Integer, ForeignKey( "library_dataset_collection_association.id" ), index=True ), - Column( "tag_id", Integer, ForeignKey( "tag.id" ), index=True ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), - Column( "user_tname", Unicode(255), index=True), - Column( "value", Unicode(255), index=True), - Column( "user_value", Unicode(255), index=True) ) - -JobToInputDatasetCollectionAssociation_table = Table( "job_to_input_dataset_collection", metadata, - Column( "id", Integer, primary_key=True ), - Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ), - Column( "dataset_collection_id", Integer, ForeignKey( "history_dataset_collection_association.id" ), index=True ), - Column( "name", Unicode(255) ) ) - -JobToOutputDatasetCollectionAssociation_table = Table( "job_to_output_dataset_collection", metadata, - Column( "id", Integer, primary_key=True ), - Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ), - Column( "dataset_collection_id", Integer, ForeignKey( "history_dataset_collection_association.id" ), index=True ), - Column( "name", Unicode(255) ) ) - -ImplicitlyCreatedDatasetCollectionInput_table = Table( "implicitly_created_dataset_collection_inputs", metadata, - Column( "id", Integer, primary_key=True ), - Column( "dataset_collection_id", Integer, ForeignKey( "history_dataset_collection_association.id" ), index=True ), - Column( "input_dataset_collection_id", Integer, ForeignKey( "history_dataset_collection_association.id" ), index=True ), - Column( "name", Unicode(255) ) ) +DatasetCollection_table = Table("dataset_collection", metadata, + Column("id", Integer, primary_key=True), + Column("collection_type", Unicode(255), nullable=False, ), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now)) + +HistoryDatasetCollectionAssociation_table = Table("history_dataset_collection_association", metadata, + Column("id", Integer, primary_key=True), + Column("collection_id", Integer, ForeignKey("dataset_collection.id"), index=True), + Column("history_id", Integer, ForeignKey("history.id"), index=True), + Column("hid", Integer), + Column("name", TrimmedString(255)), + Column("deleted", Boolean, default=False), + Column("visible", Boolean, default=True), + Column("copied_from_history_dataset_collection_association_id", Integer, ForeignKey("history_dataset_collection_association.id"), nullable=True), + Column("implicit_output_name", Unicode(255), nullable=True)) + +LibraryDatasetCollectionAssociation_table = Table("library_dataset_collection_association", metadata, + Column("id", Integer, primary_key=True), + Column("collection_id", Integer, ForeignKey("dataset_collection.id"), index=True), + Column("name", TrimmedString(255)), + Column("deleted", Boolean, default=False), + Column("folder_id", Integer, ForeignKey("library_folder.id"), index=True)) + +DatasetCollectionElement_table = Table("dataset_collection_element", metadata, + Column("id", Integer, primary_key=True), + Column("dataset_collection_id", Integer, ForeignKey("dataset_collection.id"), index=True, nullable=False), + Column("hda_id", Integer, ForeignKey("history_dataset_association.id"), index=True, nullable=True), + Column("ldda_id", Integer, ForeignKey("library_dataset_dataset_association.id"), index=True, nullable=True), + Column("child_collection_id", Integer, ForeignKey("dataset_collection.id"), index=True, nullable=True), + Column("element_index", Integer, nullable=False), + Column("element_identifier", Unicode(255), nullable=False)) + +HistoryDatasetCollectionAnnotationAssociation_table = Table("history_dataset_collection_annotation_association", metadata, + Column("id", Integer, primary_key=True), + Column("history_dataset_collection_id", Integer, ForeignKey("history_dataset_collection_association.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("annotation", TEXT)) + +LibraryDatasetCollectionAnnotationAssociation_table = Table("library_dataset_collection_annotation_association", metadata, + Column("id", Integer, primary_key=True), + Column("library_dataset_collection_id", Integer, ForeignKey("library_dataset_collection_association.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("annotation", TEXT)) + +HistoryDatasetCollectionRatingAssociation_table = Table("history_dataset_collection_rating_association", metadata, + Column("id", Integer, primary_key=True), + Column("history_dataset_collection_id", Integer, ForeignKey("history_dataset_collection_association.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("rating", Integer, index=True)) + +LibraryDatasetCollectionRatingAssociation_table = Table("library_dataset_collection_rating_association", metadata, + Column("id", Integer, primary_key=True), + Column("library_dataset_collection_id", Integer, ForeignKey("library_dataset_collection_association.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("rating", Integer, index=True)) + +HistoryDatasetCollectionTagAssociation_table = Table("history_dataset_collection_tag_association", metadata, + Column("id", Integer, primary_key=True), + Column("history_dataset_collection_id", Integer, ForeignKey("history_dataset_collection_association.id"), index=True), + Column("tag_id", Integer, ForeignKey("tag.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("user_tname", Unicode(255), index=True), + Column("value", Unicode(255), index=True), + Column("user_value", Unicode(255), index=True)) + +LibraryDatasetCollectionTagAssociation_table = Table("library_dataset_collection_tag_association", metadata, + Column("id", Integer, primary_key=True), + Column("library_dataset_collection_id", Integer, ForeignKey("library_dataset_collection_association.id"), index=True), + Column("tag_id", Integer, ForeignKey("tag.id"), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True), + Column("user_tname", Unicode(255), index=True), + Column("value", Unicode(255), index=True), + Column("user_value", Unicode(255), index=True)) + +JobToInputDatasetCollectionAssociation_table = Table("job_to_input_dataset_collection", metadata, + Column("id", Integer, primary_key=True), + Column("job_id", Integer, ForeignKey("job.id"), index=True), + Column("dataset_collection_id", Integer, ForeignKey("history_dataset_collection_association.id"), index=True), + Column("name", Unicode(255))) + +JobToOutputDatasetCollectionAssociation_table = Table("job_to_output_dataset_collection", metadata, + Column("id", Integer, primary_key=True), + Column("job_id", Integer, ForeignKey("job.id"), index=True), + Column("dataset_collection_id", Integer, ForeignKey("history_dataset_collection_association.id"), index=True), + Column("name", Unicode(255))) + +ImplicitlyCreatedDatasetCollectionInput_table = Table("implicitly_created_dataset_collection_inputs", metadata, + Column("id", Integer, primary_key=True), + Column("dataset_collection_id", Integer, ForeignKey("history_dataset_collection_association.id"), index=True), + Column("input_dataset_collection_id", Integer, ForeignKey("history_dataset_collection_association.id"), index=True), + Column("name", Unicode(255))) # TODO: Find a better name for this column... -HiddenBeneathCollection_column = Column( "hidden_beneath_collection_instance_id", Integer, ForeignKey( "history_dataset_collection_association.id" ), nullable=True ) +HiddenBeneathCollection_column = Column("hidden_beneath_collection_instance_id", Integer, ForeignKey("history_dataset_collection_association.id"), nullable=True) TABLES = [ @@ -137,8 +137,8 @@ def upgrade(migrate_engine): __create(table) try: - hda_table = Table( "history_dataset_association", metadata, autoload=True ) - HiddenBeneathCollection_column.create( hda_table ) + hda_table = Table("history_dataset_association", metadata, autoload=True) + HiddenBeneathCollection_column.create(hda_table) except Exception: log.exception("Creating HDA column failed.") @@ -148,7 +148,7 @@ def downgrade(migrate_engine): metadata.reflect() try: - hda_table = Table( "history_dataset_association", metadata, autoload=True ) + hda_table = Table("history_dataset_association", metadata, autoload=True) hidden_beneath_collection_instance_id_col = hda_table.c.hidden_beneath_collection_instance_id hidden_beneath_collection_instance_id_col.drop() except Exception: diff --git a/lib/galaxy/model/migrate/versions/0121_workflow_uuids.py b/lib/galaxy/model/migrate/versions/0121_workflow_uuids.py index 3a28b6eff216..f7d366a28ba9 100644 --- a/lib/galaxy/model/migrate/versions/0121_workflow_uuids.py +++ b/lib/galaxy/model/migrate/versions/0121_workflow_uuids.py @@ -9,7 +9,7 @@ from galaxy.model.custom_types import UUIDType -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() @@ -18,7 +18,7 @@ based the a fixed data structure, their IDs are based on hashing the data structure """ -workflow_uuid_column = Column( "uuid", UUIDType, nullable=True ) +workflow_uuid_column = Column("uuid", UUIDType, nullable=True) def upgrade(migrate_engine): @@ -28,8 +28,8 @@ def upgrade(migrate_engine): # Add the uuid colum to the workflow table try: - workflow_table = Table( "workflow", metadata, autoload=True ) - workflow_uuid_column.create( workflow_table ) + workflow_table = Table("workflow", metadata, autoload=True) + workflow_uuid_column.create(workflow_table) assert workflow_uuid_column is workflow_table.c.uuid except Exception: log.exception("Adding column 'uuid' to workflow table failed.") @@ -41,7 +41,7 @@ def downgrade(migrate_engine): # Drop the workflow table's uuid column. try: - workflow_table = Table( "workflow", metadata, autoload=True ) + workflow_table = Table("workflow", metadata, autoload=True) workflow_uuid = workflow_table.c.uuid workflow_uuid.drop() except Exception: diff --git a/lib/galaxy/model/migrate/versions/0122_grow_mysql_blobs.py b/lib/galaxy/model/migrate/versions/0122_grow_mysql_blobs.py index 6e41098a913c..398426164101 100644 --- a/lib/galaxy/model/migrate/versions/0122_grow_mysql_blobs.py +++ b/lib/galaxy/model/migrate/versions/0122_grow_mysql_blobs.py @@ -7,7 +7,7 @@ from sqlalchemy import MetaData -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() BLOB_COLUMNS = [ @@ -43,7 +43,7 @@ def upgrade(migrate_engine): for (table, column) in BLOB_COLUMNS: cmd = "ALTER TABLE %s MODIFY COLUMN %s MEDIUMBLOB;" % (table, column) try: - migrate_engine.execute( cmd ) + migrate_engine.execute(cmd) except Exception: log.exception("Failed to grow column %s.%s", table, column) diff --git a/lib/galaxy/model/migrate/versions/0123_add_workflow_request_tables.py b/lib/galaxy/model/migrate/versions/0123_add_workflow_request_tables.py index f53287b81319..3a288f02d310 100644 --- a/lib/galaxy/model/migrate/versions/0123_add_workflow_request_tables.py +++ b/lib/galaxy/model/migrate/versions/0123_add_workflow_request_tables.py @@ -9,46 +9,46 @@ from galaxy.model.custom_types import JSONType, TrimmedString, UUIDType -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() WorkflowRequestInputParameter_table = Table( "workflow_request_input_parameters", metadata, - Column( "id", Integer, primary_key=True ), - Column( "workflow_invocation_id", Integer, ForeignKey("workflow_invocation.id", onupdate="CASCADE", ondelete="CASCADE" )), - Column( "name", Unicode(255) ), - Column( "type", Unicode(255) ), - Column( "value", TEXT ), + Column("id", Integer, primary_key=True), + Column("workflow_invocation_id", Integer, ForeignKey("workflow_invocation.id", onupdate="CASCADE", ondelete="CASCADE")), + Column("name", Unicode(255)), + Column("type", Unicode(255)), + Column("value", TEXT), ) WorkflowRequestStepState_table = Table( "workflow_request_step_states", metadata, - Column( "id", Integer, primary_key=True ), - Column( "workflow_invocation_id", Integer, ForeignKey("workflow_invocation.id", onupdate="CASCADE", ondelete="CASCADE" )), - Column( "workflow_step_id", Integer, ForeignKey("workflow_step.id" )), - Column( "value", JSONType ), + Column("id", Integer, primary_key=True), + Column("workflow_invocation_id", Integer, ForeignKey("workflow_invocation.id", onupdate="CASCADE", ondelete="CASCADE")), + Column("workflow_step_id", Integer, ForeignKey("workflow_step.id")), + Column("value", JSONType), ) WorkflowRequestToInputDatasetAssociation_table = Table( "workflow_request_to_input_dataset", metadata, - Column( "id", Integer, primary_key=True ), - Column( "name", String(255) ), - Column( "workflow_invocation_id", Integer, ForeignKey( "workflow_invocation.id" ), index=True ), - Column( "workflow_step_id", Integer, ForeignKey("workflow_step.id") ), - Column( "dataset_id", Integer, ForeignKey( "history_dataset_association.id" ), index=True ), + Column("id", Integer, primary_key=True), + Column("name", String(255)), + Column("workflow_invocation_id", Integer, ForeignKey("workflow_invocation.id"), index=True), + Column("workflow_step_id", Integer, ForeignKey("workflow_step.id")), + Column("dataset_id", Integer, ForeignKey("history_dataset_association.id"), index=True), ) WorkflowRequestToInputDatasetCollectionAssociation_table = Table( "workflow_request_to_input_collection_dataset", metadata, - Column( "id", Integer, primary_key=True ), - Column( "name", String(255) ), - Column( "workflow_invocation_id", Integer, ForeignKey( "workflow_invocation.id" ), index=True ), - Column( "workflow_step_id", Integer, ForeignKey("workflow_step.id") ), - Column( "dataset_collection_id", Integer, ForeignKey( "history_dataset_collection_association.id" ), index=True ), + Column("id", Integer, primary_key=True), + Column("name", String(255)), + Column("workflow_invocation_id", Integer, ForeignKey("workflow_invocation.id"), index=True), + Column("workflow_step_id", Integer, ForeignKey("workflow_step.id")), + Column("dataset_collection_id", Integer, ForeignKey("history_dataset_collection_association.id"), index=True), ) @@ -68,28 +68,28 @@ def upgrade(migrate_engine): for table in TABLES: __create(table) - History_column = Column( "history_id", Integer, ForeignKey( "history.id" ), nullable=True ) - State_column = Column( "state", TrimmedString( 64 ) ) + History_column = Column("history_id", Integer, ForeignKey("history.id"), nullable=True) + State_column = Column("state", TrimmedString(64)) # TODO: Handle indexes correctly - SchedulerId_column = Column( "scheduler", TrimmedString(255) ) - HandlerId_column = Column( "handler", TrimmedString(255) ) - WorkflowUUID_column = Column( "uuid", UUIDType, nullable=True ) - __add_column( History_column, "workflow_invocation", metadata ) - __add_column( State_column, "workflow_invocation", metadata ) - __add_column( SchedulerId_column, "workflow_invocation", metadata, index_nane="id_workflow_invocation_scheduler" ) - __add_column( HandlerId_column, "workflow_invocation", metadata, index_name="id_workflow_invocation_handler" ) - __add_column( WorkflowUUID_column, "workflow_invocation", metadata ) + SchedulerId_column = Column("scheduler", TrimmedString(255)) + HandlerId_column = Column("handler", TrimmedString(255)) + WorkflowUUID_column = Column("uuid", UUIDType, nullable=True) + __add_column(History_column, "workflow_invocation", metadata) + __add_column(State_column, "workflow_invocation", metadata) + __add_column(SchedulerId_column, "workflow_invocation", metadata, index_nane="id_workflow_invocation_scheduler") + __add_column(HandlerId_column, "workflow_invocation", metadata, index_name="id_workflow_invocation_handler") + __add_column(WorkflowUUID_column, "workflow_invocation", metadata) # All previous invocations have been scheduled... cmd = "UPDATE workflow_invocation SET state = 'scheduled'" try: - migrate_engine.execute( cmd ) + migrate_engine.execute(cmd) except Exception: log.exception("failed to update past workflow invocation states.") - WorkflowInvocationStepAction_column = Column( "action", JSONType, nullable=True ) - __add_column( WorkflowInvocationStepAction_column, "workflow_invocation_step", metadata ) + WorkflowInvocationStepAction_column = Column("action", JSONType, nullable=True) + __add_column(WorkflowInvocationStepAction_column, "workflow_invocation_step", metadata) def downgrade(migrate_engine): @@ -99,26 +99,26 @@ def downgrade(migrate_engine): for table in TABLES: __drop(table) - __drop_column( "state", "workflow_invocation", metadata ) - __drop_column( "scheduler", "workflow_invocation", metadata ) - __drop_column( "uuid", "workflow_invocation", metadata ) - __drop_column( "history_id", "workflow_invocation", metadata ) - __drop_column( "handler", "workflow_invocation", metadata ) - __drop_column( "action", "workflow_invocation_step", metadata ) + __drop_column("state", "workflow_invocation", metadata) + __drop_column("scheduler", "workflow_invocation", metadata) + __drop_column("uuid", "workflow_invocation", metadata) + __drop_column("history_id", "workflow_invocation", metadata) + __drop_column("handler", "workflow_invocation", metadata) + __drop_column("action", "workflow_invocation_step", metadata) def __add_column(column, table_name, metadata, **kwds): try: - table = Table( table_name, metadata, autoload=True ) - column.create( table, **kwds ) + table = Table(table_name, metadata, autoload=True) + column.create(table, **kwds) except Exception: log.exception("Adding column %s column failed.", column) -def __drop_column( column_name, table_name, metadata ): +def __drop_column(column_name, table_name, metadata): try: - table = Table( table_name, metadata, autoload=True ) - getattr( table.c, column_name ).drop() + table = Table(table_name, metadata, autoload=True) + getattr(table.c, column_name).drop() except Exception: log.exception("Dropping column %s failed.", column_name) diff --git a/lib/galaxy/model/migrate/versions/0124_job_state_history.py b/lib/galaxy/model/migrate/versions/0124_job_state_history.py index ab6c485176cf..d7740c941575 100644 --- a/lib/galaxy/model/migrate/versions/0124_job_state_history.py +++ b/lib/galaxy/model/migrate/versions/0124_job_state_history.py @@ -11,16 +11,16 @@ from galaxy.model.custom_types import TrimmedString now = datetime.datetime.utcnow -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() -JobStateHistory_table = Table( "job_state_history", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ), - Column( "state", String( 64 ), index=True ), - Column( "info", TrimmedString( 255 ) ) ) +JobStateHistory_table = Table("job_state_history", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("job_id", Integer, ForeignKey("job.id"), index=True), + Column("state", String(64), index=True), + Column("info", TrimmedString(255))) def upgrade(migrate_engine): diff --git a/lib/galaxy/model/migrate/versions/0125_workflow_step_tracking.py b/lib/galaxy/model/migrate/versions/0125_workflow_step_tracking.py index 39c89aa40dcc..0726fd756cf1 100644 --- a/lib/galaxy/model/migrate/versions/0125_workflow_step_tracking.py +++ b/lib/galaxy/model/migrate/versions/0125_workflow_step_tracking.py @@ -9,7 +9,7 @@ from galaxy.model.custom_types import TrimmedString, UUIDType -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() @@ -18,31 +18,31 @@ def upgrade(migrate_engine): print(__doc__) metadata.reflect() - StepLabel_column = Column( "label", TrimmedString(255) ) - StepUUID_column = Column( "uuid", UUIDType, nullable=True ) - __add_column( StepLabel_column, "workflow_step", metadata ) - __add_column( StepUUID_column, "workflow_step", metadata ) + StepLabel_column = Column("label", TrimmedString(255)) + StepUUID_column = Column("uuid", UUIDType, nullable=True) + __add_column(StepLabel_column, "workflow_step", metadata) + __add_column(StepUUID_column, "workflow_step", metadata) def downgrade(migrate_engine): metadata.bind = migrate_engine metadata.reflect() - __drop_column( "label", "workflow_step", metadata ) - __drop_column( "uuid", "workflow_step", metadata ) + __drop_column("label", "workflow_step", metadata) + __drop_column("uuid", "workflow_step", metadata) def __add_column(column, table_name, metadata, **kwds): try: - table = Table( table_name, metadata, autoload=True ) - column.create( table, **kwds ) + table = Table(table_name, metadata, autoload=True) + column.create(table, **kwds) except Exception: log.exception("Adding column %s failed.", column) -def __drop_column( column_name, table_name, metadata ): +def __drop_column(column_name, table_name, metadata): try: - table = Table( table_name, metadata, autoload=True ) - getattr( table.c, column_name ).drop() + table = Table(table_name, metadata, autoload=True) + getattr(table.c, column_name).drop() except Exception: log.exception("Dropping column %s failed.", column_name) diff --git a/lib/galaxy/model/migrate/versions/0126_password_reset.py b/lib/galaxy/model/migrate/versions/0126_password_reset.py index 168da25fd4be..f218c05ce03d 100644 --- a/lib/galaxy/model/migrate/versions/0126_password_reset.py +++ b/lib/galaxy/model/migrate/versions/0126_password_reset.py @@ -7,13 +7,13 @@ from sqlalchemy import Column, DateTime, ForeignKey, Integer, MetaData, String, Table -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() PasswordResetToken_table = Table("password_reset_token", metadata, - Column( "token", String( 32 ), primary_key=True, unique=True, index=True ), - Column( "expiration_time", DateTime ), - Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True )) + Column("token", String(32), primary_key=True, unique=True, index=True), + Column("expiration_time", DateTime), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True)) def upgrade(migrate_engine): diff --git a/lib/galaxy/model/migrate/versions/0127_output_collection_adjustments.py b/lib/galaxy/model/migrate/versions/0127_output_collection_adjustments.py index 78d1baeaa259..771ef05c1035 100644 --- a/lib/galaxy/model/migrate/versions/0127_output_collection_adjustments.py +++ b/lib/galaxy/model/migrate/versions/0127_output_collection_adjustments.py @@ -9,15 +9,15 @@ from galaxy.model.custom_types import TrimmedString -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() JobToImplicitOutputDatasetCollectionAssociation_table = Table( "job_to_implicit_output_dataset_collection", metadata, - Column( "id", Integer, primary_key=True ), - Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ), - Column( "dataset_collection_id", Integer, ForeignKey( "dataset_collection.id" ), index=True ), - Column( "name", Unicode(255) ) + Column("id", Integer, primary_key=True), + Column("job_id", Integer, ForeignKey("job.id"), index=True), + Column("dataset_collection_id", Integer, ForeignKey("dataset_collection.id"), index=True), + Column("name", Unicode(255)) ) @@ -35,13 +35,13 @@ def upgrade(migrate_engine): __create(table) try: - dataset_collection_table = Table( "dataset_collection", metadata, autoload=True ) + dataset_collection_table = Table("dataset_collection", metadata, autoload=True) # need server_default because column in non-null - populated_state_column = Column( 'populated_state', TrimmedString( 64 ), default='ok', server_default="ok", nullable=False ) - populated_state_column.create( dataset_collection_table ) + populated_state_column = Column('populated_state', TrimmedString(64), default='ok', server_default="ok", nullable=False) + populated_state_column.create(dataset_collection_table) - populated_message_column = Column( 'populated_state_message', TEXT, nullable=True ) - populated_message_column.create( dataset_collection_table ) + populated_message_column = Column('populated_state_message', TEXT, nullable=True) + populated_message_column.create(dataset_collection_table) except Exception: log.exception("Creating dataset collection populated column failed.") @@ -54,7 +54,7 @@ def downgrade(migrate_engine): __drop(table) try: - dataset_collection_table = Table( "dataset_collection", metadata, autoload=True ) + dataset_collection_table = Table("dataset_collection", metadata, autoload=True) populated_state_column = dataset_collection_table.c.populated_state populated_state_column.drop() populated_message_column = dataset_collection_table.c.populated_state_message diff --git a/lib/galaxy/model/migrate/versions/0128_session_timeout.py b/lib/galaxy/model/migrate/versions/0128_session_timeout.py index bc732fbc34ed..5725f3feacc7 100644 --- a/lib/galaxy/model/migrate/versions/0128_session_timeout.py +++ b/lib/galaxy/model/migrate/versions/0128_session_timeout.py @@ -7,7 +7,7 @@ from sqlalchemy import Column, DateTime, MetaData, Table -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() @@ -16,28 +16,28 @@ def upgrade(migrate_engine): print(__doc__) metadata.reflect() - lastaction_column = Column( "last_action", DateTime ) - __add_column( lastaction_column, "galaxy_session", metadata ) + lastaction_column = Column("last_action", DateTime) + __add_column(lastaction_column, "galaxy_session", metadata) def downgrade(migrate_engine): metadata.bind = migrate_engine metadata.reflect() - __drop_column( "last_action", "galaxy_session", metadata ) + __drop_column("last_action", "galaxy_session", metadata) def __add_column(column, table_name, metadata, **kwds): try: - table = Table( table_name, metadata, autoload=True ) - column.create( table, **kwds ) + table = Table(table_name, metadata, autoload=True) + column.create(table, **kwds) except Exception: log.exception("Adding column %s failed.", column) -def __drop_column( column_name, table_name, metadata ): +def __drop_column(column_name, table_name, metadata): try: - table = Table( table_name, metadata, autoload=True ) - getattr( table.c, column_name ).drop() + table = Table(table_name, metadata, autoload=True) + getattr(table.c, column_name).drop() except Exception: log.exception("Dropping column %s failed.", column_name) diff --git a/lib/galaxy/model/migrate/versions/0129_job_external_output_metadata_validity.py b/lib/galaxy/model/migrate/versions/0129_job_external_output_metadata_validity.py index e9a038d0e8f4..4c604079b27f 100644 --- a/lib/galaxy/model/migrate/versions/0129_job_external_output_metadata_validity.py +++ b/lib/galaxy/model/migrate/versions/0129_job_external_output_metadata_validity.py @@ -7,7 +7,7 @@ from sqlalchemy import Boolean, Column, MetaData, Table -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() @@ -16,8 +16,8 @@ def upgrade(migrate_engine): print(__doc__) metadata.reflect() - isvalid_column = Column( "is_valid", Boolean, default=True ) - __add_column( isvalid_column, "job_external_output_metadata", metadata ) + isvalid_column = Column("is_valid", Boolean, default=True) + __add_column(isvalid_column, "job_external_output_metadata", metadata) def downgrade(migrate_engine): @@ -25,20 +25,20 @@ def downgrade(migrate_engine): metadata.reflect() # SQLAlchemy Migrate has a bug when dropping a boolean column in SQLite if migrate_engine.name != 'sqlite': - __drop_column( "is_valid", "job_external_output_metadata", metadata ) + __drop_column("is_valid", "job_external_output_metadata", metadata) def __add_column(column, table_name, metadata, **kwds): try: - table = Table( table_name, metadata, autoload=True ) - column.create( table, **kwds ) + table = Table(table_name, metadata, autoload=True) + column.create(table, **kwds) except Exception: log.exception("Adding column %s failed.", column) -def __drop_column( column_name, table_name, metadata ): +def __drop_column(column_name, table_name, metadata): try: - table = Table( table_name, metadata, autoload=True ) - getattr( table.c, column_name ).drop() + table = Table(table_name, metadata, autoload=True) + getattr(table.c, column_name).drop() except Exception: log.exception("Dropping column %s failed.", column_name) diff --git a/lib/galaxy/model/migrate/versions/0130_change_pref_datatype.py b/lib/galaxy/model/migrate/versions/0130_change_pref_datatype.py index 4a4197798f60..533bbb9155f0 100644 --- a/lib/galaxy/model/migrate/versions/0130_change_pref_datatype.py +++ b/lib/galaxy/model/migrate/versions/0130_change_pref_datatype.py @@ -7,7 +7,7 @@ from sqlalchemy import MetaData, Table, Text -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() diff --git a/lib/galaxy/model/migrate/versions/0131_subworkflow_and_input_parameter_modules.py b/lib/galaxy/model/migrate/versions/0131_subworkflow_and_input_parameter_modules.py index 98126dc731d0..ec06a3e7aaf3 100644 --- a/lib/galaxy/model/migrate/versions/0131_subworkflow_and_input_parameter_modules.py +++ b/lib/galaxy/model/migrate/versions/0131_subworkflow_and_input_parameter_modules.py @@ -9,15 +9,15 @@ from galaxy.model.custom_types import JSONType, TrimmedString, UUIDType -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) metadata = MetaData() WorkflowInvocationToSubworkflowInvocationAssociation_table = Table( "workflow_invocation_to_subworkflow_invocation_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "workflow_invocation_id", Integer ), - Column( "subworkflow_invocation_id", Integer ), - Column( "workflow_step_id", Integer ), + Column("id", Integer, primary_key=True), + Column("workflow_invocation_id", Integer), + Column("subworkflow_invocation_id", Integer), + Column("workflow_step_id", Integer), ForeignKeyConstraint(['workflow_invocation_id'], ['workflow_invocation.id'], name='fk_wfi_swi_wfi'), ForeignKeyConstraint(['subworkflow_invocation_id'], ['workflow_invocation.id'], name='fk_wfi_swi_swi'), ForeignKeyConstraint(['workflow_step_id'], ['workflow_step.id'], name='fk_wfi_swi_ws') @@ -25,10 +25,10 @@ WorkflowRequestInputStepParameter_table = Table( "workflow_request_input_step_parameter", metadata, - Column( "id", Integer, primary_key=True ), - Column( "workflow_invocation_id", Integer ), - Column( "workflow_step_id", Integer ), - Column( "parameter_value", JSONType ), + Column("id", Integer, primary_key=True), + Column("workflow_invocation_id", Integer), + Column("workflow_step_id", Integer), + Column("parameter_value", JSONType), ForeignKeyConstraint(['workflow_invocation_id'], ['workflow_invocation.id'], name='fk_wfreq_isp_wfi'), ForeignKeyConstraint(['workflow_step_id'], ['workflow_step.id'], name='fk_wfreq_isp_ws') ) @@ -39,9 +39,9 @@ ] INDEXES = [ - Index( "ix_wfinv_swfinv_wfi", WorkflowInvocationToSubworkflowInvocationAssociation_table.c.workflow_invocation_id), - Index( "ix_wfinv_swfinv_swfi", WorkflowInvocationToSubworkflowInvocationAssociation_table.c.subworkflow_invocation_id), - Index( "ix_wfreq_inputstep_wfi", WorkflowRequestInputStepParameter_table.c.workflow_invocation_id) + Index("ix_wfinv_swfinv_wfi", WorkflowInvocationToSubworkflowInvocationAssociation_table.c.workflow_invocation_id), + Index("ix_wfinv_swfinv_swfi", WorkflowInvocationToSubworkflowInvocationAssociation_table.c.subworkflow_invocation_id), + Index("ix_wfreq_inputstep_wfi", WorkflowRequestInputStepParameter_table.c.workflow_invocation_id) ] @@ -50,20 +50,20 @@ def upgrade(migrate_engine): print(__doc__) metadata.reflect() if migrate_engine.name in ['postgres', 'postgresql']: - subworkflow_id_column = Column( "subworkflow_id", Integer, ForeignKey("workflow.id"), nullable=True ) - input_subworkflow_step_id_column = Column( "input_subworkflow_step_id", Integer, ForeignKey("workflow_step.id"), nullable=True ) - parent_workflow_id_column = Column( "parent_workflow_id", Integer, ForeignKey("workflow.id"), nullable=True ) + subworkflow_id_column = Column("subworkflow_id", Integer, ForeignKey("workflow.id"), nullable=True) + input_subworkflow_step_id_column = Column("input_subworkflow_step_id", Integer, ForeignKey("workflow_step.id"), nullable=True) + parent_workflow_id_column = Column("parent_workflow_id", Integer, ForeignKey("workflow.id"), nullable=True) else: - subworkflow_id_column = Column( "subworkflow_id", Integer, nullable=True ) - input_subworkflow_step_id_column = Column( "input_subworkflow_step_id", Integer, nullable=True ) - parent_workflow_id_column = Column( "parent_workflow_id", Integer, nullable=True ) - __add_column( subworkflow_id_column, "workflow_step", metadata ) - __add_column( input_subworkflow_step_id_column, "workflow_step_connection", metadata ) - __add_column( parent_workflow_id_column, "workflow", metadata ) - workflow_output_label_column = Column( "label", TrimmedString(255) ) - workflow_output_uuid_column = Column( "uuid", UUIDType, nullable=True ) - __add_column( workflow_output_label_column, "workflow_output", metadata ) - __add_column( workflow_output_uuid_column, "workflow_output", metadata ) + subworkflow_id_column = Column("subworkflow_id", Integer, nullable=True) + input_subworkflow_step_id_column = Column("input_subworkflow_step_id", Integer, nullable=True) + parent_workflow_id_column = Column("parent_workflow_id", Integer, nullable=True) + __add_column(subworkflow_id_column, "workflow_step", metadata) + __add_column(input_subworkflow_step_id_column, "workflow_step_connection", metadata) + __add_column(parent_workflow_id_column, "workflow", metadata) + workflow_output_label_column = Column("label", TrimmedString(255)) + workflow_output_uuid_column = Column("uuid", UUIDType, nullable=True) + __add_column(workflow_output_label_column, "workflow_output", metadata) + __add_column(workflow_output_uuid_column, "workflow_output", metadata) # Make stored_workflow_id nullable, since now workflows can belong to either # a stored workflow or a parent workflow. @@ -78,13 +78,13 @@ def downgrade(migrate_engine): metadata.bind = migrate_engine metadata.reflect() - __drop_column( "subworkflow_id", "workflow_step", metadata ) - __drop_column( "parent_workflow_id", "workflow", metadata ) + __drop_column("subworkflow_id", "workflow_step", metadata) + __drop_column("parent_workflow_id", "workflow", metadata) - __drop_column( "input_subworkflow_step_id", "workflow_step_connection", metadata ) + __drop_column("input_subworkflow_step_id", "workflow_step_connection", metadata) - __drop_column( "label", "workflow_output", metadata ) - __drop_column( "uuid", "workflow_output", metadata ) + __drop_column("label", "workflow_output", metadata) + __drop_column("uuid", "workflow_output", metadata) for table in TABLES: __drop(table) @@ -92,24 +92,24 @@ def downgrade(migrate_engine): def __alter_column(table_name, column_name, metadata, **kwds): try: - table = Table( table_name, metadata, autoload=True ) - getattr( table.c, column_name ).alter(**kwds) + table = Table(table_name, metadata, autoload=True) + getattr(table.c, column_name).alter(**kwds) except Exception: log.exception("Adding column %s failed.", column_name) def __add_column(column, table_name, metadata, **kwds): try: - table = Table( table_name, metadata, autoload=True ) - column.create( table, **kwds ) + table = Table(table_name, metadata, autoload=True) + column.create(table, **kwds) except Exception: log.exception("Adding column %s failed.", column) -def __drop_column( column_name, table_name, metadata ): +def __drop_column(column_name, table_name, metadata): try: - table = Table( table_name, metadata, autoload=True ) - getattr( table.c, column_name ).drop() + table = Table(table_name, metadata, autoload=True) + getattr(table.c, column_name).drop() except Exception: log.exception("Dropping column %s failed.", column_name) diff --git a/lib/galaxy/model/migrate/versions/0133_add_dependency_column_to_job.py b/lib/galaxy/model/migrate/versions/0133_add_dependency_column_to_job.py index 3421faed9006..eec6bf243dfa 100644 --- a/lib/galaxy/model/migrate/versions/0133_add_dependency_column_to_job.py +++ b/lib/galaxy/model/migrate/versions/0133_add_dependency_column_to_job.py @@ -9,8 +9,8 @@ from galaxy.model.custom_types import JSONType -log = logging.getLogger( __name__ ) -jobs_dependencies_column = Column( "dependencies", JSONType, nullable=True ) +log = logging.getLogger(__name__) +jobs_dependencies_column = Column("dependencies", JSONType, nullable=True) def upgrade(migrate_engine): @@ -21,8 +21,8 @@ def upgrade(migrate_engine): # Add the dependencies column to the job table try: - jobs_table = Table( "job", metadata, autoload=True ) - jobs_dependencies_column.create( jobs_table ) + jobs_table = Table("job", metadata, autoload=True) + jobs_dependencies_column.create(jobs_table) assert jobs_dependencies_column is jobs_table.c.dependencies except Exception: log.exception("Adding column 'dependencies' to job table failed.") @@ -35,7 +35,7 @@ def downgrade(migrate_engine): # Drop the job table's dependencies column. try: - jobs_table = Table( "job", metadata, autoload=True ) + jobs_table = Table("job", metadata, autoload=True) jobs_dependencies = jobs_table.c.dependencies jobs_dependencies.drop() except Exception: diff --git a/lib/galaxy/model/migrate/versions/0134_hda_set_deleted_if_purged.py b/lib/galaxy/model/migrate/versions/0134_hda_set_deleted_if_purged.py index 7249bc225eb7..e6360006881e 100644 --- a/lib/galaxy/model/migrate/versions/0134_hda_set_deleted_if_purged.py +++ b/lib/galaxy/model/migrate/versions/0134_hda_set_deleted_if_purged.py @@ -6,7 +6,7 @@ import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) def engine_true(migrate_engine): diff --git a/lib/galaxy/model/migrate/versions/0135_add_library_tags.py b/lib/galaxy/model/migrate/versions/0135_add_library_tags.py new file mode 100644 index 000000000000..d874a2cb8769 --- /dev/null +++ b/lib/galaxy/model/migrate/versions/0135_add_library_tags.py @@ -0,0 +1,47 @@ +""" +This migration script adds support for storing tags in the context of a dataset in a library +""" +from __future__ import print_function + +import logging + +from sqlalchemy import Column, ForeignKey, Integer, MetaData, Table + +# Need our custom types, but don't import anything else from model +from galaxy.model.custom_types import TrimmedString + +log = logging.getLogger(__name__) +metadata = MetaData() + + +LibraryDatasetDatasetAssociationTagAssociation_table = Table( + "library_dataset_dataset_association_tag_association", metadata, + Column("id", Integer, primary_key=True), + Column("library_dataset_dataset_association_id", Integer, ForeignKey("library_dataset_dataset_association.id"), index=True), + Column("tag_id", Integer, ForeignKey("tag.id"), index=True), + Column("user_tname", TrimmedString(255), index=True), + Column("value", TrimmedString(255), index=True), + Column("user_value", TrimmedString(255), index=True), + Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True) +) + + +def upgrade(migrate_engine): + metadata.bind = migrate_engine + print(__doc__) + metadata.reflect() + + try: + LibraryDatasetDatasetAssociationTagAssociation_table.create() + except Exception: + log.exception("Creating library_dataset_association_tag_association table failed.") + + +def downgrade(migrate_engine): + metadata.bind = migrate_engine + metadata.reflect() + + try: + LibraryDatasetDatasetAssociationTagAssociation_table.drop() + except Exception: + log.exception("Dropping library_dataset_association_tag_association table failed.") diff --git a/lib/galaxy/model/orm/engine_factory.py b/lib/galaxy/model/orm/engine_factory.py index d373564b9e0a..58d50ae75b9c 100644 --- a/lib/galaxy/model/orm/engine_factory.py +++ b/lib/galaxy/model/orm/engine_factory.py @@ -4,7 +4,7 @@ from sqlalchemy import create_engine, event from sqlalchemy.engine import Engine -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) def build_engine(url, engine_options, database_query_profiling_proxy=False, trace_logger=None, slow_query_log_threshold=0): @@ -15,7 +15,7 @@ def build_engine(url, engine_options, database_query_profiling_proxy=False, trac # If metlog is enabled, do micrologging elif trace_logger: import galaxy.model.orm.logging_connection_proxy as logging_connection_proxy - proxy = logging_connection_proxy.TraceLoggerProxy( trace_logger ) + proxy = logging_connection_proxy.TraceLoggerProxy(trace_logger) else: proxy = None if slow_query_log_threshold: @@ -32,5 +32,5 @@ def after_cursor_execute(conn, cursor, statement, log.debug("Slow query: %f(s)\n%s\nParameters: %s" % (total, statement, parameters)) # Create the database engine - engine = create_engine( url, proxy=proxy, **engine_options ) + engine = create_engine(url, proxy=proxy, **engine_options) return engine diff --git a/lib/galaxy/model/orm/logging_connection_proxy.py b/lib/galaxy/model/orm/logging_connection_proxy.py index 985c0a2091d4..5b883a3f6ee6 100644 --- a/lib/galaxy/model/orm/logging_connection_proxy.py +++ b/lib/galaxy/model/orm/logging_connection_proxy.py @@ -6,13 +6,13 @@ from sqlalchemy.interfaces import ConnectionProxy -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) wd = os.getcwd() -def stripwd( s ): - if s.startswith( wd ): +def stripwd(s): + if s.startswith(wd): return s[len(wd):] return s @@ -20,7 +20,7 @@ def stripwd( s ): def pretty_stack(): rval = [] for frame, fname, line, funcname, _, _ in inspect.stack()[2:]: - rval.append( "%s:%s@%d" % ( stripwd( fname ), funcname, line ) ) + rval.append("%s:%s@%d" % (stripwd(fname), funcname, line)) return rval @@ -49,8 +49,8 @@ def cursor_execute(self, execute, cursor, statement, parameters, context, execut start = time.clock() rval = execute(cursor, statement, parameters, context) duration = time.clock() - start - log.debug( "statement: %r parameters: %r executemany: %r duration: %r stack: %r thread: %r", - statement, parameters, executemany, duration, " > ".join( pretty_stack() ), thread_ident ) + log.debug("statement: %r parameters: %r executemany: %r duration: %r stack: %r thread: %r", + statement, parameters, executemany, duration, " > ".join(pretty_stack()), thread_ident) return rval @@ -58,7 +58,8 @@ class TraceLoggerProxy(ConnectionProxy): """ Logs SQL statements using a metlog client """ - def __init__( self, trace_logger ): + + def __init__(self, trace_logger): self.trace_logger = trace_logger def cursor_execute(self, execute, cursor, statement, parameters, context, executemany): diff --git a/lib/galaxy/model/orm/scripts.py b/lib/galaxy/model/orm/scripts.py index a7a922341cd6..dbca8fbb6bf0 100644 --- a/lib/galaxy/model/orm/scripts.py +++ b/lib/galaxy/model/orm/scripts.py @@ -3,10 +3,11 @@ """ import logging +from galaxy.util import listify from galaxy.util.properties import find_config_file, load_app_properties -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) DEFAULT_CONFIG_FILE = 'config/galaxy.ini' DEFAULT_CONFIG_PREFIX = '' @@ -16,22 +17,23 @@ "galaxy": { 'repo': 'lib/galaxy/model/migrate', - 'old_config_file': 'universe_wsgi.ini', + 'old_config_files': ['universe_wsgi.ini'], 'default_sqlite_file': './database/universe.sqlite', 'config_override': 'GALAXY_CONFIG_', }, "tool_shed": { 'repo': 'lib/galaxy/webapps/tool_shed/model/migrate', - 'config_file': 'config/tool_shed.ini', - 'old_config_file': 'tool_shed_wsgi.ini', + 'config_file': 'config/tool_shed.yml', + 'old_config_files': ['config/tool_shed.ini', 'tool_shed_wsgi.ini'], 'default_sqlite_file': './database/community.sqlite', 'config_override': 'TOOL_SHED_CONFIG_', + 'config_section': 'tool_shed', }, "install": { 'repo': 'lib/galaxy/model/tool_shed_install/migrate', - 'old_config_file': 'universe_wsgi.ini', + 'old_config_files': ['universe_wsgi.ini'], 'config_prefix': 'install_', 'default_sqlite_file': './database/install.sqlite', 'config_override': 'GALAXY_INSTALL_CONFIG_', @@ -39,17 +41,17 @@ } -def read_config_file_arg( argv, default, old_default, cwd=None ): +def read_config_file_arg(argv, default, old_defaults, cwd=None): config_file = None if '-c' in argv: - pos = argv.index( '-c' ) + pos = argv.index('-c') argv.pop(pos) - config_file = argv.pop( pos ) + config_file = argv.pop(pos) + old_defaults = listify(old_defaults) + return find_config_file(default, old_defaults, config_file, cwd=cwd) - return find_config_file( default, old_default, config_file, cwd=cwd ) - -def get_config( argv, cwd=None ): +def get_config(argv, cwd=None): """ Read sys.argv and parse out repository of migrations and database url. @@ -80,22 +82,26 @@ def get_config( argv, cwd=None ): database = argv.pop() # database name tool_shed, galaxy, or install. else: database = 'galaxy' - database_defaults = DATABASE[ database ] - - default = database_defaults.get( 'config_file', DEFAULT_CONFIG_FILE ) - old_default = database_defaults.get( 'old_config_file' ) - config_file = read_config_file_arg( argv, default, old_default, cwd=cwd ) - repo = database_defaults[ 'repo' ] - config_prefix = database_defaults.get( 'config_prefix', DEFAULT_CONFIG_PREFIX ) - config_override = database_defaults.get( 'config_override', 'GALAXY_CONFIG_' ) - default_sqlite_file = database_defaults[ 'default_sqlite_file' ] + database_defaults = DATABASE[database] - properties = load_app_properties( ini_file=config_file, config_prefix=config_override ) + default = database_defaults.get('config_file', DEFAULT_CONFIG_FILE) + old_defaults = database_defaults.get('old_config_files') + config_file = read_config_file_arg(argv, default, old_defaults, cwd=cwd) + repo = database_defaults['repo'] + config_prefix = database_defaults.get('config_prefix', DEFAULT_CONFIG_PREFIX) + config_override = database_defaults.get('config_override', 'GALAXY_CONFIG_') + default_sqlite_file = database_defaults['default_sqlite_file'] + if config_file.endswith(".yml") or config_file.endswith(".yml.sample"): + config_section = database_defaults.get('config_section', None) + else: + # An .ini file - just let load_app_properties find app:main. + config_section = None + properties = load_app_properties(config_file=config_file, config_prefix=config_override, config_section=config_section) if ("%sdatabase_connection" % config_prefix) in properties: - db_url = properties[ "%sdatabase_connection" % config_prefix ] + db_url = properties["%sdatabase_connection" % config_prefix] elif ("%sdatabase_file" % config_prefix) in properties: - database_file = properties[ "%sdatabase_file" % config_prefix ] + database_file = properties["%sdatabase_file" % config_prefix] db_url = "sqlite:///%s?isolation_level=IMMEDIATE" % database_file else: db_url = "sqlite:///%s?isolation_level=IMMEDIATE" % default_sqlite_file diff --git a/lib/galaxy/model/search.py b/lib/galaxy/model/search.py index 7ddde634a9d9..c8d39e5fd6d5 100644 --- a/lib/galaxy/model/search.py +++ b/lib/galaxy/model/search.py @@ -56,7 +56,7 @@ ) from galaxy.model.tool_shed_install import ToolVersion -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) class ViewField(object): @@ -79,6 +79,7 @@ class ViewField(object): its chain of parents to find out which library it belongs to """ + def __init__(self, name, sqlalchemy_field=None, handler=None, post_filter=None, id_decode=False): self.name = name self.sqlalchemy_field = sqlalchemy_field @@ -106,7 +107,7 @@ def decode_query_ids(self, trans, conditional): if left_base in self.FIELDS: field = self.FIELDS[left_base] if field.id_decode: - conditional.right = trans.security.decode_id( conditional.right ) + conditional.right = trans.security.decode_id(conditional.right) def filter(self, left, operator, right): if operator == 'and': @@ -122,17 +123,17 @@ def filter(self, left, operator, right): sqlalchemy_field_value = getattr(clazz, attribute) if operator == "=": # print field.sqlalchemy_field == right, field.sqlalchemy_field, right - self.query = self.query.filter( sqlalchemy_field_value == right ) + self.query = self.query.filter(sqlalchemy_field_value == right) elif operator == "!=": - self.query = self.query.filter( sqlalchemy_field_value != right ) + self.query = self.query.filter(sqlalchemy_field_value != right) elif operator == "like": - self.query = self.query.filter( sqlalchemy_field_value.like(right) ) + self.query = self.query.filter(sqlalchemy_field_value.like(right)) else: raise GalaxyParseError("Invalid comparison operator: %s" % (operator)) elif field.handler is not None: field.handler(self, left, operator, right) elif field.post_filter is not None: - self.post_filter.append( [field.post_filter, left, operator, right] ) + self.post_filter.append([field.post_filter, left, operator, right]) else: raise GalaxyParseError("Unable to filter on field: %s" % (left)) @@ -161,9 +162,9 @@ def get_results(self, force_query=False): def library_extended_metadata_filter(view, left, operator, right): view.do_query = True if 'extended_metadata_joined' not in view.state: - view.query = view.query.join( ExtendedMetadata ) + view.query = view.query.join(ExtendedMetadata) view.state['extended_metadata_joined'] = True - alias = aliased( ExtendedMetadataIndex ) + alias = aliased(ExtendedMetadataIndex) field = "/%s" % ("/".join(left.split(".")[1:])) # print "FIELD", field view.query = view.query.filter( @@ -195,7 +196,7 @@ class LibraryDatasetDatasetView(ViewQueryBaseClass): } def search(self, trans): - self.query = trans.sa_session.query( LibraryDatasetDatasetAssociation ) + self.query = trans.sa_session.query(LibraryDatasetDatasetAssociation) ################## @@ -211,7 +212,7 @@ class LibraryView(ViewQueryBaseClass): } def search(self, trans): - self.query = trans.sa_session.query( Library ) + self.query = trans.sa_session.query(Library) ################## @@ -245,7 +246,7 @@ class LibraryFolderView(ViewQueryBaseClass): } def search(self, trans): - self.query = trans.sa_session.query( LibraryFolder ) + self.query = trans.sa_session.query(LibraryFolder) ################## @@ -268,7 +269,7 @@ class LibraryDatasetView(ViewQueryBaseClass): } def search(self, trans): - self.query = trans.sa_session.query( LibraryDataset ) + self.query = trans.sa_session.query(LibraryDataset) ################## @@ -282,7 +283,7 @@ class ToolView(ViewQueryBaseClass): } def search(self, trans): - self.query = trans.install_model.context.query( ToolVersion ) + self.query = trans.install_model.context.query(ToolVersion) ################## @@ -298,9 +299,9 @@ def history_dataset_handle_tag(view, left, operator, right): HistoryDatasetAssociation.id == tag_table.history_dataset_association_id ) tmp = right.split(":") - view.query = view.query.filter( tag_table.user_tname == tmp[0] ) + view.query = view.query.filter(tag_table.user_tname == tmp[0]) if len(tmp) > 1: - view.query = view.query.filter( tag_table.user_value == tmp[1] ) + view.query = view.query.filter(tag_table.user_value == tmp[1]) else: raise GalaxyParseError("Invalid comparison operator: %s" % (operator)) @@ -308,9 +309,9 @@ def history_dataset_handle_tag(view, left, operator, right): def history_dataset_extended_metadata_filter(view, left, operator, right): view.do_query = True if 'extended_metadata_joined' not in view.state: - view.query = view.query.join( ExtendedMetadata ) + view.query = view.query.join(ExtendedMetadata) view.state['extended_metadata_joined'] = True - alias = aliased( ExtendedMetadataIndex ) + alias = aliased(ExtendedMetadataIndex) field = "/%s" % ("/".join(left.split(".")[1:])) # print "FIELD", field view.query = view.query.filter( @@ -340,7 +341,7 @@ class HistoryDatasetView(ViewQueryBaseClass): } def search(self, trans): - self.query = trans.sa_session.query( HistoryDatasetAssociation ) + self.query = trans.sa_session.query(HistoryDatasetAssociation) ################## @@ -356,9 +357,9 @@ def history_handle_tag(view, left, operator, right): History.id == tag_table.history_id ) tmp = right.split(":") - view.query = view.query.filter( tag_table.user_tname == tmp[0] ) + view.query = view.query.filter(tag_table.user_tname == tmp[0]) if len(tmp) > 1: - view.query = view.query.filter( tag_table.user_value == tmp[1] ) + view.query = view.query.filter(tag_table.user_value == tmp[1]) else: raise GalaxyParseError("Invalid comparison operator: %s" % (operator)) @@ -366,16 +367,16 @@ def history_handle_tag(view, left, operator, right): def history_handle_annotation(view, left, operator, right): if operator == "=": view.do_query = True - view.query = view.query.filter( and_( + view.query = view.query.filter(and_( HistoryAnnotationAssociation.history_id == History.id, HistoryAnnotationAssociation.annotation == right - ) ) + )) elif operator == "like": view.do_query = True - view.query = view.query.filter( and_( + view.query = view.query.filter(and_( HistoryAnnotationAssociation.history_id == History.id, - HistoryAnnotationAssociation.annotation.like( right ) - ) ) + HistoryAnnotationAssociation.annotation.like(right) + )) else: raise GalaxyParseError("Invalid comparison operator: %s" % (operator)) @@ -391,7 +392,7 @@ class HistoryView(ViewQueryBaseClass): } def search(self, trans): - self.query = trans.sa_session.query( History ) + self.query = trans.sa_session.query(History) ################## @@ -406,9 +407,9 @@ def workflow_tag_handler(view, left, operator, right): StoredWorkflow.id == StoredWorkflowTagAssociation.stored_workflow_id ) tmp = right.split(":") - view.query = view.query.filter( StoredWorkflowTagAssociation.user_tname == tmp[0] ) + view.query = view.query.filter(StoredWorkflowTagAssociation.user_tname == tmp[0]) if len(tmp) > 1: - view.query = view.query.filter( StoredWorkflowTagAssociation.user_value == tmp[1] ) + view.query = view.query.filter(StoredWorkflowTagAssociation.user_value == tmp[1]) else: raise GalaxyParseError("Invalid comparison operator: %s" % (operator)) @@ -423,7 +424,7 @@ class WorkflowView(ViewQueryBaseClass): } def search(self, trans): - self.query = trans.sa_session.query( StoredWorkflow ) + self.query = trans.sa_session.query(StoredWorkflow) ################## @@ -433,7 +434,7 @@ def search(self, trans): def job_param_filter(view, left, operator, right): view.do_query = True - alias = aliased( JobParameter ) + alias = aliased(JobParameter) param_name = re.sub(r'^param.', '', left) view.query = view.query.filter( and_( @@ -446,7 +447,7 @@ def job_param_filter(view, left, operator, right): def job_input_hda_filter(view, left, operator, right): view.do_query = True - alias = aliased( JobToInputDatasetAssociation ) + alias = aliased(JobToInputDatasetAssociation) param_name = re.sub(r'^input_hda.', '', left) view.query = view.query.filter( and_( @@ -459,7 +460,7 @@ def job_input_hda_filter(view, left, operator, right): def job_input_ldda_filter(view, left, operator, right): view.do_query = True - alias = aliased( JobToInputLibraryDatasetAssociation ) + alias = aliased(JobToInputLibraryDatasetAssociation) param_name = re.sub(r'^input_ldda.', '', left) view.query = view.query.filter( and_( @@ -472,7 +473,7 @@ def job_input_ldda_filter(view, left, operator, right): def job_output_hda_filter(view, left, operator, right): view.do_query = True - alias = aliased( JobToOutputDatasetAssociation ) + alias = aliased(JobToOutputDatasetAssociation) param_name = re.sub(r'^output_hda.', '', left) view.query = view.query.filter( and_( @@ -495,7 +496,7 @@ class JobView(ViewQueryBaseClass): } def search(self, trans): - self.query = trans.sa_session.query( Job ) + self.query = trans.sa_session.query(Job) ################## @@ -513,7 +514,7 @@ class PageView(ViewQueryBaseClass): } def search(self, trans): - self.query = trans.sa_session.query( Page ) + self.query = trans.sa_session.query(Page) ################## @@ -530,7 +531,7 @@ class PageRevisionView(ViewQueryBaseClass): } def search(self, trans): - self.query = trans.sa_session.query( PageRevision ) + self.query = trans.sa_session.query(PageRevision) # The view mapping takes a user's name for a table and maps it to a View class @@ -603,6 +604,7 @@ class GalaxyQuery(object): """ This class represents a data structure of a compiled GQL query """ + def __init__(self, field_list, table_name, conditional): self.field_list = field_list self.table_name = table_name @@ -614,6 +616,7 @@ class GalaxyQueryComparison(object): This class represents the data structure of the comparison arguments of a compiled GQL query (ie where name='Untitled History') """ + def __init__(self, left, operator, right): self.left = left self.operator = operator @@ -625,6 +628,7 @@ class GalaxyQueryAnd(object): This class represents the data structure of the comparison arguments of a compiled GQL query (ie where name='Untitled History') """ + def __init__(self, left, right): self.left = left self.operator = 'and' @@ -655,7 +659,7 @@ def process(self, trans): return self.view.get_results(True) def item_to_api_value(self, item): - r = item.to_dict( view='element' ) + r = item.to_dict(view='element') if self.query.field_list.count("*"): return r o = {} @@ -669,6 +673,7 @@ class GalaxySearchEngine(object): """ Primary class for searching. Parses GQL (Galaxy Query Language) queries and returns a 'SearchQuery' class """ + def __init__(self): self.parser = parsley.makeGrammar(gqlGrammar, { 're': re, diff --git a/lib/galaxy/model/tool_shed_install/__init__.py b/lib/galaxy/model/tool_shed_install/__init__.py index c369ea4746a1..f36ae712f101 100644 --- a/lib/galaxy/model/tool_shed_install/__init__.py +++ b/lib/galaxy/model/tool_shed_install/__init__.py @@ -8,33 +8,33 @@ from galaxy.util.dictifiable import Dictifiable from tool_shed.util import common_util -log = logging.getLogger( __name__ ) - - -class ToolShedRepository( object ): - dict_collection_visible_keys = ( 'id', 'tool_shed', 'name', 'owner', 'installed_changeset_revision', 'changeset_revision', 'ctx_rev', 'includes_datatypes', - 'tool_shed_status', 'deleted', 'uninstalled', 'dist_to_shed', 'status', 'error_message' ) - dict_element_visible_keys = ( 'id', 'tool_shed', 'name', 'owner', 'installed_changeset_revision', 'changeset_revision', 'ctx_rev', 'includes_datatypes', - 'tool_shed_status', 'deleted', 'uninstalled', 'dist_to_shed', 'status', 'error_message' ) - installation_status = Bunch( NEW='New', - CLONING='Cloning', - SETTING_TOOL_VERSIONS='Setting tool versions', - INSTALLING_REPOSITORY_DEPENDENCIES='Installing repository dependencies', - INSTALLING_TOOL_DEPENDENCIES='Installing tool dependencies', - LOADING_PROPRIETARY_DATATYPES='Loading proprietary datatypes', - INSTALLED='Installed', - DEACTIVATED='Deactivated', - ERROR='Error', - UNINSTALLED='Uninstalled' ) - states = Bunch( INSTALLING='running', - OK='ok', - WARNING='queued', - ERROR='error', - UNINSTALLED='deleted_new' ) - - def __init__( self, id=None, create_time=None, tool_shed=None, name=None, description=None, owner=None, installed_changeset_revision=None, - changeset_revision=None, ctx_rev=None, metadata=None, includes_datatypes=False, tool_shed_status=None, deleted=False, - uninstalled=False, dist_to_shed=False, status=None, error_message=None ): +log = logging.getLogger(__name__) + + +class ToolShedRepository(object): + dict_collection_visible_keys = ('id', 'tool_shed', 'name', 'owner', 'installed_changeset_revision', 'changeset_revision', 'ctx_rev', 'includes_datatypes', + 'tool_shed_status', 'deleted', 'uninstalled', 'dist_to_shed', 'status', 'error_message') + dict_element_visible_keys = ('id', 'tool_shed', 'name', 'owner', 'installed_changeset_revision', 'changeset_revision', 'ctx_rev', 'includes_datatypes', + 'tool_shed_status', 'deleted', 'uninstalled', 'dist_to_shed', 'status', 'error_message') + installation_status = Bunch(NEW='New', + CLONING='Cloning', + SETTING_TOOL_VERSIONS='Setting tool versions', + INSTALLING_REPOSITORY_DEPENDENCIES='Installing repository dependencies', + INSTALLING_TOOL_DEPENDENCIES='Installing tool dependencies', + LOADING_PROPRIETARY_DATATYPES='Loading proprietary datatypes', + INSTALLED='Installed', + DEACTIVATED='Deactivated', + ERROR='Error', + UNINSTALLED='Uninstalled') + states = Bunch(INSTALLING='running', + OK='ok', + WARNING='queued', + ERROR='error', + UNINSTALLED='deleted_new') + + def __init__(self, id=None, create_time=None, tool_shed=None, name=None, description=None, owner=None, installed_changeset_revision=None, + changeset_revision=None, ctx_rev=None, metadata=None, includes_datatypes=False, tool_shed_status=None, deleted=False, + uninstalled=False, dist_to_shed=False, status=None, error_message=None): self.id = id self.create_time = create_time self.tool_shed = tool_shed @@ -53,255 +53,255 @@ def __init__( self, id=None, create_time=None, tool_shed=None, name=None, descri self.status = status self.error_message = error_message - def as_dict( self, value_mapper=None ): - return self.to_dict( view='element', value_mapper=value_mapper ) + def as_dict(self, value_mapper=None): + return self.to_dict(view='element', value_mapper=value_mapper) @property - def can_install( self ): + def can_install(self): return self.status == self.installation_status.NEW @property - def can_reset_metadata( self ): + def can_reset_metadata(self): return self.status == self.installation_status.INSTALLED @property - def can_uninstall( self ): + def can_uninstall(self): return self.status != self.installation_status.UNINSTALLED @property - def can_deactivate( self ): - return self.status not in [ self.installation_status.DEACTIVATED, - self.installation_status.ERROR, - self.installation_status.UNINSTALLED ] + def can_deactivate(self): + return self.status not in [self.installation_status.DEACTIVATED, + self.installation_status.ERROR, + self.installation_status.UNINSTALLED] @property - def can_reinstall_or_activate( self ): + def can_reinstall_or_activate(self): return self.deleted - def get_sharable_url( self, app ): - tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry( app, self.tool_shed ) + def get_sharable_url(self, app): + tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry(app, self.tool_shed) if tool_shed_url: # Append a slash to the tool shed URL, because urlparse.urljoin will eliminate # the last part of a URL if it does not end with a forward slash. tool_shed_url = '%s/' % tool_shed_url - return urljoin( tool_shed_url, 'view/%s/%s' % ( self.owner, self.name ) ) + return urljoin(tool_shed_url, 'view/%s/%s' % (self.owner, self.name)) return tool_shed_url - def get_shed_config_filename( self ): + def get_shed_config_filename(self): shed_config_filename = None if self.metadata: - shed_config_filename = self.metadata.get( 'shed_config_filename', shed_config_filename ) + shed_config_filename = self.metadata.get('shed_config_filename', shed_config_filename) return shed_config_filename - def get_shed_config_dict( self, app, default=None ): + def get_shed_config_dict(self, app, default=None): """ Return the in-memory version of the shed_tool_conf file, which is stored in the config_elems entry in the shed_tool_conf_dict. """ - def _is_valid_shed_config_filename( filename ): - for shed_tool_conf_dict in app.toolbox.dynamic_confs( include_migrated_tool_conf=True ): - if filename == shed_tool_conf_dict[ 'config_filename' ]: + def _is_valid_shed_config_filename(filename): + for shed_tool_conf_dict in app.toolbox.dynamic_confs(include_migrated_tool_conf=True): + if filename == shed_tool_conf_dict['config_filename']: return True return False - if not self.shed_config_filename or not _is_valid_shed_config_filename( self.shed_config_filename ): - self.guess_shed_config( app, default=default ) + if not self.shed_config_filename or not _is_valid_shed_config_filename(self.shed_config_filename): + self.guess_shed_config(app, default=default) if self.shed_config_filename: - for shed_tool_conf_dict in app.toolbox.dynamic_confs( include_migrated_tool_conf=True ): - if self.shed_config_filename == shed_tool_conf_dict[ 'config_filename' ]: + for shed_tool_conf_dict in app.toolbox.dynamic_confs(include_migrated_tool_conf=True): + if self.shed_config_filename == shed_tool_conf_dict['config_filename']: return shed_tool_conf_dict return default - def get_tool_relative_path( self, app ): - shed_conf_dict = self.get_shed_config_dict( app ) + def get_tool_relative_path(self, app): + shed_conf_dict = self.get_shed_config_dict(app) tool_path = None relative_path = None if shed_conf_dict: - tool_path = shed_conf_dict[ 'tool_path' ] - relative_path = os.path.join( self.tool_shed_path_name, 'repos', self.owner, self.name, self.installed_changeset_revision ) + tool_path = shed_conf_dict['tool_path'] + relative_path = os.path.join(self.tool_shed_path_name, 'repos', self.owner, self.name, self.installed_changeset_revision) return tool_path, relative_path - def guess_shed_config( self, app, default=None ): + def guess_shed_config(self, app, default=None): tool_ids = [] metadata = self.metadata or {} - for tool in metadata.get( 'tools', [] ): - tool_ids.append( tool.get( 'guid' ) ) - for shed_tool_conf_dict in app.toolbox.dynamic_confs( include_migrated_tool_conf=True ): - name = shed_tool_conf_dict[ 'config_filename' ] - for elem in shed_tool_conf_dict[ 'config_elems' ]: + for tool in metadata.get('tools', []): + tool_ids.append(tool.get('guid')) + for shed_tool_conf_dict in app.toolbox.dynamic_confs(include_migrated_tool_conf=True): + name = shed_tool_conf_dict['config_filename'] + for elem in shed_tool_conf_dict['config_elems']: if elem.tag == 'tool': - for sub_elem in elem.findall( 'id' ): + for sub_elem in elem.findall('id'): tool_id = sub_elem.text.strip() if tool_id in tool_ids: self.shed_config_filename = name return shed_tool_conf_dict elif elem.tag == "section": - for tool_elem in elem.findall( 'tool' ): - for sub_elem in tool_elem.findall( 'id' ): + for tool_elem in elem.findall('tool'): + for sub_elem in tool_elem.findall('id'): tool_id = sub_elem.text.strip() if tool_id in tool_ids: self.shed_config_filename = name return shed_tool_conf_dict if self.includes_datatypes: # We need to search by file paths here, which is less desirable. - tool_shed = common_util.remove_protocol_and_port_from_tool_shed_url( self.tool_shed ) - for shed_tool_conf_dict in app.toolbox.dynamic_confs( include_migrated_tool_conf=True ): - tool_path = shed_tool_conf_dict[ 'tool_path' ] - relative_path = os.path.join( tool_path, tool_shed, 'repos', self.owner, self.name, self.installed_changeset_revision ) - if os.path.exists( relative_path ): - self.shed_config_filename = shed_tool_conf_dict[ 'config_filename' ] + tool_shed = common_util.remove_protocol_and_port_from_tool_shed_url(self.tool_shed) + for shed_tool_conf_dict in app.toolbox.dynamic_confs(include_migrated_tool_conf=True): + tool_path = shed_tool_conf_dict['tool_path'] + relative_path = os.path.join(tool_path, tool_shed, 'repos', self.owner, self.name, self.installed_changeset_revision) + if os.path.exists(relative_path): + self.shed_config_filename = shed_tool_conf_dict['config_filename'] return shed_tool_conf_dict return default @property - def has_readme_files( self ): + def has_readme_files(self): if self.metadata: return 'readme_files' in self.metadata return False @property - def has_repository_dependencies( self ): + def has_repository_dependencies(self): if self.metadata: - repository_dependencies_dict = self.metadata.get( 'repository_dependencies', {} ) - repository_dependencies = repository_dependencies_dict.get( 'repository_dependencies', [] ) + repository_dependencies_dict = self.metadata.get('repository_dependencies', {}) + repository_dependencies = repository_dependencies_dict.get('repository_dependencies', []) # [["http://localhost:9009", "package_libgtextutils_0_6", "test", "e2003cbf18cd", "True", "True"]] for rd_tup in repository_dependencies: tool_shed, name, owner, changeset_revision, prior_installation_required, only_if_compiling_contained_td = \ - common_util.parse_repository_dependency_tuple( rd_tup ) - if not asbool( only_if_compiling_contained_td ): + common_util.parse_repository_dependency_tuple(rd_tup) + if not asbool(only_if_compiling_contained_td): return True return False @property - def has_repository_dependencies_only_if_compiling_contained_td( self ): + def has_repository_dependencies_only_if_compiling_contained_td(self): if self.metadata: - repository_dependencies_dict = self.metadata.get( 'repository_dependencies', {} ) - repository_dependencies = repository_dependencies_dict.get( 'repository_dependencies', [] ) + repository_dependencies_dict = self.metadata.get('repository_dependencies', {}) + repository_dependencies = repository_dependencies_dict.get('repository_dependencies', []) # [["http://localhost:9009", "package_libgtextutils_0_6", "test", "e2003cbf18cd", "True", "True"]] for rd_tup in repository_dependencies: tool_shed, name, owner, changeset_revision, prior_installation_required, only_if_compiling_contained_td = \ - common_util.parse_repository_dependency_tuple( rd_tup ) - if not asbool( only_if_compiling_contained_td ): + common_util.parse_repository_dependency_tuple(rd_tup) + if not asbool(only_if_compiling_contained_td): return False return True return False @property - def in_error_state( self ): + def in_error_state(self): return self.status == self.installation_status.ERROR @property - def includes_data_managers( self ): + def includes_data_managers(self): if self.metadata: - return bool( len( self.metadata.get( 'data_manager', {} ).get( 'data_managers', {} ) ) ) + return bool(len(self.metadata.get('data_manager', {}).get('data_managers', {}))) return False @property - def includes_tools( self ): + def includes_tools(self): if self.metadata: return 'tools' in self.metadata return False @property - def includes_tools_for_display_in_tool_panel( self ): + def includes_tools_for_display_in_tool_panel(self): if self.includes_tools: - tool_dicts = self.metadata[ 'tools' ] + tool_dicts = self.metadata['tools'] for tool_dict in tool_dicts: - if tool_dict.get( 'add_to_tool_panel', True ): + if tool_dict.get('add_to_tool_panel', True): return True return False @property - def includes_tool_dependencies( self ): + def includes_tool_dependencies(self): if self.metadata: return 'tool_dependencies' in self.metadata return False @property - def includes_workflows( self ): + def includes_workflows(self): if self.metadata: return 'workflows' in self.metadata return False @property - def installed_repository_dependencies( self ): + def installed_repository_dependencies(self): """Return the repository's repository dependencies that are currently installed.""" installed_required_repositories = [] for required_repository in self.repository_dependencies: if required_repository.status == self.installation_status.INSTALLED: - installed_required_repositories.append( required_repository ) + installed_required_repositories.append(required_repository) return installed_required_repositories @property - def installed_tool_dependencies( self ): + def installed_tool_dependencies(self): """Return the repository's tool dependencies that are currently installed, but possibly in an error state.""" installed_dependencies = [] for tool_dependency in self.tool_dependencies: - if tool_dependency.status in [ ToolDependency.installation_status.INSTALLED ]: - installed_dependencies.append( tool_dependency ) + if tool_dependency.status in [ToolDependency.installation_status.INSTALLED]: + installed_dependencies.append(tool_dependency) return installed_dependencies @property - def is_deprecated_in_tool_shed( self ): + def is_deprecated_in_tool_shed(self): if self.tool_shed_status: - return asbool( self.tool_shed_status.get( 'repository_deprecated', False ) ) + return asbool(self.tool_shed_status.get('repository_deprecated', False)) return False @property - def is_deactivated_or_installed( self ): - return self.status in [ self.installation_status.DEACTIVATED, - self.installation_status.INSTALLED ] + def is_deactivated_or_installed(self): + return self.status in [self.installation_status.DEACTIVATED, + self.installation_status.INSTALLED] @property - def is_installed( self ): + def is_installed(self): return self.status == self.installation_status.INSTALLED @property - def is_latest_installable_revision( self ): + def is_latest_installable_revision(self): if self.tool_shed_status: - return asbool( self.tool_shed_status.get( 'latest_installable_revision', False ) ) + return asbool(self.tool_shed_status.get('latest_installable_revision', False)) return False @property - def is_new( self ): + def is_new(self): return self.status == self.installation_status.NEW @property - def missing_repository_dependencies( self ): + def missing_repository_dependencies(self): """Return the repository's repository dependencies that are not currently installed, and may not ever have been installed.""" missing_required_repositories = [] for required_repository in self.repository_dependencies: - if required_repository.status not in [ self.installation_status.INSTALLED ]: - missing_required_repositories.append( required_repository ) + if required_repository.status not in [self.installation_status.INSTALLED]: + missing_required_repositories.append(required_repository) return missing_required_repositories @property - def missing_tool_dependencies( self ): + def missing_tool_dependencies(self): """Return the repository's tool dependencies that are not currently installed, and may not ever have been installed.""" missing_dependencies = [] for tool_dependency in self.tool_dependencies: - if tool_dependency.status not in [ ToolDependency.installation_status.INSTALLED ]: - missing_dependencies.append( tool_dependency ) + if tool_dependency.status not in [ToolDependency.installation_status.INSTALLED]: + missing_dependencies.append(tool_dependency) return missing_dependencies - def repo_files_directory( self, app ): - repo_path = self.repo_path( app ) + def repo_files_directory(self, app): + repo_path = self.repo_path(app) if repo_path: - return os.path.join( repo_path, self.name ) + return os.path.join(repo_path, self.name) return None - def repo_path( self, app ): - tool_shed = common_util.remove_protocol_and_port_from_tool_shed_url( self.tool_shed ) - for shed_tool_conf_dict in app.toolbox.dynamic_confs( include_migrated_tool_conf=True ): - tool_path = shed_tool_conf_dict[ 'tool_path' ] - relative_path = os.path.join( tool_path, tool_shed, 'repos', self.owner, self.name, self.installed_changeset_revision ) - if os.path.exists( relative_path ): + def repo_path(self, app): + tool_shed = common_util.remove_protocol_and_port_from_tool_shed_url(self.tool_shed) + for shed_tool_conf_dict in app.toolbox.dynamic_confs(include_migrated_tool_conf=True): + tool_path = shed_tool_conf_dict['tool_path'] + relative_path = os.path.join(tool_path, tool_shed, 'repos', self.owner, self.name, self.installed_changeset_revision) + if os.path.exists(relative_path): return relative_path return None @property - def repository_dependencies( self ): + def repository_dependencies(self): """ Return all of this repository's repository dependencies, ignoring their attributes like prior_installation_required and only_if_compiling_contained_td. @@ -311,45 +311,45 @@ def repository_dependencies( self ): repository_dependency = rrda.repository_dependency required_repository = repository_dependency.repository if required_repository: - required_repositories.append( required_repository ) + required_repositories.append(required_repository) return required_repositories @property - def repository_dependencies_being_installed( self ): + def repository_dependencies_being_installed(self): """Return the repository's repository dependencies that are currently being installed.""" required_repositories_being_installed = [] for required_repository in self.repository_dependencies: - if required_repository.status in [ self.installation_status.CLONING, - self.installation_status.INSTALLING_REPOSITORY_DEPENDENCIES, - self.installation_status.INSTALLING_TOOL_DEPENDENCIES, - self.installation_status.LOADING_PROPRIETARY_DATATYPES, - self.installation_status.SETTING_TOOL_VERSIONS ]: - required_repositories_being_installed.append( required_repository ) + if required_repository.status in [self.installation_status.CLONING, + self.installation_status.INSTALLING_REPOSITORY_DEPENDENCIES, + self.installation_status.INSTALLING_TOOL_DEPENDENCIES, + self.installation_status.LOADING_PROPRIETARY_DATATYPES, + self.installation_status.SETTING_TOOL_VERSIONS]: + required_repositories_being_installed.append(required_repository) return required_repositories_being_installed @property - def repository_dependencies_missing_or_being_installed( self ): + def repository_dependencies_missing_or_being_installed(self): """Return the repository's repository dependencies that are either missing or currently being installed.""" required_repositories_missing_or_being_installed = [] for required_repository in self.repository_dependencies: - if required_repository.status in [ self.installation_status.ERROR, - self.installation_status.INSTALLING, - self.installation_status.NEVER_INSTALLED, - self.installation_status.UNINSTALLED ]: - required_repositories_missing_or_being_installed.append( required_repository ) + if required_repository.status in [self.installation_status.ERROR, + self.installation_status.INSTALLING, + self.installation_status.NEVER_INSTALLED, + self.installation_status.UNINSTALLED]: + required_repositories_missing_or_being_installed.append(required_repository) return required_repositories_missing_or_being_installed @property - def repository_dependencies_with_installation_errors( self ): + def repository_dependencies_with_installation_errors(self): """Return the repository's repository dependencies that have installation errors.""" required_repositories_with_installation_errors = [] for required_repository in self.repository_dependencies: if required_repository.status == self.installation_status.ERROR: - required_repositories_with_installation_errors.append( required_repository ) + required_repositories_with_installation_errors.append(required_repository) return required_repositories_with_installation_errors @property - def requires_prior_installation_of( self ): + def requires_prior_installation_of(self): """ Return a list of repository dependency tuples like (tool_shed, name, owner, changeset_revision, prior_installation_required) for this repository's repository dependencies where prior_installation_required is True. By definition, repository dependencies are required to @@ -361,173 +361,173 @@ def requires_prior_installation_of( self ): """ required_rd_tups_that_must_be_installed = [] if self.has_repository_dependencies: - rd_tups = self.metadata[ 'repository_dependencies' ][ 'repository_dependencies' ] + rd_tups = self.metadata['repository_dependencies']['repository_dependencies'] for rd_tup in rd_tups: - if len( rd_tup ) == 5: + if len(rd_tup) == 5: tool_shed, name, owner, changeset_revision, prior_installation_required, only_if_compiling_contained_td = \ - common_util.parse_repository_dependency_tuple( rd_tup, contains_error=False ) - if asbool( prior_installation_required ): - required_rd_tups_that_must_be_installed.append( ( tool_shed, name, owner, changeset_revision, 'True', 'False' ) ) - elif len( rd_tup ) == 6: + common_util.parse_repository_dependency_tuple(rd_tup, contains_error=False) + if asbool(prior_installation_required): + required_rd_tups_that_must_be_installed.append((tool_shed, name, owner, changeset_revision, 'True', 'False')) + elif len(rd_tup) == 6: tool_shed, name, owner, changeset_revision, prior_installation_required, only_if_compiling_contained_td = \ - common_util.parse_repository_dependency_tuple( rd_tup, contains_error=False ) + common_util.parse_repository_dependency_tuple(rd_tup, contains_error=False) # The repository dependency will only be required to be previously installed if it does not fall into the category of # a repository that must be installed only so that its contained tool dependency can be used for compiling the tool # dependency of the dependent repository. - if not asbool( only_if_compiling_contained_td ): - if asbool( prior_installation_required ): - required_rd_tups_that_must_be_installed.append( ( tool_shed, name, owner, changeset_revision, 'True', 'False' ) ) + if not asbool(only_if_compiling_contained_td): + if asbool(prior_installation_required): + required_rd_tups_that_must_be_installed.append((tool_shed, name, owner, changeset_revision, 'True', 'False')) return required_rd_tups_that_must_be_installed @property - def revision_update_available( self ): + def revision_update_available(self): # This method should be named update_available, but since it is no longer possible to drop a table column using migration scripts # with the sqlite database (see ~/galaxy/model/migrate/versions/0016_drop_update_available_col_add_tool_shed_status_col.py), we # have to name it in such a way that it will not conflict with the eliminated tool_shed_repository.update_available column (which # cannot be eliminated if using the sqlite database). if self.tool_shed_status: - return asbool( self.tool_shed_status.get( 'revision_update', False ) ) + return asbool(self.tool_shed_status.get('revision_update', False)) return False - def set_shed_config_filename( self, value ): - self.metadata[ 'shed_config_filename' ] = value + def set_shed_config_filename(self, value): + self.metadata['shed_config_filename'] = value - shed_config_filename = property( get_shed_config_filename, set_shed_config_filename ) + shed_config_filename = property(get_shed_config_filename, set_shed_config_filename) - def to_dict( self, view='collection', value_mapper=None ): + def to_dict(self, view='collection', value_mapper=None): if value_mapper is None: value_mapper = {} rval = {} try: - visible_keys = self.__getattribute__( 'dict_' + view + '_visible_keys' ) + visible_keys = self.__getattribute__('dict_' + view + '_visible_keys') except AttributeError: - raise Exception( 'Unknown API view: %s' % view ) + raise Exception('Unknown API view: %s' % view) for key in visible_keys: try: - rval[ key ] = self.__getattribute__( key ) + rval[key] = self.__getattribute__(key) if key in value_mapper: - rval[ key ] = value_mapper.get( key, rval[ key ] ) + rval[key] = value_mapper.get(key, rval[key]) except AttributeError: - rval[ key ] = None + rval[key] = None return rval @property - def tool_dependencies_being_installed( self ): + def tool_dependencies_being_installed(self): dependencies_being_installed = [] for tool_dependency in self.tool_dependencies: if tool_dependency.status == ToolDependency.installation_status.INSTALLING: - dependencies_being_installed.append( tool_dependency ) + dependencies_being_installed.append(tool_dependency) return dependencies_being_installed @property - def tool_dependencies_installed_or_in_error( self ): + def tool_dependencies_installed_or_in_error(self): """Return the repository's tool dependencies that are currently installed, but possibly in an error state.""" installed_dependencies = [] for tool_dependency in self.tool_dependencies: - if tool_dependency.status in [ ToolDependency.installation_status.INSTALLED, - ToolDependency.installation_status.ERROR ]: - installed_dependencies.append( tool_dependency ) + if tool_dependency.status in [ToolDependency.installation_status.INSTALLED, + ToolDependency.installation_status.ERROR]: + installed_dependencies.append(tool_dependency) return installed_dependencies @property - def tool_dependencies_missing_or_being_installed( self ): + def tool_dependencies_missing_or_being_installed(self): dependencies_missing_or_being_installed = [] for tool_dependency in self.tool_dependencies: - if tool_dependency.status in [ ToolDependency.installation_status.ERROR, - ToolDependency.installation_status.INSTALLING, - ToolDependency.installation_status.NEVER_INSTALLED, - ToolDependency.installation_status.UNINSTALLED ]: - dependencies_missing_or_being_installed.append( tool_dependency ) + if tool_dependency.status in [ToolDependency.installation_status.ERROR, + ToolDependency.installation_status.INSTALLING, + ToolDependency.installation_status.NEVER_INSTALLED, + ToolDependency.installation_status.UNINSTALLED]: + dependencies_missing_or_being_installed.append(tool_dependency) return dependencies_missing_or_being_installed @property - def tool_dependencies_with_installation_errors( self ): + def tool_dependencies_with_installation_errors(self): dependencies_with_installation_errors = [] for tool_dependency in self.tool_dependencies: if tool_dependency.status == ToolDependency.installation_status.ERROR: - dependencies_with_installation_errors.append( tool_dependency ) + dependencies_with_installation_errors.append(tool_dependency) return dependencies_with_installation_errors @property - def tool_shed_path_name( self ): + def tool_shed_path_name(self): tool_shed_url = self.tool_shed - if tool_shed_url.find( ':' ) > 0: + if tool_shed_url.find(':') > 0: # Eliminate the port, if any, since it will result in an invalid directory name. - tool_shed_url = tool_shed_url.split( ':' )[ 0 ] - return tool_shed_url.rstrip( '/' ) + tool_shed_url = tool_shed_url.split(':')[0] + return tool_shed_url.rstrip('/') @property - def tuples_of_repository_dependencies_needed_for_compiling_td( self ): + def tuples_of_repository_dependencies_needed_for_compiling_td(self): """ Return tuples defining this repository's repository dependencies that are necessary only for compiling this repository's tool dependencies. """ rd_tups_of_repositories_needed_for_compiling_td = [] if self.metadata: - repository_dependencies = self.metadata.get( 'repository_dependencies', None ) - rd_tups = repository_dependencies[ 'repository_dependencies' ] + repository_dependencies = self.metadata.get('repository_dependencies', None) + rd_tups = repository_dependencies['repository_dependencies'] for rd_tup in rd_tups: - if len( rd_tup ) == 6: + if len(rd_tup) == 6: tool_shed, name, owner, changeset_revision, prior_installation_required, only_if_compiling_contained_td = rd_tup - if asbool( only_if_compiling_contained_td ): - rd_tups_of_repositories_needed_for_compiling_td.append( ( tool_shed, name, owner, changeset_revision, 'False', 'True' ) ) + if asbool(only_if_compiling_contained_td): + rd_tups_of_repositories_needed_for_compiling_td.append((tool_shed, name, owner, changeset_revision, 'False', 'True')) return rd_tups_of_repositories_needed_for_compiling_td @property - def uninstalled_repository_dependencies( self ): + def uninstalled_repository_dependencies(self): """Return the repository's repository dependencies that have been uninstalled.""" uninstalled_required_repositories = [] for required_repository in self.repository_dependencies: if required_repository.status == self.installation_status.UNINSTALLED: - uninstalled_required_repositories.append( required_repository ) + uninstalled_required_repositories.append(required_repository) return uninstalled_required_repositories @property - def uninstalled_tool_dependencies( self ): + def uninstalled_tool_dependencies(self): """Return the repository's tool dependencies that have been uninstalled.""" uninstalled_tool_dependencies = [] for tool_dependency in self.tool_dependencies: if tool_dependency.status == ToolDependency.installation_status.UNINSTALLED: - uninstalled_tool_dependencies.append( tool_dependency ) + uninstalled_tool_dependencies.append(tool_dependency) return uninstalled_tool_dependencies @property - def upgrade_available( self ): + def upgrade_available(self): if self.tool_shed_status: if self.is_deprecated_in_tool_shed: # Only allow revision upgrades if the repository is not deprecated in the tool shed. return False - return asbool( self.tool_shed_status.get( 'revision_upgrade', False ) ) + return asbool(self.tool_shed_status.get('revision_upgrade', False)) return False -class RepositoryRepositoryDependencyAssociation( object ): +class RepositoryRepositoryDependencyAssociation(object): - def __init__( self, tool_shed_repository_id=None, repository_dependency_id=None ): + def __init__(self, tool_shed_repository_id=None, repository_dependency_id=None): self.tool_shed_repository_id = tool_shed_repository_id self.repository_dependency_id = repository_dependency_id -class RepositoryDependency( object ): +class RepositoryDependency(object): - def __init__( self, tool_shed_repository_id=None ): + def __init__(self, tool_shed_repository_id=None): self.tool_shed_repository_id = tool_shed_repository_id -class ToolDependency( object ): - installation_status = Bunch( NEVER_INSTALLED='Never installed', - INSTALLING='Installing', - INSTALLED='Installed', - ERROR='Error', - UNINSTALLED='Uninstalled' ) +class ToolDependency(object): + installation_status = Bunch(NEVER_INSTALLED='Never installed', + INSTALLING='Installing', + INSTALLED='Installed', + ERROR='Error', + UNINSTALLED='Uninstalled') - states = Bunch( INSTALLING='running', - OK='ok', - WARNING='queued', - ERROR='error', - UNINSTALLED='deleted_new' ) + states = Bunch(INSTALLING='running', + OK='ok', + WARNING='queued', + ERROR='error', + UNINSTALLED='deleted_new') - def __init__( self, tool_shed_repository_id=None, name=None, version=None, type=None, status=None, error_message=None ): + def __init__(self, tool_shed_repository_id=None, name=None, version=None, type=None, status=None, error_message=None): self.tool_shed_repository_id = tool_shed_repository_id self.name = name self.version = version @@ -536,82 +536,82 @@ def __init__( self, tool_shed_repository_id=None, name=None, version=None, type= self.error_message = error_message @property - def can_install( self ): - return self.status in [ self.installation_status.NEVER_INSTALLED, self.installation_status.UNINSTALLED ] + def can_install(self): + return self.status in [self.installation_status.NEVER_INSTALLED, self.installation_status.UNINSTALLED] @property - def can_uninstall( self ): - return self.status in [ self.installation_status.ERROR, self.installation_status.INSTALLED ] + def can_uninstall(self): + return self.status in [self.installation_status.ERROR, self.installation_status.INSTALLED] @property - def can_update( self ): - return self.status in [ self.installation_status.NEVER_INSTALLED, - self.installation_status.INSTALLED, - self.installation_status.ERROR, - self.installation_status.UNINSTALLED ] + def can_update(self): + return self.status in [self.installation_status.NEVER_INSTALLED, + self.installation_status.INSTALLED, + self.installation_status.ERROR, + self.installation_status.UNINSTALLED] - def get_env_shell_file_path( self, app ): - installation_directory = self.installation_directory( app ) - file_path = os.path.join( installation_directory, 'env.sh' ) - if os.path.exists( file_path ): + def get_env_shell_file_path(self, app): + installation_directory = self.installation_directory(app) + file_path = os.path.join(installation_directory, 'env.sh') + if os.path.exists(file_path): return file_path return None @property - def in_error_state( self ): + def in_error_state(self): return self.status == self.installation_status.ERROR - def installation_directory( self, app ): + def installation_directory(self, app): if self.type == 'package': - return os.path.join( app.config.tool_dependency_dir, - self.name, - self.version, - self.tool_shed_repository.owner, - self.tool_shed_repository.name, - self.tool_shed_repository.installed_changeset_revision ) + return os.path.join(app.config.tool_dependency_dir, + self.name, + self.version, + self.tool_shed_repository.owner, + self.tool_shed_repository.name, + self.tool_shed_repository.installed_changeset_revision) if self.type == 'set_environment': - return os.path.join( app.config.tool_dependency_dir, - 'environment_settings', - self.name, - self.tool_shed_repository.owner, - self.tool_shed_repository.name, - self.tool_shed_repository.installed_changeset_revision ) + return os.path.join(app.config.tool_dependency_dir, + 'environment_settings', + self.name, + self.tool_shed_repository.owner, + self.tool_shed_repository.name, + self.tool_shed_repository.installed_changeset_revision) @property - def is_installed( self ): + def is_installed(self): return self.status == self.installation_status.INSTALLED -class ToolVersion( object, Dictifiable ): - dict_element_visible_keys = ( 'id', 'tool_shed_repository' ) +class ToolVersion(object, Dictifiable): + dict_element_visible_keys = ('id', 'tool_shed_repository') - def __init__( self, id=None, create_time=None, tool_id=None, tool_shed_repository=None ): + def __init__(self, id=None, create_time=None, tool_id=None, tool_shed_repository=None): self.id = id self.create_time = create_time self.tool_id = tool_id self.tool_shed_repository = tool_shed_repository - def to_dict( self, view='element' ): - rval = super( ToolVersion, self ).to_dict( view=view ) - rval[ 'tool_name' ] = self.tool_id + def to_dict(self, view='element'): + rval = super(ToolVersion, self).to_dict(view=view) + rval['tool_name'] = self.tool_id for a in self.parent_tool_association: - rval[ 'parent_tool_id' ] = a.parent_id + rval['parent_tool_id'] = a.parent_id for a in self.child_tool_association: - rval[ 'child_tool_id' ] = a.tool_id + rval['child_tool_id'] = a.tool_id return rval -class ToolVersionAssociation( object ): +class ToolVersionAssociation(object): - def __init__( self, id=None, tool_id=None, parent_id=None ): + def __init__(self, id=None, tool_id=None, parent_id=None): self.id = id self.tool_id = tool_id self.parent_id = parent_id -class MigrateTools( object ): +class MigrateTools(object): - def __init__( self, repository_id=None, repository_path=None, version=None ): + def __init__(self, repository_id=None, repository_path=None, version=None): self.repository_id = repository_id self.repository_path = repository_path self.version = version diff --git a/lib/galaxy/model/tool_shed_install/mapping.py b/lib/galaxy/model/tool_shed_install/mapping.py index 8c4e5f438f96..268103833b4b 100644 --- a/lib/galaxy/model/tool_shed_install/mapping.py +++ b/lib/galaxy/model/tool_shed_install/mapping.py @@ -25,107 +25,107 @@ metadata = MetaData() -install_model.ToolShedRepository.table = Table( "tool_shed_repository", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "tool_shed", TrimmedString( 255 ), index=True ), - Column( "name", TrimmedString( 255 ), index=True ), - Column( "description", TEXT ), - Column( "owner", TrimmedString( 255 ), index=True ), - Column( "installed_changeset_revision", TrimmedString( 255 ) ), - Column( "changeset_revision", TrimmedString( 255 ), index=True ), - Column( "ctx_rev", TrimmedString( 10 ) ), - Column( "metadata", JSONType, nullable=True ), - Column( "includes_datatypes", Boolean, index=True, default=False ), - Column( "tool_shed_status", JSONType, nullable=True ), - Column( "deleted", Boolean, index=True, default=False ), - Column( "uninstalled", Boolean, default=False ), - Column( "dist_to_shed", Boolean, default=False ), - Column( "status", TrimmedString( 255 ) ), - Column( "error_message", TEXT ) ) - -install_model.RepositoryRepositoryDependencyAssociation.table = Table( 'repository_repository_dependency_association', metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "tool_shed_repository_id", Integer, ForeignKey( "tool_shed_repository.id" ), index=True ), - Column( "repository_dependency_id", Integer, ForeignKey( "repository_dependency.id" ), index=True ) ) - -install_model.RepositoryDependency.table = Table( "repository_dependency", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "tool_shed_repository_id", Integer, ForeignKey( "tool_shed_repository.id" ), index=True, nullable=False ) ) - -install_model.ToolDependency.table = Table( "tool_dependency", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "tool_shed_repository_id", Integer, ForeignKey( "tool_shed_repository.id" ), index=True, nullable=False ), - Column( "name", TrimmedString( 255 ) ), - Column( "version", TEXT ), - Column( "type", TrimmedString( 40 ) ), - Column( "status", TrimmedString( 255 ), nullable=False ), - Column( "error_message", TEXT ) ) - -install_model.ToolVersion.table = Table( "tool_version", metadata, - Column( "id", Integer, primary_key=True ), - Column( "create_time", DateTime, default=now ), - Column( "update_time", DateTime, default=now, onupdate=now ), - Column( "tool_id", String( 255 ) ), - Column( "tool_shed_repository_id", Integer, ForeignKey( "tool_shed_repository.id" ), index=True, nullable=True ) ) - -install_model.ToolVersionAssociation.table = Table( "tool_version_association", metadata, - Column( "id", Integer, primary_key=True ), - Column( "tool_id", Integer, ForeignKey( "tool_version.id" ), index=True, nullable=False ), - Column( "parent_id", Integer, ForeignKey( "tool_version.id" ), index=True, nullable=False ) ) - -install_model.MigrateTools.table = Table( "migrate_tools", metadata, - Column( "repository_id", TrimmedString( 255 ) ), - Column( "repository_path", TEXT ), - Column( "version", Integer ) ) - -mapper( install_model.ToolShedRepository, install_model.ToolShedRepository.table, - properties=dict( tool_versions=relation( install_model.ToolVersion, - primaryjoin=( install_model.ToolShedRepository.table.c.id == install_model.ToolVersion.table.c.tool_shed_repository_id ), - backref='tool_shed_repository' ), - tool_dependencies=relation( install_model.ToolDependency, - primaryjoin=( install_model.ToolShedRepository.table.c.id == install_model.ToolDependency.table.c.tool_shed_repository_id ), - order_by=install_model.ToolDependency.table.c.name, - backref='tool_shed_repository' ), - required_repositories=relation( install_model.RepositoryRepositoryDependencyAssociation, - primaryjoin=( install_model.ToolShedRepository.table.c.id == install_model.RepositoryRepositoryDependencyAssociation.table.c.tool_shed_repository_id ) ) ) ) - -mapper( install_model.RepositoryRepositoryDependencyAssociation, install_model.RepositoryRepositoryDependencyAssociation.table, - properties=dict( repository=relation( install_model.ToolShedRepository, - primaryjoin=( install_model.RepositoryRepositoryDependencyAssociation.table.c.tool_shed_repository_id == install_model.ToolShedRepository.table.c.id ) ), - repository_dependency=relation( install_model.RepositoryDependency, - primaryjoin=( install_model.RepositoryRepositoryDependencyAssociation.table.c.repository_dependency_id == install_model.RepositoryDependency.table.c.id ) ) ) ) - -mapper( install_model.RepositoryDependency, install_model.RepositoryDependency.table, - properties=dict( repository=relation( install_model.ToolShedRepository, - primaryjoin=( install_model.RepositoryDependency.table.c.tool_shed_repository_id == install_model.ToolShedRepository.table.c.id ) ) ) ) - -mapper( install_model.ToolDependency, install_model.ToolDependency.table ) - -mapper( install_model.ToolVersion, install_model.ToolVersion.table, - properties=dict( - parent_tool_association=relation( install_model.ToolVersionAssociation, - primaryjoin=( install_model.ToolVersion.table.c.id == install_model.ToolVersionAssociation.table.c.tool_id ) ), - child_tool_association=relation( install_model.ToolVersionAssociation, - primaryjoin=( install_model.ToolVersion.table.c.id == install_model.ToolVersionAssociation.table.c.parent_id ) ) ) ) - -mapper( install_model.ToolVersionAssociation, install_model.ToolVersionAssociation.table ) - - -def init( url, engine_options={}, create_tables=False ): +install_model.ToolShedRepository.table = Table("tool_shed_repository", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("tool_shed", TrimmedString(255), index=True), + Column("name", TrimmedString(255), index=True), + Column("description", TEXT), + Column("owner", TrimmedString(255), index=True), + Column("installed_changeset_revision", TrimmedString(255)), + Column("changeset_revision", TrimmedString(255), index=True), + Column("ctx_rev", TrimmedString(10)), + Column("metadata", JSONType, nullable=True), + Column("includes_datatypes", Boolean, index=True, default=False), + Column("tool_shed_status", JSONType, nullable=True), + Column("deleted", Boolean, index=True, default=False), + Column("uninstalled", Boolean, default=False), + Column("dist_to_shed", Boolean, default=False), + Column("status", TrimmedString(255)), + Column("error_message", TEXT)) + +install_model.RepositoryRepositoryDependencyAssociation.table = Table('repository_repository_dependency_association', metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("tool_shed_repository_id", Integer, ForeignKey("tool_shed_repository.id"), index=True), + Column("repository_dependency_id", Integer, ForeignKey("repository_dependency.id"), index=True)) + +install_model.RepositoryDependency.table = Table("repository_dependency", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("tool_shed_repository_id", Integer, ForeignKey("tool_shed_repository.id"), index=True, nullable=False)) + +install_model.ToolDependency.table = Table("tool_dependency", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("tool_shed_repository_id", Integer, ForeignKey("tool_shed_repository.id"), index=True, nullable=False), + Column("name", TrimmedString(255)), + Column("version", TEXT), + Column("type", TrimmedString(40)), + Column("status", TrimmedString(255), nullable=False), + Column("error_message", TEXT)) + +install_model.ToolVersion.table = Table("tool_version", metadata, + Column("id", Integer, primary_key=True), + Column("create_time", DateTime, default=now), + Column("update_time", DateTime, default=now, onupdate=now), + Column("tool_id", String(255)), + Column("tool_shed_repository_id", Integer, ForeignKey("tool_shed_repository.id"), index=True, nullable=True)) + +install_model.ToolVersionAssociation.table = Table("tool_version_association", metadata, + Column("id", Integer, primary_key=True), + Column("tool_id", Integer, ForeignKey("tool_version.id"), index=True, nullable=False), + Column("parent_id", Integer, ForeignKey("tool_version.id"), index=True, nullable=False)) + +install_model.MigrateTools.table = Table("migrate_tools", metadata, + Column("repository_id", TrimmedString(255)), + Column("repository_path", TEXT), + Column("version", Integer)) + +mapper(install_model.ToolShedRepository, install_model.ToolShedRepository.table, + properties=dict(tool_versions=relation(install_model.ToolVersion, + primaryjoin=(install_model.ToolShedRepository.table.c.id == install_model.ToolVersion.table.c.tool_shed_repository_id), + backref='tool_shed_repository'), + tool_dependencies=relation(install_model.ToolDependency, + primaryjoin=(install_model.ToolShedRepository.table.c.id == install_model.ToolDependency.table.c.tool_shed_repository_id), + order_by=install_model.ToolDependency.table.c.name, + backref='tool_shed_repository'), + required_repositories=relation(install_model.RepositoryRepositoryDependencyAssociation, + primaryjoin=(install_model.ToolShedRepository.table.c.id == install_model.RepositoryRepositoryDependencyAssociation.table.c.tool_shed_repository_id)))) + +mapper(install_model.RepositoryRepositoryDependencyAssociation, install_model.RepositoryRepositoryDependencyAssociation.table, + properties=dict(repository=relation(install_model.ToolShedRepository, + primaryjoin=(install_model.RepositoryRepositoryDependencyAssociation.table.c.tool_shed_repository_id == install_model.ToolShedRepository.table.c.id)), + repository_dependency=relation(install_model.RepositoryDependency, + primaryjoin=(install_model.RepositoryRepositoryDependencyAssociation.table.c.repository_dependency_id == install_model.RepositoryDependency.table.c.id)))) + +mapper(install_model.RepositoryDependency, install_model.RepositoryDependency.table, + properties=dict(repository=relation(install_model.ToolShedRepository, + primaryjoin=(install_model.RepositoryDependency.table.c.tool_shed_repository_id == install_model.ToolShedRepository.table.c.id)))) + +mapper(install_model.ToolDependency, install_model.ToolDependency.table) + +mapper(install_model.ToolVersion, install_model.ToolVersion.table, + properties=dict( + parent_tool_association=relation(install_model.ToolVersionAssociation, + primaryjoin=(install_model.ToolVersion.table.c.id == install_model.ToolVersionAssociation.table.c.tool_id)), + child_tool_association=relation(install_model.ToolVersionAssociation, + primaryjoin=(install_model.ToolVersion.table.c.id == install_model.ToolVersionAssociation.table.c.parent_id)))) + +mapper(install_model.ToolVersionAssociation, install_model.ToolVersionAssociation.table) + + +def init(url, engine_options={}, create_tables=False): """Connect mappings to the database""" # Load the appropriate db module - engine = build_engine( url, engine_options ) + engine = build_engine(url, engine_options) # Connect the metadata to the database. metadata.bind = engine - result = ModelMapping( [ install_model ], engine=engine ) + result = ModelMapping([install_model], engine=engine) # Create tables if needed if create_tables: metadata.create_all() diff --git a/lib/galaxy/model/tool_shed_install/migrate/check.py b/lib/galaxy/model/tool_shed_install/migrate/check.py index 478f8fa61362..36e40483c8d2 100644 --- a/lib/galaxy/model/tool_shed_install/migrate/check.py +++ b/lib/galaxy/model/tool_shed_install/migrate/check.py @@ -13,37 +13,37 @@ ) from sqlalchemy.exc import NoSuchTableError -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) # path relative to galaxy -migrate_repository_directory = os.path.abspath(os.path.dirname( __file__ )).replace( os.getcwd() + os.path.sep, '', 1 ) -migrate_repository = repository.Repository( migrate_repository_directory ) +migrate_repository_directory = os.path.abspath(os.path.dirname(__file__)).replace(os.getcwd() + os.path.sep, '', 1) +migrate_repository = repository.Repository(migrate_repository_directory) -def create_or_verify_database( url, engine_options={}, app=None ): +def create_or_verify_database(url, engine_options={}, app=None): """ """ # Create engine and metadata - engine = create_engine( url, **engine_options ) + engine = create_engine(url, **engine_options) def migrate(): try: # Declare the database to be under a repository's version control - db_schema = schema.ControlledSchema.create( engine, migrate_repository ) + db_schema = schema.ControlledSchema.create(engine, migrate_repository) except: # The database is already under version control - db_schema = schema.ControlledSchema( engine, migrate_repository ) + db_schema = schema.ControlledSchema(engine, migrate_repository) # Apply all scripts to get to current version - migrate_to_current_version( engine, db_schema ) + migrate_to_current_version(engine, db_schema) - meta = MetaData( bind=engine ) - if app and getattr( app.config, 'database_auto_migrate', False ): + meta = MetaData(bind=engine) + if app and getattr(app.config, 'database_auto_migrate', False): migrate() return # Try to load tool_shed_repository table try: - Table( "tool_shed_repository", meta, autoload=True ) + Table("tool_shed_repository", meta, autoload=True) except NoSuchTableError: # No table means a completely uninitialized database. If we # have an app, we'll set its new_installation setting to True @@ -52,49 +52,49 @@ def migrate(): return try: - Table( "migrate_version", meta, autoload=True ) + Table("migrate_version", meta, autoload=True) except NoSuchTableError: # The database exists but is not yet under migrate version control, so init with version 1 - log.info( "Adding version control to existing database" ) + log.info("Adding version control to existing database") try: - Table( "metadata_file", meta, autoload=True ) - schema.ControlledSchema.create( engine, migrate_repository, version=2 ) + Table("metadata_file", meta, autoload=True) + schema.ControlledSchema.create(engine, migrate_repository, version=2) except NoSuchTableError: - schema.ControlledSchema.create( engine, migrate_repository, version=1 ) + schema.ControlledSchema.create(engine, migrate_repository, version=1) # Verify that the code and the DB are in sync - db_schema = schema.ControlledSchema( engine, migrate_repository ) + db_schema = schema.ControlledSchema(engine, migrate_repository) if migrate_repository.versions.latest != db_schema.version: - exception_msg = "Your database has version '%d' but this code expects version '%d'. " % ( db_schema.version, migrate_repository.versions.latest ) + exception_msg = "Your database has version '%d' but this code expects version '%d'. " % (db_schema.version, migrate_repository.versions.latest) exception_msg += "Back up your database and then migrate the schema by running the following from your Galaxy installation directory:" exception_msg += "\n\nsh manage_db.sh upgrade install\n" else: - log.info( "At database version %d" % db_schema.version ) + log.info("At database version %d" % db_schema.version) -def migrate_to_current_version( engine, schema ): +def migrate_to_current_version(engine, schema): # Changes to get to current version - changeset = schema.changeset( None ) + changeset = schema.changeset(None) for ver, change in changeset: nextver = ver + changeset.step - log.info( 'Migrating %s -> %s... ' % ( ver, nextver ) ) + log.info('Migrating %s -> %s... ' % (ver, nextver)) old_stdout = sys.stdout - class FakeStdout( object ): - def __init__( self ): + class FakeStdout(object): + def __init__(self): self.buffer = [] - def write( self, s ): - self.buffer.append( s ) + def write(self, s): + self.buffer.append(s) - def flush( self ): + def flush(self): pass sys.stdout = FakeStdout() try: - schema.runchange( ver, change, changeset.step ) + schema.runchange(ver, change, changeset.step) finally: - for message in "".join( sys.stdout.buffer ).split( "\n" ): - log.info( message ) + for message in "".join(sys.stdout.buffer).split("\n"): + log.info(message) sys.stdout = old_stdout diff --git a/lib/galaxy/model/util.py b/lib/galaxy/model/util.py index 7295589ab446..6b7131261940 100644 --- a/lib/galaxy/model/util.py +++ b/lib/galaxy/model/util.py @@ -3,7 +3,7 @@ """ -def pgcalc( sa_session, id, dryrun=False ): +def pgcalc(sa_session, id, dryrun=False): """ Utility method for quickly recalculating user disk usage in postgres. diff --git a/lib/galaxy/objectstore/__init__.py b/lib/galaxy/objectstore/__init__.py index 30fc086c3cc5..6dfb49a2d01c 100644 --- a/lib/galaxy/objectstore/__init__.py +++ b/lib/galaxy/objectstore/__init__.py @@ -31,7 +31,7 @@ NO_SESSION_ERROR_MESSAGE = "Attempted to 'create' object store entity in configuration with no database session present." -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) class ObjectStore(object): @@ -250,7 +250,7 @@ def __init__(self, config, config_xml=None, file_path=None, extra_dirs=None): else: self.extra_dirs[e.get('type')] = e.get('path') if extra_dirs is not None: - self.extra_dirs.update( extra_dirs ) + self.extra_dirs.update(extra_dirs) def _get_filename(self, obj, base_dir=None, dir_only=False, extra_dir=None, extra_dir_at_root=False, alt_name=None, obj_dir=False): """ @@ -413,7 +413,7 @@ def get_filename(self, obj, **kwargs): def update_from_file(self, obj, file_name=None, create=False, **kwargs): """`create` parameter is not used in this implementation.""" - preserve_symlinks = kwargs.pop( 'preserve_symlinks', False ) + preserve_symlinks = kwargs.pop('preserve_symlinks', False) # FIXME: symlinks and the object store model may not play well together # these should be handled better, e.g. registering the symlink'd file # as an object @@ -421,10 +421,10 @@ def update_from_file(self, obj, file_name=None, create=False, **kwargs): self.create(obj, **kwargs) if file_name and self.exists(obj, **kwargs): try: - if preserve_symlinks and os.path.islink( file_name ): - force_symlink( os.readlink( file_name ), self.get_filename( obj, **kwargs ) ) + if preserve_symlinks and os.path.islink(file_name): + force_symlink(os.readlink(file_name), self.get_filename(obj, **kwargs)) else: - shutil.copy( file_name, self.get_filename( obj, **kwargs ) ) + shutil.copy(file_name, self.get_filename(obj, **kwargs)) except IOError as ex: log.critical('Error copying %s to %s: %s' % (file_name, self._get_filename(obj, **kwargs), ex)) raise ex @@ -440,7 +440,7 @@ def get_object_url(self, obj, **kwargs): def get_store_usage_percent(self): """Override `ObjectStore`'s stub by return percent storage used.""" st = os.statvfs(self.file_path) - return ( float( st.f_blocks - st.f_bavail ) / st.f_blocks ) * 100 + return (float(st.f_blocks - st.f_bavail) / st.f_blocks) * 100 class NestedObjectStore(ObjectStore): @@ -513,7 +513,7 @@ def _call_method(self, method, obj, default, default_is_exception, return store.__getattribute__(method)(obj, **kwargs) if default_is_exception: raise default('objectstore, _call_method failed: %s on %s, kwargs: %s' - % ( method, str( obj ), str( kwargs ) ) ) + % (method, str(obj), str(kwargs))) else: return default @@ -559,10 +559,10 @@ def __init__(self, config, config_xml=None, fsmon=False): random.seed() self.__parse_distributed_config(config, config_xml) self.sleeper = None - if fsmon and ( self.global_max_percent_full or [_ for _ in self.max_percent_full.values() if _ != 0.0] ): + if fsmon and (self.global_max_percent_full or [_ for _ in self.max_percent_full.values() if _ != 0.0]): self.sleeper = Sleeper() self.filesystem_monitor_thread = threading.Thread(target=self.__filesystem_monitor) - self.filesystem_monitor_thread.setDaemon( True ) + self.filesystem_monitor_thread.setDaemon(True) self.filesystem_monitor_thread.start() log.info("Filesystem space monitor started") @@ -574,7 +574,7 @@ def __parse_distributed_config(self, config, config_xml=None): root = config_xml.find('backends') log.debug('Loading backends for distributed object store from %s', config_xml.get('id')) self.global_max_percent_full = float(root.get('maxpctfull', 0)) - for elem in [ e for e in root if e.tag == 'backend' ]: + for elem in [e for e in root if e.tag == 'backend']: id = elem.get('id') weight = int(elem.get('weight', 1)) maxpctfull = float(elem.get('maxpctfull', 0)) @@ -627,8 +627,8 @@ def create(self, obj, **kwargs): except IndexError: raise ObjectInvalid('objectstore.create, could not generate ' 'obj.object_store_id: %s, kwargs: %s' - % ( str( obj ), str( kwargs ) ) ) - _create_object_in_session( obj ) + % (str(obj), str(kwargs))) + _create_object_in_session(obj) log.debug("Selected backend '%s' for creation of %s %s" % (obj.object_store_id, obj.__class__.__name__, obj.id)) else: @@ -642,7 +642,7 @@ def _call_method(self, method, obj, default, default_is_exception, **kwargs): return self.backends[object_store_id].__getattribute__(method)(obj, **kwargs) if default_is_exception: raise default('objectstore, _call_method failed: %s on %s, kwargs: %s' - % ( method, str( obj ), str( kwargs ) ) ) + % (method, str(obj), str(kwargs))) else: return default @@ -660,7 +660,7 @@ def __get_store_id_for(self, obj, **kwargs): log.warning('%s object with ID %s found in backend object store with ID %s' % (obj.__class__.__name__, obj.id, id)) obj.object_store_id = id - _create_object_in_session( obj ) + _create_object_in_session(obj) return id return None @@ -705,7 +705,7 @@ def build_object_store_from_config(config, fsmon=False, config_xml=None): 'hierarchical', 'irods', and 'pulsar' are supported values. """ - if config_xml is None and os.path.exists( config.object_store_config_file ): + if config_xml is None and os.path.exists(config.object_store_config_file): # This is a top level invocation of build_object_store_from_config, and # we have an object_store_conf.xml -- read the .xml and build # accordingly @@ -722,6 +722,9 @@ def build_object_store_from_config(config, fsmon=False, config_xml=None): elif store == 's3': from .s3 import S3ObjectStore return S3ObjectStore(config=config, config_xml=config_xml) + elif store == 'cloud': + from .cloud import Cloud + return Cloud(config=config, config_xml=config_xml) elif store == 'swift': from .s3 import SwiftObjectStore return SwiftObjectStore(config=config, config_xml=config_xml) @@ -747,18 +750,19 @@ def build_object_store_from_config(config, fsmon=False, config_xml=None): log.error("Unrecognized object store definition: {0}".format(store)) -def local_extra_dirs( func ): +def local_extra_dirs(func): """Non-local plugin decorator using local directories for the extra_dirs (job_work and temp).""" - def wraps( self, *args, **kwargs ): - if kwargs.get( 'base_dir', None ) is None: - return func( self, *args, **kwargs ) + + def wraps(self, *args, **kwargs): + if kwargs.get('base_dir', None) is None: + return func(self, *args, **kwargs) else: for c in self.__class__.__mro__: if c.__name__ == 'DiskObjectStore': - return getattr( c, func.__name__ )( self, *args, **kwargs ) + return getattr(c, func.__name__)(self, *args, **kwargs) raise Exception("Could not call DiskObjectStore's %s method, does your " "Object Store plugin inherit from DiskObjectStore?" - % func.__name__ ) + % func.__name__) return wraps @@ -785,10 +789,10 @@ def convert_bytes(bytes): return size -def _create_object_in_session( obj ): - session = object_session( obj ) if object_session is not None else None +def _create_object_in_session(obj): + session = object_session(obj) if object_session is not None else None if session is not None: - object_session( obj ).add( obj ) - object_session( obj ).flush() + object_session(obj).add(obj) + object_session(obj).flush() else: - raise Exception( NO_SESSION_ERROR_MESSAGE ) + raise Exception(NO_SESSION_ERROR_MESSAGE) diff --git a/lib/galaxy/objectstore/azure_blob.py b/lib/galaxy/objectstore/azure_blob.py index 533a546b6505..4afd8751eb73 100644 --- a/lib/galaxy/objectstore/azure_blob.py +++ b/lib/galaxy/objectstore/azure_blob.py @@ -26,7 +26,7 @@ NO_BLOBSERVICE_ERROR_MESSAGE = ("ObjectStore configured, but no azure.storage.blob dependency available." "Please install and properly configure azure.storage.blob or modify Object Store configuration.") -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) class AzureBlobObjectStore(ObjectStore): @@ -35,6 +35,7 @@ class AzureBlobObjectStore(ObjectStore): cache exists that is used as an intermediate location for files between Galaxy and Azure. """ + def __init__(self, config, config_xml): if BlockBlobService is None: raise Exception(NO_BLOBSERVICE_ERROR_MESSAGE) @@ -328,7 +329,7 @@ def empty(self, obj, **kwargs): if self.exists(obj, **kwargs): return bool(self.size(obj, **kwargs) > 0) else: - raise ObjectNotFound( 'objectstore.empty, object does not exist: %s, kwargs: %s' % ( str( obj ), str( kwargs ) ) ) + raise ObjectNotFound('objectstore.empty, object does not exist: %s, kwargs: %s' % (str(obj), str(kwargs))) def size(self, obj, **kwargs): rel_path = self._construct_path(obj, **kwargs) @@ -424,7 +425,7 @@ def get_filename(self, obj, **kwargs): # even if it does not exist. # if dir_only: # return cache_path - raise ObjectNotFound( 'objectstore.get_filename, no cache_path: %s, kwargs: %s' % ( str( obj ), str( kwargs ) ) ) + raise ObjectNotFound('objectstore.get_filename, no cache_path: %s, kwargs: %s' % (str(obj), str(kwargs))) return cache_path # Until the upload tool does not explicitly create the dataset, return expected path @@ -451,7 +452,7 @@ def update_from_file(self, obj, file_name=None, create=False, **kwargs): self._push_to_os(rel_path, source_file) else: - raise ObjectNotFound( 'objectstore.update_from_file, object does not exist: %s, kwargs: %s' % ( str( obj ), str( kwargs ) ) ) + raise ObjectNotFound('objectstore.update_from_file, object does not exist: %s, kwargs: %s' % (str(obj), str(kwargs))) def get_object_url(self, obj, **kwargs): if self.exists(obj, **kwargs): diff --git a/lib/galaxy/objectstore/cloud.py b/lib/galaxy/objectstore/cloud.py new file mode 100644 index 000000000000..10d2951c74f1 --- /dev/null +++ b/lib/galaxy/objectstore/cloud.py @@ -0,0 +1,566 @@ +""" +Object Store plugin for Cloud storage. +""" + +import logging +import multiprocessing +import os +import shutil +import subprocess +import threading +import time + +from datetime import datetime + +from galaxy.exceptions import ObjectInvalid, ObjectNotFound +from galaxy.util import ( + directory_hash_id, + safe_relpath, + string_as_bool, + umask_fix_perms, +) +from galaxy.util.sleeper import Sleeper + +from ..objectstore import convert_bytes, ObjectStore +from cloudbridge.cloud.factory import CloudProviderFactory, ProviderList + +NO_BOTO_ERROR_MESSAGE = ("Cloud object store is configured, but no boto dependency available." + "Please install and properly configure boto or modify object store configuration.") + +log = logging.getLogger(__name__) +logging.getLogger('boto').setLevel(logging.INFO) # Otherwise boto is quite noisy + + +class Cloud(ObjectStore): + """ + Object store that stores objects as items in an cloud storage. A local + cache exists that is used as an intermediate location for files between + Galaxy and the cloud storage. + """ + def __init__(self, config, config_xml): + super(Cloud, self).__init__(config) + self.staging_path = self.config.file_path + self.transfer_progress = 0 + self._parse_config_xml(config_xml) + self._configure_connection() + self.bucket = self._get_bucket(self.bucket) + # Clean cache only if value is set in galaxy.ini + if self.cache_size != -1: + # Convert GBs to bytes for comparison + self.cache_size = self.cache_size * 1073741824 + # Helper for interruptable sleep + self.sleeper = Sleeper() + self.cache_monitor_thread = threading.Thread(target=self.__cache_monitor) + self.cache_monitor_thread.start() + log.info("Cache cleaner manager started") + # Test if 'axel' is available for parallel download and pull the key into cache + try: + subprocess.call('axel') + self.use_axel = True + except OSError: + self.use_axel = False + + def _configure_connection(self): + log.debug("Configuring AWS-S3 Connection") + aws_config = {'aws_access_key': self.access_key, + 'aws_secret_key': self.secret_key} + self.conn = CloudProviderFactory().create_provider(ProviderList.AWS, aws_config) + + def _parse_config_xml(self, config_xml): + try: + a_xml = config_xml.findall('auth')[0] + self.access_key = a_xml.get('access_key') + self.secret_key = a_xml.get('secret_key') + b_xml = config_xml.findall('bucket')[0] + self.bucket = b_xml.get('name') + self.max_chunk_size = int(b_xml.get('max_chunk_size', 250)) + cn_xml = config_xml.findall('connection') + if not cn_xml: + cn_xml = {} + else: + cn_xml = cn_xml[0] + self.host = cn_xml.get('host', None) + self.port = int(cn_xml.get('port', 6000)) + self.multipart = string_as_bool(cn_xml.get('multipart', 'True')) + self.is_secure = string_as_bool(cn_xml.get('is_secure', 'True')) + self.conn_path = cn_xml.get('conn_path', '/') + c_xml = config_xml.findall('cache')[0] + self.cache_size = float(c_xml.get('size', -1)) + self.staging_path = c_xml.get('path', self.config.object_store_cache_path) + + for d_xml in config_xml.findall('extra_dir'): + self.extra_dirs[d_xml.get('type')] = d_xml.get('path') + + log.debug("Object cache dir: %s", self.staging_path) + log.debug(" job work dir: %s", self.extra_dirs['job_work']) + + except Exception: + # Toss it back up after logging, we can't continue loading at this point. + log.exception("Malformed ObjectStore Configuration XML -- unable to continue") + raise + + def __cache_monitor(self): + time.sleep(2) # Wait for things to load before starting the monitor + while self.running: + total_size = 0 + # Is this going to be too expensive of an operation to be done frequently? + file_list = [] + for dirpath, _, filenames in os.walk(self.staging_path): + for filename in filenames: + filepath = os.path.join(dirpath, filename) + file_size = os.path.getsize(filepath) + total_size += file_size + # Get the time given file was last accessed + last_access_time = time.localtime(os.stat(filepath)[7]) + # Compose a tuple of the access time and the file path + file_tuple = last_access_time, filepath, file_size + file_list.append(file_tuple) + # Sort the file list (based on access time) + file_list.sort() + # Initiate cleaning once within 10% of the defined cache size? + cache_limit = self.cache_size * 0.9 + if total_size > cache_limit: + log.info("Initiating cache cleaning: current cache size: %s; clean until smaller than: %s", + convert_bytes(total_size), convert_bytes(cache_limit)) + # How much to delete? If simply deleting up to the cache-10% limit, + # is likely to be deleting frequently and may run the risk of hitting + # the limit - maybe delete additional #%? + # For now, delete enough to leave at least 10% of the total cache free + delete_this_much = total_size - cache_limit + self.__clean_cache(file_list, delete_this_much) + self.sleeper.sleep(30) # Test cache size every 30 seconds? + + def __clean_cache(self, file_list, delete_this_much): + """ Keep deleting files from the file_list until the size of the deleted + files is greater than the value in delete_this_much parameter. + + :type file_list: list + :param file_list: List of candidate files that can be deleted. This method + will start deleting files from the beginning of the list so the list + should be sorted accordingly. The list must contains 3-element tuples, + positioned as follows: position 0 holds file last accessed timestamp + (as time.struct_time), position 1 holds file path, and position 2 has + file size (e.g., (, /mnt/data/dataset_1.dat), 472394) + + :type delete_this_much: int + :param delete_this_much: Total size of files, in bytes, that should be deleted. + """ + # Keep deleting datasets from file_list until deleted_amount does not + # exceed delete_this_much; start deleting from the front of the file list, + # which assumes the oldest files come first on the list. + deleted_amount = 0 + for entry in enumerate(file_list): + if deleted_amount < delete_this_much: + deleted_amount += entry[2] + os.remove(entry[1]) + # Debugging code for printing deleted files' stats + # folder, file_name = os.path.split(f[1]) + # file_date = time.strftime("%m/%d/%y %H:%M:%S", f[0]) + # log.debug("%s. %-25s %s, size %s (deleted %s/%s)" \ + # % (i, file_name, convert_bytes(f[2]), file_date, \ + # convert_bytes(deleted_amount), convert_bytes(delete_this_much))) + else: + log.debug("Cache cleaning done. Total space freed: %s", convert_bytes(deleted_amount)) + return + + def _get_bucket(self, bucket_name): + """ Sometimes a handle to a bucket is not established right away so try + it a few times. Raise error if connection is not established. """ + for i in range(5): + try: + bucket = self.conn.object_store.get(bucket_name) + if bucket is None: + log.debug("Bucket not found, creating a bucket with handle '%s'", bucket_name) + bucket = self.conn.object_store.create(bucket_name) + log.debug("Using cloud object store with bucket '%s'", bucket.name) + return bucket + except Exception: + log.exception("Could not get bucket '%s', attempt %s/5", bucket_name, i + 1) + time.sleep(2) + # All the attempts have been exhausted and connection was not established, + # raise error + raise Exception + + def _fix_permissions(self, rel_path): + """ Set permissions on rel_path""" + for basedir, _, files in os.walk(rel_path): + umask_fix_perms(basedir, self.config.umask, 0o777, self.config.gid) + for filename in files: + path = os.path.join(basedir, filename) + # Ignore symlinks + if os.path.islink(path): + continue + umask_fix_perms(path, self.config.umask, 0o666, self.config.gid) + + def _construct_path(self, obj, base_dir=None, dir_only=None, extra_dir=None, extra_dir_at_root=False, alt_name=None, + obj_dir=False, **kwargs): + # extra_dir should never be constructed from provided data but just + # make sure there are no shenannigans afoot + if extra_dir and extra_dir != os.path.normpath(extra_dir): + log.warning('extra_dir is not normalized: %s', extra_dir) + raise ObjectInvalid("The requested object is invalid") + # ensure that any parent directory references in alt_name would not + # result in a path not contained in the directory path constructed here + if alt_name: + if not safe_relpath(alt_name): + log.warning('alt_name would locate path outside dir: %s', alt_name) + raise ObjectInvalid("The requested object is invalid") + # alt_name can contain parent directory references, but S3 will not + # follow them, so if they are valid we normalize them out + alt_name = os.path.normpath(alt_name) + rel_path = os.path.join(*directory_hash_id(obj.id)) + if extra_dir is not None: + if extra_dir_at_root: + rel_path = os.path.join(extra_dir, rel_path) + else: + rel_path = os.path.join(rel_path, extra_dir) + + # for JOB_WORK directory + if obj_dir: + rel_path = os.path.join(rel_path, str(obj.id)) + if base_dir: + base = self.extra_dirs.get(base_dir) + return os.path.join(base, rel_path) + + # S3 folders are marked by having trailing '/' so add it now + rel_path = '%s/' % rel_path + + if not dir_only: + rel_path = os.path.join(rel_path, alt_name if alt_name else "dataset_%s.dat" % obj.id) + return rel_path + + def _get_cache_path(self, rel_path): + return os.path.abspath(os.path.join(self.staging_path, rel_path)) + + def _get_transfer_progress(self): + return self.transfer_progress + + def _get_size_in_cloud(self, rel_path): + try: + obj = self.bucket.get(rel_path) + if obj: + return obj.size + except Exception: + log.exception("Could not get size of key '%s' from S3", rel_path) + return -1 + + def _key_exists(self, rel_path): + exists = False + try: + # A hackish way of testing if the rel_path is a folder vs a file + is_dir = rel_path[-1] == '/' + if is_dir: + keyresult = self.bucket.list(prefix=rel_path) + if len(keyresult) > 0: + exists = True + else: + exists = False + else: + exists = True if self.bucket.get(rel_path) is not None else False + except Exception: + log.exception("Trouble checking existence of S3 key '%s'", rel_path) + return False + if rel_path[0] == '/': + raise + return exists + + def _in_cache(self, rel_path): + """ Check if the given dataset is in the local cache and return True if so. """ + # log.debug("------ Checking cache for rel_path %s" % rel_path) + cache_path = self._get_cache_path(rel_path) + return os.path.exists(cache_path) + + def _pull_into_cache(self, rel_path): + # Ensure the cache directory structure exists (e.g., dataset_#_files/) + rel_path_dir = os.path.dirname(rel_path) + if not os.path.exists(self._get_cache_path(rel_path_dir)): + os.makedirs(self._get_cache_path(rel_path_dir)) + # Now pull in the file + file_ok = self._download(rel_path) + self._fix_permissions(self._get_cache_path(rel_path_dir)) + return file_ok + + def _transfer_cb(self, complete, total): + self.transfer_progress += 10 + + def _download(self, rel_path): + try: + log.debug("Pulling key '%s' into cache to %s", rel_path, self._get_cache_path(rel_path)) + key = self.bucket.get(rel_path) + # Test if cache is large enough to hold the new file + if self.cache_size > 0 and key.size > self.cache_size: + log.critical("File %s is larger (%s) than the cache size (%s). Cannot download.", + rel_path, key.size, self.cache_size) + return False + if self.use_axel: + log.debug("Parallel pulled key '%s' into cache to %s", rel_path, self._get_cache_path(rel_path)) + ncores = multiprocessing.cpu_count() + url = key.generate_url(7200) + ret_code = subprocess.call("axel -a -n %s '%s'" % (ncores, url)) + if ret_code == 0: + return True + else: + log.debug("Pulled key '%s' into cache to %s", rel_path, self._get_cache_path(rel_path)) + self.transfer_progress = 0 # Reset transfer progress counter + with open(self._get_cache_path(rel_path), "w+") as downloaded_file_handle: + key.save_content(downloaded_file_handle) + return True + except Exception: + log.exception("Problem downloading key '%s' from S3 bucket '%s'", rel_path, self.bucket.name) + return False + + def _push_to_os(self, rel_path, source_file=None, from_string=None): + """ + Push the file pointed to by ``rel_path`` to the object store naming the key + ``rel_path``. If ``source_file`` is provided, push that file instead while + still using ``rel_path`` as the key name. + If ``from_string`` is provided, set contents of the file to the value of + the string. + """ + try: + source_file = source_file if source_file else self._get_cache_path(rel_path) + if os.path.exists(source_file): + if os.path.getsize(source_file) == 0 and (self.bucket.get(rel_path) is not None): + log.debug("Wanted to push file '%s' to S3 key '%s' but its size is 0; skipping.", source_file, + rel_path) + return True + start_time = datetime.now() + log.debug("Pushing cache file '%s' of size %s bytes to key '%s'", source_file, + os.path.getsize(source_file), rel_path) + self.transfer_progress = 0 # Reset transfer progress counter + if self.bucket.get(rel_path): + self.bucket.get(rel_path).upload(source_file) + else: + created_obj = self.bucket.create_object(rel_path) + created_obj.upload(source_file) + end_time = datetime.now() + log.debug("Pushed cache file '%s' to key '%s' (%s bytes transfered in %s sec)", + source_file, rel_path, os.path.getsize(source_file), end_time - start_time) + return True + else: + log.error("Tried updating key '%s' from source file '%s', but source file does not exist.", + rel_path, source_file) + except Exception: + log.exception("Trouble pushing S3 key '%s' from file '%s'", rel_path, source_file) + return False + + def file_ready(self, obj, **kwargs): + """ + A helper method that checks if a file corresponding to a dataset is + ready and available to be used. Return ``True`` if so, ``False`` otherwise. + """ + rel_path = self._construct_path(obj, **kwargs) + # Make sure the size in cache is available in its entirety + if self._in_cache(rel_path): + if os.path.getsize(self._get_cache_path(rel_path)) == self._get_size_in_cloud(rel_path): + return True + log.debug("Waiting for dataset %s to transfer from OS: %s/%s", rel_path, + os.path.getsize(self._get_cache_path(rel_path)), self._get_size_in_cloud(rel_path)) + return False + + def exists(self, obj, **kwargs): + in_cache = False + rel_path = self._construct_path(obj, **kwargs) + + # Check cache + if self._in_cache(rel_path): + in_cache = True + # Check cloud + in_cloud = self._key_exists(rel_path) + # log.debug("~~~~~~ File '%s' exists in cache: %s; in s3: %s" % (rel_path, in_cache, in_s3)) + # dir_only does not get synced so shortcut the decision + dir_only = kwargs.get('dir_only', False) + base_dir = kwargs.get('base_dir', None) + if dir_only: + if in_cache or in_cloud: + return True + # for JOB_WORK directory + elif base_dir: + if not os.path.exists(rel_path): + os.makedirs(rel_path) + return True + else: + return False + + # TODO: Sync should probably not be done here. Add this to an async upload stack? + if in_cache and not in_cloud: + self._push_to_os(rel_path, source_file=self._get_cache_path(rel_path)) + return True + elif in_cloud: + return True + else: + return False + + def create(self, obj, **kwargs): + if not self.exists(obj, **kwargs): + + # Pull out locally used fields + extra_dir = kwargs.get('extra_dir', None) + extra_dir_at_root = kwargs.get('extra_dir_at_root', False) + dir_only = kwargs.get('dir_only', False) + alt_name = kwargs.get('alt_name', None) + + # Construct hashed path + rel_path = os.path.join(*directory_hash_id(obj.id)) + + # Optionally append extra_dir + if extra_dir is not None: + if extra_dir_at_root: + rel_path = os.path.join(extra_dir, rel_path) + else: + rel_path = os.path.join(rel_path, extra_dir) + + # Create given directory in cache + cache_dir = os.path.join(self.staging_path, rel_path) + if not os.path.exists(cache_dir): + os.makedirs(cache_dir) + + if not dir_only: + rel_path = os.path.join(rel_path, alt_name if alt_name else "dataset_%s.dat" % obj.id) + open(os.path.join(self.staging_path, rel_path), 'w').close() + self._push_to_os(rel_path, from_string='') + + def empty(self, obj, **kwargs): + if self.exists(obj, **kwargs): + return bool(self.size(obj, **kwargs) > 0) + else: + raise ObjectNotFound('objectstore.empty, object does not exist: %s, kwargs: %s' + % (str(obj), str(kwargs))) + + def size(self, obj, **kwargs): + rel_path = self._construct_path(obj, **kwargs) + if self._in_cache(rel_path): + try: + return os.path.getsize(self._get_cache_path(rel_path)) + except OSError as ex: + log.info("Could not get size of file '%s' in local cache, will try cloud. Error: %s", rel_path, ex) + elif self.exists(obj, **kwargs): + return self._get_size_in_cloud(rel_path) + log.warning("Did not find dataset '%s', returning 0 for size", rel_path) + return 0 + + def delete(self, obj, entire_dir=False, **kwargs): + rel_path = self._construct_path(obj, **kwargs) + extra_dir = kwargs.get('extra_dir', None) + base_dir = kwargs.get('base_dir', None) + dir_only = kwargs.get('dir_only', False) + obj_dir = kwargs.get('obj_dir', False) + try: + # Remove temparory data in JOB_WORK directory + if base_dir and dir_only and obj_dir: + shutil.rmtree(os.path.abspath(rel_path)) + return True + + # For the case of extra_files, because we don't have a reference to + # individual files/keys we need to remove the entire directory structure + # with all the files in it. This is easy for the local file system, + # but requires iterating through each individual key in S3 and deleing it. + if entire_dir and extra_dir: + shutil.rmtree(self._get_cache_path(rel_path)) + results = self.bucket.list(prefix=rel_path) + for key in results: + log.debug("Deleting key %s", key.name) + key.delete() + return True + else: + # Delete from cache first + os.unlink(self._get_cache_path(rel_path)) + # Delete from S3 as well + if self._key_exists(rel_path): + key = self.bucket.get(rel_path) + log.debug("Deleting key %s", key.name) + key.delete() + return True + except Exception: + log.exception("Could not delete key '%s' from cloud", rel_path) + except OSError: + log.exception('%s delete error', self.get_filename(obj, **kwargs)) + return False + + def get_data(self, obj, start=0, count=-1, **kwargs): + rel_path = self._construct_path(obj, **kwargs) + # Check cache first and get file if not there + if not self._in_cache(rel_path): + self._pull_into_cache(rel_path) + # Read the file content from cache + data_file = open(self._get_cache_path(rel_path), 'r') + data_file.seek(start) + content = data_file.read(count) + data_file.close() + return content + + def get_filename(self, obj, **kwargs): + base_dir = kwargs.get('base_dir', None) + dir_only = kwargs.get('dir_only', False) + obj_dir = kwargs.get('obj_dir', False) + rel_path = self._construct_path(obj, **kwargs) + + # for JOB_WORK directory + if base_dir and dir_only and obj_dir: + return os.path.abspath(rel_path) + + cache_path = self._get_cache_path(rel_path) + # S3 does not recognize directories as files so cannot check if those exist. + # So, if checking dir only, ensure given dir exists in cache and return + # the expected cache path. + # dir_only = kwargs.get('dir_only', False) + # if dir_only: + # if not os.path.exists(cache_path): + # os.makedirs(cache_path) + # return cache_path + # Check if the file exists in the cache first + if self._in_cache(rel_path): + return cache_path + # Check if the file exists in persistent storage and, if it does, pull it into cache + elif self.exists(obj, **kwargs): + if dir_only: # Directories do not get pulled into cache + return cache_path + else: + if self._pull_into_cache(rel_path): + return cache_path + # For the case of retrieving a directory only, return the expected path + # even if it does not exist. + # if dir_only: + # return cache_path + raise ObjectNotFound('objectstore.get_filename, no cache_path: %s, kwargs: %s' + % (str(obj), str(kwargs))) + + # return cache_path # Until the upload tool does not explicitly create the dataset, return expected path + + def update_from_file(self, obj, file_name=None, create=False, **kwargs): + if create: + self.create(obj, **kwargs) + if self.exists(obj, **kwargs): + rel_path = self._construct_path(obj, **kwargs) + # Chose whether to use the dataset file itself or an alternate file + if file_name: + source_file = os.path.abspath(file_name) + # Copy into cache + cache_file = self._get_cache_path(rel_path) + try: + if source_file != cache_file: + # FIXME? Should this be a `move`? + shutil.copy2(source_file, cache_file) + self._fix_permissions(cache_file) + except OSError: + log.exception("Trouble copying source file '%s' to cache '%s'", source_file, cache_file) + else: + source_file = self._get_cache_path(rel_path) + # Update the file on cloud + self._push_to_os(rel_path, source_file) + else: + raise ObjectNotFound('objectstore.update_from_file, object does not exist: %s, kwargs: %s' + % (str(obj), str(kwargs))) + + def get_object_url(self, obj, **kwargs): + if self.exists(obj, **kwargs): + rel_path = self._construct_path(obj, **kwargs) + try: + key = self.bucket.get(rel_path) + return key.generate_url(expires_in=86400) # 24hrs + except Exception: + log.exception("Trouble generating URL for dataset '%s'", rel_path) + return None + + def get_store_usage_percent(self): + return 0.0 diff --git a/lib/galaxy/objectstore/pithos.py b/lib/galaxy/objectstore/pithos.py index 10afd90ef937..6fd888223266 100644 --- a/lib/galaxy/objectstore/pithos.py +++ b/lib/galaxy/objectstore/pithos.py @@ -75,6 +75,7 @@ class PithosObjectStore(ObjectStore): Object store that stores objects as items in a Pithos+ container. Cache is ignored for the time being. """ + def __init__(self, config, config_xml): if KamakiClient is None: raise Exception(NO_KAMAKI_ERROR_MESSAGE) diff --git a/lib/galaxy/objectstore/rods.py b/lib/galaxy/objectstore/rods.py index a6e70e7ed203..fcf61c0f0214 100644 --- a/lib/galaxy/objectstore/rods.py +++ b/lib/galaxy/objectstore/rods.py @@ -26,15 +26,16 @@ IRODS_IMPORT_MESSAGE = ('The Python irods package is required to use this ' 'feature, please install it') -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class IRODSObjectStore( DiskObjectStore ): +class IRODSObjectStore(DiskObjectStore): """ Galaxy object store based on iRODS """ - def __init__( self, config, file_path=None, extra_dirs=None ): - super( IRODSObjectStore, self ).__init__( config, file_path=file_path, extra_dirs=extra_dirs ) + + def __init__(self, config, file_path=None, extra_dirs=None): + super(IRODSObjectStore, self).__init__(config, file_path=file_path, extra_dirs=extra_dirs) assert irods is not None, IRODS_IMPORT_MESSAGE self.cache_path = config.object_store_cache_path self.default_resource = config.irods_default_resource or None @@ -43,32 +44,32 @@ def __init__( self, config, file_path=None, extra_dirs=None ): self.rods_env, self.rods_conn = rods_connect() # if the root collection path in the config is unset or relative, try to use a sensible default - if config.irods_root_collection_path is None or ( config.irods_root_collection_path is not None and not config.irods_root_collection_path.startswith( '/' ) ): + if config.irods_root_collection_path is None or (config.irods_root_collection_path is not None and not config.irods_root_collection_path.startswith('/')): rods_home = self.rods_env.rodsHome assert rods_home != '', "Unable to initialize iRODS Object Store: rodsHome cannot be determined and irods_root_collection_path in Galaxy config is unset or not absolute." if config.irods_root_collection_path is None: - self.root_collection_path = path_join( rods_home, 'galaxy_data' ) + self.root_collection_path = path_join(rods_home, 'galaxy_data') else: - self.root_collection_path = path_join( rods_home, config.irods_root_collection_path ) + self.root_collection_path = path_join(rods_home, config.irods_root_collection_path) else: self.root_collection_path = config.irods_root_collection_path # will return a collection object regardless of whether it exists - self.root_collection = irods.irodsCollection( self.rods_conn, self.root_collection_path ) + self.root_collection = irods.irodsCollection(self.rods_conn, self.root_collection_path) if self.root_collection.getId() == -1: - log.warning( "iRODS root collection does not exist, will attempt to create: %s", self.root_collection_path ) + log.warning("iRODS root collection does not exist, will attempt to create: %s", self.root_collection_path) self.root_collection.upCollection() - assert self.root_collection.createCollection( os.path.basename( self.root_collection_path ) ) == 0, "iRODS root collection creation failed: %s" % self.root_collection_path - self.root_collection = irods.irodsCollection( self.rods_conn, self.root_collection_path ) + assert self.root_collection.createCollection(os.path.basename(self.root_collection_path)) == 0, "iRODS root collection creation failed: %s" % self.root_collection_path + self.root_collection = irods.irodsCollection(self.rods_conn, self.root_collection_path) assert self.root_collection.getId() != -1, "iRODS root collection creation claimed success but still does not exist" if self.default_resource is None: self.default_resource = self.rods_env.rodsDefResource - log.info( "iRODS data for this instance will be stored in collection: %s, resource: %s", self.root_collection_path, self.default_resource ) + log.info("iRODS data for this instance will be stored in collection: %s, resource: %s", self.root_collection_path, self.default_resource) - def __get_rods_path( self, obj, base_dir=None, dir_only=False, extra_dir=None, extra_dir_at_root=False, alt_name=None, strip_dat=True, **kwargs ): + def __get_rods_path(self, obj, base_dir=None, dir_only=False, extra_dir=None, extra_dir_at_root=False, alt_name=None, strip_dat=True, **kwargs): # extra_dir should never be constructed from provided data but just # make sure there are no shenannigans afoot if extra_dir and extra_dir != os.path.normpath(extra_dir): @@ -93,72 +94,72 @@ def __get_rods_path( self, obj, base_dir=None, dir_only=False, extra_dir=None, e if not dir_only: # the .dat extension is stripped when stored in iRODS # TODO: is the strip_dat kwarg the best way to implement this? - if strip_dat and alt_name and alt_name.endswith( '.dat' ): - alt_name = os.path.splitext( alt_name )[0] + if strip_dat and alt_name and alt_name.endswith('.dat'): + alt_name = os.path.splitext(alt_name)[0] default_name = 'dataset_%s' % obj.id if not strip_dat: default_name += '.dat' - path = path_join( path, alt_name if alt_name else default_name ) + path = path_join(path, alt_name if alt_name else default_name) - path = path_join( self.root_collection_path, path ) + path = path_join(self.root_collection_path, path) return path - def __get_cache_path( self, obj, **kwargs ): + def __get_cache_path(self, obj, **kwargs): # FIXME: does not handle collections # FIXME: collisions could occur here - return os.path.join( self.cache_path, path_basename( self.__get_rods_path( obj, strip_dat=False, **kwargs ) ) ) + return os.path.join(self.cache_path, path_basename(self.__get_rods_path(obj, strip_dat=False, **kwargs))) - def __clean_cache_entry( self, obj, **kwargs ): + def __clean_cache_entry(self, obj, **kwargs): # FIXME: does not handle collections try: - os.unlink( self.__get_cache_path( obj, **kwargs ) ) + os.unlink(self.__get_cache_path(obj, **kwargs)) except OSError: # it is expected that we'll call this method a lot regardless of # whether we think the cached file exists pass - def __get_rods_handle( self, obj, mode='r', **kwargs ): - if kwargs.get( 'dir_only', False ): - return irods.irodsCollection( self.rods_conn, self.__get_rods_path( obj, **kwargs ) ) + def __get_rods_handle(self, obj, mode='r', **kwargs): + if kwargs.get('dir_only', False): + return irods.irodsCollection(self.rods_conn, self.__get_rods_path(obj, **kwargs)) else: - return irods.irodsOpen( self.rods_conn, self.__get_rods_path( obj, **kwargs ), mode ) + return irods.irodsOpen(self.rods_conn, self.__get_rods_path(obj, **kwargs), mode) - def __mkcolls( self, rods_path ): + def __mkcolls(self, rods_path): """ An os.makedirs() for iRODS collections. `rods_path` is the desired collection to create. """ - assert rods_path.startswith( self.root_collection_path + '/' ), '__mkcolls(): Creating collections outside the root collection is not allowed (requested path was: %s)' % rods_path + assert rods_path.startswith(self.root_collection_path + '/'), '__mkcolls(): Creating collections outside the root collection is not allowed (requested path was: %s)' % rods_path mkcolls = [] - c = irods.irodsCollection( self.rods_conn, rods_path ) + c = irods.irodsCollection(self.rods_conn, rods_path) while c.getId() == -1: - assert c.getCollName().startswith( self.root_collection_path + '/' ), '__mkcolls(): Attempted to move above the root collection: %s' % c.getCollName() - mkcolls.append( c.getCollName() ) + assert c.getCollName().startswith(self.root_collection_path + '/'), '__mkcolls(): Attempted to move above the root collection: %s' % c.getCollName() + mkcolls.append(c.getCollName()) c.upCollection() - for collname in reversed( mkcolls ): - log.debug( 'Creating collection %s' % collname ) + for collname in reversed(mkcolls): + log.debug('Creating collection %s' % collname) ci = irods.collInp_t() ci.collName = collname - status = irods.rcCollCreate( self.rods_conn, ci ) + status = irods.rcCollCreate(self.rods_conn, ci) assert status == 0, '__mkcolls(): Failed to create collection: %s' % collname @local_extra_dirs - def exists( self, obj, **kwargs ): + def exists(self, obj, **kwargs): doi = irods.dataObjInp_t() - doi.objPath = self.__get_rods_path( obj, **kwargs ) - log.debug( 'exists(): checking: %s', doi.objPath ) - return irods.rcObjStat( self.rods_conn, doi ) is not None + doi.objPath = self.__get_rods_path(obj, **kwargs) + log.debug('exists(): checking: %s', doi.objPath) + return irods.rcObjStat(self.rods_conn, doi) is not None @local_extra_dirs def create(self, obj, **kwargs): - if not self.exists( obj, **kwargs ): - rods_path = self.__get_rods_path( obj, **kwargs ) - log.debug( 'create(): %s', rods_path ) - dir_only = kwargs.get( 'dir_only', False ) + if not self.exists(obj, **kwargs): + rods_path = self.__get_rods_path(obj, **kwargs) + log.debug('create(): %s', rods_path) + dir_only = kwargs.get('dir_only', False) # short circuit collection creation since most of the time it will # be the root collection which already exists - collection_path = rods_path if dir_only else path_dirname( rods_path ) + collection_path = rods_path if dir_only else path_dirname(rods_path) if collection_path != self.root_collection_path: - self.__mkcolls( collection_path ) + self.__mkcolls(collection_path) if not dir_only: # rcDataObjCreate is used instead of the irodsOpen wrapper so # that we can prevent overwriting @@ -166,23 +167,23 @@ def create(self, obj, **kwargs): doi.objPath = rods_path doi.createMode = 0o640 doi.dataSize = 0 # 0 actually means "unknown", although literally 0 would be preferable - irods.addKeyVal( doi.condInput, irods.DEST_RESC_NAME_KW, self.default_resource ) - status = irods.rcDataObjCreate( self.rods_conn, doi ) - assert status >= 0, 'create(): rcDataObjCreate() failed: %s: %s: %s' % ( rods_path, status, irods.strerror( status ) ) + irods.addKeyVal(doi.condInput, irods.DEST_RESC_NAME_KW, self.default_resource) + status = irods.rcDataObjCreate(self.rods_conn, doi) + assert status >= 0, 'create(): rcDataObjCreate() failed: %s: %s: %s' % (rods_path, status, irods.strerror(status)) @local_extra_dirs - def empty( self, obj, **kwargs ): + def empty(self, obj, **kwargs): assert 'dir_only' not in kwargs, 'empty(): `dir_only` parameter is invalid here' - h = self.__get_rods_handle( obj, **kwargs ) + h = self.__get_rods_handle(obj, **kwargs) try: return h.getSize() == 0 except AttributeError: # h is None raise ObjectNotFound() - def size( self, obj, **kwargs ): + def size(self, obj, **kwargs): assert 'dir_only' not in kwargs, 'size(): `dir_only` parameter is invalid here' - h = self.__get_rods_handle( obj, **kwargs ) + h = self.__get_rods_handle(obj, **kwargs) try: return h.getSize() except AttributeError: @@ -190,53 +191,53 @@ def size( self, obj, **kwargs ): return 0 @local_extra_dirs - def delete( self, obj, entire_dir=False, **kwargs ): + def delete(self, obj, entire_dir=False, **kwargs): assert 'dir_only' not in kwargs, 'delete(): `dir_only` parameter is invalid here' - rods_path = self.__get_rods_path( obj, **kwargs ) + rods_path = self.__get_rods_path(obj, **kwargs) # __get_rods_path prepends self.root_collection_path but we are going # to ensure that it's valid anyway for safety's sake - assert rods_path.startswith( self.root_collection_path + '/' ), 'ERROR: attempt to delete object outside root collection (path was: %s)' % rods_path + assert rods_path.startswith(self.root_collection_path + '/'), 'ERROR: attempt to delete object outside root collection (path was: %s)' % rods_path if entire_dir: # TODO raise NotImplementedError() - h = self.__get_rods_handle( obj, **kwargs ) + h = self.__get_rods_handle(obj, **kwargs) try: # note: PyRods' irodsFile.delete() does not set force status = h.delete() - assert status == 0, '%d: %s' % ( status, irods.strerror( status ) ) + assert status == 0, '%d: %s' % (status, irods.strerror(status)) return True except AttributeError: - log.warning( 'delete(): operation failed: object does not exist: %s', rods_path ) + log.warning('delete(): operation failed: object does not exist: %s', rods_path) except AssertionError as e: # delete() does not raise on deletion failure - log.error( 'delete(): operation failed: %s', e ) + log.error('delete(): operation failed: %s', e) finally: # remove the cached entry (finally is executed even when the try # contains a return) - self.__clean_cache_entry( self, obj, **kwargs ) + self.__clean_cache_entry(self, obj, **kwargs) return False @local_extra_dirs - def get_data( self, obj, start=0, count=-1, **kwargs ): - log.debug( 'get_data(): %s' ) - h = self.__get_rods_handle( obj, **kwargs ) + def get_data(self, obj, start=0, count=-1, **kwargs): + log.debug('get_data(): %s') + h = self.__get_rods_handle(obj, **kwargs) try: - h.seek( start ) + h.seek(start) except AttributeError: raise ObjectNotFound() if count == -1: return h.read() else: - return h.read( count ) + return h.read(count) # TODO: make sure implicit close is okay, DiskObjectStore actually # reads data into a var, closes, and returns the var @local_extra_dirs - def get_filename( self, obj, **kwargs ): - log.debug( "get_filename(): called on %s %s. For better performance, avoid this method and use get_data() instead.", obj.__class__.__name__, obj.id ) - cached_path = self.__get_cache_path( obj, **kwargs ) + def get_filename(self, obj, **kwargs): + log.debug("get_filename(): called on %s %s. For better performance, avoid this method and use get_data() instead.", obj.__class__.__name__, obj.id) + cached_path = self.__get_cache_path(obj, **kwargs) - if not self.exists( obj, **kwargs ): + if not self.exists(obj, **kwargs): raise ObjectNotFound() # TODO: implement or define whether dir_only is valid @@ -244,66 +245,66 @@ def get_filename( self, obj, **kwargs ): raise NotImplementedError() # cache hit - if os.path.exists( cached_path ): - return os.path.abspath( cached_path ) + if os.path.exists(cached_path): + return os.path.abspath(cached_path) # cache miss # TODO: thread this - incoming_path = os.path.join( os.path.dirname( cached_path ), "__incoming_%s" % os.path.basename( cached_path ) ) + incoming_path = os.path.join(os.path.dirname(cached_path), "__incoming_%s" % os.path.basename(cached_path)) doi = irods.dataObjInp_t() - doi.objPath = self.__get_rods_path( obj, **kwargs ) + doi.objPath = self.__get_rods_path(obj, **kwargs) doi.dataSize = 0 # TODO: does this affect performance? should we get size? doi.numThreads = 0 # TODO: might want to VERIFY_CHKSUM_KW - log.debug( 'get_filename(): caching %s to %s', doi.objPath, incoming_path ) + log.debug('get_filename(): caching %s to %s', doi.objPath, incoming_path) # do the iget - status = irods.rcDataObjGet( self.rods_conn, doi, incoming_path ) + status = irods.rcDataObjGet(self.rods_conn, doi, incoming_path) # if incoming already exists, we'll wait for another process or thread # to finish caching if status != irods.OVERWRITE_WITHOUT_FORCE_FLAG: - assert status == 0, 'get_filename(): iget %s failed (%s): %s' % ( doi.objPath, status, irods.strerror( status ) ) + assert status == 0, 'get_filename(): iget %s failed (%s): %s' % (doi.objPath, status, irods.strerror(status)) # POSIX rename is atomic # TODO: rename without clobbering - os.rename( incoming_path, cached_path ) - log.debug( 'get_filename(): cached %s to %s', doi.objPath, cached_path ) + os.rename(incoming_path, cached_path) + log.debug('get_filename(): cached %s to %s', doi.objPath, cached_path) # another process or thread is caching, wait for it - while not os.path.exists( cached_path ): + while not os.path.exists(cached_path): # TODO: force restart after mod time > some configurable, or # otherwise deal with this potential deadlock and interrupted # transfers - time.sleep( 5 ) - log.debug( "get_filename(): waiting on incoming '%s' for %s %s", incoming_path, obj.__class__.__name__, obj.id ) + time.sleep(5) + log.debug("get_filename(): waiting on incoming '%s' for %s %s", incoming_path, obj.__class__.__name__, obj.id) - return os.path.abspath( cached_path ) + return os.path.abspath(cached_path) @local_extra_dirs def update_from_file(self, obj, file_name=None, create=False, **kwargs): assert 'dir_only' not in kwargs, 'update_from_file(): `dir_only` parameter is invalid here' # do not create if not requested - if create and not self.exists( obj, **kwargs ): + if create and not self.exists(obj, **kwargs): raise ObjectNotFound() if file_name is None: - file_name = self.__get_cache_path( obj, **kwargs ) + file_name = self.__get_cache_path(obj, **kwargs) # put will create if necessary doi = irods.dataObjInp_t() - doi.objPath = self.__get_rods_path( obj, **kwargs ) + doi.objPath = self.__get_rods_path(obj, **kwargs) doi.createMode = 0o640 - doi.dataSize = os.stat( file_name ).st_size + doi.dataSize = os.stat(file_name).st_size doi.numThreads = 0 - irods.addKeyVal( doi.condInput, irods.DEST_RESC_NAME_KW, self.default_resource ) - irods.addKeyVal( doi.condInput, irods.FORCE_FLAG_KW, '' ) + irods.addKeyVal(doi.condInput, irods.DEST_RESC_NAME_KW, self.default_resource) + irods.addKeyVal(doi.condInput, irods.FORCE_FLAG_KW, '') # TODO: might want to VERIFY_CHKSUM_KW - log.debug( 'update_from_file(): updating %s to %s', file_name, doi.objPath ) + log.debug('update_from_file(): updating %s to %s', file_name, doi.objPath) # do the iput - status = irods.rcDataObjPut( self.rods_conn, doi, file_name ) - assert status == 0, 'update_from_file(): iput %s failed (%s): %s' % ( doi.objPath, status, irods.strerror( status ) ) + status = irods.rcDataObjPut(self.rods_conn, doi, file_name) + assert status == 0, 'update_from_file(): iput %s failed (%s): %s' % (doi.objPath, status, irods.strerror(status)) def get_object_url(self, obj, **kwargs): return None @@ -313,17 +314,17 @@ def get_store_usage_percent(self): # monkeypatch an strerror method into the irods module -def _rods_strerror( errno ): +def _rods_strerror(errno): """ The missing `strerror` for iRODS error codes """ - if not hasattr( irods, '__rods_strerror_map' ): + if not hasattr(irods, '__rods_strerror_map'): irods.__rods_strerror_map = {} - for name in dir( irods ): - v = getattr( irods, name ) - if type( v ) == int and v < 0: - irods.__rods_strerror_map[ v ] = name - return irods.__rods_strerror_map.get( errno, 'GALAXY_NO_ERRNO_MAPPING_FOUND' ) + for name in dir(irods): + v = getattr(irods, name) + if type(v) == int and v < 0: + irods.__rods_strerror_map[v] = name + return irods.__rods_strerror_map.get(errno, 'GALAXY_NO_ERRNO_MAPPING_FOUND') if irods is not None: @@ -336,14 +337,14 @@ def rods_connect(): environment """ status, env = irods.getRodsEnv() - assert status == 0, 'connect(): getRodsEnv() failed (%s): %s' % ( status, irods.strerror( status ) ) - conn, err = irods.rcConnect( env.rodsHost, - env.rodsPort, - env.rodsUserName, - env.rodsZone ) - assert err.status == 0, 'connect(): rcConnect() failed (%s): %s' % ( err.status, err.msg ) + assert status == 0, 'connect(): getRodsEnv() failed (%s): %s' % (status, irods.strerror(status)) + conn, err = irods.rcConnect(env.rodsHost, + env.rodsPort, + env.rodsUserName, + env.rodsZone) + assert err.status == 0, 'connect(): rcConnect() failed (%s): %s' % (err.status, err.msg) status, pw = irods.obfGetPw() - assert status == 0, 'connect(): getting password with obfGetPw() failed (%s): %s' % ( status, irods.strerror( status ) ) - status = irods.clientLoginWithObfPassword( conn, pw ) - assert status == 0, 'connect(): logging in with clientLoginWithObfPassword() failed (%s): %s' % ( status, irods.strerror( status ) ) + assert status == 0, 'connect(): getting password with obfGetPw() failed (%s): %s' % (status, irods.strerror(status)) + status = irods.clientLoginWithObfPassword(conn, pw) + assert status == 0, 'connect(): logging in with clientLoginWithObfPassword() failed (%s): %s' % (status, irods.strerror(status)) return env, conn diff --git a/lib/galaxy/objectstore/s3.py b/lib/galaxy/objectstore/s3.py index e16962c042bb..f832688741e2 100644 --- a/lib/galaxy/objectstore/s3.py +++ b/lib/galaxy/objectstore/s3.py @@ -37,7 +37,7 @@ NO_BOTO_ERROR_MESSAGE = ("S3/Swift object store configured, but no boto dependency available." "Please install and properly configure boto or modify object store configuration.") -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) logging.getLogger('boto').setLevel(logging.INFO) # Otherwise boto is quite noisy @@ -47,6 +47,7 @@ class S3ObjectStore(ObjectStore): cache exists that is used as an intermediate location for files between Galaxy and S3. """ + def __init__(self, config, config_xml): if boto is None: raise Exception(NO_BOTO_ERROR_MESSAGE) @@ -211,7 +212,7 @@ def _fix_permissions(self, rel_path): # Ignore symlinks if os.path.islink(path): continue - umask_fix_perms( path, self.config.umask, 0o666, self.config.gid ) + umask_fix_perms(path, self.config.umask, 0o666, self.config.gid) def _construct_path(self, obj, base_dir=None, dir_only=None, extra_dir=None, extra_dir_at_root=False, alt_name=None, obj_dir=False, **kwargs): # extra_dir should never be constructed from provided data but just @@ -479,8 +480,8 @@ def empty(self, obj, **kwargs): if self.exists(obj, **kwargs): return bool(self.size(obj, **kwargs) > 0) else: - raise ObjectNotFound( 'objectstore.empty, object does not exist: %s, kwargs: %s' - % ( str( obj ), str( kwargs ) ) ) + raise ObjectNotFound('objectstore.empty, object does not exist: %s, kwargs: %s' + % (str(obj), str(kwargs))) def size(self, obj, **kwargs): rel_path = self._construct_path(obj, **kwargs) @@ -577,8 +578,8 @@ def get_filename(self, obj, **kwargs): # even if it does not exist. # if dir_only: # return cache_path - raise ObjectNotFound( 'objectstore.get_filename, no cache_path: %s, kwargs: %s' - % ( str( obj ), str( kwargs ) ) ) + raise ObjectNotFound('objectstore.get_filename, no cache_path: %s, kwargs: %s' + % (str(obj), str(kwargs))) # return cache_path # Until the upload tool does not explicitly create the dataset, return expected path def update_from_file(self, obj, file_name=None, create=False, **kwargs): @@ -603,8 +604,8 @@ def update_from_file(self, obj, file_name=None, create=False, **kwargs): # Update the file on S3 self._push_to_os(rel_path, source_file) else: - raise ObjectNotFound( 'objectstore.update_from_file, object does not exist: %s, kwargs: %s' - % ( str( obj ), str( kwargs ) ) ) + raise ObjectNotFound('objectstore.update_from_file, object does not exist: %s, kwargs: %s' + % (str(obj), str(kwargs))) def get_object_url(self, obj, **kwargs): if self.exists(obj, **kwargs): diff --git a/lib/galaxy/objectstore/s3_multipart_upload.py b/lib/galaxy/objectstore/s3_multipart_upload.py index 9b40b70cbb45..704d94417253 100644 --- a/lib/galaxy/objectstore/s3_multipart_upload.py +++ b/lib/galaxy/objectstore/s3_multipart_upload.py @@ -23,8 +23,8 @@ def map_wrap(f): @functools.wraps(f) - def wrapper(*args, **kwargs): - return f(*args, **kwargs) + def wrapper(args): + return f(*args) return wrapper diff --git a/lib/galaxy/openid/providers.py b/lib/galaxy/openid/providers.py index ac123003c14f..8c1ae0d7ff51 100644 --- a/lib/galaxy/openid/providers.py +++ b/lib/galaxy/openid/providers.py @@ -8,60 +8,60 @@ from galaxy.util.odict import odict -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) NO_PROVIDER_ID = 'None' -RESERVED_PROVIDER_IDS = [ NO_PROVIDER_ID ] +RESERVED_PROVIDER_IDS = [NO_PROVIDER_ID] -class OpenIDProvider( object ): +class OpenIDProvider(object): '''An OpenID Provider object.''' @classmethod - def from_file( cls, filename ): - return cls.from_elem( parse_xml( filename ).getroot() ) + def from_file(cls, filename): + return cls.from_elem(parse_xml(filename).getroot()) @classmethod - def from_elem( cls, xml_root ): + def from_elem(cls, xml_root): provider_elem = xml_root - provider_id = provider_elem.get( 'id', None ) - provider_name = provider_elem.get( 'name', provider_id ) - op_endpoint_url = provider_elem.find( 'op_endpoint_url' ) + provider_id = provider_elem.get('id', None) + provider_name = provider_elem.get('name', provider_id) + op_endpoint_url = provider_elem.find('op_endpoint_url') if op_endpoint_url is not None: op_endpoint_url = op_endpoint_url.text - never_associate_with_user = string_as_bool( provider_elem.get( 'never_associate_with_user', 'False' ) ) - assert (provider_id and provider_name and op_endpoint_url), Exception( "OpenID Provider improperly configured" ) - assert provider_id not in RESERVED_PROVIDER_IDS, Exception( 'Specified OpenID Provider uses a reserved id: %s' % ( provider_id ) ) + never_associate_with_user = string_as_bool(provider_elem.get('never_associate_with_user', 'False')) + assert (provider_id and provider_name and op_endpoint_url), Exception("OpenID Provider improperly configured") + assert provider_id not in RESERVED_PROVIDER_IDS, Exception('Specified OpenID Provider uses a reserved id: %s' % (provider_id)) sreg_required = [] sreg_optional = [] use_for = {} store_user_preference = {} use_default_sreg = True - for elem in provider_elem.findall( 'sreg' ): + for elem in provider_elem.findall('sreg'): use_default_sreg = False - for field_elem in elem.findall( 'field' ): - sreg_name = field_elem.get( 'name' ) - assert sreg_name, Exception( 'A name is required for a sreg element' ) - if string_as_bool( field_elem.get( 'required' ) ): - sreg_required.append( sreg_name ) + for field_elem in elem.findall('field'): + sreg_name = field_elem.get('name') + assert sreg_name, Exception('A name is required for a sreg element') + if string_as_bool(field_elem.get('required')): + sreg_required.append(sreg_name) else: - sreg_optional.append( sreg_name ) - for use_elem in field_elem.findall( 'use_for' ): - use_for[ use_elem.get( 'name' ) ] = sreg_name - for store_user_preference_elem in field_elem.findall( 'store_user_preference' ): - store_user_preference[ store_user_preference_elem.get( 'name' ) ] = sreg_name + sreg_optional.append(sreg_name) + for use_elem in field_elem.findall('use_for'): + use_for[use_elem.get('name')] = sreg_name + for store_user_preference_elem in field_elem.findall('store_user_preference'): + store_user_preference[store_user_preference_elem.get('name')] = sreg_name if use_default_sreg: sreg_required = None sreg_optional = None use_for = None - return cls( provider_id, provider_name, op_endpoint_url, sreg_required=sreg_required, sreg_optional=sreg_optional, use_for=use_for, store_user_preference=store_user_preference, never_associate_with_user=never_associate_with_user ) + return cls(provider_id, provider_name, op_endpoint_url, sreg_required=sreg_required, sreg_optional=sreg_optional, use_for=use_for, store_user_preference=store_user_preference, never_associate_with_user=never_associate_with_user) - def __init__( self, id, name, op_endpoint_url, sreg_required=None, sreg_optional=None, use_for=None, store_user_preference=None, never_associate_with_user=None ): + def __init__(self, id, name, op_endpoint_url, sreg_required=None, sreg_optional=None, use_for=None, store_user_preference=None, never_associate_with_user=None): '''When sreg options are not specified, defaults are used.''' self.id = id self.name = name self.op_endpoint_url = op_endpoint_url if sreg_optional is None: - self.sreg_optional = [ 'nickname', 'email' ] + self.sreg_optional = ['nickname', 'email'] else: self.sreg_optional = sreg_optional if sreg_required: @@ -72,10 +72,10 @@ def __init__( self, id, name, op_endpoint_url, sreg_required=None, sreg_optional self.use_for = use_for else: self.use_for = {} - if 'nickname' in ( self.sreg_optional + self.sreg_required ): - self.use_for[ 'username' ] = 'nickname' - if 'email' in ( self.sreg_optional + self.sreg_required ): - self.use_for[ 'email' ] = 'email' + if 'nickname' in (self.sreg_optional + self.sreg_required): + self.use_for['username'] = 'nickname' + if 'email' in (self.sreg_optional + self.sreg_required): + self.use_for['email'] = 'email' if store_user_preference: self.store_user_preference = store_user_preference else: @@ -85,61 +85,61 @@ def __init__( self, id, name, op_endpoint_url, sreg_required=None, sreg_optional else: self.never_associate_with_user = False - def post_authentication( self, trans, openid_manager, info ): - sreg_attributes = openid_manager.get_sreg( info ) + def post_authentication(self, trans, openid_manager, info): + sreg_attributes = openid_manager.get_sreg(info) for store_pref_name, store_pref_value_name in self.store_user_preference.iteritems(): - if store_pref_value_name in ( self.sreg_optional + self.sreg_required ): - trans.user.preferences[ store_pref_name ] = sreg_attributes.get( store_pref_value_name ) + if store_pref_value_name in (self.sreg_optional + self.sreg_required): + trans.user.preferences[store_pref_name] = sreg_attributes.get(store_pref_value_name) else: - raise Exception( 'Only sreg is currently supported.' ) - trans.sa_session.add( trans.user ) + raise Exception('Only sreg is currently supported.') + trans.sa_session.add(trans.user) trans.sa_session.flush() - def has_post_authentication_actions( self ): - return bool( self.store_user_preference ) + def has_post_authentication_actions(self): + return bool(self.store_user_preference) -class OpenIDProviders( object ): +class OpenIDProviders(object): '''Collection of OpenID Providers''' NO_PROVIDER_ID = NO_PROVIDER_ID @classmethod - def from_file( cls, filename ): + def from_file(cls, filename): try: - return cls.from_elem( parse_xml( filename ).getroot() ) + return cls.from_elem(parse_xml(filename).getroot()) except Exception as e: - log.error( 'Failed to load OpenID Providers: %s' % ( e ) ) + log.error('Failed to load OpenID Providers: %s' % (e)) return cls() @classmethod - def from_elem( cls, xml_root ): + def from_elem(cls, xml_root): oid_elem = xml_root providers = odict() - for elem in oid_elem.findall( 'provider' ): + for elem in oid_elem.findall('provider'): try: - provider = OpenIDProvider.from_file( os.path.join( 'openid', elem.get( 'file' ) ) ) - providers[ provider.id ] = provider - log.debug( 'Loaded OpenID provider: %s (%s)' % ( provider.name, provider.id ) ) + provider = OpenIDProvider.from_file(os.path.join('openid', elem.get('file'))) + providers[provider.id] = provider + log.debug('Loaded OpenID provider: %s (%s)' % (provider.name, provider.id)) except Exception as e: - log.error( 'Failed to add OpenID provider: %s' % ( e ) ) - return cls( providers ) + log.error('Failed to add OpenID provider: %s' % (e)) + return cls(providers) - def __init__( self, providers=None ): + def __init__(self, providers=None): if providers: self.providers = providers else: self.providers = odict() - self._banned_identifiers = [ provider.op_endpoint_url for provider in self.providers.itervalues() if provider.never_associate_with_user ] + self._banned_identifiers = [provider.op_endpoint_url for provider in self.providers.itervalues() if provider.never_associate_with_user] - def __iter__( self ): + def __iter__(self): for provider in self.providers.itervalues(): yield provider - def get( self, name, default=None ): + def get(self, name, default=None): if name in self.providers: - return self.providers[ name ] + return self.providers[name] else: return default - def new_provider_from_identifier( self, identifier ): - return OpenIDProvider( None, identifier, identifier, never_associate_with_user=identifier in self._banned_identifiers ) + def new_provider_from_identifier(self, identifier): + return OpenIDProvider(None, identifier, identifier, never_associate_with_user=identifier in self._banned_identifiers) diff --git a/lib/galaxy/queue_worker.py b/lib/galaxy/queue_worker.py index b76ddfc7afcc..cb1ef4b92b5b 100644 --- a/lib/galaxy/queue_worker.py +++ b/lib/galaxy/queue_worker.py @@ -78,7 +78,7 @@ def reload_tool(app, **kwargs): tool_id = params.get('tool_id', None) log.debug("Executing reload tool task for %s" % tool_id) if tool_id: - app.toolbox.reload_tool_by_id( tool_id ) + app.toolbox.reload_tool_by_id(tool_id) else: log.error("Reload tool invoked without tool id.") @@ -103,7 +103,7 @@ def _get_new_toolbox(app): from galaxy import tools from galaxy.tools.special_tools import load_lib_tools if hasattr(app, 'tool_shed_repository_cache'): - app.tool_shed_repository_cache.rebuild() + app.tool_shed_repository_cache.rebuild() tool_configs = app.config.tool_configs if app.config.migrated_tools_config not in tool_configs: tool_configs.append(app.config.migrated_tools_config) @@ -136,7 +136,7 @@ def reload_data_managers(app, **kwargs): def reload_display_application(app, **kwargs): display_application_ids = kwargs.get('display_application_ids', None) log.debug("Executing display application reload task for %s" % display_application_ids) - app.datatypes_registry.reload_display_applications( display_application_ids) + app.datatypes_registry.reload_display_applications(display_application_ids) def reload_sanitize_whitelist(app): @@ -148,7 +148,7 @@ def recalculate_user_disk_usage(app, **kwargs): user_id = kwargs.get('user_id', None) sa_session = app.model.context if user_id: - user = sa_session.query( app.model.User ).get( app.security.decode_id( user_id ) ) + user = sa_session.query(app.model.User).get(app.security.decode_id(user_id)) if user: user.calculate_and_set_disk_usage() else: @@ -178,16 +178,16 @@ def admin_job_lock(app, **kwargs): % (job_lock, "not" if job_lock else "now")) -control_message_to_task = { 'create_panel_section': create_panel_section, - 'reload_tool': reload_tool, - 'reload_toolbox': reload_toolbox, - 'reload_data_managers': reload_data_managers, - 'reload_display_application': reload_display_application, - 'reload_tool_data_tables': reload_tool_data_tables, - 'admin_job_lock': admin_job_lock, - 'reload_sanitize_whitelist': reload_sanitize_whitelist, - 'recalculate_user_disk_usage': recalculate_user_disk_usage, - 'rebuild_toolbox_search_index': rebuild_toolbox_search_index} +control_message_to_task = {'create_panel_section': create_panel_section, + 'reload_tool': reload_tool, + 'reload_toolbox': reload_toolbox, + 'reload_data_managers': reload_data_managers, + 'reload_display_application': reload_display_application, + 'reload_tool_data_tables': reload_tool_data_tables, + 'admin_job_lock': admin_job_lock, + 'reload_sanitize_whitelist': reload_sanitize_whitelist, + 'recalculate_user_disk_usage': recalculate_user_disk_usage, + 'rebuild_toolbox_search_index': rebuild_toolbox_search_index} class GalaxyQueueWorker(ConsumerMixin, threading.Thread): @@ -196,6 +196,7 @@ class GalaxyQueueWorker(ConsumerMixin, threading.Thread): handler, will have one of these used for dispatching so called 'control' tasks. """ + def __init__(self, app, queue=None, task_mapping=control_message_to_task, connection=None): super(GalaxyQueueWorker, self).__init__() log.info("Initializing %s Galaxy Queue Worker on %s", app.config.server_name, util.mask_password_from_url(app.config.amqp_internal_connection)) diff --git a/lib/galaxy/quota/__init__.py b/lib/galaxy/quota/__init__.py index 864b7325cccb..0d3326bcc4fd 100644 --- a/lib/galaxy/quota/__init__.py +++ b/lib/galaxy/quota/__init__.py @@ -8,20 +8,21 @@ log = logging.getLogger(__name__) -class NoQuotaAgent( object ): +class NoQuotaAgent(object): """Base quota agent, always returns no quota""" - def __init__( self, model ): + + def __init__(self, model): self.model = model self.sa_session = model.context - def get_quota( self, user, nice_size=False ): + def get_quota(self, user, nice_size=False): return None @property - def default_quota( self ): + def default_quota(self): return None - def get_usage( self, trans=None, user=False, history=False ): + def get_usage(self, trans=None, user=False, history=False): if trans: user = trans.user history = trans.history @@ -33,16 +34,17 @@ def get_usage( self, trans=None, user=False, history=False ): usage = user.total_disk_usage return usage - def get_percent( self, trans=None, user=False, history=False, usage=False, quota=False ): + def get_percent(self, trans=None, user=False, history=False, usage=False, quota=False): return None - def get_user_quotas( self, user ): + def get_user_quotas(self, user): return [] -class QuotaAgent( NoQuotaAgent ): +class QuotaAgent(NoQuotaAgent): """Class that handles galaxy quotas""" - def get_quota( self, user, nice_size=False ): + + def get_quota(self, user, nice_size=False): """ Calculated like so: @@ -57,13 +59,13 @@ def get_quota( self, user, nice_size=False ): if not user: return self.default_unregistered_quota quotas = [] - for group in [ uga.group for uga in user.groups ]: - for quota in [ gqa.quota for gqa in group.quotas ]: + for group in [uga.group for uga in user.groups]: + for quota in [gqa.quota for gqa in group.quotas]: if quota not in quotas: - quotas.append( quota ) - for quota in [ uqa.quota for uqa in user.quotas ]: + quotas.append(quota) + for quota in [uqa.quota for uqa in user.quotas]: if quota not in quotas: - quotas.append( quota ) + quotas.append(quota) use_default = True max = 0 adjustment = 0 @@ -92,47 +94,47 @@ def get_quota( self, user, nice_size=False ): rval = 0 if nice_size: if rval is not None: - rval = galaxy.util.nice_size( rval ) + rval = galaxy.util.nice_size(rval) else: rval = 'unlimited' return rval @property - def default_unregistered_quota( self ): - return self._default_quota( self.model.DefaultQuotaAssociation.types.UNREGISTERED ) + def default_unregistered_quota(self): + return self._default_quota(self.model.DefaultQuotaAssociation.types.UNREGISTERED) @property - def default_registered_quota( self ): - return self._default_quota( self.model.DefaultQuotaAssociation.types.REGISTERED ) + def default_registered_quota(self): + return self._default_quota(self.model.DefaultQuotaAssociation.types.REGISTERED) - def _default_quota( self, default_type ): - dqa = self.sa_session.query( self.model.DefaultQuotaAssociation ).filter( self.model.DefaultQuotaAssociation.table.c.type == default_type ).first() + def _default_quota(self, default_type): + dqa = self.sa_session.query(self.model.DefaultQuotaAssociation).filter(self.model.DefaultQuotaAssociation.table.c.type == default_type).first() if not dqa: return None if dqa.quota.bytes < 0: return None return dqa.quota.bytes - def set_default_quota( self, default_type, quota ): + def set_default_quota(self, default_type, quota): # Unset the current default(s) associated with this quota, if there are any for dqa in quota.default: - self.sa_session.delete( dqa ) + self.sa_session.delete(dqa) # Unset the current users/groups associated with this quota for uqa in quota.users: - self.sa_session.delete( uqa ) + self.sa_session.delete(uqa) for gqa in quota.groups: - self.sa_session.delete( gqa ) + self.sa_session.delete(gqa) # Find the old default, assign the new quota if it exists - dqa = self.sa_session.query( self.model.DefaultQuotaAssociation ).filter( self.model.DefaultQuotaAssociation.table.c.type == default_type ).first() + dqa = self.sa_session.query(self.model.DefaultQuotaAssociation).filter(self.model.DefaultQuotaAssociation.table.c.type == default_type).first() if dqa: dqa.quota = quota # Or create if necessary else: - dqa = self.model.DefaultQuotaAssociation( default_type, quota ) - self.sa_session.add( dqa ) + dqa = self.model.DefaultQuotaAssociation(default_type, quota) + self.sa_session.add(dqa) self.sa_session.flush() - def get_percent( self, trans=None, user=False, history=False, usage=False, quota=False ): + def get_percent(self, trans=None, user=False, history=False, usage=False, quota=False): """ Return the percentage of any storage quota applicable to the user/transaction. """ @@ -142,50 +144,50 @@ def get_percent( self, trans=None, user=False, history=False, usage=False, quota history = trans.history # if quota wasn't passed, attempt to get the quota if quota is False: - quota = self.get_quota( user ) + quota = self.get_quota(user) # return none if no applicable quotas or quotas disabled if quota is None: return None # get the usage, if it wasn't passed if usage is False: - usage = self.get_usage( trans, user, history ) + usage = self.get_usage(trans, user, history) try: - return min( ( int( float( usage ) / quota * 100 ), 100 ) ) + return min((int(float(usage) / quota * 100), 100)) except ZeroDivisionError: return 100 - def set_entity_quota_associations( self, quotas=[], users=[], groups=[], delete_existing_assocs=True ): + def set_entity_quota_associations(self, quotas=[], users=[], groups=[], delete_existing_assocs=True): for quota in quotas: if delete_existing_assocs: flush_needed = False for a in quota.users + quota.groups: - self.sa_session.delete( a ) + self.sa_session.delete(a) flush_needed = True if flush_needed: self.sa_session.flush() for user in users: - uqa = self.model.UserQuotaAssociation( user, quota ) - self.sa_session.add( uqa ) + uqa = self.model.UserQuotaAssociation(user, quota) + self.sa_session.add(uqa) for group in groups: - gqa = self.model.GroupQuotaAssociation( group, quota ) - self.sa_session.add( gqa ) + gqa = self.model.GroupQuotaAssociation(group, quota) + self.sa_session.add(gqa) self.sa_session.flush() - def get_user_quotas( self, user ): + def get_user_quotas(self, user): rval = [] if not user: - dqa = self.sa_session.query( self.model.DefaultQuotaAssociation ) \ - .filter( self.model.DefaultQuotaAssociation.table.c.type == self.model.DefaultQuotaAssociation.types.UNREGISTERED ).first() + dqa = self.sa_session.query(self.model.DefaultQuotaAssociation) \ + .filter(self.model.DefaultQuotaAssociation.table.c.type == self.model.DefaultQuotaAssociation.types.UNREGISTERED).first() if dqa: - rval.append( dqa.quota ) + rval.append(dqa.quota) else: - dqa = self.sa_session.query( self.model.DefaultQuotaAssociation ) \ - .filter( self.model.DefaultQuotaAssociation.table.c.type == self.model.DefaultQuotaAssociation.types.REGISTERED ).first() + dqa = self.sa_session.query(self.model.DefaultQuotaAssociation) \ + .filter(self.model.DefaultQuotaAssociation.table.c.type == self.model.DefaultQuotaAssociation.types.REGISTERED).first() if dqa: - rval.append( dqa.quota ) + rval.append(dqa.quota) for uqa in user.quotas: - rval.append( uqa.quota ) - for group in [ uga.group for uga in user.groups ]: + rval.append(uqa.quota) + for group in [uga.group for uga in user.groups]: for gqa in group.quotas: - rval.append( gqa.quota ) + rval.append(gqa.quota) return rval diff --git a/lib/galaxy/sample_tracking/data_transfer.py b/lib/galaxy/sample_tracking/data_transfer.py index 13b3a662c57b..f7740722d110 100644 --- a/lib/galaxy/sample_tracking/data_transfer.py +++ b/lib/galaxy/sample_tracking/data_transfer.py @@ -1,58 +1,58 @@ -class DataTransferFactory( object ): +class DataTransferFactory(object): type = None - def parse( self ): + def parse(self): pass -class ScpDataTransferFactory( DataTransferFactory ): +class ScpDataTransferFactory(DataTransferFactory): type = 'scp' - def __init__( self ): + def __init__(self): pass - def parse( self, config_file, elem ): + def parse(self, config_file, elem): self.config = {} # TODO: The 'automatic_transfer' setting is for future use. If set to True, we will need to # ensure the sample has an associated destination data library before it moves to a certain state # ( e.g., Run started ). - self.config[ 'automatic_transfer' ] = elem.get( 'automatic_transfer' ) - self.config[ 'host' ] = elem.get( 'host' ) - self.config[ 'user_name' ] = elem.get( 'user_name' ) - self.config[ 'password' ] = elem.get( 'password' ) - self.config[ 'data_location' ] = elem.get( 'data_location' ) + self.config['automatic_transfer'] = elem.get('automatic_transfer') + self.config['host'] = elem.get('host') + self.config['user_name'] = elem.get('user_name') + self.config['password'] = elem.get('password') + self.config['data_location'] = elem.get('data_location') # 'rename_dataset' is optional and it may not be defined in all external types # It is only used is AB SOLiD external service type for now - rename_dataset = elem.get( 'rename_dataset', None ) + rename_dataset = elem.get('rename_dataset', None) if rename_dataset: self.config['rename_dataset'] = rename_dataset # Validate for name, value in self.config.items(): - assert value, "'%s' attribute missing in 'data_transfer' element of type 'scp' in external_service_type xml config file: '%s'." % ( name, config_file ) + assert value, "'%s' attribute missing in 'data_transfer' element of type 'scp' in external_service_type xml config file: '%s'." % (name, config_file) -class HttpDataTransferFactory( DataTransferFactory ): +class HttpDataTransferFactory(DataTransferFactory): type = 'http' - def __init__( self ): + def __init__(self): pass - def parse( self, config_file, elem ): + def parse(self, config_file, elem): self.config = {} - self.config[ 'automatic_transfer' ] = elem.get( 'automatic_transfer' ) + self.config['automatic_transfer'] = elem.get('automatic_transfer') # Validate for name, value in self.config.items(): - assert value, "'%s' attribute missing in 'data_transfer' element of type 'http' in external_service_type xml config file: '%s'." % ( name, config_file ) + assert value, "'%s' attribute missing in 'data_transfer' element of type 'http' in external_service_type xml config file: '%s'." % (name, config_file) -class FtpDataTransferFactory( DataTransferFactory ): +class FtpDataTransferFactory(DataTransferFactory): type = 'ftp' - def __init__( self ): + def __init__(self): pass - def parse( self, elem ): + def parse(self, elem): pass -data_transfer_factories = dict( [ ( data_transfer.type, data_transfer() ) for data_transfer in [ ScpDataTransferFactory, HttpDataTransferFactory, FtpDataTransferFactory ] ] ) +data_transfer_factories = dict([(data_transfer.type, data_transfer()) for data_transfer in [ScpDataTransferFactory, HttpDataTransferFactory, FtpDataTransferFactory]]) diff --git a/lib/galaxy/sample_tracking/external_service_types.py b/lib/galaxy/sample_tracking/external_service_types.py index e3fc6df8075a..a1b04b88005f 100644 --- a/lib/galaxy/sample_tracking/external_service_types.py +++ b/lib/galaxy/sample_tracking/external_service_types.py @@ -5,118 +5,118 @@ from galaxy.forms.forms import form_factory from galaxy.external_services.service import ExternalServiceActionsGroup from galaxy.sample_tracking.data_transfer import data_transfer_factories -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class ExternalServiceTypeNotFoundException( Exception ): +class ExternalServiceTypeNotFoundException(Exception): pass -class ExternalServiceTypesCollection( object ): +class ExternalServiceTypesCollection(object): - def __init__( self, config_filename, root_dir, app ): + def __init__(self, config_filename, root_dir, app): self.all_external_service_types = odict() self.root_dir = root_dir self.app = app try: - self.load_all( config_filename ) + self.load_all(config_filename) except: - log.exception( "ExternalServiceTypesCollection error reading %s", config_filename ) + log.exception("ExternalServiceTypesCollection error reading %s", config_filename) - def load_all( self, config_filename ): + def load_all(self, config_filename): self.visible_external_service_types = [] - tree = util.parse_xml( config_filename ) + tree = util.parse_xml(config_filename) root = tree.getroot() for elem in root: try: if elem.tag == 'external_service_type': - file_path = elem.get( "file" ) - visible = util.string_as_bool( elem.get( "visible" ) ) - external_service_type = self.load_external_service_type( os.path.join( self.root_dir, file_path ), visible ) - self.all_external_service_types[ external_service_type.id ] = external_service_type - log.debug( "Loaded external_service_type: %s %s" % ( external_service_type.name, external_service_type.config_version ) ) + file_path = elem.get("file") + visible = util.string_as_bool(elem.get("visible")) + external_service_type = self.load_external_service_type(os.path.join(self.root_dir, file_path), visible) + self.all_external_service_types[external_service_type.id] = external_service_type + log.debug("Loaded external_service_type: %s %s" % (external_service_type.name, external_service_type.config_version)) if visible: - self.visible_external_service_types.append( external_service_type.id ) + self.visible_external_service_types.append(external_service_type.id) except: - log.exception( "error reading external_service_type from path: %s", file_path ) + log.exception("error reading external_service_type from path: %s", file_path) - def load_external_service_type( self, config_file, visible=True ): + def load_external_service_type(self, config_file, visible=True): # Parse XML configuration file and get the root element - tree = util.parse_xml( config_file ) + tree = util.parse_xml(config_file) root = tree.getroot() - return ExternalServiceType( config_file, root, visible ) + return ExternalServiceType(config_file, root, visible) - def reload( self, external_service_type_id ): + def reload(self, external_service_type_id): """ Attempt to reload the external_service_type identified by 'external_service_type_id', if successful replace the old external_service_type. """ if external_service_type_id not in self.all_external_service_types.keys(): - raise ExternalServiceTypeNotFoundException( "No external_service_type with id %s" % external_service_type_id ) - old_external_service_type = self.all_external_service_types[ external_service_type_id ] - new_external_service_type = self.load_external_service_type( old_external_service_type.config_file ) - self.all_external_service_types[ external_service_type_id ] = new_external_service_type - log.debug( "Reloaded external_service_type %s" % ( external_service_type_id ) ) + raise ExternalServiceTypeNotFoundException("No external_service_type with id %s" % external_service_type_id) + old_external_service_type = self.all_external_service_types[external_service_type_id] + new_external_service_type = self.load_external_service_type(old_external_service_type.config_file) + self.all_external_service_types[external_service_type_id] = new_external_service_type + log.debug("Reloaded external_service_type %s" % (external_service_type_id)) return new_external_service_type -class ExternalServiceType( object ): - def __init__( self, external_service_type_xml_config, root, visible=True ): +class ExternalServiceType(object): + def __init__(self, external_service_type_xml_config, root, visible=True): self.config_file = external_service_type_xml_config - self.parse( root ) + self.parse(root) self.visible = visible root.clear() - def parse( self, root ): + def parse(self, root): # Get the name - self.name = root.get( "name" ) + self.name = root.get("name") if not self.name: - raise Exception( "Missing external_service_type 'name'" ) + raise Exception("Missing external_service_type 'name'") # Get the UNIQUE id for the tool - self.id = root.get( "id" ) + self.id = root.get("id") if not self.id: - raise Exception( "Missing external_service_type 'id'" ) - self.config_version = root.get( "version" ) + raise Exception("Missing external_service_type 'id'") + self.config_version = root.get("version") if not self.config_version: self.config_version = '1.0.0' self.description = util.xml_text(root, "description") - self.version = util.xml_text( root.find( "version" ) ) + self.version = util.xml_text(root.find("version")) # parse the form - self.form_definition = form_factory.from_elem( root.find( 'form' ) ) - self.parse_data_transfer_settings( root ) - self.parse_run_details( root ) + self.form_definition = form_factory.from_elem(root.find('form')) + self.parse_data_transfer_settings(root) + self.parse_run_details(root) # external services actions - self.actions = ExternalServiceActionsGroup.from_elem( root.find( 'actions' ), parent=self ) + self.actions = ExternalServiceActionsGroup.from_elem(root.find('actions'), parent=self) - def parse_data_transfer_settings( self, root ): + def parse_data_transfer_settings(self, root): self.data_transfer = {} - data_transfer_settings_elem = root.find( 'data_transfer_settings' ) + data_transfer_settings_elem = root.find('data_transfer_settings') # Currently only data transfer using scp or http is supported. - for data_transfer_elem in data_transfer_settings_elem.findall( "data_transfer" ): - if data_transfer_elem.get( 'protocol' ) == model.ExternalService.data_transfer_protocol.SCP: - scp_data_transfer = data_transfer_factories[ model.ExternalService.data_transfer_protocol.SCP ] - scp_data_transfer.parse( self.config_file, data_transfer_elem ) - self.data_transfer[ model.ExternalService.data_transfer_protocol.SCP ] = scp_data_transfer - if data_transfer_elem.get( 'protocol' ) == model.ExternalService.data_transfer_protocol.HTTP: - http_data_transfer = data_transfer_factories[ model.ExternalService.data_transfer_protocol.HTTP ] - http_data_transfer.parse( self.config_file, data_transfer_elem ) - self.data_transfer[ model.ExternalService.data_transfer_protocol.HTTP ] = http_data_transfer + for data_transfer_elem in data_transfer_settings_elem.findall("data_transfer"): + if data_transfer_elem.get('protocol') == model.ExternalService.data_transfer_protocol.SCP: + scp_data_transfer = data_transfer_factories[model.ExternalService.data_transfer_protocol.SCP] + scp_data_transfer.parse(self.config_file, data_transfer_elem) + self.data_transfer[model.ExternalService.data_transfer_protocol.SCP] = scp_data_transfer + if data_transfer_elem.get('protocol') == model.ExternalService.data_transfer_protocol.HTTP: + http_data_transfer = data_transfer_factories[model.ExternalService.data_transfer_protocol.HTTP] + http_data_transfer.parse(self.config_file, data_transfer_elem) + self.data_transfer[model.ExternalService.data_transfer_protocol.HTTP] = http_data_transfer - def parse_run_details( self, root ): + def parse_run_details(self, root): self.run_details = {} - run_details_elem = root.find( 'run_details' ) + run_details_elem = root.find('run_details') if run_details_elem is not None: - results_elem = run_details_elem.find( 'results' ) + results_elem = run_details_elem.find('results') if results_elem is not None: # Get the list of resulting datatypes # TODO: the 'results_urls' attribute is only useful if the transfer protocol is http(s), so check if that is the case. - self.run_details[ 'results' ], self.run_details[ 'results_urls' ] = self.parse_run_details_results( results_elem ) + self.run_details['results'], self.run_details['results_urls'] = self.parse_run_details_results(results_elem) - def parse_run_details_results( self, root ): + def parse_run_details_results(self, root): datatypes_dict = {} urls_dict = {} - for datatype_elem in root.findall( "dataset" ): - name = datatype_elem.get( 'name' ) - datatypes_dict[ name ] = datatype_elem.get( 'datatype' ) - urls_dict[ name ] = datatype_elem.get( 'url', None ) + for datatype_elem in root.findall("dataset"): + name = datatype_elem.get('name') + datatypes_dict[name] = datatype_elem.get('datatype') + urls_dict[name] = datatype_elem.get('url', None) return datatypes_dict, urls_dict diff --git a/lib/galaxy/sample_tracking/request_types.py b/lib/galaxy/sample_tracking/request_types.py index 632b19047e96..6ca651f3b222 100644 --- a/lib/galaxy/sample_tracking/request_types.py +++ b/lib/galaxy/sample_tracking/request_types.py @@ -4,30 +4,30 @@ from galaxy.model import RequestType from sample import sample_state_factory -RENAME_DATASET_OPTIONS = dict( [ ( f_type.lower(), f_descript ) for f_type, f_descript in RequestType.rename_dataset_options.items() ] ) +RENAME_DATASET_OPTIONS = dict([(f_type.lower(), f_descript) for f_type, f_descript in RequestType.rename_dataset_options.items()]) -class RequestTypeFactory( object ): - def __init__( self, sample_state_factory, rename_dataset_options ): +class RequestTypeFactory(object): + def __init__(self, sample_state_factory, rename_dataset_options): self.sample_state_factory = sample_state_factory self.rename_dataset_options = rename_dataset_options - def new( self, name, request_form, sample_form, external_service, description=None, sample_states=None ): + def new(self, name, request_form, sample_form, external_service, description=None, sample_states=None): """Return new RequestType.""" assert name, 'RequestType requires a name' - return RequestType( name=name, desc=description, request_form=request_form, sample_form=sample_form, external_service=external_service ) + return RequestType(name=name, desc=description, request_form=request_form, sample_form=sample_form, external_service=external_service) - def from_elem( self, elem, request_form, sample_form, external_service ): + def from_elem(self, elem, request_form, sample_form, external_service): """Return RequestType created from an xml string.""" - name = elem.get( 'name', '' ) - description = elem.get( 'description', '' ) - rval = self.new( name, request_form, sample_form, external_service=external_service, description=description ) + name = elem.get('name', '') + description = elem.get('description', '') + rval = self.new(name, request_form, sample_form, external_service=external_service, description=description) # load states - sample_states_elem = elem.find( 'sample_states' ) + sample_states_elem = elem.find('sample_states') if sample_states_elem: - for sample_state_elem in sample_states_elem.findall( 'state' ): - self.sample_state_factory.from_elem( rval, sample_state_elem ) # should this be the return value?? + for sample_state_elem in sample_states_elem.findall('state'): + self.sample_state_factory.from_elem(rval, sample_state_elem) # should this be the return value?? return rval -request_type_factory = RequestTypeFactory( sample_state_factory, RENAME_DATASET_OPTIONS ) +request_type_factory = RequestTypeFactory(sample_state_factory, RENAME_DATASET_OPTIONS) diff --git a/lib/galaxy/sample_tracking/sample.py b/lib/galaxy/sample_tracking/sample.py index 64b406232b9b..1b5171eb0c1f 100644 --- a/lib/galaxy/sample_tracking/sample.py +++ b/lib/galaxy/sample_tracking/sample.py @@ -4,23 +4,23 @@ from galaxy.model import SampleState -class SampleStateFactory( object ): - def new( self, request_type, name, description=None ): +class SampleStateFactory(object): + def new(self, request_type, name, description=None): """ Return new SampleState. """ assert name, 'SampleState requires a name' if description is None: description = '' - return SampleState( name=name, desc=description, request_type=request_type ) + return SampleState(name=name, desc=description, request_type=request_type) - def from_elem( self, request_type, elem ): + def from_elem(self, request_type, elem): """ Return SampleState created from an xml string. """ - name = elem.get( 'name', None ) - description = elem.get( 'description', None ) - return self.new( request_type, name, description=description ) + name = elem.get('name', None) + description = elem.get('description', None) + return self.new(request_type, name, description=description) sample_state_factory = SampleStateFactory() diff --git a/lib/galaxy/security/__init__.py b/lib/galaxy/security/__init__.py index 2283e6f85f89..26fa6d3d3cd6 100644 --- a/lib/galaxy/security/__init__.py +++ b/lib/galaxy/security/__init__.py @@ -15,8 +15,8 @@ log = logging.getLogger(__name__) -class Action( object ): - def __init__( self, action, description, model ): +class Action(object): + def __init__(self, action, description, model): self.action = action self.description = description self.model = model @@ -25,149 +25,149 @@ def __init__( self, action, description, model ): class RBACAgent: """Class that handles galaxy security""" permitted_actions = Bunch( - DATASET_MANAGE_PERMISSIONS=Action( "manage permissions", "Users having associated role can manage the roles associated with permissions on this dataset.", "grant" ), - DATASET_ACCESS=Action( "access", "Users having associated role can import this dataset into their history for analysis.", "restrict" ), - LIBRARY_ACCESS=Action( "access library", "Restrict access to this library to only users having associated role", "restrict" ), - LIBRARY_ADD=Action( "add library item", "Users having associated role can add library items to this library item", "grant" ), - LIBRARY_MODIFY=Action( "modify library item", "Users having associated role can modify this library item", "grant" ), - LIBRARY_MANAGE=Action( "manage library permissions", "Users having associated role can manage roles associated with permissions on this library item", "grant" ), + DATASET_MANAGE_PERMISSIONS=Action("manage permissions", "Users having associated role can manage the roles associated with permissions on this dataset.", "grant"), + DATASET_ACCESS=Action("access", "Users having associated role can import this dataset into their history for analysis.", "restrict"), + LIBRARY_ACCESS=Action("access library", "Restrict access to this library to only users having associated role", "restrict"), + LIBRARY_ADD=Action("add library item", "Users having associated role can add library items to this library item", "grant"), + LIBRARY_MODIFY=Action("modify library item", "Users having associated role can modify this library item", "grant"), + LIBRARY_MANAGE=Action("manage library permissions", "Users having associated role can manage roles associated with permissions on this library item", "grant"), # Request type permissions - REQUEST_TYPE_ACCESS=Action( "access request_type", "Restrict access to this request type to only users having associated role", "restrict" ) + REQUEST_TYPE_ACCESS=Action("access request_type", "Restrict access to this request type to only users having associated role", "restrict") ) - def get_action( self, name, default=None ): + def get_action(self, name, default=None): """Get a permitted action by its dict key or action name""" for k, v in self.permitted_actions.items(): if k == name or v.action == name: return v return default - def get_actions( self ): + def get_actions(self): """Get all permitted actions as a list of Action objects""" return list(self.permitted_actions.__dict__.values()) - def get_item_actions( self, action, item ): - raise Exception( 'No valid method of retrieving action (%s) for item %s.' % ( action, item ) ) + def get_item_actions(self, action, item): + raise Exception('No valid method of retrieving action (%s) for item %s.' % (action, item)) - def guess_derived_permissions_for_datasets( self, datasets=[] ): - raise Exception( "Unimplemented Method" ) + def guess_derived_permissions_for_datasets(self, datasets=[]): + raise Exception("Unimplemented Method") - def can_access_dataset( self, roles, dataset ): - raise Exception( "Unimplemented Method" ) + def can_access_dataset(self, roles, dataset): + raise Exception("Unimplemented Method") - def can_manage_dataset( self, roles, dataset ): - raise Exception( "Unimplemented Method" ) + def can_manage_dataset(self, roles, dataset): + raise Exception("Unimplemented Method") - def can_access_library( self, roles, library ): - raise Exception( "Unimplemented Method" ) + def can_access_library(self, roles, library): + raise Exception("Unimplemented Method") - def can_add_library_item( self, roles, item ): - raise Exception( "Unimplemented Method" ) + def can_add_library_item(self, roles, item): + raise Exception("Unimplemented Method") - def can_modify_library_item( self, roles, item ): - raise Exception( "Unimplemented Method" ) + def can_modify_library_item(self, roles, item): + raise Exception("Unimplemented Method") - def can_manage_library_item( self, roles, item ): - raise Exception( "Unimplemented Method" ) + def can_manage_library_item(self, roles, item): + raise Exception("Unimplemented Method") - def associate_components( self, **kwd ): - raise Exception( 'No valid method of associating provided components: %s' % kwd ) + def associate_components(self, **kwd): + raise Exception('No valid method of associating provided components: %s' % kwd) - def create_private_user_role( self, user ): - raise Exception( "Unimplemented Method" ) + def create_private_user_role(self, user): + raise Exception("Unimplemented Method") - def get_private_user_role( self, user ): - raise Exception( "Unimplemented Method" ) + def get_private_user_role(self, user): + raise Exception("Unimplemented Method") - def get_accessible_request_types( self, trans, user ): - raise Exception( "Unimplemented Method" ) + def get_accessible_request_types(self, trans, user): + raise Exception("Unimplemented Method") - def user_set_default_permissions( self, user, permissions={}, history=False, dataset=False ): - raise Exception( "Unimplemented Method" ) + def user_set_default_permissions(self, user, permissions={}, history=False, dataset=False): + raise Exception("Unimplemented Method") - def history_set_default_permissions( self, history, permissions=None, dataset=False, bypass_manage_permission=False ): - raise Exception( "Unimplemented Method" ) + def history_set_default_permissions(self, history, permissions=None, dataset=False, bypass_manage_permission=False): + raise Exception("Unimplemented Method") - def set_all_dataset_permissions( self, dataset, permissions, new=False ): - raise Exception( "Unimplemented Method" ) + def set_all_dataset_permissions(self, dataset, permissions, new=False): + raise Exception("Unimplemented Method") - def set_dataset_permission( self, dataset, permission ): - raise Exception( "Unimplemented Method" ) + def set_dataset_permission(self, dataset, permission): + raise Exception("Unimplemented Method") - def set_all_library_permissions( self, trans, dataset, permissions ): - raise Exception( "Unimplemented Method" ) + def set_all_library_permissions(self, trans, dataset, permissions): + raise Exception("Unimplemented Method") - def set_library_item_permission( self, library_item, permission ): - raise Exception( "Unimplemented Method" ) + def set_library_item_permission(self, library_item, permission): + raise Exception("Unimplemented Method") - def library_is_public( self, library ): - raise Exception( "Unimplemented Method" ) + def library_is_public(self, library): + raise Exception("Unimplemented Method") - def make_library_public( self, library ): - raise Exception( "Unimplemented Method" ) + def make_library_public(self, library): + raise Exception("Unimplemented Method") - def get_accessible_libraries( self, trans, user ): - raise Exception( "Unimplemented Method" ) + def get_accessible_libraries(self, trans, user): + raise Exception("Unimplemented Method") - def get_permitted_libraries( self, trans, user, actions ): - raise Exception( "Unimplemented Method" ) + def get_permitted_libraries(self, trans, user, actions): + raise Exception("Unimplemented Method") - def folder_is_public( self, library ): - raise Exception( "Unimplemented Method" ) + def folder_is_public(self, library): + raise Exception("Unimplemented Method") - def make_folder_public( self, folder, count=0 ): - raise Exception( "Unimplemented Method" ) + def make_folder_public(self, folder, count=0): + raise Exception("Unimplemented Method") - def dataset_is_public( self, dataset ): - raise Exception( "Unimplemented Method" ) + def dataset_is_public(self, dataset): + raise Exception("Unimplemented Method") - def make_dataset_public( self, dataset ): - raise Exception( "Unimplemented Method" ) + def make_dataset_public(self, dataset): + raise Exception("Unimplemented Method") - def get_permissions( self, library_dataset ): - raise Exception( "Unimplemented Method" ) + def get_permissions(self, library_dataset): + raise Exception("Unimplemented Method") - def get_all_roles( self, trans, cntrller ): - raise Exception( "Unimplemented Method" ) + def get_all_roles(self, trans, cntrller): + raise Exception("Unimplemented Method") - def get_legitimate_roles( self, trans, item, cntrller ): - raise Exception( "Unimplemented Method" ) + def get_legitimate_roles(self, trans, item, cntrller): + raise Exception("Unimplemented Method") - def derive_roles_from_access( self, trans, item_id, cntrller, library=False, **kwd ): - raise Exception( "Unimplemented Method" ) + def derive_roles_from_access(self, trans, item_id, cntrller, library=False, **kwd): + raise Exception("Unimplemented Method") - def get_component_associations( self, **kwd ): - raise Exception( "Unimplemented Method" ) + def get_component_associations(self, **kwd): + raise Exception("Unimplemented Method") - def components_are_associated( self, **kwd ): - return bool( self.get_component_associations( **kwd ) ) + def components_are_associated(self, **kwd): + return bool(self.get_component_associations(**kwd)) - def convert_permitted_action_strings( self, permitted_action_strings ): + def convert_permitted_action_strings(self, permitted_action_strings): """ When getting permitted actions from an untrusted source like a form, ensure that they match our actual permitted actions. """ - return [_ for _ in [ self.permitted_actions.get( action_string ) for action_string in permitted_action_strings ] if _ is not None] + return [_ for _ in [self.permitted_actions.get(action_string) for action_string in permitted_action_strings] if _ is not None] -class GalaxyRBACAgent( RBACAgent ): - def __init__( self, model, permitted_actions=None ): +class GalaxyRBACAgent(RBACAgent): + def __init__(self, model, permitted_actions=None): self.model = model if permitted_actions: self.permitted_actions = permitted_actions # List of "library_item" objects and their associated permissions and info template objects self.library_item_assocs = ( - ( self.model.Library, self.model.LibraryPermissions ), - ( self.model.LibraryFolder, self.model.LibraryFolderPermissions ), - ( self.model.LibraryDataset, self.model.LibraryDatasetPermissions ), - ( self.model.LibraryDatasetDatasetAssociation, self.model.LibraryDatasetDatasetAssociationPermissions ) ) + (self.model.Library, self.model.LibraryPermissions), + (self.model.LibraryFolder, self.model.LibraryFolderPermissions), + (self.model.LibraryDataset, self.model.LibraryDatasetPermissions), + (self.model.LibraryDatasetDatasetAssociation, self.model.LibraryDatasetDatasetAssociationPermissions)) @property - def sa_session( self ): + def sa_session(self): """Returns a SQLAlchemy session""" return self.model.context - def sort_by_attr( self, seq, attr ): + def sort_by_attr(self, seq, attr): """ Sort the sequence of objects by object's attribute Arguments: @@ -179,43 +179,43 @@ def sort_by_attr( self, seq, attr ): # (seq[i].attr, i, seq[i]) and sort it. The second item of tuple is needed not # only to provide stable sorting, but mainly to eliminate comparison of objects # (which can be expensive or prohibited) in case of equal attribute values. - intermed = map( None, (getattr(_, attr) for _ in seq), range( len( seq ) ), seq ) + intermed = map(None, (getattr(_, attr) for _ in seq), range(len(seq)), seq) intermed.sort() return [_[-1] for _ in intermed] - def _get_npns_roles( self, trans ): + def _get_npns_roles(self, trans): """ non-private, non-sharing roles """ - return trans.sa_session.query( trans.app.model.Role ) \ - .filter( and_( self.model.Role.table.c.deleted == false(), + return trans.sa_session.query(trans.app.model.Role) \ + .filter(and_(self.model.Role.table.c.deleted == false(), self.model.Role.table.c.type != self.model.Role.types.PRIVATE, - self.model.Role.table.c.type != self.model.Role.types.SHARING ) ) \ - .order_by( self.model.Role.table.c.name ) + self.model.Role.table.c.type != self.model.Role.types.SHARING)) \ + .order_by(self.model.Role.table.c.name) - def get_all_roles( self, trans, cntrller ): - admin_controller = cntrller in [ 'library_admin' ] + def get_all_roles(self, trans, cntrller): + admin_controller = cntrller in ['library_admin'] roles = set() if not trans.user: - return self._get_npns_roles( trans ) + return self._get_npns_roles(trans) if admin_controller: # The library is public and the user is an admin, so all roles are legitimate - for role in trans.sa_session.query( trans.app.model.Role ) \ - .filter( self.model.Role.table.c.deleted == false() ) \ - .order_by( self.model.Role.table.c.name ): - roles.add( role ) + for role in trans.sa_session.query(trans.app.model.Role) \ + .filter(self.model.Role.table.c.deleted == false()) \ + .order_by(self.model.Role.table.c.name): + roles.add(role) else: # Add the current user's private role - roles.add( self.get_private_user_role( trans.user ) ) + roles.add(self.get_private_user_role(trans.user)) # Add the current user's sharing roles - for role in self.get_sharing_roles( trans.user ): - roles.add( role ) + for role in self.get_sharing_roles(trans.user): + roles.add(role) # Add all remaining non-private, non-sharing roles - for role in self._get_npns_roles( trans): - roles.add( role ) - return self.sort_by_attr( [ role for role in roles ], 'name' ) + for role in self._get_npns_roles(trans): + roles.add(role) + return self.sort_by_attr([role for role in roles], 'name') - def get_roles_for_action( self, item, action ): + def get_roles_for_action(self, item, action): """ Return a list containing the roles associated with given action on given item where item is one of Library, LibraryFolder, LibraryDatasetDatasetAssociation, @@ -223,12 +223,12 @@ def get_roles_for_action( self, item, action ): """ roles = [] for item_permission in item.actions: - permission_action = self.get_action( item_permission.action ) - if permission_action == action: - roles.append( item_permission.role ) + permission_action = self.get_action(item_permission.action) + if permission_action == action: + roles.append(item_permission.role) return roles - def get_valid_roles( self, trans, item, query=None, page=None, page_limit=None, is_library_access=False ): + def get_valid_roles(self, trans, item, query=None, page=None, page_limit=None, is_library_access=False): """ This method retrieves the list of possible roles that user can select in the item permissions form. Admins can select any role so the @@ -239,7 +239,7 @@ def get_valid_roles( self, trans, item, query=None, page=None, page_limit=None, """ roles = [] if query is not None: - query = query.replace( '_', '/_' ).replace( '%', '/%' ).replace( '/', '//' ) + query = query.replace('_', '/_').replace('%', '/%').replace('/', '//') search_query = query + '%' log.debug('search_query: ' + str(search_query)) @@ -252,59 +252,59 @@ def get_valid_roles( self, trans, item, query=None, page=None, page_limit=None, total_count = None - if isinstance( item, self.model.Library ) and self.library_is_public( item ): + if isinstance(item, self.model.Library) and self.library_is_public(item): is_public_item = True - elif isinstance( item, self.model.Dataset ) and self.dataset_is_public( item ): + elif isinstance(item, self.model.Dataset) and self.dataset_is_public(item): is_public_item = True - elif isinstance( item, self.model.LibraryFolder ): + elif isinstance(item, self.model.LibraryFolder): is_public_item = True else: is_public_item = False # For public items and for library access admins can choose from all roles - if trans.user_is_admin() and ( is_public_item or is_library_access ): + if trans.user_is_admin() and (is_public_item or is_library_access): # Add all non-deleted roles that fit the query - db_query = trans.sa_session.query( trans.app.model.Role ).filter( self.model.Role.table.c.deleted == false() ) + db_query = trans.sa_session.query(trans.app.model.Role).filter(self.model.Role.table.c.deleted == false()) if query is not None: - db_query = db_query.filter( self.model.Role.table.c.name.like( search_query, escape='/' ) ) + db_query = db_query.filter(self.model.Role.table.c.name.like(search_query, escape='/')) total_count = db_query.count() if paginated: # Takes the least number of results from beginning that includes the requested page - roles = db_query.order_by( self.model.Role.table.c.name ).limit( limit ).all() - page_start = ( page * page_limit ) - page_limit + roles = db_query.order_by(self.model.Role.table.c.name).limit(limit).all() + page_start = (page * page_limit) - page_limit page_end = page_start + page_limit if total_count < page_start: # Return empty list if there are less results than the requested position roles = [] else: - roles = roles[ page_start:page_end ] + roles = roles[page_start:page_end] else: - roles = db_query.order_by( self.model.Role.table.c.name ) + roles = db_query.order_by(self.model.Role.table.c.name) # Non-admin and public item elif is_public_item: # Add the current user's private role - roles.append( self.get_private_user_role( trans.user ) ) + roles.append(self.get_private_user_role(trans.user)) # Add the current user's sharing roles - for role in self.get_sharing_roles( trans.user ): - roles.append( role ) + for role in self.get_sharing_roles(trans.user): + roles.append(role) # Add all remaining non-private, non-sharing roles - for role in self._get_npns_roles( trans ): - roles.append( role ) + for role in self._get_npns_roles(trans): + roles.append(role) # User is not admin and item is not public # User will see all the roles derived from the access roles on the item else: # If item has roles associated with the access permission, we need to start with them. - access_roles = item.get_access_roles( trans ) + access_roles = item.get_access_roles(trans) for role in access_roles: - if trans.user_is_admin() or self.ok_to_display( trans.user, role ): - roles.append( role ) + if trans.user_is_admin() or self.ok_to_display(trans.user, role): + roles.append(role) # Each role potentially has users. We need to find all roles that each of those users have. for ura in role.users: user = ura.user for ura2 in user.roles: - if trans.user_is_admin() or self.ok_to_display( trans.user, ura2.role ): - roles.append( ura2.role ) + if trans.user_is_admin() or self.ok_to_display(trans.user, ura2.role): + roles.append(ura2.role) # Each role also potentially has groups which, in turn, have members ( users ). We need to # find all roles that each group's members have. for gra in role.groups: @@ -312,16 +312,16 @@ def get_valid_roles( self, trans, item, query=None, page=None, page_limit=None, for uga in group.users: user = uga.user for ura in user.roles: - if trans.user_is_admin() or self.ok_to_display( trans.user, ura.role ): - roles.append( ura.role ) + if trans.user_is_admin() or self.ok_to_display(trans.user, ura.role): + roles.append(ura.role) # Omit duplicated roles by converting to set - return_roles = set( roles ) + return_roles = set(roles) if total_count is None: - total_count = len( return_roles ) - return self.sort_by_attr( [ role for role in return_roles ], 'name' ), total_count + total_count = len(return_roles) + return self.sort_by_attr([role for role in return_roles], 'name'), total_count - def get_legitimate_roles( self, trans, item, cntrller ): + def get_legitimate_roles(self, trans, item, cntrller): """ Return a sorted list of legitimate roles that can be associated with a permission on item where item is a Library or a Dataset. The cntrller param is the controller from @@ -344,22 +344,22 @@ def get_legitimate_roles( self, trans, item, cntrller ): with each role that is associated with the access permission on item. Private roles, except for the current user's private role, will be excluded. """ - admin_controller = cntrller in [ 'library_admin' ] + admin_controller = cntrller in ['library_admin'] roles = set() - if ( isinstance( item, self.model.Library ) and self.library_is_public( item ) ) or \ - ( isinstance( item, self.model.Dataset ) and self.dataset_is_public( item ) ): - return self.get_all_roles( trans, cntrller ) + if (isinstance(item, self.model.Library) and self.library_is_public(item)) or \ + (isinstance(item, self.model.Dataset) and self.dataset_is_public(item)): + return self.get_all_roles(trans, cntrller) # If item has roles associated with the access permission, we need to start with them. - access_roles = item.get_access_roles( trans ) + access_roles = item.get_access_roles(trans) for role in access_roles: - if admin_controller or self.ok_to_display( trans.user, role ): - roles.add( role ) + if admin_controller or self.ok_to_display(trans.user, role): + roles.add(role) # Each role potentially has users. We need to find all roles that each of those users have. for ura in role.users: user = ura.user for ura2 in user.roles: - if admin_controller or self.ok_to_display( trans.user, ura2.role ): - roles.add( ura2.role ) + if admin_controller or self.ok_to_display(trans.user, ura2.role): + roles.add(ura2.role) # Each role also potentially has groups which, in turn, have members ( users ). We need to # find all roles that each group's members have. for gra in role.groups: @@ -367,11 +367,11 @@ def get_legitimate_roles( self, trans, item, cntrller ): for uga in group.users: user = uga.user for ura in user.roles: - if admin_controller or self.ok_to_display( trans.user, ura.role ): - roles.add( ura.role ) - return self.sort_by_attr( [ role for role in roles ], 'name' ) + if admin_controller or self.ok_to_display(trans.user, ura.role): + roles.add(ura.role) + return self.sort_by_attr([role for role in roles], 'name') - def ok_to_display( self, user, role ): + def ok_to_display(self, user, role): """ Method for checking if: - a role is private and is the current user's private role @@ -379,21 +379,21 @@ def ok_to_display( self, user, role ): """ if user: if role.type == self.model.Role.types.PRIVATE: - return role == self.get_private_user_role( user ) + return role == self.get_private_user_role(user) if role.type == self.model.Role.types.SHARING: - return role in self.get_sharing_roles( user ) + return role in self.get_sharing_roles(user) # If role.type is neither private nor sharing, it's ok to display return True return role.type != self.model.Role.types.PRIVATE and role.type != self.model.Role.types.SHARING - def allow_action( self, roles, action, item ): + def allow_action(self, roles, action, item): """ Method for checking a permission for the current user ( based on roles ) to perform a specific action on an item, which must be one of: Dataset, Library, LibraryFolder, LibraryDataset, LibraryDatasetDatasetAssociation """ # SM: Note that calling get_item_actions will emit a query. - item_actions = self.get_item_actions( action, item ) + item_actions = self.get_item_actions(action, item) if not item_actions: return action.model == 'restrict' @@ -413,7 +413,7 @@ def allow_action( self, roles, action, item ): break return ret_val - def get_actions_for_items( self, trans, action, permission_items ): + def get_actions_for_items(self, trans, action, permission_items): # TODO: Rename this; it's a replacement for get_item_actions, but it # doesn't represent what it's really doing, which is confusing. # TODO: Make this work for other classes besides lib_datasets. @@ -433,13 +433,13 @@ def get_actions_for_items( self, trans, action, permission_items ): # If the dataset id has no corresponding action in its permissions, # then the returned permissions will not carry an entry for the dataset. ret_permissions = {} - if ( len( permission_items ) > 0 ): + if (len(permission_items) > 0): # SM: NB: LibraryDatasets became Datasets for some odd reason. - if ( isinstance( permission_items[0], trans.model.LibraryDataset ) ): - ids = [ item.library_dataset_id for item in permission_items ] - permissions = trans.sa_session.query( trans.model.LibraryDatasetPermissions ) \ - .filter( and_( trans.model.LibraryDatasetPermissions.library_dataset_id.in_( ids ), - trans.model.LibraryDatasetPermissions.action == action.action ) ) \ + if (isinstance(permission_items[0], trans.model.LibraryDataset)): + ids = [item.library_dataset_id for item in permission_items] + permissions = trans.sa_session.query(trans.model.LibraryDatasetPermissions) \ + .filter(and_(trans.model.LibraryDatasetPermissions.library_dataset_id.in_(ids), + trans.model.LibraryDatasetPermissions.action == action.action)) \ .all() # Massage the return data. We will return a list of permissions @@ -449,14 +449,14 @@ def get_actions_for_items( self, trans, action, permission_items ): # TODO: Consider eliminating the initialization and just return # empty values for each library dataset id. for item in permission_items: - ret_permissions[ item.library_dataset_id ] = [] + ret_permissions[item.library_dataset_id] = [] for permission in permissions: - ret_permissions[ permission.library_dataset_id ].append( permission ) - elif ( isinstance( permission_items[0], trans.model.Dataset ) ): - ids = [ item.id for item in permission_items ] - permissions = trans.sa_session.query( trans.model.DatasetPermissions ) \ - .filter( and_( trans.model.DatasetPermissions.dataset_id.in_( ids ), - trans.model.DatasetPermissions.action == action.action ) ) \ + ret_permissions[permission.library_dataset_id].append(permission) + elif (isinstance(permission_items[0], trans.model.Dataset)): + ids = [item.id for item in permission_items] + permissions = trans.sa_session.query(trans.model.DatasetPermissions) \ + .filter(and_(trans.model.DatasetPermissions.dataset_id.in_(ids), + trans.model.DatasetPermissions.action == action.action)) \ .all() # Massage the return data. We will return a list of permissions @@ -466,41 +466,41 @@ def get_actions_for_items( self, trans, action, permission_items ): # TODO: Consider eliminating the initialization and just return # empty values for each library dataset id. for item in permission_items: - ret_permissions[ item.id ] = [] + ret_permissions[item.id] = [] for permission in permissions: - ret_permissions[ permission.dataset_id ].append( permission ) + ret_permissions[permission.dataset_id].append(permission) # Test that we get the same response from get_item_actions each item: test_code = False if test_code: try: - log.debug( "get_actions_for_items: Test start" ) + log.debug("get_actions_for_items: Test start") for item in permission_items: - base_result = self.get_item_actions( action, item ) - new_result = ret_permissions[ item.library_dataset_id ] + base_result = self.get_item_actions(action, item) + new_result = ret_permissions[item.library_dataset_id] # For now, just test against LibraryDatasetIds; other classes # are not tested yet. - if len( base_result ) == len( new_result ): - common_result = set(base_result).intersection( new_result ) - if len( common_result ) == len( base_result ): - log.debug( "Match on permissions for id %d" % - item.library_dataset_id ) + if len(base_result) == len(new_result): + common_result = set(base_result).intersection(new_result) + if len(common_result) == len(base_result): + log.debug("Match on permissions for id %d" % + item.library_dataset_id) # TODO: Fix this failure message: else: - log.debug( "Error: dataset %d; originally: %s; now: %s" - % ( item.library_dataset_id, - base_result, new_result ) ) + log.debug("Error: dataset %d; originally: %s; now: %s" + % (item.library_dataset_id, + base_result, new_result)) else: - log.debug( "Error: dataset %d: had %d entries, now %d entries" - % ( item.library_dataset_id, len( base_result ), - len( new_result ) ) ) - log.debug( "get_actions_for_items: Test end" ) + log.debug("Error: dataset %d: had %d entries, now %d entries" + % (item.library_dataset_id, len(base_result), + len(new_result))) + log.debug("get_actions_for_items: Test end") except Exception as e: - log.debug( "Exception in test code: %s" % e ) + log.debug("Exception in test code: %s" % e) return ret_permissions - def allow_action_on_libitems( self, trans, user_roles, action, items ): + def allow_action_on_libitems(self, trans, user_roles, action, items): """ This should be the equivalent of allow_action defined on multiple items. It is meant to specifically replace allow_action for multiple @@ -508,66 +508,66 @@ def allow_action_on_libitems( self, trans, user_roles, action, items ): allow_action's permitted classes - Dataset, Library, LibraryFolder, and LDDAs. """ - all_items_actions = self.get_actions_for_items( trans, action, items ) + all_items_actions = self.get_actions_for_items(trans, action, items) ret_allow_action = {} # Change item to lib_dataset or vice-versa. for item in items: if item.id in all_items_actions: - item_actions = all_items_actions[ item.id ] + item_actions = all_items_actions[item.id] if self.permitted_actions.DATASET_ACCESS == action: - ret_allow_action[ item.id ] = True + ret_allow_action[item.id] = True for item_action in item_actions: if item_action.role not in user_roles: - ret_allow_action[ item.id ] = False + ret_allow_action[item.id] = False break # Else look for just one dataset role to be in the list of # acceptable user roles: else: - ret_allow_action[ item.id ] = False + ret_allow_action[item.id] = False for item_action in item_actions: if item_action.role in user_roles: - ret_allow_action[ item.id ] = True + ret_allow_action[item.id] = True break else: if 'restrict' == action.model: - ret_allow_action[ item.id ] = True + ret_allow_action[item.id] = True else: - ret_allow_action[ item.id ] = False + ret_allow_action[item.id] = False # Test it: the result for each dataset should match the result for # allow_action: test_code = False if test_code: - log.debug( "allow_action_for_items: test start" ) + log.debug("allow_action_for_items: test start") for item in items: - orig_value = self.allow_action( user_roles, action, item ) - if orig_value == ret_allow_action[ item.id ]: - log.debug( "Item %d: success" % item.id ) + orig_value = self.allow_action(user_roles, action, item) + if orig_value == ret_allow_action[item.id]: + log.debug("Item %d: success" % item.id) else: - log.debug( "Item %d: fail: original: %s; new: %s" - % ( item.id, orig_value, ret_allow_action[ item.id ] ) ) - log.debug( "allow_action_for_items: test end" ) + log.debug("Item %d: fail: original: %s; new: %s" + % (item.id, orig_value, ret_allow_action[item.id])) + log.debug("allow_action_for_items: test end") return ret_allow_action # DELETEME: SM: DO NOT TOUCH! This actually works. - def dataset_access_mapping( self, trans, user_roles, datasets ): + def dataset_access_mapping(self, trans, user_roles, datasets): ''' For the given list of datasets, return a mapping of the datasets' ids to whether they can be accessed by the user or not. The datasets input is expected to be a simple list of Dataset objects. ''' - datasets_public_map = self.datasets_are_public( trans, datasets ) - datasets_allow_action_map = self.allow_action_on_libitems( trans, user_roles, self.permitted_actions.DATASET_ACCESS, datasets ) + datasets_public_map = self.datasets_are_public(trans, datasets) + datasets_allow_action_map = self.allow_action_on_libitems(trans, user_roles, self.permitted_actions.DATASET_ACCESS, datasets) can_access = {} for dataset in datasets: - can_access[ dataset.id ] = datasets_public_map[ dataset.id ] or datasets_allow_action_map[ dataset.id ] + can_access[dataset.id] = datasets_public_map[dataset.id] or datasets_allow_action_map[dataset.id] return can_access - def dataset_permission_map_for_access( self, trans, user_roles, libitems ): + def dataset_permission_map_for_access(self, trans, user_roles, libitems): ''' For a given list of library items (e.g., Datasets), return a map of the datasets' ids to whether they can have permission to use that action @@ -583,249 +583,249 @@ def dataset_permission_map_for_access( self, trans, user_roles, libitems ): # or the right permissions are enabled. # TODO: This only works for Datasets; other code is using X_is_public, # so this will have to be rewritten to support other items. - libitems_public_map = self.datasets_are_public( trans, libitems ) + libitems_public_map = self.datasets_are_public(trans, libitems) libitems_allow_action_map = self.allow_action_on_libitems( trans, user_roles, self.permitted_actions.DATASET_ACCESS, libitems ) can_access = {} for libitem in libitems: - can_access[ libitem.id ] = libitems_public_map[ libitem.id ] or libitems_allow_action_map[ libitem.id ] + can_access[libitem.id] = libitems_public_map[libitem.id] or libitems_allow_action_map[libitem.id] return can_access - def item_permission_map_for_modify( self, trans, user_roles, libitems ): + def item_permission_map_for_modify(self, trans, user_roles, libitems): return self.allow_action_on_libitems( trans, user_roles, self.permitted_actions.LIBRARY_MODIFY, libitems ) - def item_permission_map_for_manage( self, trans, user_roles, libitems ): + def item_permission_map_for_manage(self, trans, user_roles, libitems): return self.allow_action_on_libitems( trans, user_roles, self.permitted_actions.LIBRARY_MANAGE, libitems ) - def item_permission_map_for_add( self, trans, user_roles, libitems ): + def item_permission_map_for_add(self, trans, user_roles, libitems): return self.allow_action_on_libitems( trans, user_roles, self.permitted_actions.LIBRARY_ADD, libitems ) - def can_access_dataset( self, user_roles, dataset ): + def can_access_dataset(self, user_roles, dataset): # SM: dataset_is_public will access dataset.actions, which is a # backref that causes a query to be made to DatasetPermissions - retval = self.dataset_is_public( dataset ) or self.allow_action( user_roles, self.permitted_actions.DATASET_ACCESS, dataset ) + retval = self.dataset_is_public(dataset) or self.allow_action(user_roles, self.permitted_actions.DATASET_ACCESS, dataset) return retval - def can_manage_dataset( self, roles, dataset ): - return self.allow_action( roles, self.permitted_actions.DATASET_MANAGE_PERMISSIONS, dataset ) + def can_manage_dataset(self, roles, dataset): + return self.allow_action(roles, self.permitted_actions.DATASET_MANAGE_PERMISSIONS, dataset) - def can_access_library( self, roles, library ): - return self.library_is_public( library ) or self.allow_action( roles, self.permitted_actions.LIBRARY_ACCESS, library ) + def can_access_library(self, roles, library): + return self.library_is_public(library) or self.allow_action(roles, self.permitted_actions.LIBRARY_ACCESS, library) - def get_accessible_libraries( self, trans, user ): + def get_accessible_libraries(self, trans, user): """Return all data libraries that the received user can access""" accessible_libraries = [] - current_user_role_ids = [ role.id for role in user.all_roles() ] + current_user_role_ids = [role.id for role in user.all_roles()] library_access_action = self.permitted_actions.LIBRARY_ACCESS.action - restricted_library_ids = [ lp.library_id for lp in trans.sa_session.query( trans.model.LibraryPermissions ) - .filter( trans.model.LibraryPermissions.table.c.action == library_access_action ).distinct() ] - accessible_restricted_library_ids = [ lp.library_id for lp in trans.sa_session.query( - trans.model.LibraryPermissions ).filter( - and_( trans.model.LibraryPermissions.table.c.action == library_access_action, - trans.model.LibraryPermissions.table.c.role_id.in_( current_user_role_ids ) ) ) ] + restricted_library_ids = [lp.library_id for lp in trans.sa_session.query(trans.model.LibraryPermissions) + .filter(trans.model.LibraryPermissions.table.c.action == library_access_action).distinct()] + accessible_restricted_library_ids = [lp.library_id for lp in trans.sa_session.query(trans.model.LibraryPermissions) + .filter(and_( + trans.model.LibraryPermissions.table.c.action == library_access_action, + trans.model.LibraryPermissions.table.c.role_id.in_(current_user_role_ids)))] # Filter to get libraries accessible by the current user. Get both # public libraries and restricted libraries accessible by the current user. - for library in trans.sa_session.query( trans.model.Library ) \ - .filter( and_( trans.model.Library.table.c.deleted == false(), - ( or_( not_( trans.model.Library.table.c.id.in_( restricted_library_ids ) ), - trans.model.Library.table.c.id.in_( accessible_restricted_library_ids ) ) ) ) ) \ - .order_by( trans.app.model.Library.name ): - accessible_libraries.append( library ) + for library in trans.sa_session.query(trans.model.Library) \ + .filter(and_(trans.model.Library.table.c.deleted == false(), + (or_(not_(trans.model.Library.table.c.id.in_(restricted_library_ids)), + trans.model.Library.table.c.id.in_(accessible_restricted_library_ids))))) \ + .order_by(trans.app.model.Library.name): + accessible_libraries.append(library) return accessible_libraries - def has_accessible_folders( self, trans, folder, user, roles, search_downward=True ): - if self.has_accessible_library_datasets( trans, folder, user, roles, search_downward=search_downward ) or \ - self.can_add_library_item( roles, folder ) or \ - self.can_modify_library_item( roles, folder ) or \ - self.can_manage_library_item( roles, folder ): + def has_accessible_folders(self, trans, folder, user, roles, search_downward=True): + if self.has_accessible_library_datasets(trans, folder, user, roles, search_downward=search_downward) or \ + self.can_add_library_item(roles, folder) or \ + self.can_modify_library_item(roles, folder) or \ + self.can_manage_library_item(roles, folder): return True if search_downward: for folder in folder.active_folders: - return self.has_accessible_folders( trans, folder, user, roles, search_downward=search_downward ) + return self.has_accessible_folders(trans, folder, user, roles, search_downward=search_downward) return False - def has_accessible_library_datasets( self, trans, folder, user, roles, search_downward=True ): - for library_dataset in trans.sa_session.query( trans.model.LibraryDataset ) \ - .filter( and_( trans.model.LibraryDataset.table.c.deleted == false(), - trans.app.model.LibraryDataset.table.c.folder_id == folder.id ) ): - if self.can_access_library_item( roles, library_dataset, user ): + def has_accessible_library_datasets(self, trans, folder, user, roles, search_downward=True): + for library_dataset in trans.sa_session.query(trans.model.LibraryDataset) \ + .filter(and_(trans.model.LibraryDataset.table.c.deleted == false(), + trans.app.model.LibraryDataset.table.c.folder_id == folder.id)): + if self.can_access_library_item(roles, library_dataset, user): return True if search_downward: - return self.__active_folders_have_accessible_library_datasets( trans, folder, user, roles ) + return self.__active_folders_have_accessible_library_datasets(trans, folder, user, roles) return False - def __active_folders_have_accessible_library_datasets( self, trans, folder, user, roles ): + def __active_folders_have_accessible_library_datasets(self, trans, folder, user, roles): for active_folder in folder.active_folders: - if self.has_accessible_library_datasets( trans, active_folder, user, roles ): + if self.has_accessible_library_datasets(trans, active_folder, user, roles): return True return False - def can_access_library_item( self, roles, item, user ): - if type( item ) == self.model.Library: - return self.can_access_library( roles, item ) - elif type( item ) == self.model.LibraryFolder: - return self.can_access_library( roles, item.parent_library ) and self.check_folder_contents( user, roles, item )[0] - elif type( item ) == self.model.LibraryDataset: - return self.can_access_library( roles, item.folder.parent_library ) and self.can_access_dataset( roles, item.library_dataset_dataset_association.dataset ) - elif type( item ) == self.model.LibraryDatasetDatasetAssociation: - return self.can_access_library( roles, item.library_dataset.folder.parent_library ) and self.can_access_dataset( roles, item.dataset ) - elif type( item ) == self.model.LibraryDatasetCollectionAssociation: - return self.can_access_library( roles, item.folder.parent_library ) + def can_access_library_item(self, roles, item, user): + if type(item) == self.model.Library: + return self.can_access_library(roles, item) + elif type(item) == self.model.LibraryFolder: + return self.can_access_library(roles, item.parent_library) and self.check_folder_contents(user, roles, item)[0] + elif type(item) == self.model.LibraryDataset: + return self.can_access_library(roles, item.folder.parent_library) and self.can_access_dataset(roles, item.library_dataset_dataset_association.dataset) + elif type(item) == self.model.LibraryDatasetDatasetAssociation: + return self.can_access_library(roles, item.library_dataset.folder.parent_library) and self.can_access_dataset(roles, item.dataset) + elif type(item) == self.model.LibraryDatasetCollectionAssociation: + return self.can_access_library(roles, item.folder.parent_library) else: - log.warning( 'Unknown library item type: %s' % type( item ) ) + log.warning('Unknown library item type: %s' % type(item)) return False - def can_add_library_item( self, roles, item ): - return self.allow_action( roles, self.permitted_actions.LIBRARY_ADD, item ) + def can_add_library_item(self, roles, item): + return self.allow_action(roles, self.permitted_actions.LIBRARY_ADD, item) - def can_modify_library_item( self, roles, item ): - return self.allow_action( roles, self.permitted_actions.LIBRARY_MODIFY, item ) + def can_modify_library_item(self, roles, item): + return self.allow_action(roles, self.permitted_actions.LIBRARY_MODIFY, item) - def can_manage_library_item( self, roles, item ): - return self.allow_action( roles, self.permitted_actions.LIBRARY_MANAGE, item ) + def can_manage_library_item(self, roles, item): + return self.allow_action(roles, self.permitted_actions.LIBRARY_MANAGE, item) - def get_item_actions( self, action, item ): + def get_item_actions(self, action, item): # item must be one of: Dataset, Library, LibraryFolder, LibraryDataset, LibraryDatasetDatasetAssociation # SM: Accessing item.actions emits a query to Library_Dataset_Permissions # if the item is a LibraryDataset: # TODO: Pass in the item's actions - the item isn't needed - return [ permission for permission in item.actions if permission.action == action.action ] + return [permission for permission in item.actions if permission.action == action.action] - def guess_derived_permissions_for_datasets( self, datasets=[] ): + def guess_derived_permissions_for_datasets(self, datasets=[]): """Returns a dict of { action : [ role, role, ... ] } for the output dataset based upon provided datasets""" perms = {} for dataset in datasets: - if not isinstance( dataset, self.model.Dataset ): + if not isinstance(dataset, self.model.Dataset): dataset = dataset.dataset these_perms = {} # initialize blank perms for action in self.get_actions(): - these_perms[ action ] = [] + these_perms[action] = [] # collect this dataset's perms - these_perms = self.get_permissions( dataset ) + these_perms = self.get_permissions(dataset) # join or intersect this dataset's permissions with others for action, roles in these_perms.items(): if action not in perms.keys(): - perms[ action ] = roles + perms[action] = roles else: if action.model == 'grant': # intersect existing roles with new roles - perms[ action ] = [_ for _ in roles if _ in perms[ action ]] + perms[action] = [_ for _ in roles if _ in perms[action]] elif action.model == 'restrict': # join existing roles with new roles - perms[ action ].extend( [_ for _ in roles if _ not in perms[ action ]] ) + perms[action].extend([_ for _ in roles if _ not in perms[action]]) return perms - def associate_components( self, **kwd ): + def associate_components(self, **kwd): if 'user' in kwd: if 'group' in kwd: - return self.associate_user_group( kwd['user'], kwd['group'] ) + return self.associate_user_group(kwd['user'], kwd['group']) elif 'role' in kwd: - return self.associate_user_role( kwd['user'], kwd['role'] ) + return self.associate_user_role(kwd['user'], kwd['role']) elif 'role' in kwd: if 'group' in kwd: - return self.associate_group_role( kwd['group'], kwd['role'] ) + return self.associate_group_role(kwd['group'], kwd['role']) if 'action' in kwd: if 'dataset' in kwd and 'role' in kwd: - return self.associate_action_dataset_role( kwd['action'], kwd['dataset'], kwd['role'] ) - raise Exception( 'No valid method of associating provided components: %s' % kwd ) + return self.associate_action_dataset_role(kwd['action'], kwd['dataset'], kwd['role']) + raise Exception('No valid method of associating provided components: %s' % kwd) - def associate_user_group( self, user, group ): - assoc = self.model.UserGroupAssociation( user, group ) - self.sa_session.add( assoc ) + def associate_user_group(self, user, group): + assoc = self.model.UserGroupAssociation(user, group) + self.sa_session.add(assoc) self.sa_session.flush() return assoc - def associate_user_role( self, user, role ): - assoc = self.model.UserRoleAssociation( user, role ) - self.sa_session.add( assoc ) + def associate_user_role(self, user, role): + assoc = self.model.UserRoleAssociation(user, role) + self.sa_session.add(assoc) self.sa_session.flush() return assoc - def associate_group_role( self, group, role ): - assoc = self.model.GroupRoleAssociation( group, role ) - self.sa_session.add( assoc ) + def associate_group_role(self, group, role): + assoc = self.model.GroupRoleAssociation(group, role) + self.sa_session.add(assoc) self.sa_session.flush() return assoc - def associate_action_dataset_role( self, action, dataset, role ): - assoc = self.model.DatasetPermissions( action, dataset, role ) - self.sa_session.add( assoc ) + def associate_action_dataset_role(self, action, dataset, role): + assoc = self.model.DatasetPermissions(action, dataset, role) + self.sa_session.add(assoc) self.sa_session.flush() return assoc - def create_private_user_role( self, user ): + def create_private_user_role(self, user): # Create private role - role = self.model.Role( name=user.email, description='Private Role for ' + user.email, type=self.model.Role.types.PRIVATE ) - self.sa_session.add( role ) + role = self.model.Role(name=user.email, description='Private Role for ' + user.email, type=self.model.Role.types.PRIVATE) + self.sa_session.add(role) self.sa_session.flush() # Add user to role - self.associate_components( role=role, user=user ) + self.associate_components(role=role, user=user) return role - def get_private_user_role( self, user, auto_create=False ): - role = self.sa_session.query( self.model.Role ) \ - .filter( and_( self.model.Role.table.c.name == user.email, - self.model.Role.table.c.type == self.model.Role.types.PRIVATE ) ) \ + def get_private_user_role(self, user, auto_create=False): + role = self.sa_session.query(self.model.Role) \ + .filter(and_(self.model.Role.table.c.name == user.email, + self.model.Role.table.c.type == self.model.Role.types.PRIVATE)) \ .first() if not role: if auto_create: - return self.create_private_user_role( user ) + return self.create_private_user_role(user) else: return None return role - def get_sharing_roles( self, user ): - return self.sa_session.query( self.model.Role ) \ - .filter( and_( ( self.model.Role.table.c.name ).like( "Sharing role for: %" + user.email + "%" ), - self.model.Role.table.c.type == self.model.Role.types.SHARING ) ) + def get_sharing_roles(self, user): + return self.sa_session.query(self.model.Role) \ + .filter(and_((self.model.Role.table.c.name).like("Sharing role for: %" + user.email + "%"), + self.model.Role.table.c.type == self.model.Role.types.SHARING)) - def user_set_default_permissions( self, user, permissions={}, history=False, dataset=False, bypass_manage_permission=False, default_access_private=False ): + def user_set_default_permissions(self, user, permissions={}, history=False, dataset=False, bypass_manage_permission=False, default_access_private=False): # bypass_manage_permission is used to change permissions of datasets in a userless history when logging in flush_needed = False if user is None: return None if not permissions: # default permissions - permissions = { self.permitted_actions.DATASET_MANAGE_PERMISSIONS: [ self.get_private_user_role( user, auto_create=True ) ] } + permissions = {self.permitted_actions.DATASET_MANAGE_PERMISSIONS: [self.get_private_user_role(user, auto_create=True)]} # new_user_dataset_access_role_default_private is set as True in config file if default_access_private: - permissions[ self.permitted_actions.DATASET_ACCESS ] = next(iter(permissions.values())) + permissions[self.permitted_actions.DATASET_ACCESS] = next(iter(permissions.values())) # Delete all of the current default permissions for the user for dup in user.default_permissions: - self.sa_session.delete( dup ) + self.sa_session.delete(dup) flush_needed = True # Add the new default permissions for the user for action, roles in permissions.items(): - if isinstance( action, Action ): + if isinstance(action, Action): action = action.action - for dup in [ self.model.DefaultUserPermissions( user, action, role ) for role in roles ]: - self.sa_session.add( dup ) + for dup in [self.model.DefaultUserPermissions(user, action, role) for role in roles]: + self.sa_session.add(dup) flush_needed = True if flush_needed: self.sa_session.flush() if history: for history in user.active_histories: - self.history_set_default_permissions( history, permissions=permissions, dataset=dataset, bypass_manage_permission=bypass_manage_permission ) + self.history_set_default_permissions(history, permissions=permissions, dataset=dataset, bypass_manage_permission=bypass_manage_permission) - def user_get_default_permissions( self, user ): + def user_get_default_permissions(self, user): permissions = {} for dup in user.default_permissions: - action = self.get_action( dup.action ) + action = self.get_action(dup.action) if action in permissions: - permissions[ action ].append( dup.role ) + permissions[action].append(dup.role) else: - permissions[ action ] = [ dup.role ] + permissions[action] = [dup.role] return permissions - def history_set_default_permissions( self, history, permissions={}, dataset=False, bypass_manage_permission=False ): + def history_set_default_permissions(self, history, permissions={}, dataset=False, bypass_manage_permission=False): # bypass_manage_permission is used to change permissions of datasets in a user-less history when logging in flush_needed = False user = history.user @@ -833,17 +833,17 @@ def history_set_default_permissions( self, history, permissions={}, dataset=Fals # default permissions on a user-less history are None return None if not permissions: - permissions = self.user_get_default_permissions( user ) + permissions = self.user_get_default_permissions(user) # Delete all of the current default permission for the history for dhp in history.default_permissions: - self.sa_session.delete( dhp ) + self.sa_session.delete(dhp) flush_needed = True # Add the new default permissions for the history for action, roles in permissions.items(): - if isinstance( action, Action ): + if isinstance(action, Action): action = action.action - for dhp in [ self.model.DefaultHistoryPermissions( history, action, role ) for role in roles ]: - self.sa_session.add( dhp ) + for dhp in [self.model.DefaultHistoryPermissions(history, action, role) for role in roles]: + self.sa_session.add(dhp) flush_needed = True if flush_needed: self.sa_session.flush() @@ -854,23 +854,23 @@ def history_set_default_permissions( self, history, permissions={}, dataset=Fals if dataset.library_associations: # Don't change permissions on a dataset associated with a library continue - if [ assoc for assoc in dataset.history_associations if assoc.history not in user.histories ]: + if [assoc for assoc in dataset.history_associations if assoc.history not in user.histories]: # Don't change permissions on a dataset associated with a history not owned by the user continue - if bypass_manage_permission or self.can_manage_dataset( user.all_roles(), dataset ): - self.set_all_dataset_permissions( dataset, permissions ) + if bypass_manage_permission or self.can_manage_dataset(user.all_roles(), dataset): + self.set_all_dataset_permissions(dataset, permissions) - def history_get_default_permissions( self, history ): + def history_get_default_permissions(self, history): permissions = {} for dhp in history.default_permissions: - action = self.get_action( dhp.action ) + action = self.get_action(dhp.action) if action in permissions: - permissions[ action ].append( dhp.role ) + permissions[action].append(dhp.role) else: - permissions[ action ] = [ dhp.role ] + permissions[action] = [dhp.role] return permissions - def set_all_dataset_permissions( self, dataset, permissions={}, new=False, flush=True ): + def set_all_dataset_permissions(self, dataset, permissions={}, new=False, flush=True): """ Set new full permissions on a dataset, eliminating all current permissions. Permission looks like: { Action : [ Role, Role ] } @@ -878,7 +878,7 @@ def set_all_dataset_permissions( self, dataset, permissions={}, new=False, flush # Make sure that DATASET_MANAGE_PERMISSIONS is associated with at least 1 role has_dataset_manage_permissions = False for action, roles in permissions.items(): - if isinstance( action, Action ): + if isinstance(action, Action): if action == self.permitted_actions.DATASET_MANAGE_PERMISSIONS and roles: has_dataset_manage_permissions = True break @@ -891,42 +891,42 @@ def set_all_dataset_permissions( self, dataset, permissions={}, new=False, flush # Delete all of the current permissions on the dataset if not new: for dp in dataset.actions: - self.sa_session.delete( dp ) + self.sa_session.delete(dp) flush_needed = True # Add the new permissions on the dataset for action, roles in permissions.items(): - if isinstance( action, Action ): + if isinstance(action, Action): action = action.action for role in roles: - dp = self.model.DatasetPermissions( action, dataset, role_id=role.id ) - self.sa_session.add( dp ) + dp = self.model.DatasetPermissions(action, dataset, role_id=role.id) + self.sa_session.add(dp) flush_needed = True if flush_needed and flush: self.sa_session.flush() return "" - def set_dataset_permission( self, dataset, permission={} ): + def set_dataset_permission(self, dataset, permission={}): """ Set a specific permission on a dataset, leaving all other current permissions on the dataset alone. Permission looks like: { Action.action : [ Role, Role ] } """ flush_needed = False for action, roles in permission.items(): - if isinstance( action, Action ): + if isinstance(action, Action): action = action.action # Delete the current specific permission on the dataset if one exists for dp in dataset.actions: if dp.action == action: - self.sa_session.delete( dp ) + self.sa_session.delete(dp) flush_needed = True # Add the new specific permission on the dataset - for dp in [ self.model.DatasetPermissions( action, dataset, role ) for role in roles ]: - self.sa_session.add( dp ) + for dp in [self.model.DatasetPermissions(action, dataset, role) for role in roles]: + self.sa_session.add(dp) flush_needed = True if flush_needed: self.sa_session.flush() - def get_permissions( self, item ): + def get_permissions(self, item): """ Return a dictionary containing the actions and associated roles on item where item is one of Library, LibraryFolder, LibraryDatasetDatasetAssociation, @@ -934,205 +934,203 @@ def get_permissions( self, item ): """ permissions = {} for item_permission in item.actions: - action = self.get_action( item_permission.action ) + action = self.get_action(item_permission.action) if action in permissions: - permissions[ action ].append( item_permission.role ) + permissions[action].append(item_permission.role) else: - permissions[ action ] = [ item_permission.role ] + permissions[action] = [item_permission.role] return permissions - def get_accessible_request_types( self, trans, user ): + def get_accessible_request_types(self, trans, user): """Return all RequestTypes that the received user has permission to access.""" accessible_request_types = [] - current_user_role_ids = [ role.id for role in user.all_roles() ] + current_user_role_ids = [role.id for role in user.all_roles()] request_type_access_action = self.permitted_actions.REQUEST_TYPE_ACCESS.action - restricted_request_type_ids = [ rtp.request_type_id for rtp in trans.sa_session.query( - trans.model.RequestTypePermissions ).filter( - trans.model.RequestTypePermissions.table.c.action == request_type_access_action ).distinct() - ] - accessible_restricted_request_type_ids = [ rtp.request_type_id for rtp in trans.sa_session.query( - trans.model.RequestTypePermissions ).filter( - and_( trans.model.RequestTypePermissions.table.c.action == request_type_access_action, - trans.model.RequestTypePermissions.table.c.role_id.in_( current_user_role_ids ) ) ) ] + restricted_request_type_ids = [rtp.request_type_id for rtp in trans.sa_session.query(trans.model.RequestTypePermissions) + .filter(trans.model.RequestTypePermissions.table.c.action == request_type_access_action).distinct()] + accessible_restricted_request_type_ids = [rtp.request_type_id for rtp in trans.sa_session.query(trans.model.RequestTypePermissions) + .filter(and_( + trans.model.RequestTypePermissions.table.c.action == request_type_access_action, + trans.model.RequestTypePermissions.table.c.role_id.in_(current_user_role_ids)))] # Filter to get libraries accessible by the current user. Get both # public libraries and restricted libraries accessible by the current user. - for request_type in trans.sa_session.query( trans.model.RequestType ) \ - .filter( and_( trans.model.RequestType.table.c.deleted == false(), - ( or_( not_( trans.model.RequestType.table.c.id.in_( restricted_request_type_ids ) ), - trans.model.RequestType.table.c.id.in_( accessible_restricted_request_type_ids ) ) ) ) ) \ - .order_by( trans.app.model.RequestType.name ): - accessible_request_types.append( request_type ) + for request_type in trans.sa_session.query(trans.model.RequestType) \ + .filter(and_(trans.model.RequestType.table.c.deleted == false(), + (or_(not_(trans.model.RequestType.table.c.id.in_(restricted_request_type_ids)), + trans.model.RequestType.table.c.id.in_(accessible_restricted_request_type_ids))))) \ + .order_by(trans.app.model.RequestType.name): + accessible_request_types.append(request_type) return accessible_request_types - def copy_dataset_permissions( self, src, dst ): - if not isinstance( src, self.model.Dataset ): + def copy_dataset_permissions(self, src, dst): + if not isinstance(src, self.model.Dataset): src = src.dataset - if not isinstance( dst, self.model.Dataset ): + if not isinstance(dst, self.model.Dataset): dst = dst.dataset - self.set_all_dataset_permissions( dst, self.get_permissions( src ) ) + self.set_all_dataset_permissions(dst, self.get_permissions(src)) - def privately_share_dataset( self, dataset, users=[] ): + def privately_share_dataset(self, dataset, users=[]): intersect = None for user in users: - roles = [ ura.role for ura in user.roles if ura.role.type == self.model.Role.types.SHARING ] + roles = [ura.role for ura in user.roles if ura.role.type == self.model.Role.types.SHARING] if intersect is None: intersect = roles else: new_intersect = [] for role in roles: if role in intersect: - new_intersect.append( role ) + new_intersect.append(role) intersect = new_intersect sharing_role = None if intersect: for role in intersect: - if not [_ for _ in [ ura.user for ura in role.users ] if _ not in users]: + if not [_ for _ in [ura.user for ura in role.users] if _ not in users]: # only use a role if it contains ONLY the users we're sharing with sharing_role = role break if sharing_role is None: - sharing_role = self.model.Role( name="Sharing role for: " + ", ".join( [ u.email for u in users ] ), - type=self.model.Role.types.SHARING ) - self.sa_session.add( sharing_role ) + sharing_role = self.model.Role(name="Sharing role for: " + ", ".join([u.email for u in users]), + type=self.model.Role.types.SHARING) + self.sa_session.add(sharing_role) self.sa_session.flush() for user in users: - self.associate_components( user=user, role=sharing_role ) - self.set_dataset_permission( dataset, { self.permitted_actions.DATASET_ACCESS: [ sharing_role ] } ) + self.associate_components(user=user, role=sharing_role) + self.set_dataset_permission(dataset, {self.permitted_actions.DATASET_ACCESS: [sharing_role]}) - def set_all_library_permissions( self, trans, library_item, permissions={} ): + def set_all_library_permissions(self, trans, library_item, permissions={}): # Set new permissions on library_item, eliminating all current permissions flush_needed = False for role_assoc in library_item.actions: - self.sa_session.delete( role_assoc ) + self.sa_session.delete(role_assoc) flush_needed = True # Add the new permissions on library_item for item_class, permission_class in self.library_item_assocs: - if isinstance( library_item, item_class ): + if isinstance(library_item, item_class): for action, roles in list(permissions.items()): - if isinstance( action, Action ): + if isinstance(action, Action): action = action.action - for role_assoc in [ permission_class( action, library_item, role ) for role in roles ]: - self.sa_session.add( role_assoc ) + for role_assoc in [permission_class(action, library_item, role) for role in roles]: + self.sa_session.add(role_assoc) flush_needed = True - if isinstance( library_item, self.model.LibraryDatasetDatasetAssociation ): + if isinstance(library_item, self.model.LibraryDatasetDatasetAssociation): # Permission setting related to DATASET_MANAGE_PERMISSIONS was broken for a period of time, # so it is possible that some Datasets have no roles associated with the DATASET_MANAGE_PERMISSIONS # permission. In this case, we'll reset this permission to the library_item user's private role. - if not library_item.dataset.has_manage_permissions_roles( trans ): + if not library_item.dataset.has_manage_permissions_roles(trans): # Well this looks like a bug, this should be looked at. # Default permissions above is single hash that keeps getting reeditted here # because permission is being defined instead of permissions. -John - permissions[ self.permitted_actions.DATASET_MANAGE_PERMISSIONS ] = [ trans.app.security_agent.get_private_user_role( library_item.user ) ] - self.set_dataset_permission( library_item.dataset, permissions ) + permissions[self.permitted_actions.DATASET_MANAGE_PERMISSIONS] = [trans.app.security_agent.get_private_user_role(library_item.user)] + self.set_dataset_permission(library_item.dataset, permissions) if action == self.permitted_actions.LIBRARY_MANAGE.action and roles: # Handle the special case when we are setting the LIBRARY_MANAGE_PERMISSION on a # library_dataset_dataset_association since the roles need to be applied to the # DATASET_MANAGE_PERMISSIONS permission on the associated dataset. permissions = {} - permissions[ self.permitted_actions.DATASET_MANAGE_PERMISSIONS ] = roles - self.set_dataset_permission( library_item.dataset, permissions ) + permissions[self.permitted_actions.DATASET_MANAGE_PERMISSIONS] = roles + self.set_dataset_permission(library_item.dataset, permissions) if flush_needed: self.sa_session.flush() - def set_library_item_permission( self, library_item, permission={} ): + def set_library_item_permission(self, library_item, permission={}): """ Set a specific permission on a library item, leaving all other current permissions on the item alone. Permission looks like: { Action.action : [ Role, Role ] } """ flush_needed = False for action, roles in permission.items(): - if isinstance( action, Action ): + if isinstance(action, Action): action = action.action # Delete the current specific permission on the library item if one exists for item_permission in library_item.actions: if item_permission.action == action: - self.sa_session.delete( item_permission ) + self.sa_session.delete(item_permission) flush_needed = True # Add the new specific permission on the library item - if isinstance( library_item, self.model.LibraryDataset ): - for item_permission in [ self.model.LibraryDatasetPermissions( action, library_item, role ) for role in roles ]: - self.sa_session.add( item_permission ) + if isinstance(library_item, self.model.LibraryDataset): + for item_permission in [self.model.LibraryDatasetPermissions(action, library_item, role) for role in roles]: + self.sa_session.add(item_permission) flush_needed = True - elif isinstance( library_item, self.model.LibraryPermissions): - for item_permission in [ self.model.LibraryPermissions( action, library_item, role ) for role in roles ]: - self.sa_session.add( item_permission ) + elif isinstance(library_item, self.model.LibraryPermissions): + for item_permission in [self.model.LibraryPermissions(action, library_item, role) for role in roles]: + self.sa_session.add(item_permission) flush_needed = True if flush_needed: self.sa_session.flush() - def library_is_public( self, library, contents=False ): + def library_is_public(self, library, contents=False): if contents: # Check all contained folders and datasets to find any that are not public - if not self.folder_is_public( library.root_folder ): + if not self.folder_is_public(library.root_folder): return False # A library is considered public if there are no "access" actions associated with it. - return self.permitted_actions.LIBRARY_ACCESS.action not in [ a.action for a in library.actions ] + return self.permitted_actions.LIBRARY_ACCESS.action not in [a.action for a in library.actions] - def library_is_unrestricted( self, library ): + def library_is_unrestricted(self, library): # A library is considered unrestricted if there are no "access" actions associated with it. - return self.permitted_actions.LIBRARY_ACCESS.action not in [ a.action for a in library.actions ] + return self.permitted_actions.LIBRARY_ACCESS.action not in [a.action for a in library.actions] - def make_library_public( self, library, contents=False ): + def make_library_public(self, library, contents=False): flush_needed = False if contents: # Make all contained folders (include deleted folders, but not purged folders), public - self.make_folder_public( library.root_folder ) + self.make_folder_public(library.root_folder) # A library is considered public if there are no LIBRARY_ACCESS actions associated with it. for lp in library.actions: if lp.action == self.permitted_actions.LIBRARY_ACCESS.action: - self.sa_session.delete( lp ) + self.sa_session.delete(lp) flush_needed = True if flush_needed: self.sa_session.flush() - def folder_is_public( self, folder ): + def folder_is_public(self, folder): for sub_folder in folder.folders: - if not self.folder_is_public( sub_folder ): + if not self.folder_is_public(sub_folder): return False for library_dataset in folder.datasets: ldda = library_dataset.library_dataset_dataset_association - if ldda and ldda.dataset and not self.dataset_is_public( ldda.dataset ): + if ldda and ldda.dataset and not self.dataset_is_public(ldda.dataset): return False return True - def folder_is_unrestricted( self, folder ): + def folder_is_unrestricted(self, folder): # TODO implement folder restrictions # for now all folders are _visible_ but the restricted datasets within are not visible return True - def make_folder_public( self, folder ): + def make_folder_public(self, folder): # Make all of the contents (include deleted contents, but not purged contents) of folder public for sub_folder in folder.folders: if not sub_folder.purged: - self.make_folder_public( sub_folder ) + self.make_folder_public(sub_folder) for library_dataset in folder.datasets: dataset = library_dataset.library_dataset_dataset_association.dataset - if not dataset.purged and not self.dataset_is_public( dataset ): - self.make_dataset_public( dataset ) + if not dataset.purged and not self.dataset_is_public(dataset): + self.make_dataset_public(dataset) - def dataset_is_public( self, dataset ): + def dataset_is_public(self, dataset): """ A dataset is considered public if there are no "access" actions associated with it. Any other actions ( 'manage permissions', 'edit metadata' ) are irrelevant. Accessing dataset.actions will cause a query to be emitted. """ - return self.permitted_actions.DATASET_ACCESS.action not in [ a.action for a in dataset.actions ] + return self.permitted_actions.DATASET_ACCESS.action not in [a.action for a in dataset.actions] - def dataset_is_unrestricted( self, trans, dataset): + def dataset_is_unrestricted(self, trans, dataset): """ Different implementation of the method above with signature: def dataset_is_public( self, dataset ) """ - return len( dataset.library_dataset_dataset_association.get_access_roles( trans ) ) == 0 + return len(dataset.library_dataset_dataset_association.get_access_roles(trans)) == 0 - def dataset_is_private_to_user( self, trans, dataset ): + def dataset_is_private_to_user(self, trans, dataset): """ If the LibraryDataset object has exactly one access role and that is the current user's private role then we consider the dataset private. """ - private_role = self.get_private_user_role( trans.user ) - access_roles = dataset.library_dataset_dataset_association.get_access_roles( trans ) + private_role = self.get_private_user_role(trans.user) + access_roles = dataset.library_dataset_dataset_association.get_access_roles(trans) if len(access_roles) != 1: return False @@ -1142,7 +1140,7 @@ def dataset_is_private_to_user( self, trans, dataset ): else: return False - def datasets_are_public( self, trans, datasets ): + def datasets_are_public(self, trans, datasets): ''' Given a transaction object and a list of Datasets, return a mapping from Dataset ids to whether the Dataset is public @@ -1152,33 +1150,33 @@ def datasets_are_public( self, trans, datasets ): # all datasets being marked as public. If there is an access action # associated with the dataset, then we mark it as nonpublic: datasets_public = {} - dataset_ids = [ dataset.id for dataset in datasets ] + dataset_ids = [dataset.id for dataset in datasets] for dataset_id in dataset_ids: - datasets_public[ dataset_id ] = True + datasets_public[dataset_id] = True # Now get all datasets which have DATASET_ACCESS actions: - access_data_perms = trans.sa_session.query( trans.app.model.DatasetPermissions ) \ - .filter( and_( trans.app.model.DatasetPermissions.dataset_id.in_( dataset_ids ), - trans.app.model.DatasetPermissions.action == self.permitted_actions.DATASET_ACCESS.action ) ) \ + access_data_perms = trans.sa_session.query(trans.app.model.DatasetPermissions) \ + .filter(and_(trans.app.model.DatasetPermissions.dataset_id.in_(dataset_ids), + trans.app.model.DatasetPermissions.action == self.permitted_actions.DATASET_ACCESS.action)) \ .all() # Every dataset returned has "access" privileges associated with it, # so it's not public. for permission in access_data_perms: - datasets_public[ permission.dataset_id ] = False + datasets_public[permission.dataset_id] = False return datasets_public - def make_dataset_public( self, dataset ): + def make_dataset_public(self, dataset): # A dataset is considered public if there are no "access" actions associated with it. Any # other actions ( 'manage permissions', 'edit metadata' ) are irrelevant. flush_needed = False for dp in dataset.actions: if dp.action == self.permitted_actions.DATASET_ACCESS.action: - self.sa_session.delete( dp ) + self.sa_session.delete(dp) flush_needed = True if flush_needed: self.sa_session.flush() - def derive_roles_from_access( self, trans, item_id, cntrller, library=False, **kwd ): + def derive_roles_from_access(self, trans, item_id, cntrller, library=False, **kwd): # Check the access permission on a dataset. If library is true, item_id refers to a library. If library # is False, item_id refers to a dataset ( item_id must currently be decoded before being sent ). The # cntrller param is the calling controller, which needs to be passed to get_legitimate_roles(). @@ -1193,24 +1191,27 @@ def derive_roles_from_access( self, trans, item_id, cntrller, library=False, **k # permission on item, and at least 1 of the roles is private. private_role_found = False error = False - for k, v in get_permitted_actions( filter='DATASET' ).items(): - in_roles = [ self.sa_session.query( self.model.Role ).get( x ) for x in listify( kwd.get( k + '_in', [] ) ) ] + for k, v in get_permitted_actions(filter='DATASET').items(): + # Change for removing the prefix '_in' from the roles select box + in_roles = [self.sa_session.query(self.model.Role).get(x) for x in listify(kwd[k])] + if not in_roles: + in_roles = [self.sa_session.query(self.model.Role).get(x) for x in listify(kwd.get(k + '_in', []))] if v == self.permitted_actions.DATASET_ACCESS and in_roles: if library: - item = self.sa_session.query( self.model.Library ).get( item_id ) + item = self.sa_session.query(self.model.Library).get(item_id) else: - item = self.sa_session.query( self.model.Dataset ).get( item_id ) - if ( library and not self.library_is_public( item ) ) or ( not library and not self.dataset_is_public( item ) ): + item = self.sa_session.query(self.model.Dataset).get(item_id) + if (library and not self.library_is_public(item)) or (not library and not self.dataset_is_public(item)): # Ensure that roles being associated with DATASET_ACCESS are a subset of the legitimate roles # derived from the roles associated with the access permission on item if it's not public. This # will keep illegitimate roles from being associated with the DATASET_ACCESS permission on the # dataset (i.e., in the case where item is .a library, if Role1 is associated with LIBRARY_ACCESS, # then only those users that have Role1 should be associated with DATASET_ACCESS. - legitimate_roles = self.get_legitimate_roles( trans, item, cntrller ) + legitimate_roles = self.get_legitimate_roles(trans, item, cntrller) illegitimate_roles = [] for role in in_roles: if role not in legitimate_roles: - illegitimate_roles.append( role ) + illegitimate_roles.append(role) if illegitimate_roles: # This condition should never occur since illegitimate roles are filtered out of the set of # roles displayed on the forms, but just in case there is a bug somewhere that incorrectly @@ -1220,13 +1221,13 @@ def derive_roles_from_access( self, trans, item_id, cntrller, library=False, **k msg += "item, so they were incorrectly displayed: " for role in illegitimate_roles: msg += "%s, " % role.name - msg = msg.rstrip( ", " ) + msg = msg.rstrip(", ") new_in_roles = [] for role in in_roles: if role in legitimate_roles: - new_in_roles.append( role ) + new_in_roles.append(role) in_roles = new_in_roles - if len( in_roles ) > 1: + if len(in_roles) > 1: # At least 1 user must have every role associated with the access # permission on this dataset, or the dataset is not accessible. # Since we have more than 1 role, none of them can be private. @@ -1234,75 +1235,75 @@ def derive_roles_from_access( self, trans, item_id, cntrller, library=False, **k if role.type == self.model.Role.types.PRIVATE: private_role_found = True break - if len( in_roles ) == 1: + if len(in_roles) == 1: accessible = True else: # At least 1 user must have every role associated with the access # permission on this dataset, or the dataset is not accessible. in_roles_set = set() for role in in_roles: - in_roles_set.add( role ) + in_roles_set.add(role) users_set = set() for role in in_roles: for ura in role.users: - users_set.add( ura.user ) + users_set.add(ura.user) for gra in role.groups: group = gra.group for uga in group.users: - users_set.add( uga.user ) + users_set.add(uga.user) # Make sure that at least 1 user has every role being associated with the dataset. for user in users_set: user_roles_set = set() for ura in user.roles: - user_roles_set.add( ura.role ) - if in_roles_set.issubset( user_roles_set ): + user_roles_set.add(ura.role) + if in_roles_set.issubset(user_roles_set): accessible = True break if private_role_found or not accessible: error = True # Don't set the permissions for DATASET_ACCESS if inaccessible or multiple roles with # at least 1 private, but set all other permissions. - permissions[ self.get_action( v.action ) ] = [] + permissions[self.get_action(v.action)] = [] msg = "At least 1 user must have every role associated with accessing datasets. " if private_role_found: msg += "Since you are associating more than 1 role, no private roles are allowed." if not accessible: msg += "The roles you attempted to associate for access would make the datasets in-accessible by everyone." else: - permissions[ self.get_action( v.action ) ] = in_roles + permissions[self.get_action(v.action)] = in_roles else: - permissions[ self.get_action( v.action ) ] = in_roles + permissions[self.get_action(v.action)] = in_roles return permissions, in_roles, error, msg - def copy_library_permissions( self, trans, source_library_item, target_library_item, user=None ): + def copy_library_permissions(self, trans, source_library_item, target_library_item, user=None): # Copy all relevant permissions from source. permissions = {} for role_assoc in source_library_item.actions: if role_assoc.action != self.permitted_actions.LIBRARY_ACCESS.action: # LIBRARY_ACCESS is a special permission that is set only at the library level. if role_assoc.action in permissions: - permissions[role_assoc.action].append( role_assoc.role ) + permissions[role_assoc.action].append(role_assoc.role) else: - permissions[role_assoc.action] = [ role_assoc.role ] - self.set_all_library_permissions( trans, target_library_item, permissions ) + permissions[role_assoc.action] = [role_assoc.role] + self.set_all_library_permissions(trans, target_library_item, permissions) if user: item_class = None for item_class, permission_class in self.library_item_assocs: - if isinstance( target_library_item, item_class ): + if isinstance(target_library_item, item_class): break if item_class: # Make sure user's private role is included - private_role = self.model.security_agent.get_private_user_role( user ) + private_role = self.model.security_agent.get_private_user_role(user) for name, action in self.permitted_actions.items(): - if not permission_class.filter_by( role_id=private_role.id, action=action.action ).first(): - lp = permission_class( action.action, target_library_item, private_role ) - self.sa_session.add( lp ) + if not permission_class.filter_by(role_id=private_role.id, action=action.action).first(): + lp = permission_class(action.action, target_library_item, private_role) + self.sa_session.add(lp) self.sa_session.flush() else: - raise Exception( 'Invalid class (%s) specified for target_library_item (%s)' % - ( target_library_item.__class__, target_library_item.__class__.__name__ ) ) + raise Exception('Invalid class (%s) specified for target_library_item (%s)' % + (target_library_item.__class__, target_library_item.__class__.__name__)) - def get_permitted_libraries( self, trans, user, actions ): + def get_permitted_libraries(self, trans, user, actions): """ This method is historical (it is not currently used), but may be useful again at some point. It returns a dictionary whose keys are library objects and whose values are a @@ -1314,9 +1315,9 @@ def get_permitted_libraries( self, trans, user, actions ): libraries = trans.app.security_agent.get_permitted_libraries( trans, user, [ trans.app.security_agent.permitted_actions.LIBRARY_ADD ] ) """ - all_libraries = trans.sa_session.query( trans.app.model.Library ) \ - .filter( trans.app.model.Library.table.c.deleted == false() ) \ - .order_by( trans.app.model.Library.name ) + all_libraries = trans.sa_session.query(trans.app.model.Library) \ + .filter(trans.app.model.Library.table.c.deleted == false()) \ + .order_by(trans.app.model.Library.name) roles = user.all_roles() actions_to_check = actions # The libraries dictionary looks like: { library : '1,2' }, library : '3' } @@ -1327,12 +1328,12 @@ def get_permitted_libraries( self, trans, user, actions ): # select lists are rendered. libraries = {} for library in all_libraries: - can_show, hidden_folder_ids = self.show_library_item( self, roles, library, actions_to_check ) + can_show, hidden_folder_ids = self.show_library_item(self, roles, library, actions_to_check) if can_show: - libraries[ library ] = hidden_folder_ids + libraries[library] = hidden_folder_ids return libraries - def show_library_item( self, user, roles, library_item, actions_to_check, hidden_folder_ids='' ): + def show_library_item(self, user, roles, library_item, actions_to_check, hidden_folder_ids=''): """ This method must be sent an instance of Library() or LibraryFolder(). Recursive execution produces a comma-separated string of folder ids whose folders do NOT meet the criteria for showing. Along with @@ -1342,101 +1343,101 @@ def show_library_item( self, user, roles, library_item, actions_to_check, hidden when it finds the first library_item that allows user to perform any one action in actions_to_check. """ for action in actions_to_check: - if self.allow_action( roles, action, library_item ): + if self.allow_action(roles, action, library_item): return True, hidden_folder_ids - if isinstance( library_item, self.model.Library ): - return self.show_library_item( user, roles, library_item.root_folder, actions_to_check, hidden_folder_ids='' ) - if isinstance( library_item, self.model.LibraryFolder ): + if isinstance(library_item, self.model.Library): + return self.show_library_item(user, roles, library_item.root_folder, actions_to_check, hidden_folder_ids='') + if isinstance(library_item, self.model.LibraryFolder): for folder in library_item.active_folders: - can_show, hidden_folder_ids = self.show_library_item( user, roles, folder, actions_to_check, hidden_folder_ids=hidden_folder_ids ) + can_show, hidden_folder_ids = self.show_library_item(user, roles, folder, actions_to_check, hidden_folder_ids=hidden_folder_ids) if can_show: return True, hidden_folder_ids if hidden_folder_ids: - hidden_folder_ids = '%s,%d' % ( hidden_folder_ids, folder.id ) + hidden_folder_ids = '%s,%d' % (hidden_folder_ids, folder.id) else: hidden_folder_ids = '%d' % folder.id return False, hidden_folder_ids - def get_showable_folders( self, user, roles, library_item, actions_to_check, hidden_folder_ids=[], showable_folders=[] ): + def get_showable_folders(self, user, roles, library_item, actions_to_check, hidden_folder_ids=[], showable_folders=[]): """ This method must be sent an instance of Library(), all the folders of which are scanned to determine if user is allowed to perform any action in actions_to_check. The param hidden_folder_ids, if passed, should contain a list of folder IDs which was generated when the library was previously scanned using the same actions_to_check. A list of showable folders is generated. This method scans the entire library. """ - if isinstance( library_item, self.model.Library ): - return self.get_showable_folders( user, roles, library_item.root_folder, actions_to_check, showable_folders=[] ) - if isinstance( library_item, self.model.LibraryFolder ): + if isinstance(library_item, self.model.Library): + return self.get_showable_folders(user, roles, library_item.root_folder, actions_to_check, showable_folders=[]) + if isinstance(library_item, self.model.LibraryFolder): if library_item.id not in hidden_folder_ids: for action in actions_to_check: - if self.allow_action( roles, action, library_item ): - showable_folders.append( library_item ) + if self.allow_action(roles, action, library_item): + showable_folders.append(library_item) break for folder in library_item.active_folders: - self.get_showable_folders( user, roles, folder, actions_to_check, showable_folders=showable_folders ) + self.get_showable_folders(user, roles, folder, actions_to_check, showable_folders=showable_folders) return showable_folders - def set_entity_user_associations( self, users=[], roles=[], groups=[], delete_existing_assocs=True ): + def set_entity_user_associations(self, users=[], roles=[], groups=[], delete_existing_assocs=True): for user in users: if delete_existing_assocs: flush_needed = False for a in user.non_private_roles + user.groups: - self.sa_session.delete( a ) + self.sa_session.delete(a) flush_needed = True if flush_needed: self.sa_session.flush() - self.sa_session.refresh( user ) + self.sa_session.refresh(user) for role in roles: # Make sure we are not creating an additional association with a PRIVATE role - if role not in user.roles: - self.associate_components( user=user, role=role ) + if role not in [x.role for x in user.roles]: + self.associate_components(user=user, role=role) for group in groups: - self.associate_components( user=user, group=group ) + self.associate_components(user=user, group=group) - def set_entity_group_associations( self, groups=[], users=[], roles=[], delete_existing_assocs=True ): + def set_entity_group_associations(self, groups=[], users=[], roles=[], delete_existing_assocs=True): for group in groups: if delete_existing_assocs: flush_needed = False for a in group.roles + group.users: - self.sa_session.delete( a ) + self.sa_session.delete(a) flush_needed = True if flush_needed: self.sa_session.flush() for role in roles: - self.associate_components( group=group, role=role ) + self.associate_components(group=group, role=role) for user in users: - self.associate_components( group=group, user=user ) + self.associate_components(group=group, user=user) - def set_entity_role_associations( self, roles=[], users=[], groups=[], delete_existing_assocs=True ): + def set_entity_role_associations(self, roles=[], users=[], groups=[], delete_existing_assocs=True): for role in roles: if delete_existing_assocs: flush_needed = False for a in role.users + role.groups: - self.sa_session.delete( a ) + self.sa_session.delete(a) flush_needed = True if flush_needed: self.sa_session.flush() for user in users: - self.associate_components( user=user, role=role ) + self.associate_components(user=user, role=role) for group in groups: - self.associate_components( group=group, role=role ) + self.associate_components(group=group, role=role) - def get_component_associations( self, **kwd ): - assert len( kwd ) == 2, 'You must specify exactly 2 Galaxy security components to check for associations.' + def get_component_associations(self, **kwd): + assert len(kwd) == 2, 'You must specify exactly 2 Galaxy security components to check for associations.' if 'dataset' in kwd: if 'action' in kwd: - return self.sa_session.query( self.model.DatasetPermissions ).filter_by( action=kwd['action'].action, dataset_id=kwd['dataset'].id ).first() + return self.sa_session.query(self.model.DatasetPermissions).filter_by(action=kwd['action'].action, dataset_id=kwd['dataset'].id).first() elif 'user' in kwd: if 'group' in kwd: - return self.sa_session.query( self.model.UserGroupAssociation ).filter_by( group_id=kwd['group'].id, user_id=kwd['user'].id ).first() + return self.sa_session.query(self.model.UserGroupAssociation).filter_by(group_id=kwd['group'].id, user_id=kwd['user'].id).first() elif 'role' in kwd: - return self.sa_session.query( self.model.UserRoleAssociation ).filter_by( role_id=kwd['role'].id, user_id=kwd['user'].id ).first() + return self.sa_session.query(self.model.UserRoleAssociation).filter_by(role_id=kwd['role'].id, user_id=kwd['user'].id).first() elif 'group' in kwd: if 'role' in kwd: - return self.sa_session.query( self.model.GroupRoleAssociation ).filter_by( role_id=kwd['role'].id, group_id=kwd['group'].id ).first() - raise Exception( 'No valid method of associating provided components: %s' % kwd ) + return self.sa_session.query(self.model.GroupRoleAssociation).filter_by(role_id=kwd['role'].id, group_id=kwd['group'].id).first() + raise Exception('No valid method of associating provided components: %s' % kwd) - def check_folder_contents( self, user, roles, folder, hidden_folder_ids='' ): + def check_folder_contents(self, user, roles, folder, hidden_folder_ids=''): """ This method must always be sent an instance of LibraryFolder(). Recursive execution produces a comma-separated string of folder ids whose folders do NOT meet the criteria for showing. Along @@ -1446,19 +1447,19 @@ def check_folder_contents( self, user, roles, folder, hidden_folder_ids='' ): folder that is accessible to user. """ # If a folder is writeable, it's accessable and we need not go further - if self.can_add_library_item( roles, folder ): + if self.can_add_library_item(roles, folder): return True, '' action = self.permitted_actions.DATASET_ACCESS - lddas = self.sa_session.query( self.model.LibraryDatasetDatasetAssociation ) \ - .join( "library_dataset" ) \ - .filter( self.model.LibraryDataset.folder == folder ) \ - .join( "dataset" ) \ - .options( eagerload_all( "dataset.actions" ) ) \ + lddas = self.sa_session.query(self.model.LibraryDatasetDatasetAssociation) \ + .join("library_dataset") \ + .filter(self.model.LibraryDataset.folder == folder) \ + .join("dataset") \ + .options(eagerload_all("dataset.actions")) \ .all() for ldda in lddas: - ldda_access_permissions = self.get_item_actions( action, ldda.dataset ) + ldda_access_permissions = self.get_item_actions(action, ldda.dataset) if not ldda_access_permissions: # Dataset is public return True, hidden_folder_ids @@ -1467,21 +1468,21 @@ def check_folder_contents( self, user, roles, folder, hidden_folder_ids='' ): # The current user has access permission on the dataset return True, hidden_folder_ids for sub_folder in folder.active_folders: - can_access, hidden_folder_ids = self.check_folder_contents( user, roles, sub_folder, hidden_folder_ids=hidden_folder_ids ) + can_access, hidden_folder_ids = self.check_folder_contents(user, roles, sub_folder, hidden_folder_ids=hidden_folder_ids) if can_access: return True, hidden_folder_ids if hidden_folder_ids: - hidden_folder_ids = '%s,%d' % ( hidden_folder_ids, sub_folder.id ) + hidden_folder_ids = '%s,%d' % (hidden_folder_ids, sub_folder.id) else: hidden_folder_ids = '%d' % sub_folder.id return False, hidden_folder_ids - def can_access_request_type( self, roles, request_type ): + def can_access_request_type(self, roles, request_type): action = self.permitted_actions.REQUEST_TYPE_ACCESS request_type_actions = [] for permission in request_type.actions: if permission.action == action.action: - request_type_actions.append( permission ) + request_type_actions.append(permission) if not request_type_actions: return True ret_val = False @@ -1491,24 +1492,24 @@ def can_access_request_type( self, roles, request_type ): break return ret_val - def set_request_type_permissions( self, request_type, permissions={} ): + def set_request_type_permissions(self, request_type, permissions={}): # Set new permissions on request_type, eliminating all current permissions for role_assoc in request_type.actions: - self.sa_session.delete( role_assoc ) + self.sa_session.delete(role_assoc) # Add the new permissions on request_type permission_class = self.model.RequestTypePermissions flush_needed = False for action, roles in permissions.items(): - if isinstance( action, Action ): + if isinstance(action, Action): action = action.action - for role_assoc in [ permission_class( action, request_type, role ) for role in roles ]: - self.sa_session.add( role_assoc ) + for role_assoc in [permission_class(action, request_type, role) for role in roles]: + self.sa_session.add(role_assoc) flush_needed = True if flush_needed: self.sa_session.flush() -class HostAgent( RBACAgent ): +class HostAgent(RBACAgent): """ A simple security agent which allows access to datasets based on host. This exists so that externals sites such as UCSC can gain access to @@ -1516,32 +1517,32 @@ class HostAgent( RBACAgent ): """ # TODO: Make sites user configurable sites = Bunch( - ucsc_main=( 'hgw1.cse.ucsc.edu', 'hgw2.cse.ucsc.edu', 'hgw3.cse.ucsc.edu', 'hgw4.cse.ucsc.edu', - 'hgw5.cse.ucsc.edu', 'hgw6.cse.ucsc.edu', 'hgw7.cse.ucsc.edu', 'hgw8.cse.ucsc.edu' ), - ucsc_test=( 'hgwdev.cse.ucsc.edu', ), - ucsc_archaea=( 'lowepub.cse.ucsc.edu', ) + ucsc_main=('hgw1.cse.ucsc.edu', 'hgw2.cse.ucsc.edu', 'hgw3.cse.ucsc.edu', 'hgw4.cse.ucsc.edu', + 'hgw5.cse.ucsc.edu', 'hgw6.cse.ucsc.edu', 'hgw7.cse.ucsc.edu', 'hgw8.cse.ucsc.edu'), + ucsc_test=('hgwdev.cse.ucsc.edu', ), + ucsc_archaea=('lowepub.cse.ucsc.edu', ) ) - def __init__( self, model, permitted_actions=None ): + def __init__(self, model, permitted_actions=None): self.model = model if permitted_actions: self.permitted_actions = permitted_actions @property - def sa_session( self ): + def sa_session(self): """Returns a SQLAlchemy session""" return self.model.context - def allow_action( self, addr, action, **kwd ): + def allow_action(self, addr, action, **kwd): if 'dataset' in kwd and action == self.permitted_actions.DATASET_ACCESS: hda = kwd['dataset'] - if action == self.permitted_actions.DATASET_ACCESS and action.action not in [ dp.action for dp in hda.dataset.actions ]: - log.debug( 'Allowing access to public dataset with hda: %i.' % hda.id ) + if action == self.permitted_actions.DATASET_ACCESS and action.action not in [dp.action for dp in hda.dataset.actions]: + log.debug('Allowing access to public dataset with hda: %i.' % hda.id) return True # dataset has no roles associated with the access permission, thus is already public - hdadaa = self.sa_session.query( self.model.HistoryDatasetAssociationDisplayAtAuthorization ) \ - .filter_by( history_dataset_association_id=hda.id ).first() + hdadaa = self.sa_session.query(self.model.HistoryDatasetAssociationDisplayAtAuthorization) \ + .filter_by(history_dataset_association_id=hda.id).first() if not hdadaa: - log.debug( 'Denying access to private dataset with hda: %i. No hdadaa record for this dataset.' % hda.id ) + log.debug('Denying access to private dataset with hda: %i. No hdadaa record for this dataset.' % hda.id) return False # no auth # We could just look up the reverse of addr, but then we'd also # have to verify it with the forward address and special case any @@ -1549,40 +1550,40 @@ def allow_action( self, addr, action, **kwd ): # # This would be improved by caching, but that's what the OS's name # service cache daemon is for (you ARE running nscd, right?). - for server in HostAgent.sites.get( hdadaa.site, [] ): + for server in HostAgent.sites.get(hdadaa.site, []): # We're going to search in order, but if the remote site is load # balancing their connections (as UCSC does), this is okay. try: - if socket.gethostbyname( server ) == addr: + if socket.gethostbyname(server) == addr: break # remote host is in the server list - except ( socket.error, socket.gaierror ): + except (socket.error, socket.gaierror): pass # can't resolve, try next else: - log.debug( 'Denying access to private dataset with hda: %i. Remote addr is not a valid server for site: %s.' % ( hda.id, hdadaa.site ) ) + log.debug('Denying access to private dataset with hda: %i. Remote addr is not a valid server for site: %s.' % (hda.id, hdadaa.site)) return False # remote addr is not in the server list - if ( datetime.utcnow() - hdadaa.update_time ) > timedelta( seconds=60 ): - log.debug( 'Denying access to private dataset with hda: %i. Authorization was granted, but has expired.' % hda.id ) + if (datetime.utcnow() - hdadaa.update_time) > timedelta(seconds=60): + log.debug('Denying access to private dataset with hda: %i. Authorization was granted, but has expired.' % hda.id) return False # not authz'd in the last 60 seconds - log.debug( 'Allowing access to private dataset with hda: %i. Remote server is: %s.' % ( hda.id, server ) ) + log.debug('Allowing access to private dataset with hda: %i. Remote server is: %s.' % (hda.id, server)) return True else: - raise Exception( 'The dataset access permission is the only valid permission in the host security agent.' ) + raise Exception('The dataset access permission is the only valid permission in the host security agent.') - def set_dataset_permissions( self, hda, user, site ): - hdadaa = self.sa_session.query( self.model.HistoryDatasetAssociationDisplayAtAuthorization ) \ - .filter_by( history_dataset_association_id=hda.id ).first() + def set_dataset_permissions(self, hda, user, site): + hdadaa = self.sa_session.query(self.model.HistoryDatasetAssociationDisplayAtAuthorization) \ + .filter_by(history_dataset_association_id=hda.id).first() if hdadaa: hdadaa.update_time = datetime.utcnow() else: - hdadaa = self.model.HistoryDatasetAssociationDisplayAtAuthorization( hda=hda, user=user, site=site ) - self.sa_session.add( hdadaa ) + hdadaa = self.model.HistoryDatasetAssociationDisplayAtAuthorization(hda=hda, user=user, site=site) + self.sa_session.add(hdadaa) self.sa_session.flush() -def get_permitted_actions( filter=None ): +def get_permitted_actions(filter=None): '''Utility method to return a subset of RBACAgent's permitted actions''' if filter is None: return RBACAgent.permitted_actions tmp_bunch = Bunch() - [ tmp_bunch.__dict__.__setitem__(k, v) for k, v in RBACAgent.permitted_actions.items() if k.startswith( filter ) ] + [tmp_bunch.__dict__.__setitem__(k, v) for k, v in RBACAgent.permitted_actions.items() if k.startswith(filter)] return tmp_bunch diff --git a/lib/galaxy/security/passwords.py b/lib/galaxy/security/passwords.py index 14e81314af8f..0745d0d2665c 100644 --- a/lib/galaxy/security/passwords.py +++ b/lib/galaxy/security/passwords.py @@ -14,45 +14,45 @@ COST_FACTOR = 10000 -def hash_password( password ): +def hash_password(password): """ Hash a password, currently will use the PBKDF2 scheme. """ - return hash_password_PBKDF2( password ) + return hash_password_PBKDF2(password) -def check_password( guess, hashed ): +def check_password(guess, hashed): """ Check a hashed password. Supports either PBKDF2 if the hash is prefixed with that string, or sha1 otherwise. """ - if hashed.startswith( "PBKDF2" ): - if check_password_PBKDF2( guess, hashed ): + if hashed.startswith("PBKDF2"): + if check_password_PBKDF2(guess, hashed): return True else: # Passwords were originally encoded with sha1 and hexed - if hashlib.sha1( guess ).hexdigest() == hashed: + if hashlib.sha1(guess).hexdigest() == hashed: return True # Password does not match return False -def hash_password_PBKDF2( password ): +def hash_password_PBKDF2(password): # Generate a random salt - salt = b64encode( urandom( SALT_LENGTH ) ) + salt = b64encode(urandom(SALT_LENGTH)) # Apply the pbkdf2 encoding - hashed = pbkdf2_bin( bytes( password ), salt, COST_FACTOR, KEY_LENGTH, getattr( hashlib, HASH_FUNCTION ) ) + hashed = pbkdf2_bin(bytes(password), salt, COST_FACTOR, KEY_LENGTH, getattr(hashlib, HASH_FUNCTION)) # Format - return 'PBKDF2${0}${1}${2}${3}'.format( HASH_FUNCTION, COST_FACTOR, salt, b64encode( hashed ) ) + return 'PBKDF2${0}${1}${2}${3}'.format(HASH_FUNCTION, COST_FACTOR, salt, b64encode(hashed)) -def check_password_PBKDF2( guess, hashed ): +def check_password_PBKDF2(guess, hashed): # Split the database representation to extract cost_factor and salt - name, hash_function, cost_factor, salt, encoded_original = hashed.split( '$', 5 ) + name, hash_function, cost_factor, salt, encoded_original = hashed.split('$', 5) # Hash the guess using the same parameters - hashed_guess = pbkdf2_bin( bytes( guess ), salt, int( cost_factor ), KEY_LENGTH, getattr( hashlib, hash_function ) ) - encoded_guess = b64encode( hashed_guess ) - return safe_str_cmp( encoded_original, encoded_guess ) + hashed_guess = pbkdf2_bin(bytes(guess), salt, int(cost_factor), KEY_LENGTH, getattr(hashlib, hash_function)) + encoded_guess = b64encode(hashed_guess) + return safe_str_cmp(encoded_original, encoded_guess) # Taken from https://github.com/mitsuhiko/python-pbkdf2/blob/master/pbkdf2.py @@ -60,7 +60,7 @@ def check_password_PBKDF2( guess, hashed ): _pack_int = Struct('>I').pack -def pbkdf2_bin( data, salt, iterations=1000, keylen=24, hashfunc=None ): +def pbkdf2_bin(data, salt, iterations=1000, keylen=24, hashfunc=None): """Returns a binary digest for the PBKDF2 hash algorithm of `data` with the given `salt`. It iterates `iterations` time and produces a key of `keylen` bytes. By default SHA-1 is used as hash function, @@ -78,6 +78,6 @@ def _pseudorandom(x, mac=mac): rv = u = _pseudorandom(salt + _pack_int(block)) for i in range(iterations - 1): u = _pseudorandom(''.join(map(chr, u))) - rv = starmap( xor, zip( rv, u ) ) + rv = starmap(xor, zip(rv, u)) buf.extend(rv) return ''.join(map(chr, buf))[:keylen] diff --git a/lib/galaxy/security/validate_user_input.py b/lib/galaxy/security/validate_user_input.py index c3b927a2d6e7..5375a4afbb7b 100644 --- a/lib/galaxy/security/validate_user_input.py +++ b/lib/galaxy/security/validate_user_input.py @@ -7,79 +7,79 @@ import logging import re -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) # Email validity parameters -VALID_EMAIL_RE = re.compile( "[^@]+@[^@]+\.[^@]+" ) +VALID_EMAIL_RE = re.compile("[^@]+@[^@]+\.[^@]+") EMAIL_MAX_LEN = 255 # Public name validity parameters PUBLICNAME_MIN_LEN = 3 PUBLICNAME_MAX_LEN = 255 -VALID_PUBLICNAME_RE = re.compile( "^[a-z0-9._\-]+$" ) -VALID_PUBLICNAME_SUB = re.compile( "[^a-z0-9._\-]" ) +VALID_PUBLICNAME_RE = re.compile("^[a-z0-9._\-]+$") +VALID_PUBLICNAME_SUB = re.compile("[^a-z0-9._\-]") FILL_CHAR = '-' # Password validity parameters PASSWORD_MIN_LEN = 6 -def validate_email( trans, email, user=None, check_dup=True ): +def validate_email(trans, email, user=None, check_dup=True): """ Validates the email format, also checks whether the domain is blacklisted in the disposable domains configuration. """ message = '' if user and user.email == email: return message - if not( VALID_EMAIL_RE.match( email ) ): + if not(VALID_EMAIL_RE.match(email)): message = "The format of the email address is not correct." - elif len( email ) > EMAIL_MAX_LEN: + elif len(email) > EMAIL_MAX_LEN: message = "Email address cannot be more than %d characters in length." % EMAIL_MAX_LEN - elif check_dup and trans.sa_session.query( trans.app.model.User ).filter_by( email=email ).first(): + elif check_dup and trans.sa_session.query(trans.app.model.User).filter_by(email=email).first(): message = "User with that email already exists." # If the blacklist is not empty filter out the disposable domains. elif trans.app.config.blacklist_content is not None: domain = email.split('@')[1] - if len( domain.split('.') ) > 2: - domain = ('.').join( domain.split('.')[-2:] ) + if len(domain.split('.')) > 2: + domain = ('.').join(domain.split('.')[-2:]) if domain in trans.app.config.blacklist_content: message = "Please enter your permanent email address." return message -def validate_publicname( trans, publicname, user=None ): +def validate_publicname(trans, publicname, user=None): # User names must be at least three characters in length and contain only lower-case # letters, numbers, and the '-' character. if user and user.username == publicname: return '' - if len( publicname ) < PUBLICNAME_MIN_LEN: - return "Public name must be at least %d characters in length." % ( PUBLICNAME_MIN_LEN ) - if len( publicname ) > PUBLICNAME_MAX_LEN: - return "Public name cannot be more than %d characters in length." % ( PUBLICNAME_MAX_LEN ) - if not( VALID_PUBLICNAME_RE.match( publicname ) ): + if len(publicname) < PUBLICNAME_MIN_LEN: + return "Public name must be at least %d characters in length." % (PUBLICNAME_MIN_LEN) + if len(publicname) > PUBLICNAME_MAX_LEN: + return "Public name cannot be more than %d characters in length." % (PUBLICNAME_MAX_LEN) + if not(VALID_PUBLICNAME_RE.match(publicname)): return "Public name must contain only lower-case letters, numbers, '.', '_' and '-'." - if trans.sa_session.query( trans.app.model.User ).filter_by( username=publicname ).first(): + if trans.sa_session.query(trans.app.model.User).filter_by(username=publicname).first(): return "Public name is taken; please choose another." return '' -def transform_publicname( trans, publicname, user=None ): +def transform_publicname(trans, publicname, user=None): # User names must be at least four characters in length and contain only lower-case # letters, numbers, and the '-' character. # TODO: Enhance to allow generation of semi-random publicnnames e.g., when valid but taken if user and user.username == publicname: return publicname - elif publicname not in [ 'None', None, '' ]: + elif publicname not in ['None', None, '']: publicname = publicname.lower() - publicname = re.sub( VALID_PUBLICNAME_SUB, FILL_CHAR, publicname ) - publicname = publicname.ljust( PUBLICNAME_MIN_LEN + 1, FILL_CHAR )[:PUBLICNAME_MAX_LEN] - if not trans.sa_session.query( trans.app.model.User ).filter_by( username=publicname ).first(): + publicname = re.sub(VALID_PUBLICNAME_SUB, FILL_CHAR, publicname) + publicname = publicname.ljust(PUBLICNAME_MIN_LEN + 1, FILL_CHAR)[:PUBLICNAME_MAX_LEN] + if not trans.sa_session.query(trans.app.model.User).filter_by(username=publicname).first(): return publicname return '' -def validate_password( trans, password, confirm ): - if len( password ) < PASSWORD_MIN_LEN: +def validate_password(trans, password, confirm): + if len(password) < PASSWORD_MIN_LEN: return "Use a password of at least %d characters." % PASSWORD_MIN_LEN elif password != confirm: return "Passwords don't match." diff --git a/lib/galaxy/tools/__init__.py b/lib/galaxy/tools/__init__.py index 4c2887bd132e..6241409850a7 100755 --- a/lib/galaxy/tools/__init__.py +++ b/lib/galaxy/tools/__init__.py @@ -19,7 +19,6 @@ from six import string_types from six.moves.urllib.parse import unquote_plus -import galaxy.jobs import tool_shed.util.repository_util as repository_util import tool_shed.util.shed_util_common @@ -69,6 +68,7 @@ from galaxy.util import ( ExecutionTimer, listify, + Params, rst_to_html, string_as_bool, unicodify @@ -93,7 +93,7 @@ template_macro_params ) -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) HELP_UNINITIALIZED = threading.Lock() MODEL_TOOLS_PATH = os.path.abspath(os.path.dirname(__file__)) @@ -154,6 +154,8 @@ "gd_phylogenetic_tree", "gd_population_structure", "gd_prepare_population_structure", + # Datasources + "genomespace_importer" ] # Tools that needed galaxy on the PATH in the past but no longer do along # with the version at which they were fixed. @@ -175,7 +177,7 @@ def add_error(self, file, phase, exception): "time": str(datetime.now()), "phase": phase, "error": str(exception) - } ) + }) if len(self.error_stack) > self.max_errors: self.error_stack.pop() @@ -183,23 +185,41 @@ def add_error(self, file, phase, exception): global_tool_errors = ToolErrorLog() -class ToolInputsNotReadyException( Exception ): +class ToolInputsNotReadyException(Exception): pass -class ToolNotFoundException( Exception ): +class ToolNotFoundException(Exception): pass -class ToolBox( BaseGalaxyToolBox ): +def create_tool_from_source(app, tool_source, config_file=None, **kwds): + # Allow specifying a different tool subclass to instantiate + tool_module = tool_source.parse_tool_module() + if tool_module is not None: + module, cls = tool_module + mod = __import__(module, globals(), locals(), [cls]) + ToolClass = getattr(mod, cls) + elif tool_source.parse_tool_type(): + tool_type = tool_source.parse_tool_type() + ToolClass = tool_types.get(tool_type) + else: + # Normal tool + root = getattr(tool_source, 'root', None) + ToolClass = Tool + tool = ToolClass(config_file, tool_source, app, **kwds) + return tool + + +class ToolBox(BaseGalaxyToolBox): """ A derivative of AbstractToolBox with knowledge about Tool internals - how to construct them, action types, dependency management, etc.... """ - def __init__( self, config_filenames, tool_root_dir, app ): + def __init__(self, config_filenames, tool_root_dir, app): self._reload_count = 0 self.tool_location_fetcher = ToolLocationFetcher() - super( ToolBox, self ).__init__( + super(ToolBox, self).__init__( config_filenames=config_filenames, tool_root_dir=tool_root_dir, app=app, @@ -224,38 +244,27 @@ def all_requirements(self): return [r.to_dict() for r in reqs] @property - def tools_by_id( self ): + def tools_by_id(self): # Deprecated method, TODO - eliminate calls to this in test/. return self._tools_by_id - def create_tool( self, config_file, repository_id=None, guid=None, **kwds ): + def create_tool(self, config_file, **kwds): try: tool_source = get_tool_source( config_file, - enable_beta_formats=getattr( self.app.config, "enable_beta_tool_formats", False ), + enable_beta_formats=getattr(self.app.config, "enable_beta_tool_formats", False), tool_location_fetcher=self.tool_location_fetcher, ) except Exception as e: # capture and log parsing errors global_tool_errors.add_error(config_file, "Tool XML parsing", e) raise e - # Allow specifying a different tool subclass to instantiate - tool_module = tool_source.parse_tool_module() - if tool_module is not None: - module, cls = tool_module - mod = __import__( module, globals(), locals(), [cls] ) - ToolClass = getattr( mod, cls ) - elif tool_source.parse_tool_type(): - tool_type = tool_source.parse_tool_type() - ToolClass = tool_types.get( tool_type ) - else: - # Normal tool - root = getattr( tool_source, 'root', None ) - ToolClass = Tool - tool = ToolClass( config_file, tool_source, self.app, guid=guid, repository_id=repository_id, **kwds ) - return tool + return self._create_tool_from_source(tool_source, config_file=config_file, **kwds) - def get_tool_components( self, tool_id, tool_version=None, get_loaded_tools_by_lineage=False, set_selected=False ): + def _create_tool_from_source(self, tool_source, **kwds): + return create_tool_from_source(self.app, tool_source, **kwds) + + def get_tool_components(self, tool_id, tool_version=None, get_loaded_tools_by_lineage=False, set_selected=False): """ Retrieve all loaded versions of a tool from the toolbox and return a select list enabling selection of a different version, the list of the tool's loaded versions, and the specified tool. @@ -266,25 +275,25 @@ def get_tool_components( self, tool_id, tool_version=None, get_loaded_tools_by_l tool = None # Backwards compatibility for datasource tools that have default tool_id configured, but which # are now using only GALAXY_URL. - tool_ids = listify( tool_id ) + tool_ids = listify(tool_id) for tool_id in tool_ids: if get_loaded_tools_by_lineage: - tools = toolbox.get_loaded_tools_by_lineage( tool_id ) + tools = toolbox.get_loaded_tools_by_lineage(tool_id) else: - tools = toolbox.get_tool( tool_id, tool_version=tool_version, get_all_versions=True ) + tools = toolbox.get_tool(tool_id, tool_version=tool_version, get_all_versions=True) if tools: - tool = toolbox.get_tool( tool_id, tool_version=tool_version, get_all_versions=False ) - if len( tools ) > 1: - tool_version_select_field = self.__build_tool_version_select_field( tools, tool.id, set_selected ) + tool = toolbox.get_tool(tool_id, tool_version=tool_version, get_all_versions=False) + if len(tools) > 1: + tool_version_select_field = self.__build_tool_version_select_field(tools, tool.id, set_selected) break return tool_version_select_field, tools, tool - def _path_template_kwds( self ): + def _path_template_kwds(self): return { "model_tools_path": MODEL_TOOLS_PATH, } - def _get_tool_shed_repository( self, tool_shed, name, owner, installed_changeset_revision ): + def _get_tool_shed_repository(self, tool_shed, name, owner, installed_changeset_revision): # Abstract toolbox doesn't have a dependency on the the database, so # override _get_tool_shed_repository here to provide this information. @@ -296,62 +305,63 @@ def _get_tool_shed_repository( self, tool_shed, name, owner, installed_changeset installed_changeset_revision=installed_changeset_revision ) - def __build_tool_version_select_field( self, tools, tool_id, set_selected ): + def __build_tool_version_select_field(self, tools, tool_id, set_selected): """Build a SelectField whose options are the ids for the received list of tools.""" options = [] refresh_on_change_values = [] for tool in tools: - options.insert( 0, ( tool.version, tool.id ) ) - refresh_on_change_values.append( tool.id ) - select_field = SelectField( name='tool_id', refresh_on_change=True, refresh_on_change_values=refresh_on_change_values ) + options.insert(0, (tool.version, tool.id)) + refresh_on_change_values.append(tool.id) + select_field = SelectField(name='tool_id', refresh_on_change=True, refresh_on_change_values=refresh_on_change_values) for option_tup in options: - selected = set_selected and option_tup[ 1 ] == tool_id + selected = set_selected and option_tup[1] == tool_id if selected: - select_field.add_option( 'version %s' % option_tup[ 0 ], option_tup[ 1 ], selected=True ) + select_field.add_option('version %s' % option_tup[0], option_tup[1], selected=True) else: - select_field.add_option( 'version %s' % option_tup[ 0 ], option_tup[ 1 ] ) + select_field.add_option('version %s' % option_tup[0], option_tup[1]) return select_field -class DefaultToolState( object ): +class DefaultToolState(object): """ Keeps track of the state of a users interaction with a tool between requests. """ - def __init__( self ): + + def __init__(self): self.page = 0 self.rerun_remap_job_id = None self.inputs = {} - def initialize( self, trans, tool ): + def initialize(self, trans, tool): """ Create a new `DefaultToolState` for this tool. It will be initialized with default values for inputs. Grouping elements are filled in recursively. """ self.inputs = {} - context = ExpressionContext( self.inputs ) + context = ExpressionContext(self.inputs) for input in tool.inputs.itervalues(): - self.inputs[ input.name ] = input.get_initial_value( trans, context ) + self.inputs[input.name] = input.get_initial_value(trans, context) - def encode( self, tool, app, nested=False ): + def encode(self, tool, app, nested=False): """ Convert the data to a string """ - value = params_to_strings( tool.inputs, self.inputs, app, nested=nested ) + value = params_to_strings(tool.inputs, self.inputs, app, nested=nested) value["__page__"] = self.page value["__rerun_remap_job_id__"] = self.rerun_remap_job_id return value - def decode( self, values, tool, app ): + def decode(self, values, tool, app): """ Restore the state from a string """ - values = json_fix( safe_loads( values ) ) or {} - self.page = values.pop( "__page__" ) if "__page__" in values else None - self.rerun_remap_job_id = values.pop( "__rerun_remap_job_id__" ) if "__rerun_remap_job_id__" in values else None - self.inputs = params_from_strings( tool.inputs, values, app, ignore_errors=True ) + values = json_fix(safe_loads(values)) or {} + self.page = values.pop("__page__") if "__page__" in values else None + self.rerun_remap_job_id = values.pop("__rerun_remap_job_id__") if "__rerun_remap_job_id__" in values else None + self.inputs = params_from_strings(tool.inputs, values, app, ignore_errors=True) - def copy( self ): + def copy(self): """ Shallow copy of the state """ @@ -362,7 +372,7 @@ def copy( self ): return new_state -class Tool( object, Dictifiable ): +class Tool(object, Dictifiable): """ Represents a computational tool that can be executed through Galaxy. """ @@ -370,13 +380,13 @@ class Tool( object, Dictifiable ): tool_type = 'default' requires_setting_metadata = True default_tool_action = DefaultToolAction - dict_collection_visible_keys = ( 'id', 'name', 'version', 'description', 'labels' ) + dict_collection_visible_keys = ('id', 'name', 'version', 'description', 'labels') - def __init__( self, config_file, tool_source, app, guid=None, repository_id=None, allow_code_files=True ): + def __init__(self, config_file, tool_source, app, guid=None, repository_id=None, allow_code_files=True): """Load a tool from the config named by `config_file`""" # Determine the full path of the directory where the tool config is self.config_file = config_file - self.tool_dir = os.path.dirname( config_file ) + self.tool_dir = os.path.dirname(config_file) self.app = app self.repository_id = repository_id self._allow_code_files = allow_code_files @@ -419,14 +429,14 @@ def __init__( self, config_file, tool_source, app, guid=None, repository_id=None # populate toolshed repository info, if available self.populate_tool_shed_info() # add tool resource parameters - self.populate_resource_parameters( tool_source ) + self.populate_resource_parameters(tool_source) # Parse XML element containing configuration try: - self.parse( tool_source, guid=guid ) + self.parse(tool_source, guid=guid) except Exception as e: global_tool_errors.add_error(config_file, "Tool Loading", e) raise e - self.history_manager = histories.HistoryManager( app ) + self.history_manager = histories.HistoryManager(app) self._view = views.DependencyResolversView(app) @property @@ -434,7 +444,7 @@ def version_object(self): return LooseVersion(self.version) @property - def sa_session( self ): + def sa_session(self): """Returns a SQLAlchemy session""" return self.app.model.context @@ -444,7 +454,7 @@ def lineage(self): return self._lineage @property - def tool_versions( self ): + def tool_versions(self): # If we have versions, return them. if self.lineage: return self.lineage.get_versions() @@ -452,18 +462,18 @@ def tool_versions( self ): return [] @property - def tool_shed_repository( self ): + def tool_shed_repository(self): # If this tool is included in an installed tool shed repository, return it. if self.tool_shed: - return repository_util.get_installed_repository( self.app, - tool_shed=self.tool_shed, - name=self.repository_name, - owner=self.repository_owner, - installed_changeset_revision=self.installed_changeset_revision ) + return repository_util.get_installed_repository(self.app, + tool_shed=self.tool_shed, + name=self.repository_name, + owner=self.repository_owner, + installed_changeset_revision=self.installed_changeset_revision) return None @property - def produces_collections_of_unknown_type( self ): + def produces_collections_of_unknown_type(self): def output_is_dynamic_collection(output): if not output.collection: @@ -472,20 +482,20 @@ def output_is_dynamic_collection(output): return False return True - return any( map( output_is_dynamic_collection, self.outputs.values() ) ) + return any(map(output_is_dynamic_collection, self.outputs.values())) @property - def produces_collections_with_unknown_structure( self ): + def produces_collections_with_unknown_structure(self): def output_is_dynamic(output): if not output.collection: return False return output.dynamic_structure - return any( map( output_is_dynamic, self.outputs.values() ) ) + return any(map(output_is_dynamic, self.outputs.values())) @property - def valid_input_states( self ): + def valid_input_states(self): return model.Dataset.valid_input_states @property @@ -557,10 +567,10 @@ def get_job_destination(self, job_params=None): """ return self.app.job_config.get_destination(self.__get_job_tool_configuration(job_params=job_params).destination) - def get_panel_section( self ): - return self.app.toolbox.get_integrated_section_for_tool( self ) + def get_panel_section(self): + return self.app.toolbox.get_integrated_section_for_tool(self) - def allow_user_access( self, user, attempting_access=True ): + def allow_user_access(self, user, attempting_access=True): """ :returns: bool -- Whether the user is allowed to access the tool. """ @@ -568,11 +578,11 @@ def allow_user_access( self, user, attempting_access=True ): return False return True - def parse( self, tool_source, guid=None ): + def parse(self, tool_source, guid=None): """ Read tool configuration from the element `root` and fill in `self`. """ - self.profile = float( tool_source.parse_profile() ) + self.profile = float(tool_source.parse_profile()) # Get the UNIQUE id for the tool self.old_id = tool_source.parse_id() if guid is None: @@ -580,17 +590,18 @@ def parse( self, tool_source, guid=None ): else: self.id = guid if not self.id: - raise Exception( "Missing tool 'id' for tool at '%s'" % tool_source ) + raise Exception("Missing tool 'id' for tool at '%s'" % tool_source) - if self.profile >= 16.04 and VERSION_MAJOR < self.profile: + profile = LooseVersion(str(self.profile)) + if profile >= LooseVersion("16.04") and LooseVersion(VERSION_MAJOR) < profile: template = "The tool %s targets version %s of Galaxy, you should upgrade Galaxy to ensure proper functioning of this tool." message = template % (self.id, self.profile) - log.warning(message) + raise Exception(message) # Get the (user visible) name of the tool self.name = tool_source.parse_name() if not self.name: - raise Exception( "Missing tool 'name' for tool with id '%s' at '%s'" % (self.id, tool_source) ) + raise Exception("Missing tool 'name' for tool with id '%s' at '%s'" % (self.id, tool_source)) self.version = tool_source.parse_version() if not self.version: @@ -598,7 +609,7 @@ def parse( self, tool_source, guid=None ): # For backward compatibility, some tools may not have versions yet. self.version = "1.0.0" else: - raise Exception( "Missing tool 'version' for tool with id '%s' at '%s'" % (self.id, tool_source) ) + raise Exception("Missing tool 'version' for tool with id '%s' at '%s'" % (self.id, tool_source)) self.edam_operations = tool_source.parse_edam_operations() self.edam_topics = tool_source.parse_edam_topics() @@ -608,19 +619,19 @@ def parse( self, tool_source, guid=None ): # Legacy feature, ignored by UI. self.force_history_refresh = False - self.display_interface = tool_source.parse_display_interface( default=self.display_interface ) + self.display_interface = tool_source.parse_display_interface(default=self.display_interface) - self.require_login = tool_source.parse_require_login( self.require_login ) + self.require_login = tool_source.parse_require_login(self.require_login) request_param_translation_elem = tool_source.parse_request_param_translation_elem() if request_param_translation_elem is not None: # Load input translator, used by datasource tools to change names/values of incoming parameters - self.input_translator = ToolInputTranslator.from_element( request_param_translation_elem ) + self.input_translator = ToolInputTranslator.from_element(request_param_translation_elem) else: self.input_translator = None - self.parse_command( tool_source ) - self.environment_variables = self.parse_environment_variables( tool_source ) + self.parse_command(tool_source) + self.environment_variables = self.parse_environment_variables(tool_source) # Parameters used to build URL for redirection to external app redirect_url_params = tool_source.parse_redirect_url_params_elem() @@ -629,7 +640,7 @@ def parse( self, tool_source, guid=None ): redirect_url_params = redirect_url_params.text.strip() # Replace remaining white space with something we can safely split on later # when we are building the params - self.redirect_url_params = redirect_url_params.replace( ' ', '**^**' ) + self.redirect_url_params = redirect_url_params.replace(' ', '**^**') else: self.redirect_url_params = '' @@ -656,14 +667,14 @@ def parse( self, tool_source, guid=None ): # a 'default' will be provided that uses the 'default' handler and # 'default' destination. I thought about moving this to the # job_config, but it makes more sense to store here. -nate - self_ids = [ self.id.lower() ] + self_ids = [self.id.lower()] if self.old_id != self.id: # Handle toolshed guids - self_ids = [ self.id.lower(), self.id.lower().rsplit('/', 1)[0], self.old_id.lower() ] + self_ids = [self.id.lower(), self.id.lower().rsplit('/', 1)[0], self.old_id.lower()] self.all_ids = self_ids # In the toolshed context, there is no job config. - if hasattr( self.app, 'job_config' ): + if hasattr(self.app, 'job_config'): self.job_tool_configurations = self.app.job_config.get_job_tool_configurations(self_ids) # Is this a 'hidden' tool (hidden in tool menu) @@ -678,17 +689,21 @@ def parse( self, tool_source, guid=None ): ) self.options = Bunch(** self.options) + # Read in name of galaxy.json metadata file and how to parse it. + self.provided_metadata_file = tool_source.parse_provided_metadata_file() + self.provided_metadata_style = tool_source.parse_provided_metadata_style() + # Parse tool inputs (if there are any required) - self.parse_inputs( tool_source ) + self.parse_inputs(tool_source) # Parse tool help - self.parse_help( tool_source ) + self.parse_help(tool_source) # Description of outputs produced by an invocation of the tool - self.parse_outputs( tool_source ) + self.parse_outputs(tool_source) # Parse result handling for tool exit codes and stdout/stderr messages: - self.parse_stdio( tool_source ) + self.parse_stdio(tool_source) self.strict_shell = tool_source.parse_strict_shell() @@ -700,8 +715,8 @@ def parse( self, tool_source, guid=None ): self.tool_action = self.default_tool_action() else: module, cls = action - mod = __import__( module, globals(), locals(), [cls]) - self.tool_action = getattr( mod, cls )() + mod = __import__(module, globals(), locals(), [cls]) + self.tool_action = getattr(mod, cls)() # Tests self.__parse_tests(tool_source) @@ -710,11 +725,11 @@ def parse( self, tool_source, guid=None ): self.requirements = requirements self.containers = containers - self.citations = self._parse_citations( tool_source ) + self.citations = self._parse_citations(tool_source) # Determine if this tool can be used in workflows self.is_workflow_compatible = self.check_workflow_compatible(tool_source) - self.__parse_trackster_conf( tool_source ) + self.__parse_trackster_conf(tool_source) def __parse_legacy_features(self, tool_source): self.code_namespace = dict() @@ -736,15 +751,15 @@ def __parse_legacy_features(self, tool_source): # map hook to function self.hook_map[key] = value file_name = code_elem.get("file") - code_path = os.path.join( self.tool_dir, file_name ) + code_path = os.path.join(self.tool_dir, file_name) with open(code_path) as f: exec(compile(f.read(), code_path, 'exec'), self.code_namespace) # User interface hints - uihints_elem = root.find( "uihints" ) + uihints_elem = root.find("uihints") if uihints_elem is not None: for key, value in uihints_elem.attrib.items(): - self.uihints[ key ] = value + self.uihints[key] = value def __parse_tests(self, tool_source): self.__tests_source = tool_source @@ -758,18 +773,18 @@ def __parse_config_files(self, tool_source): root = tool_source.root conf_parent_elem = root.find("configfiles") if conf_parent_elem is not None: - inputs_elem = conf_parent_elem.find( "inputs" ) + inputs_elem = conf_parent_elem.find("inputs") if inputs_elem is not None: - name = inputs_elem.get( "name" ) - filename = inputs_elem.get( "filename", None ) + name = inputs_elem.get("name") + filename = inputs_elem.get("filename", None) format = inputs_elem.get("format", "json") content = dict(format=format) - self.config_files.append( ( name, filename, content ) ) - for conf_elem in conf_parent_elem.findall( "configfile" ): - name = conf_elem.get( "name" ) - filename = conf_elem.get( "filename", None ) + self.config_files.append((name, filename, content)) + for conf_elem in conf_parent_elem.findall("configfile"): + name = conf_elem.get("name") + filename = conf_elem.get("filename", None) content = conf_elem.text - self.config_files.append( ( name, filename, content ) ) + self.config_files.append((name, filename, content)) def __parse_trackster_conf(self, tool_source): self.trackster_conf = None @@ -777,26 +792,40 @@ def __parse_trackster_conf(self, tool_source): return # Trackster configuration. - trackster_conf = tool_source.root.find( "trackster_conf" ) + trackster_conf = tool_source.root.find("trackster_conf") if trackster_conf is not None: - self.trackster_conf = TracksterConfig.parse( trackster_conf ) + self.trackster_conf = TracksterConfig.parse(trackster_conf) @property - def tests( self ): + def tests(self): if not self.__tests_populated: tests_source = self.__tests_source if tests_source: try: - self.__tests = parse_tests( self, tests_source ) + self.__tests = parse_tests(self, tests_source) except: self.__tests = None - log.exception( "Failed to parse tool tests" ) + log.exception("Failed to parse tool tests") else: self.__tests = None self.__tests_populated = True return self.__tests - def parse_command( self, tool_source ): + def tool_provided_metadata(self, job_wrapper): + meta_file = os.path.join(job_wrapper.tool_working_directory, self.provided_metadata_file) + # LEGACY: Remove in 17.XX + if not os.path.exists(meta_file): + # Maybe this is a legacy job, use the job working directory instead + meta_file = os.path.join(job_wrapper.working_directory, self.provided_metadata_file) + + if not os.path.exists(meta_file): + return output_collect.NullToolProvidedMetadata() + if self.provided_metadata_style == "legacy": + return output_collect.LegacyToolProvidedMetadata(job_wrapper, meta_file) + else: + return output_collect.ToolProvidedMetadata(job_wrapper, meta_file) + + def parse_command(self, tool_source): """ """ # Command line (template). Optional for tools that do not invoke a local program @@ -809,10 +838,10 @@ def parse_command( self, tool_source ): self.command = '' self.interpreter = None - def parse_environment_variables( self, tool_source ): + def parse_environment_variables(self, tool_source): return tool_source.parse_environment_variables() - def parse_inputs( self, tool_source ): + def parse_inputs(self, tool_source): """ Parse the "" element and create appropriate `ToolParameter`s. This implementation supports multiple pages and grouping constructs. @@ -824,45 +853,45 @@ def parse_inputs( self, tool_source ): if hasattr(pages, "input_elem"): input_elem = pages.input_elem # Handle properties of the input form - self.check_values = string_as_bool( input_elem.get("check_values", self.check_values ) ) - self.nginx_upload = string_as_bool( input_elem.get( "nginx_upload", self.nginx_upload ) ) - self.action = input_elem.get( 'action', self.action ) + self.check_values = string_as_bool(input_elem.get("check_values", self.check_values)) + self.nginx_upload = string_as_bool(input_elem.get("nginx_upload", self.nginx_upload)) + self.action = input_elem.get('action', self.action) # If we have an nginx upload, save the action as a tuple instead of # a string. The actual action needs to get url_for run to add any # prefixes, and we want to avoid adding the prefix to the # nginx_upload_path. This logic is handled in the tool_form.mako # template. if self.nginx_upload and self.app.config.nginx_upload_path: - if '?' in unquote_plus( self.action ): - raise Exception( 'URL parameters in a non-default tool action can not be used ' - 'in conjunction with nginx upload. Please convert them to ' - 'hidden POST parameters' ) + if '?' in unquote_plus(self.action): + raise Exception('URL parameters in a non-default tool action can not be used ' + 'in conjunction with nginx upload. Please convert them to ' + 'hidden POST parameters') self.action = (self.app.config.nginx_upload_path + '?nginx_redir=', unquote_plus(self.action)) - self.target = input_elem.get( "target", self.target ) - self.method = input_elem.get( "method", self.method ) + self.target = input_elem.get("target", self.target) + self.method = input_elem.get("method", self.method) # Parse the actual parameters # Handle multiple page case for page_source in pages.page_sources: - inputs = self.parse_input_elem( page_source, enctypes ) + inputs = self.parse_input_elem(page_source, enctypes) display = page_source.parse_display() - self.inputs_by_page.append( inputs ) - self.inputs.update( inputs ) - self.display_by_page.append( display ) + self.inputs_by_page.append(inputs) + self.inputs.update(inputs) + self.display_by_page.append(display) else: - self.inputs_by_page.append( self.inputs ) - self.display_by_page.append( None ) + self.inputs_by_page.append(self.inputs) + self.display_by_page.append(None) self.display = self.display_by_page[0] - self.npages = len( self.inputs_by_page ) - self.last_page = len( self.inputs_by_page ) - 1 - self.has_multiple_pages = bool( self.last_page ) + self.npages = len(self.inputs_by_page) + self.last_page = len(self.inputs_by_page) - 1 + self.has_multiple_pages = bool(self.last_page) # Determine the needed enctype for the form - if len( enctypes ) == 0: + if len(enctypes) == 0: self.enctype = "application/x-www-form-urlencoded" - elif len( enctypes ) == 1: + elif len(enctypes) == 1: self.enctype = enctypes.pop() else: - raise Exception( "Conflicting required enctypes: %s" % str( enctypes ) ) + raise Exception("Conflicting required enctypes: %s" % str(enctypes)) # Check if the tool either has no parameters or only hidden (and # thus hardcoded) FIXME: hidden parameters aren't # parameters at all really, and should be passed in a different @@ -872,11 +901,11 @@ def parse_inputs( self, tool_source ): template_macros = template_macro_params(tool_source.root) self.template_macro_params = template_macros for param in self.inputs.values(): - if not isinstance( param, ( HiddenToolParameter, BaseURLToolParameter ) ): + if not isinstance(param, (HiddenToolParameter, BaseURLToolParameter)): self.input_required = True break - def parse_help( self, tool_source ): + def parse_help(self, tool_source): """ Parse the help text for the tool. Formatted in reStructuredText, but stored as Mako to allow for dynamic image paths. @@ -887,14 +916,14 @@ def parse_help( self, tool_source ): self.__help_by_page = HELP_UNINITIALIZED self.__help_source = tool_source - def parse_outputs( self, tool_source ): + def parse_outputs(self, tool_source): """ Parse elements and fill in self.outputs (keyed by name) """ self.outputs, self.output_collections = tool_source.parse_outputs(self) # TODO: Include the tool's name in any parsing warnings. - def parse_stdio( self, tool_source ): + def parse_stdio(self, tool_source): """ Parse element(s) and fill in self.return_codes, self.stderr_rules, and self.stdout_rules. Return codes have a range @@ -905,7 +934,7 @@ def parse_stdio( self, tool_source ): self.stdio_exit_codes = exit_codes self.stdio_regexes = regexes - def _parse_citations( self, tool_source ): + def _parse_citations(self, tool_source): # TODO: Move following logic into ToolSource abstraction. if not hasattr(tool_source, 'root'): return [] @@ -919,155 +948,155 @@ def _parse_citations( self, tool_source ): for citation_elem in citations_elem: if citation_elem.tag != "citation": pass - citation = self.app.citations_manager.parse_citation( citation_elem, self.tool_dir ) + citation = self.app.citations_manager.parse_citation(citation_elem, self.tool_dir) if citation: - citations.append( citation ) + citations.append(citation) return citations - def parse_input_elem( self, page_source, enctypes, context=None ): + def parse_input_elem(self, page_source, enctypes, context=None): """ Parse a parent element whose children are inputs -- these could be groups (repeat, conditional) or param elements. Groups will be parsed recursively. """ rval = odict() - context = ExpressionContext( rval, context ) + context = ExpressionContext(rval, context) for input_source in page_source.parse_input_sources(): # Repeat group input_type = input_source.parse_input_type() if input_type == "repeat": group = Repeat() - group.name = input_source.get( "name" ) - group.title = input_source.get( "title" ) - group.help = input_source.get( "help", None ) + group.name = input_source.get("name") + group.title = input_source.get("title") + group.help = input_source.get("help", None) page_source = input_source.parse_nested_inputs_source() - group.inputs = self.parse_input_elem( page_source, enctypes, context ) - group.default = int( input_source.get( "default", 0 ) ) - group.min = int( input_source.get( "min", 0 ) ) + group.inputs = self.parse_input_elem(page_source, enctypes, context) + group.default = int(input_source.get("default", 0)) + group.min = int(input_source.get("min", 0)) # Use float instead of int so that 'inf' can be used for no max - group.max = float( input_source.get( "max", "inf" ) ) + group.max = float(input_source.get("max", "inf")) assert group.min <= group.max, \ - ValueError( "Min repeat count must be less-than-or-equal to the max." ) + ValueError("Min repeat count must be less-than-or-equal to the max.") # Force default to be within min-max range - group.default = min( max( group.default, group.min ), group.max ) + group.default = min(max(group.default, group.min), group.max) rval[group.name] = group elif input_type == "conditional": group = Conditional() - group.name = input_source.get( "name" ) - group.value_ref = input_source.get( 'value_ref', None ) - group.value_ref_in_group = input_source.get_bool( 'value_ref_in_group', True ) + group.name = input_source.get("name") + group.value_ref = input_source.get('value_ref', None) + group.value_ref_in_group = input_source.get_bool('value_ref_in_group', True) value_from = input_source.get("value_from", None) if value_from: - value_from = value_from.split( ':' ) - group.value_from = locals().get( value_from[0] ) - group.test_param = rval[ group.value_ref ] + value_from = value_from.split(':') + group.value_from = locals().get(value_from[0]) + group.test_param = rval[group.value_ref] group.test_param.refresh_on_change = True - for attr in value_from[1].split( '.' ): - group.value_from = getattr( group.value_from, attr ) - for case_value, case_inputs in group.value_from( context, group, self ).items(): + for attr in value_from[1].split('.'): + group.value_from = getattr(group.value_from, attr) + for case_value, case_inputs in group.value_from(context, group, self).items(): case = ConditionalWhen() case.value = case_value if case_inputs: - page_source = XmlPageSource( ElementTree.XML( "%s" % case_inputs ) ) - case.inputs = self.parse_input_elem( page_source, enctypes, context ) + page_source = XmlPageSource(ElementTree.XML("%s" % case_inputs)) + case.inputs = self.parse_input_elem(page_source, enctypes, context) else: case.inputs = odict() - group.cases.append( case ) + group.cases.append(case) else: # Should have one child "input" which determines the case test_param_input_source = input_source.parse_test_input_source() - group.test_param = self.parse_param_elem( test_param_input_source, enctypes, context ) + group.test_param = self.parse_param_elem(test_param_input_source, enctypes, context) if group.test_param.optional: log.warning("Tool with id %s declares a conditional test parameter as optional, this is invalid and will be ignored." % self.id) group.test_param.optional = False - possible_cases = list( group.test_param.legal_values ) # store possible cases, undefined whens will have no inputs + possible_cases = list(group.test_param.legal_values) # store possible cases, undefined whens will have no inputs # Must refresh when test_param changes group.test_param.refresh_on_change = True # And a set of possible cases for (value, case_inputs_source) in input_source.parse_when_input_sources(): case = ConditionalWhen() case.value = value - case.inputs = self.parse_input_elem( case_inputs_source, enctypes, context ) - group.cases.append( case ) + case.inputs = self.parse_input_elem(case_inputs_source, enctypes, context) + group.cases.append(case) try: - possible_cases.remove( case.value ) + possible_cases.remove(case.value) except: - log.warning( "Tool %s: a when tag has been defined for '%s (%s) --> %s', but does not appear to be selectable." % - ( self.id, group.name, group.test_param.name, case.value ) ) + log.warning("Tool %s: a when tag has been defined for '%s (%s) --> %s', but does not appear to be selectable." % + (self.id, group.name, group.test_param.name, case.value)) for unspecified_case in possible_cases: - log.warning( "Tool %s: a when tag has not been defined for '%s (%s) --> %s', assuming empty inputs." % - ( self.id, group.name, group.test_param.name, unspecified_case ) ) + log.warning("Tool %s: a when tag has not been defined for '%s (%s) --> %s', assuming empty inputs." % + (self.id, group.name, group.test_param.name, unspecified_case)) case = ConditionalWhen() case.value = unspecified_case case.inputs = odict() - group.cases.append( case ) + group.cases.append(case) rval[group.name] = group elif input_type == "section": group = Section() - group.name = input_source.get( "name" ) - group.title = input_source.get( "title" ) - group.help = input_source.get( "help", None ) - group.expanded = input_source.get_bool( "expanded", False ) + group.name = input_source.get("name") + group.title = input_source.get("title") + group.help = input_source.get("help", None) + group.expanded = input_source.get_bool("expanded", False) page_source = input_source.parse_nested_inputs_source() - group.inputs = self.parse_input_elem( page_source, enctypes, context ) + group.inputs = self.parse_input_elem(page_source, enctypes, context) rval[group.name] = group elif input_type == "upload_dataset": elem = input_source.elem() group = UploadDataset() - group.name = elem.get( "name" ) - group.title = elem.get( "title" ) - group.file_type_name = elem.get( 'file_type_name', group.file_type_name ) - group.default_file_type = elem.get( 'default_file_type', group.default_file_type ) - group.metadata_ref = elem.get( 'metadata_ref', group.metadata_ref ) - rval[ group.file_type_name ].refresh_on_change = True - rval[ group.file_type_name ].refresh_on_change_values = \ + group.name = elem.get("name") + group.title = elem.get("title") + group.file_type_name = elem.get('file_type_name', group.file_type_name) + group.default_file_type = elem.get('default_file_type', group.default_file_type) + group.metadata_ref = elem.get('metadata_ref', group.metadata_ref) + rval[group.file_type_name].refresh_on_change = True + rval[group.file_type_name].refresh_on_change_values = \ self.app.datatypes_registry.get_composite_extensions() group_page_source = XmlPageSource(elem) - group.inputs = self.parse_input_elem( group_page_source, enctypes, context ) - rval[ group.name ] = group + group.inputs = self.parse_input_elem(group_page_source, enctypes, context) + rval[group.name] = group elif input_type == "param": - param = self.parse_param_elem( input_source, enctypes, context ) + param = self.parse_param_elem(input_source, enctypes, context) rval[param.name] = param - if hasattr( param, 'data_ref' ): - param.ref_input = context[ param.data_ref ] - self.input_params.append( param ) + if hasattr(param, 'data_ref'): + param.ref_input = context[param.data_ref] + self.input_params.append(param) return rval - def parse_param_elem( self, input_source, enctypes, context ): + def parse_param_elem(self, input_source, enctypes, context): """ Parse a single "" element and return a ToolParameter instance. Also, if the parameter has a 'required_enctype' add it to the set enctypes. """ - param = ToolParameter.build( self, input_source ) + param = ToolParameter.build(self, input_source) param_enctype = param.get_required_enctype() if param_enctype: - enctypes.add( param_enctype ) + enctypes.add(param_enctype) # If parameter depends on any other paramters, we must refresh the # form when it changes for name in param.get_dependencies(): # Let it throw exception, but give some hint what the problem might be if name not in context: - log.error("Could not find dependency '%s' of parameter '%s' in tool %s" % (name, param.name, self.name) ) - context[ name ].refresh_on_change = True + log.error("Could not find dependency '%s' of parameter '%s' in tool %s" % (name, param.name, self.name)) + context[name].refresh_on_change = True return param - def populate_resource_parameters( self, tool_source ): - root = getattr( tool_source, 'root', None ) - if root is not None and hasattr( self.app, 'job_config' ) and hasattr( self.app.job_config, 'get_tool_resource_xml' ): - resource_xml = self.app.job_config.get_tool_resource_xml( root.get( 'id' ), self.tool_type ) + def populate_resource_parameters(self, tool_source): + root = getattr(tool_source, 'root', None) + if root is not None and hasattr(self.app, 'job_config') and hasattr(self.app.job_config, 'get_tool_resource_xml'): + resource_xml = self.app.job_config.get_tool_resource_xml(root.get('id'), self.tool_type) if resource_xml is not None: - inputs = root.find( 'inputs' ) + inputs = root.find('inputs') if inputs is None: - inputs = ElementTree.fromstring( '' ) - root.append( inputs ) - inputs.append( resource_xml ) + inputs = ElementTree.fromstring('') + root.append(inputs) + inputs.append(resource_xml) - def populate_tool_shed_info( self ): + def populate_tool_shed_info(self): if self.repository_id is not None and self.app.name == 'galaxy': - repository_id = self.app.security.decode_id( self.repository_id ) + repository_id = self.app.security.decode_id(self.repository_id) if hasattr(self.app, 'tool_shed_repository_cache'): - tool_shed_repository = self.app.tool_shed_repository_cache.get_installed_repository( repository_id=repository_id ) + tool_shed_repository = self.app.tool_shed_repository_cache.get_installed_repository(repository_id=repository_id) if tool_shed_repository: self.tool_shed = tool_shed_repository.tool_shed self.repository_name = tool_shed_repository.name @@ -1100,56 +1129,56 @@ def __inititalize_help(self): help_footer = "" help_text = tool_source.parse_help() if help_text is not None: - if self.repository_id and help_text.find( '.. image:: ' ) >= 0: + if self.repository_id and help_text.find('.. image:: ') >= 0: # Handle tool help image display for tools that are contained in repositories in the tool shed or installed into Galaxy. try: - help_text = tool_shed.util.shed_util_common.set_image_paths( self.app, self.repository_id, help_text ) + help_text = tool_shed.util.shed_util_common.set_image_paths(self.app, self.repository_id, help_text) except Exception: - log.exception( "Exception in parse_help, so images may not be properly displayed" ) + log.exception("Exception in parse_help, so images may not be properly displayed") try: - self.__help = Template( rst_to_html(help_text), input_encoding='utf-8', - output_encoding='utf-8', default_filters=[ 'decode.utf8' ], - encoding_errors='replace' ) + self.__help = Template(rst_to_html(help_text), input_encoding='utf-8', + output_encoding='utf-8', default_filters=['decode.utf8'], + encoding_errors='replace') except: - log.exception( "error in help for tool %s", self.name ) + log.exception("error in help for tool %s", self.name) # Handle deprecated multi-page help text in XML case. if hasattr(tool_source, "root"): help_elem = tool_source.root.find("help") help_header = help_text - help_pages = help_elem.findall( "page" ) + help_pages = help_elem.findall("page") # Multiple help page case if help_pages: for help_page in help_pages: - self.__help_by_page.append( help_page.text ) + self.__help_by_page.append(help_page.text) help_footer = help_footer + help_page.tail # Each page has to rendered all-together because of backreferences allowed by rst try: - self.__help_by_page = [ Template( rst_to_html( help_header + x + help_footer ), - input_encoding='utf-8', output_encoding='utf-8', - default_filters=[ 'decode.utf8' ], - encoding_errors='replace' ) - for x in self.__help_by_page ] + self.__help_by_page = [Template(rst_to_html(help_header + x + help_footer), + input_encoding='utf-8', output_encoding='utf-8', + default_filters=['decode.utf8'], + encoding_errors='replace') + for x in self.__help_by_page] except: - log.exception( "error in multi-page help for tool %s", self.name ) + log.exception("error in multi-page help for tool %s", self.name) # Pad out help pages to match npages ... could this be done better? - while len( self.__help_by_page ) < self.npages: - self.__help_by_page.append( self.__help ) + while len(self.__help_by_page) < self.npages: + self.__help_by_page.append(self.__help) - def find_output_def( self, name ): + def find_output_def(self, name): # name is JobToOutputDatasetAssociation name. # TODO: to defensive, just throw IndexError and catch somewhere # up that stack. - if ToolOutputCollectionPart.is_named_collection_part_name( name ): - collection_name, part = ToolOutputCollectionPart.split_output_name( name ) - collection_def = self.output_collections.get( collection_name, None ) + if ToolOutputCollectionPart.is_named_collection_part_name(name): + collection_name, part = ToolOutputCollectionPart.split_output_name(name) + collection_def = self.output_collections.get(collection_name, None) if not collection_def: return None - return collection_def.outputs.get( part, None ) + return collection_def.outputs.get(part, None) else: - return self.outputs.get( name, None ) + return self.outputs.get(name, None) - def check_workflow_compatible( self, tool_source ): + def check_workflow_compatible(self, tool_source): """ Determine if a tool can be used in workflows. External tools and the upload tool are currently not supported by workflows. @@ -1160,37 +1189,37 @@ def check_workflow_compatible( self, tool_source ): return False # This is probably the best bet for detecting external web tools # right now - if self.tool_type.startswith( 'data_source' ): + if self.tool_type.startswith('data_source'): return False if self.produces_collections_of_unknown_type: # Getting there... return False - if hasattr( tool_source, "root"): + if hasattr(tool_source, "root"): root = tool_source.root - if not string_as_bool( root.get( "workflow_compatible", "True" ) ): + if not string_as_bool(root.get("workflow_compatible", "True")): return False # TODO: Anyway to capture tools that dynamically change their own # outputs? return True - def new_state( self, trans ): + def new_state(self, trans): """ Create a new `DefaultToolState` for this tool. It will be initialized with default values for inputs. Grouping elements are filled in recursively. """ state = DefaultToolState() - state.initialize( trans, self ) + state.initialize(trans, self) return state - def get_param( self, key ): + def get_param(self, key): """ Returns the parameter named `key` or None if there is no such parameter. """ - return self.inputs.get( key, None ) + return self.inputs.get(key, None) def get_hook(self, name): """ @@ -1205,7 +1234,7 @@ def get_hook(self, name): return self.code_namespace[name] return None - def visit_inputs( self, values, callback ): + def visit_inputs(self, values, callback): """ Call the function `callback` on each parameter of this tool. Visits grouping parameters recursively and constructs unique prefixes for @@ -1215,34 +1244,34 @@ def visit_inputs( self, values, callback ): """ # HACK: Yet another hack around check_values -- WHY HERE? if self.check_values: - visit_input_values( self.inputs, values, callback ) + visit_input_values(self.inputs, values, callback) - def handle_input( self, trans, incoming, history=None ): + def handle_input(self, trans, incoming, history=None): """ Process incoming parameters for this tool from the dict `incoming`, update the tool state (or create if none existed), and either return to the form or execute the tool (only if 'execute' was clicked and there were no errors). """ - request_context = WorkRequestContext( app=trans.app, user=trans.user, history=history or trans.history ) + request_context = WorkRequestContext(app=trans.app, user=trans.user, history=history or trans.history) rerun_remap_job_id = None if 'rerun_remap_job_id' in incoming: try: - rerun_remap_job_id = trans.app.security.decode_id( incoming[ 'rerun_remap_job_id' ] ) + rerun_remap_job_id = trans.app.security.decode_id(incoming['rerun_remap_job_id']) except Exception as exception: - log.error( str( exception ) ) - raise exceptions.MessageException( 'Failure executing tool (attempting to rerun invalid job).' ) + log.error(str(exception)) + raise exceptions.MessageException('Failure executing tool (attempting to rerun invalid job).') # Fixed set of input parameters may correspond to any number of jobs. # Expand these out to individual parameters for given jobs (tool executions). - expanded_incomings, collection_info = expand_meta_parameters( trans, self, incoming ) + expanded_incomings, collection_info = expand_meta_parameters(trans, self, incoming) if not expanded_incomings: - raise exceptions.MessageException( 'Tool execution failed, trying to run a tool over an empty collection.' ) + raise exceptions.MessageException('Tool execution failed, trying to run a tool over an empty collection.') # Remapping a single job to many jobs doesn't make sense, so disable # remap if multi-runs of tools are being used. - if rerun_remap_job_id and len( expanded_incomings ) > 1: - raise exceptions.MessageException( 'Failure executing tool (cannot create multiple jobs when remapping existing job).' ) + if rerun_remap_job_id and len(expanded_incomings) > 1: + raise exceptions.MessageException('Failure executing tool (cannot create multiple jobs when remapping existing job).') # Process incoming data validation_timer = ExecutionTimer() @@ -1252,7 +1281,7 @@ def handle_input( self, trans, incoming, history=None ): params = {} errors = {} if self.input_translator: - self.input_translator.translate( expanded_incoming ) + self.input_translator.translate(expanded_incoming) if not self.check_values: # If `self.check_values` is false we don't do any checking or # processing on input This is used to pass raw values @@ -1261,38 +1290,38 @@ def handle_input( self, trans, incoming, history=None ): else: # Update state for all inputs on the current page taking new # values from `incoming`. - populate_state( request_context, self.inputs, expanded_incoming, params, errors ) + populate_state(request_context, self.inputs, expanded_incoming, params, errors) # If the tool provides a `validate_input` hook, call it. - validate_input = self.get_hook( 'validate_input' ) + validate_input = self.get_hook('validate_input') if validate_input: - validate_input( request_context, errors, params, self.inputs ) - all_errors.append( errors ) - all_params.append( params ) - log.debug( 'Validated and populated state for tool request %s' % validation_timer ) + validate_input(request_context, errors, params, self.inputs) + all_errors.append(errors) + all_params.append(params) + log.debug('Validated and populated state for tool request %s' % validation_timer) # If there were errors, we stay on the same page and display them - if any( all_errors ): - err_data = { key: value for d in all_errors for ( key, value ) in d.items() } - raise exceptions.MessageException( ', '.join( msg for msg in err_data.values() ), err_data=err_data ) + if any(all_errors): + err_data = {key: value for d in all_errors for (key, value) in d.items()} + raise exceptions.MessageException(', '.join(msg for msg in err_data.values()), err_data=err_data) else: - execution_tracker = execute_job( trans, self, all_params, history=request_context.history, rerun_remap_job_id=rerun_remap_job_id, collection_info=collection_info ) + execution_tracker = execute_job(trans, self, all_params, history=request_context.history, rerun_remap_job_id=rerun_remap_job_id, collection_info=collection_info) if execution_tracker.successful_jobs: - return dict( out_data=execution_tracker.output_datasets, - num_jobs=len( execution_tracker.successful_jobs ), - job_errors=execution_tracker.execution_errors, - jobs=execution_tracker.successful_jobs, - output_collections=execution_tracker.output_collections, - implicit_collections=execution_tracker.implicit_collections ) + return dict(out_data=execution_tracker.output_datasets, + num_jobs=len(execution_tracker.successful_jobs), + job_errors=execution_tracker.execution_errors, + jobs=execution_tracker.successful_jobs, + output_collections=execution_tracker.output_collections, + implicit_collections=execution_tracker.implicit_collections) else: - raise exceptions.MessageException( execution_tracker.execution_errors[ 0 ] ) + raise exceptions.MessageException(execution_tracker.execution_errors[0]) - def handle_single_execution( self, trans, rerun_remap_job_id, params, history, mapping_over_collection, execution_cache=None ): + def handle_single_execution(self, trans, rerun_remap_job_id, params, history, mapping_over_collection, execution_cache=None): """ Return a pair with whether execution is successful as well as either resulting output data or an error message indicating the problem. """ try: - job, out_data = self.execute( trans, incoming=params, history=history, rerun_remap_job_id=rerun_remap_job_id, mapping_over_collection=mapping_over_collection, execution_cache=execution_cache ) + job, out_data = self.execute(trans, incoming=params, history=history, rerun_remap_job_id=rerun_remap_job_id, mapping_over_collection=mapping_over_collection, execution_cache=execution_cache) except httpexceptions.HTTPFound as e: # if it's a paste redirect exception, pass it up the stack raise e @@ -1302,52 +1331,52 @@ def handle_single_execution( self, trans, rerun_remap_job_id, params, history, m log.exception('Exception caught while attempting tool execution:') message = 'Error executing tool: %s' % str(e) return False, message - if isinstance( out_data, odict ): + if isinstance(out_data, odict): return job, list(out_data.items()) else: - if isinstance( out_data, string_types ): + if isinstance(out_data, string_types): message = out_data else: message = 'Failure executing tool (invalid data returned from tool execution)' return False, message - def find_fieldstorage( self, x ): - if isinstance( x, FieldStorage ): - raise InterruptedUpload( None ) + def find_fieldstorage(self, x): + if isinstance(x, FieldStorage): + raise InterruptedUpload(None) elif isinstance(x, dict): - [ self.find_fieldstorage( y ) for y in x.values() ] + [self.find_fieldstorage(y) for y in x.values()] elif isinstance(x, list): - [ self.find_fieldstorage( y ) for y in x ] + [self.find_fieldstorage(y) for y in x] @property - def params_with_missing_data_table_entry( self ): + def params_with_missing_data_table_entry(self): """ Return all parameters that are dynamically generated select lists whose options require an entry not currently in the tool_data_table_conf.xml file. """ params = [] for input_param in self.input_params: - if isinstance( input_param, SelectToolParameter ) and input_param.is_dynamic: + if isinstance(input_param, SelectToolParameter) and input_param.is_dynamic: options = input_param.options if options and options.missing_tool_data_table_name and input_param not in params: - params.append( input_param ) + params.append(input_param) return params @property - def params_with_missing_index_file( self ): + def params_with_missing_index_file(self): """ Return all parameters that are dynamically generated select lists whose options refer to a missing .loc file. """ params = [] for input_param in self.input_params: - if isinstance( input_param, SelectToolParameter ) and input_param.is_dynamic: + if isinstance(input_param, SelectToolParameter) and input_param.is_dynamic: options = input_param.options if options and options.missing_index_file and input_param not in params: - params.append( input_param ) + params.append(input_param) return params - def get_static_param_values( self, trans ): + def get_static_param_values(self, trans): """ Returns a map of parameter names and values if the tool does not require any user input. Will raise an exception if any parameter @@ -1357,55 +1386,55 @@ def get_static_param_values( self, trans ): for key, param in self.inputs.items(): # BaseURLToolParameter is now a subclass of HiddenToolParameter, so # we must check if param is a BaseURLToolParameter first - if isinstance( param, BaseURLToolParameter ): - args[key] = param.get_initial_value( trans, None ) - elif isinstance( param, HiddenToolParameter ): - args[key] = model.User.expand_user_properties( trans.user, param.value ) + if isinstance(param, BaseURLToolParameter): + args[key] = param.get_initial_value(trans, None) + elif isinstance(param, HiddenToolParameter): + args[key] = model.User.expand_user_properties(trans.user, param.value) else: - raise Exception( "Unexpected parameter type" ) + raise Exception("Unexpected parameter type") return args - def execute( self, trans, incoming={}, set_output_hid=True, history=None, **kwargs ): + def execute(self, trans, incoming={}, set_output_hid=True, history=None, **kwargs): """ Execute the tool using parameter values in `incoming`. This just dispatches to the `ToolAction` instance specified by `self.tool_action`. In general this will create a `Job` that when run will build the tool's outputs, e.g. `DefaultToolAction`. """ - return self.tool_action.execute( self, trans, incoming=incoming, set_output_hid=set_output_hid, history=history, **kwargs ) + return self.tool_action.execute(self, trans, incoming=incoming, set_output_hid=set_output_hid, history=history, **kwargs) - def params_to_strings( self, params, app ): - return params_to_strings( self.inputs, params, app ) + def params_to_strings(self, params, app): + return params_to_strings(self.inputs, params, app) - def params_from_strings( self, params, app, ignore_errors=False ): - return params_from_strings( self.inputs, params, app, ignore_errors ) + def params_from_strings(self, params, app, ignore_errors=False): + return params_from_strings(self.inputs, params, app, ignore_errors) - def check_and_update_param_values( self, values, trans, update_values=True, workflow_building_mode=False ): + def check_and_update_param_values(self, values, trans, update_values=True, workflow_building_mode=False): """ Check that all parameters have values, and fill in with default values where necessary. This could be called after loading values from a database in case new parameters have been added. """ messages = {} - request_context = WorkRequestContext( app=trans.app, user=trans.user, history=trans.history, workflow_building_mode=workflow_building_mode ) + request_context = WorkRequestContext(app=trans.app, user=trans.user, history=trans.history, workflow_building_mode=workflow_building_mode) - def validate_inputs( input, value, error, parent, context, prefixed_name, prefixed_label, **kwargs ): + def validate_inputs(input, value, error, parent, context, prefixed_name, prefixed_label, **kwargs): if not error: - value, error = check_param( request_context, input, value, context ) + value, error = check_param(request_context, input, value, context) if error: if update_values: try: previous_value = value - value = input.get_initial_value( request_context, context ) - if not prefixed_name.startswith( '__' ): - messages[ prefixed_name ] = error if previous_value == value else '%s Using default: \'%s\'.' % ( error, value ) - parent[ input.name ] = value + value = input.get_initial_value(request_context, context) + if not prefixed_name.startswith('__'): + messages[prefixed_name] = error if previous_value == value else '%s Using default: \'%s\'.' % (error, value) + parent[input.name] = value except: - messages[ prefixed_name ] = 'Attempt to replace invalid value for \'%s\' failed.' % ( prefixed_label ) + messages[prefixed_name] = 'Attempt to replace invalid value for \'%s\' failed.' % (prefixed_label) else: - messages[ prefixed_name ] = error + messages[prefixed_name] = error - visit_input_values( self.inputs, values, validate_inputs ) + visit_input_values(self.inputs, values, validate_inputs) return messages def build_dependency_cache(self, **kwds): @@ -1420,7 +1449,7 @@ def build_dependency_cache(self, **kwds): **kwds ) - def build_dependency_shell_commands( self, job_directory=None, metadata=False ): + def build_dependency_shell_commands(self, job_directory=None, metadata=False): """ Return a list of commands to be run to populate the current environment to include this tools requirements. """ @@ -1456,7 +1485,7 @@ def tool_requirements_status(self): """ return self._view.get_requirements_status({self.id: self.tool_requirements}, self.installed_tool_dependencies) - def build_redirect_url_params( self, param_dict ): + def build_redirect_url_params(self, param_dict): """ Substitute parameter values into self.redirect_url_params """ @@ -1464,12 +1493,12 @@ def build_redirect_url_params( self, param_dict ): return redirect_url_params = None # Substituting parameter values into the url params - redirect_url_params = fill_template( self.redirect_url_params, context=param_dict ) + redirect_url_params = fill_template(self.redirect_url_params, context=param_dict) # Remove newlines - redirect_url_params = redirect_url_params.replace( "\n", " " ).replace( "\r", " " ) + redirect_url_params = redirect_url_params.replace("\n", " ").replace("\r", " ") return redirect_url_params - def parse_redirect_url( self, data, param_dict ): + def parse_redirect_url(self, data, param_dict): """ Parse the REDIRECT_URL tool param. Tools that send data to an external application via a redirect must include the following 3 tool params: @@ -1482,62 +1511,62 @@ def parse_redirect_url( self, data, param_dict ): 3) GALAXY_URL - the url to which the external application may post data as a response """ - redirect_url = param_dict.get( 'REDIRECT_URL' ) - redirect_url_params = self.build_redirect_url_params( param_dict ) + redirect_url = param_dict.get('REDIRECT_URL') + redirect_url_params = self.build_redirect_url_params(param_dict) # Add the parameters to the redirect url. We're splitting the param # string on '**^**' because the self.parse() method replaced white # space with that separator. - params = redirect_url_params.split( '**^**' ) + params = redirect_url_params.split('**^**') rup_dict = {} for param in params: - p_list = param.split( '=' ) + p_list = param.split('=') p_name = p_list[0] p_val = p_list[1] - rup_dict[ p_name ] = p_val - DATA_URL = param_dict.get( 'DATA_URL', None ) + rup_dict[p_name] = p_val + DATA_URL = param_dict.get('DATA_URL', None) assert DATA_URL is not None, "DATA_URL parameter missing in tool config." - DATA_URL += "/%s/display" % str( data.id ) + DATA_URL += "/%s/display" % str(data.id) redirect_url += "?DATA_URL=%s" % DATA_URL # Add the redirect_url_params to redirect_url for p_name in rup_dict: - redirect_url += "&%s=%s" % ( p_name, rup_dict[ p_name ] ) + redirect_url += "&%s=%s" % (p_name, rup_dict[p_name]) # Add the current user email to redirect_url if data.history.user: - USERNAME = str( data.history.user.email ) + USERNAME = str(data.history.user.email) else: USERNAME = 'Anonymous' redirect_url += "&USERNAME=%s" % USERNAME return redirect_url - def call_hook( self, hook_name, *args, **kwargs ): + def call_hook(self, hook_name, *args, **kwargs): """ Call the custom code hook function identified by 'hook_name' if any, and return the results """ try: - code = self.get_hook( hook_name ) + code = self.get_hook(hook_name) if code: - return code( *args, **kwargs ) + return code(*args, **kwargs) except Exception as e: original_message = '' - if len( e.args ): + if len(e.args): original_message = e.args[0] - e.args = ( "Error in '%s' hook '%s', original message: %s" % ( self.name, hook_name, original_message ), ) + e.args = ("Error in '%s' hook '%s', original message: %s" % (self.name, hook_name, original_message), ) raise - def exec_before_job( self, app, inp_data, out_data, param_dict={} ): + def exec_before_job(self, app, inp_data, out_data, param_dict={}): pass - def exec_after_process( self, app, inp_data, out_data, param_dict, job=None ): + def exec_after_process(self, app, inp_data, out_data, param_dict, job=None): pass - def job_failed( self, job_wrapper, message, exception=False ): + def job_failed(self, job_wrapper, message, exception=False): """ Called when a job has failed """ pass - def collect_child_datasets( self, output, job_working_directory ): + def collect_child_datasets(self, output, job_working_directory): """ Look for child dataset files, create HDA and attach to parent. """ @@ -1547,9 +1576,9 @@ def collect_child_datasets( self, output, job_working_directory ): for name, outdata in output.items(): filenames = [] if 'new_file_path' in self.app.config.collect_outputs_from: - filenames.extend( glob.glob(os.path.join(self.app.config.new_file_path, "child_%i_*" % outdata.id) ) ) + filenames.extend(glob.glob(os.path.join(self.app.config.new_file_path, "child_%i_*" % outdata.id))) if 'job_working_directory' in self.app.config.collect_outputs_from: - filenames.extend( glob.glob(os.path.join(job_working_directory, "child_%i_*" % outdata.id) ) ) + filenames.extend(glob.glob(os.path.join(job_working_directory, "child_%i_*" % outdata.id))) for filename in filenames: if name not in children: children[name] = {} @@ -1561,20 +1590,20 @@ def collect_child_datasets( self, output, job_working_directory ): else: visible = False ext = fields[4].lower() - child_dataset = self.app.model.HistoryDatasetAssociation( extension=ext, - parent_id=outdata.id, - designation=designation, - visible=visible, - dbkey=outdata.dbkey, - create_dataset=True, - sa_session=self.sa_session ) - self.app.security_agent.copy_dataset_permissions( outdata.dataset, child_dataset.dataset ) + child_dataset = self.app.model.HistoryDatasetAssociation(extension=ext, + parent_id=outdata.id, + designation=designation, + visible=visible, + dbkey=outdata.dbkey, + create_dataset=True, + sa_session=self.sa_session) + self.app.security_agent.copy_dataset_permissions(outdata.dataset, child_dataset.dataset) # Move data from temp location to dataset location self.app.object_store.update_from_file(child_dataset.dataset, file_name=filename, create=True) - self.sa_session.add( child_dataset ) + self.sa_session.add(child_dataset) self.sa_session.flush() child_dataset.set_size() - child_dataset.name = "Secondary Dataset (%s)" % ( designation ) + child_dataset.name = "Secondary Dataset (%s)" % (designation) child_dataset.init_meta() child_dataset.set_meta() child_dataset.set_peek() @@ -1584,12 +1613,12 @@ def collect_child_datasets( self, output, job_working_directory ): job = assoc.job break if job: - assoc = self.app.model.JobToOutputDatasetAssociation( '__new_child_file_%s|%s__' % ( name, designation ), child_dataset ) + assoc = self.app.model.JobToOutputDatasetAssociation('__new_child_file_%s|%s__' % (name, designation), child_dataset) assoc.job = job - self.sa_session.add( assoc ) + self.sa_session.add(assoc) self.sa_session.flush() child_dataset.state = outdata.state - self.sa_session.add( child_dataset ) + self.sa_session.add(child_dataset) self.sa_session.flush() # Add child to return dict children[name][designation] = child_dataset @@ -1599,131 +1628,131 @@ def collect_child_datasets( self, output, job_working_directory ): if outdata == dataset: continue # Create new child dataset - child_data = child_dataset.copy( parent_id=dataset.id ) - self.sa_session.add( child_data ) + child_data = child_dataset.copy(parent_id=dataset.id) + self.sa_session.add(child_data) self.sa_session.flush() return children - def collect_primary_datasets( self, output, job_working_directory, input_ext, input_dbkey="?" ): + def collect_primary_datasets(self, output, tool_provided_metadata, job_working_directory, input_ext, input_dbkey="?"): """ Find any additional datasets generated by a tool and attach (for cases where number of outputs is not known in advance). """ - return output_collect.collect_primary_datasets( self, output, job_working_directory, input_ext, input_dbkey=input_dbkey ) + return output_collect.collect_primary_datasets(self, output, tool_provided_metadata, job_working_directory, input_ext, input_dbkey=input_dbkey) - def collect_dynamic_collections( self, output, **kwds ): + def collect_dynamic_collections(self, output, tool_provided_metadata, **kwds): """ Find files corresponding to dynamically structured collections. """ - return output_collect.collect_dynamic_collections( self, output, **kwds ) + return output_collect.collect_dynamic_collections(self, output, tool_provided_metadata, **kwds) def to_archive(self): tool = self tarball_files = [] temp_files = [] - tool_xml = open( os.path.abspath( tool.config_file ), 'r' ).read() + tool_xml = open(os.path.abspath(tool.config_file), 'r').read() # Retrieve tool help images and rewrite the tool's xml into a temporary file with the path # modified to be relative to the repository root. image_found = False if tool.help is not None: tool_help = tool.help._source # Check each line of the rendered tool help for an image tag that points to a location under static/ - for help_line in tool_help.split( '\n' ): - image_regex = re.compile( 'img alt="[^"]+" src="\${static_path}/([^"]+)"' ) - matches = re.search( image_regex, help_line ) + for help_line in tool_help.split('\n'): + image_regex = re.compile('img alt="[^"]+" src="\${static_path}/([^"]+)"') + matches = re.search(image_regex, help_line) if matches is not None: tool_help_image = matches.group(1) tarball_path = tool_help_image - filesystem_path = os.path.abspath( os.path.join( self.app.config.root, 'static', tool_help_image ) ) - if os.path.exists( filesystem_path ): - tarball_files.append( ( filesystem_path, tarball_path ) ) + filesystem_path = os.path.abspath(os.path.join(self.app.config.root, 'static', tool_help_image)) + if os.path.exists(filesystem_path): + tarball_files.append((filesystem_path, tarball_path)) image_found = True - tool_xml = tool_xml.replace( '${static_path}/%s' % tarball_path, tarball_path ) + tool_xml = tool_xml.replace('${static_path}/%s' % tarball_path, tarball_path) # If one or more tool help images were found, add the modified tool XML to the tarball instead of the original. if image_found: - fd, new_tool_config = tempfile.mkstemp( suffix='.xml' ) - os.close( fd ) - open( new_tool_config, 'w' ).write( tool_xml ) - tool_tup = ( os.path.abspath( new_tool_config ), os.path.split( tool.config_file )[-1] ) - temp_files.append( os.path.abspath( new_tool_config ) ) + fd, new_tool_config = tempfile.mkstemp(suffix='.xml') + os.close(fd) + open(new_tool_config, 'w').write(tool_xml) + tool_tup = (os.path.abspath(new_tool_config), os.path.split(tool.config_file)[-1]) + temp_files.append(os.path.abspath(new_tool_config)) else: - tool_tup = ( os.path.abspath( tool.config_file ), os.path.split( tool.config_file )[-1] ) - tarball_files.append( tool_tup ) + tool_tup = (os.path.abspath(tool.config_file), os.path.split(tool.config_file)[-1]) + tarball_files.append(tool_tup) # TODO: This feels hacky. tool_command = tool.command.strip().split()[0] - tool_path = os.path.dirname( os.path.abspath( tool.config_file ) ) + tool_path = os.path.dirname(os.path.abspath(tool.config_file)) # Add the tool XML to the tuple that will be used to populate the tarball. - if os.path.exists( os.path.join( tool_path, tool_command ) ): - tarball_files.append( ( os.path.join( tool_path, tool_command ), tool_command ) ) + if os.path.exists(os.path.join(tool_path, tool_command)): + tarball_files.append((os.path.join(tool_path, tool_command), tool_command)) # Find and add macros and code files. - for external_file in tool.get_externally_referenced_paths( os.path.abspath( tool.config_file ) ): - external_file_abspath = os.path.abspath( os.path.join( tool_path, external_file ) ) - tarball_files.append( ( external_file_abspath, external_file ) ) - if os.path.exists( os.path.join( tool_path, "Dockerfile" ) ): - tarball_files.append( ( os.path.join( tool_path, "Dockerfile" ), "Dockerfile" ) ) + for external_file in tool.get_externally_referenced_paths(os.path.abspath(tool.config_file)): + external_file_abspath = os.path.abspath(os.path.join(tool_path, external_file)) + tarball_files.append((external_file_abspath, external_file)) + if os.path.exists(os.path.join(tool_path, "Dockerfile")): + tarball_files.append((os.path.join(tool_path, "Dockerfile"), "Dockerfile")) # Find tests, and check them for test data. tests = tool.tests if tests is not None: for test in tests: # Add input file tuples to the list. for input in test.inputs: - for input_value in test.inputs[ input ]: - input_filename = str( input_value ) - input_path = os.path.abspath( os.path.join( 'test-data', input_filename ) ) - if os.path.exists( input_path ): - td_tup = ( input_path, os.path.join( 'test-data', input_filename ) ) - tarball_files.append( td_tup ) + for input_value in test.inputs[input]: + input_filename = str(input_value) + input_path = os.path.abspath(os.path.join('test-data', input_filename)) + if os.path.exists(input_path): + td_tup = (input_path, os.path.join('test-data', input_filename)) + tarball_files.append(td_tup) # And add output file tuples to the list. for label, filename, _ in test.outputs: - output_filepath = os.path.abspath( os.path.join( 'test-data', filename ) ) - if os.path.exists( output_filepath ): - td_tup = ( output_filepath, os.path.join( 'test-data', filename ) ) - tarball_files.append( td_tup ) + output_filepath = os.path.abspath(os.path.join('test-data', filename)) + if os.path.exists(output_filepath): + td_tup = (output_filepath, os.path.join('test-data', filename)) + tarball_files.append(td_tup) for param in tool.input_params: # Check for tool data table definitions. - if hasattr( param, 'options' ): - if hasattr( param.options, 'tool_data_table' ): + if hasattr(param, 'options'): + if hasattr(param.options, 'tool_data_table'): data_table = param.options.tool_data_table - if hasattr( data_table, 'filenames' ): + if hasattr(data_table, 'filenames'): data_table_definitions = [] for data_table_filename in data_table.filenames: # FIXME: from_shed_config seems to always be False. - if not data_table.filenames[ data_table_filename ][ 'from_shed_config' ]: - tar_file = data_table.filenames[ data_table_filename ][ 'filename' ] + '.sample' - sample_file = os.path.join( data_table.filenames[ data_table_filename ][ 'tool_data_path' ], - tar_file ) + if not data_table.filenames[data_table_filename]['from_shed_config']: + tar_file = data_table.filenames[data_table_filename]['filename'] + '.sample' + sample_file = os.path.join(data_table.filenames[data_table_filename]['tool_data_path'], + tar_file) # Use the .sample file, if one exists. If not, skip this data table. - if os.path.exists( sample_file ): - tarfile_path, tarfile_name = os.path.split( tar_file ) - tarfile_path = os.path.join( 'tool-data', tarfile_name ) - tarball_files.append( ( sample_file, tarfile_path ) ) - data_table_definitions.append( data_table.xml_string ) - if len( data_table_definitions ) > 0: + if os.path.exists(sample_file): + tarfile_path, tarfile_name = os.path.split(tar_file) + tarfile_path = os.path.join('tool-data', tarfile_name) + tarball_files.append((sample_file, tarfile_path)) + data_table_definitions.append(data_table.xml_string) + if len(data_table_definitions) > 0: # Put the data table definition XML in a temporary file. table_definition = '\n\n %s' - table_definition = table_definition % '\n'.join( data_table_definitions ) + table_definition = table_definition % '\n'.join(data_table_definitions) fd, table_conf = tempfile.mkstemp() - os.close( fd ) - open( table_conf, 'w' ).write( table_definition ) - tarball_files.append( ( table_conf, os.path.join( 'tool-data', 'tool_data_table_conf.xml.sample' ) ) ) - temp_files.append( table_conf ) + os.close(fd) + open(table_conf, 'w').write(table_definition) + tarball_files.append((table_conf, os.path.join('tool-data', 'tool_data_table_conf.xml.sample'))) + temp_files.append(table_conf) # Create the tarball. - fd, tarball_archive = tempfile.mkstemp( suffix='.tgz' ) - os.close( fd ) - tarball = tarfile.open( name=tarball_archive, mode='w:gz' ) + fd, tarball_archive = tempfile.mkstemp(suffix='.tgz') + os.close(fd) + tarball = tarfile.open(name=tarball_archive, mode='w:gz') # Add the files from the previously generated list. for fspath, tarpath in tarball_files: - tarball.add( fspath, arcname=tarpath ) + tarball.add(fspath, arcname=tarpath) tarball.close() # Delete any temporary files that were generated. for temp_file in temp_files: - os.remove( temp_file ) + os.remove(temp_file) return tarball_archive - def to_dict( self, trans, link_details=False, io_details=False ): + def to_dict(self, trans, link_details=False, io_details=False): """ Returns dict of tool. """ # Basic information - tool_dict = super( Tool, self ).to_dict() + tool_dict = super(Tool, self).to_dict() tool_dict["edam_operations"] = self.edam_operations tool_dict["edam_topics"] = self.edam_topics @@ -1739,27 +1768,27 @@ def to_dict( self, trans, link_details=False, io_details=False ): # If an admin user, expose the path to the actual tool config XML file. if trans.user_is_admin(): - tool_dict[ 'config_file' ] = os.path.abspath( self.config_file ) + tool_dict['config_file'] = os.path.abspath(self.config_file) # Add link details. if link_details: # Add details for creating a hyperlink to the tool. - if not isinstance( self, DataSourceTool ): - link = url_for( controller='tool_runner', tool_id=self.id ) + if not isinstance(self, DataSourceTool): + link = url_for(controller='tool_runner', tool_id=self.id) else: - link = url_for( controller='tool_runner', action='data_source_redirect', tool_id=self.id ) + link = url_for(controller='tool_runner', action='data_source_redirect', tool_id=self.id) # Basic information - tool_dict.update( { 'link': link, - 'min_width': self.uihints.get( 'minwidth', -1 ), - 'target': self.target } ) + tool_dict.update({'link': link, + 'min_width': self.uihints.get('minwidth', -1), + 'target': self.target}) # Add input and output details. if io_details: - tool_dict[ 'inputs' ] = [ input.to_dict( trans ) for input in self.inputs.values() ] - tool_dict[ 'outputs' ] = [ output.to_dict( app=self.app ) for output in self.outputs.values() ] + tool_dict['inputs'] = [input.to_dict(trans) for input in self.inputs.values()] + tool_dict['outputs'] = [output.to_dict(app=self.app) for output in self.outputs.values()] - tool_dict[ 'panel_section_id' ], tool_dict[ 'panel_section_name' ] = self.get_panel_section() + tool_dict['panel_section_id'], tool_dict['panel_section_name'] = self.get_panel_section() tool_class = self.__class__ regular_form = tool_class == Tool or isinstance(self, DatabaseOperationTool) @@ -1767,68 +1796,68 @@ def to_dict( self, trans, link_details=False, io_details=False ): return tool_dict - def to_json( self, trans, kwd={}, job=None, workflow_building_mode=False ): + def to_json(self, trans, kwd={}, job=None, workflow_building_mode=False): """ Recursively creates a tool dictionary containing repeats, dynamic options and updated states. """ - history_id = kwd.get( 'history_id', None ) + history_id = kwd.get('history_id', None) history = None try: if history_id is not None: - history = self.history_manager.get_owned( trans.security.decode_id( history_id ), trans.user, current_history=trans.history ) + history = self.history_manager.get_owned(trans.security.decode_id(history_id), trans.user, current_history=trans.history) else: history = trans.get_history() if history is None and job is not None: - history = self.history_manager.get_owned( job.history.id, trans.user, current_history=trans.history ) + history = self.history_manager.get_owned(job.history.id, trans.user, current_history=trans.history) if history is None: - raise exceptions.MessageException( 'History unavailable. Please specify a valid history id' ) + raise exceptions.MessageException('History unavailable. Please specify a valid history id') except Exception as e: - raise exceptions.MessageException( '[history_id=%s] Failed to retrieve history. %s.' % ( history_id, str( e ) ) ) + raise exceptions.MessageException('[history_id=%s] Failed to retrieve history. %s.' % (history_id, str(e))) # build request context - request_context = WorkRequestContext( app=trans.app, user=trans.user, history=history, workflow_building_mode=workflow_building_mode ) + request_context = WorkRequestContext(app=trans.app, user=trans.user, history=history, workflow_building_mode=workflow_building_mode) # load job parameters into incoming tool_message = '' tool_warnings = '' if job: try: - job_params = job.get_param_values( self.app, ignore_errors=True ) - tool_warnings = self.check_and_update_param_values( job_params, request_context, update_values=True ) - self._map_source_to_history( request_context, self.inputs, job_params ) - tool_message = self._compare_tool_version( job ) - params_to_incoming( kwd, self.inputs, job_params, self.app ) + job_params = job.get_param_values(self.app, ignore_errors=True) + tool_warnings = self.check_and_update_param_values(job_params, request_context, update_values=True) + self._map_source_to_history(request_context, self.inputs, job_params) + tool_message = self._compare_tool_version(job) + params_to_incoming(kwd, self.inputs, job_params, self.app) except Exception as e: - raise exceptions.MessageException( str( e ) ) + raise exceptions.MessageException(str(e)) # create parameter object - params = galaxy.util.Params( kwd, sanitize=False ) + params = Params(kwd, sanitize=False) # expand incoming parameters (parameters might trigger multiple tool executions, # here we select the first execution only in order to resolve dynamic parameters) - expanded_incomings, _ = expand_meta_parameters( trans, self, params.__dict__ ) + expanded_incomings, _ = expand_meta_parameters(trans, self, params.__dict__) if expanded_incomings: - params.__dict__ = expanded_incomings[ 0 ] + params.__dict__ = expanded_incomings[0] # do param translation here, used by datasource tools if self.input_translator: - self.input_translator.translate( params ) + self.input_translator.translate(params) # create tool state state_inputs = {} state_errors = {} - populate_state( request_context, self.inputs, params.__dict__, state_inputs, state_errors ) + populate_state(request_context, self.inputs, params.__dict__, state_inputs, state_errors) # create tool model - tool_model = self.to_dict( request_context ) - tool_model[ 'inputs' ] = [] - self.populate_model( request_context, self.inputs, state_inputs, tool_model[ 'inputs' ] ) + tool_model = self.to_dict(request_context) + tool_model['inputs'] = [] + self.populate_model(request_context, self.inputs, state_inputs, tool_model['inputs']) # create tool help tool_help = '' if self.help: - tool_help = self.help.render( static_path=url_for( '/static' ), host_url=url_for( '/', qualified=True ) ) - tool_help = unicodify( tool_help, 'utf-8' ) + tool_help = self.help.render(static_path=url_for('/static'), host_url=url_for('/', qualified=True)) + tool_help = unicodify(tool_help, 'utf-8') # create tool versions tool_versions = self.lineage.tool_versions if self.lineage else [] # lineage may be `None` if tool is not loaded into tool panel @@ -1837,81 +1866,81 @@ def to_json( self, trans, kwd={}, job=None, workflow_building_mode=False ): tool_model.update({ 'id' : self.id, 'help' : tool_help, - 'citations' : bool( self.citations ), + 'citations' : bool(self.citations), 'biostar_url' : self.app.config.biostar_url, - 'sharable_url' : self.tool_shed_repository.get_sharable_url( self.app ) if self.tool_shed_repository else None, + 'sharable_url' : self.tool_shed_repository.get_sharable_url(self.app) if self.tool_shed_repository else None, 'message' : tool_message, 'warnings' : tool_warnings, 'versions' : tool_versions, - 'requirements' : [ { 'name' : r.name, 'version' : r.version } for r in self.requirements ], + 'requirements' : [{'name' : r.name, 'version' : r.version} for r in self.requirements], 'errors' : state_errors, - 'state_inputs' : params_to_strings( self.inputs, state_inputs, self.app ), - 'job_id' : trans.security.encode_id( job.id ) if job else None, - 'job_remap' : self._get_job_remap( job ), - 'history_id' : trans.security.encode_id( history.id ), + 'state_inputs' : params_to_strings(self.inputs, state_inputs, self.app), + 'job_id' : trans.security.encode_id(job.id) if job else None, + 'job_remap' : self._get_job_remap(job), + 'history_id' : trans.security.encode_id(history.id), 'display' : self.display_interface, - 'action' : url_for( self.action ), + 'action' : url_for(self.action), 'method' : self.method, 'enctype' : self.enctype }) return tool_model - def populate_model( self, request_context, inputs, state_inputs, group_inputs, other_values=None ): + def populate_model(self, request_context, inputs, state_inputs, group_inputs, other_values=None): """ Populates the tool model consumed by the client form builder. """ - other_values = ExpressionContext( state_inputs, other_values ) - for input_index, input in enumerate( inputs.values() ): + other_values = ExpressionContext(state_inputs, other_values) + for input_index, input in enumerate(inputs.values()): tool_dict = None - group_state = state_inputs.get( input.name, {} ) + group_state = state_inputs.get(input.name, {}) if input.type == 'repeat': - tool_dict = input.to_dict( request_context ) - group_cache = tool_dict[ 'cache' ] = {} - for i in range( len( group_state ) ): - group_cache[ i ] = [] - self.populate_model( request_context, input.inputs, group_state[ i ], group_cache[ i ], other_values ) + tool_dict = input.to_dict(request_context) + group_cache = tool_dict['cache'] = {} + for i in range(len(group_state)): + group_cache[i] = [] + self.populate_model(request_context, input.inputs, group_state[i], group_cache[i], other_values) elif input.type == 'conditional': - tool_dict = input.to_dict( request_context ) + tool_dict = input.to_dict(request_context) if 'test_param' in tool_dict: - test_param = tool_dict[ 'test_param' ] - test_param[ 'value' ] = input.test_param.value_to_basic( group_state.get( test_param[ 'name' ], input.test_param.get_initial_value( request_context, other_values ) ), self.app ) - test_param[ 'text_value' ] = input.test_param.value_to_display_text( test_param[ 'value' ] ) - for i in range( len( tool_dict['cases'] ) ): + test_param = tool_dict['test_param'] + test_param['value'] = input.test_param.value_to_basic(group_state.get(test_param['name'], input.test_param.get_initial_value(request_context, other_values)), self.app) + test_param['text_value'] = input.test_param.value_to_display_text(test_param['value']) + for i in range(len(tool_dict['cases'])): current_state = {} - if i == group_state.get( '__current_case__' ): + if i == group_state.get('__current_case__'): current_state = group_state - self.populate_model( request_context, input.cases[ i ].inputs, current_state, tool_dict[ 'cases' ][ i ][ 'inputs' ], other_values ) + self.populate_model(request_context, input.cases[i].inputs, current_state, tool_dict['cases'][i]['inputs'], other_values) elif input.type == 'section': - tool_dict = input.to_dict( request_context ) - self.populate_model( request_context, input.inputs, group_state, tool_dict[ 'inputs' ], other_values ) + tool_dict = input.to_dict(request_context) + self.populate_model(request_context, input.inputs, group_state, tool_dict['inputs'], other_values) else: try: - initial_value = input.get_initial_value( request_context, other_values ) - tool_dict = input.to_dict( request_context, other_values=other_values ) - tool_dict[ 'value' ] = input.value_to_basic( state_inputs.get( input.name, initial_value ), self.app, use_security=True ) - tool_dict[ 'default_value' ] = input.value_to_basic( initial_value, self.app, use_security=True ) - tool_dict[ 'text_value' ] = input.value_to_display_text( tool_dict[ 'value' ] ) + initial_value = input.get_initial_value(request_context, other_values) + tool_dict = input.to_dict(request_context, other_values=other_values) + tool_dict['value'] = input.value_to_basic(state_inputs.get(input.name, initial_value), self.app, use_security=True) + tool_dict['default_value'] = input.value_to_basic(initial_value, self.app, use_security=True) + tool_dict['text_value'] = input.value_to_display_text(tool_dict['value']) except Exception as e: - tool_dict = input.to_dict( request_context ) - log.exception('tools::to_json() - Skipping parameter expansion \'%s\': %s.' % ( input.name, e ) ) + tool_dict = input.to_dict(request_context) + log.exception('tools::to_json() - Skipping parameter expansion \'%s\': %s.' % (input.name, e)) pass - if input_index >= len( group_inputs ): - group_inputs.append( tool_dict ) + if input_index >= len(group_inputs): + group_inputs.append(tool_dict) else: - group_inputs[ input_index ] = tool_dict + group_inputs[input_index] = tool_dict - def _get_job_remap( self, job): + def _get_job_remap(self, job): if job: if job.state == job.states.ERROR: try: - if [ hda.dependent_jobs for hda in [ jtod.dataset for jtod in job.output_datasets ] if hda.dependent_jobs ]: + if [hda.dependent_jobs for hda in [jtod.dataset for jtod in job.output_datasets] if hda.dependent_jobs]: return True except Exception as exception: - log.error( str( exception ) ) + log.error(str(exception)) pass return False - def _map_source_to_history( self, trans, tool_inputs, params ): + def _map_source_to_history(self, trans, tool_inputs, params): # Need to remap dataset parameters. Job parameters point to original # dataset used; parameter should be the analygous dataset in the # current history. @@ -1920,53 +1949,53 @@ def _map_source_to_history( self, trans, tool_inputs, params ): # Create index for hdas. hda_source_dict = {} for hda in history.datasets: - key = '%s_%s' % ( hda.hid, hda.dataset.id ) - hda_source_dict[ hda.dataset.id ] = hda_source_dict[ key ] = hda + key = '%s_%s' % (hda.hid, hda.dataset.id) + hda_source_dict[hda.dataset.id] = hda_source_dict[key] = hda # Ditto for dataset collections. hdca_source_dict = {} for hdca in history.dataset_collections: - key = '%s_%s' % ( hdca.hid, hdca.collection.id ) - hdca_source_dict[ hdca.collection.id ] = hdca_source_dict[ key ] = hdca + key = '%s_%s' % (hdca.hid, hdca.collection.id) + hdca_source_dict[hdca.collection.id] = hdca_source_dict[key] = hdca # Map dataset or collection to current history - def map_to_history( value ): + def map_to_history(value): id = None source = None - if isinstance( value, self.app.model.HistoryDatasetAssociation ): + if isinstance(value, self.app.model.HistoryDatasetAssociation): id = value.dataset.id source = hda_source_dict - elif isinstance( value, self.app.model.HistoryDatasetCollectionAssociation ): + elif isinstance(value, self.app.model.HistoryDatasetCollectionAssociation): id = value.collection.id source = hdca_source_dict else: return None - key = '%s_%s' % ( value.hid, id ) + key = '%s_%s' % (value.hid, id) if key in source: - return source[ key ] + return source[key] elif id in source: - return source[ id ] + return source[id] else: return None - def mapping_callback( input, value, **kwargs ): - if isinstance( input, DataToolParameter ): + def mapping_callback(input, value, **kwargs): + if isinstance(input, DataToolParameter): if isinstance(value, list): values = [] for val in value: - new_val = map_to_history( val ) + new_val = map_to_history(val) if new_val: - values.append( new_val ) + values.append(new_val) else: - values.append( val ) + values.append(val) return values else: - return map_to_history( value ) - elif isinstance( input, DataCollectionToolParameter ): - return map_to_history( value ) - visit_input_values( tool_inputs, params, mapping_callback ) + return map_to_history(value) + elif isinstance(input, DataCollectionToolParameter): + return map_to_history(value) + visit_input_values(tool_inputs, params, mapping_callback) - def _compare_tool_version( self, job ): + def _compare_tool_version(self, job): """ Compares a tool version with the tool version from a job (from ToolRunner). """ @@ -1974,38 +2003,38 @@ def _compare_tool_version( self, job ): tool_version = job.tool_version message = '' try: - select_field, tools, tool = self.app.toolbox.get_tool_components( tool_id, tool_version=tool_version, get_loaded_tools_by_lineage=False, set_selected=True ) + select_field, tools, tool = self.app.toolbox.get_tool_components(tool_id, tool_version=tool_version, get_loaded_tools_by_lineage=False, set_selected=True) if tool is None: - raise exceptions.MessageException( 'This dataset was created by an obsolete tool (%s). Can\'t re-run.' % tool_id ) - if ( self.id != tool_id and self.old_id != tool_id ) or self.version != tool_version: + raise exceptions.MessageException('This dataset was created by an obsolete tool (%s). Can\'t re-run.' % tool_id) + if (self.id != tool_id and self.old_id != tool_id) or self.version != tool_version: if self.id == tool_id: if tool_version is None: # for some reason jobs don't always keep track of the tool version. message = '' else: message = 'This job was run with tool version "%s", which is not available. ' % tool_version - if len( tools ) > 1: + if len(tools) > 1: message += 'You can re-run the job with the selected tool or choose another version of the tool.' else: message += 'You can re-run the job with this tool version, which is a different version of the original tool.' else: - new_tool_shed_url = '%s/%s/' % ( tool.tool_shed_repository.get_sharable_url( tool.app ), tool.tool_shed_repository.changeset_revision ) - old_tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry( self.app, tool_id.split( '/repos/' )[ 0 ] ) - old_tool_shed_url = '%s/view/%s/%s/' % ( old_tool_shed_url, tool.repository_owner, tool.repository_name ) - message = 'This job was run with tool id \"%s\", version "%s", which is not available. ' % ( old_tool_shed_url, tool_id, tool_version ) - if len( tools ) > 1: - message += 'You can re-run the job with the selected tool id \"%s\" or choose another derivation of the tool.' % ( new_tool_shed_url, self.id ) + new_tool_shed_url = '%s/%s/' % (tool.tool_shed_repository.get_sharable_url(tool.app), tool.tool_shed_repository.changeset_revision) + old_tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry(self.app, tool_id.split('/repos/')[0]) + old_tool_shed_url = '%s/view/%s/%s/' % (old_tool_shed_url, tool.repository_owner, tool.repository_name) + message = 'This job was run with tool id \"%s\", version "%s", which is not available. ' % (old_tool_shed_url, tool_id, tool_version) + if len(tools) > 1: + message += 'You can re-run the job with the selected tool id \"%s\" or choose another derivation of the tool.' % (new_tool_shed_url, self.id) else: - message += 'You can re-run the job with tool id \"%s\", which is a derivation of the original tool.' % ( new_tool_shed_url, self.id ) + message += 'You can re-run the job with tool id \"%s\", which is a derivation of the original tool.' % (new_tool_shed_url, self.id) except Exception as e: - raise exceptions.MessageException( str( e ) ) + raise exceptions.MessageException(str(e)) return message - def get_default_history_by_trans( self, trans, create=False ): - return trans.get_history( create=create ) + def get_default_history_by_trans(self, trans, create=False): + return trans.get_history(create=create) @classmethod - def get_externally_referenced_paths( self, path ): + def get_externally_referenced_paths(self, path): """ Return relative paths to externally referenced files by the tool described by file at `path`. External components should not assume things about the structure of tool xml files (this is the tool's responsibility). @@ -2013,73 +2042,73 @@ def get_externally_referenced_paths( self, path ): tree = raw_tool_xml_tree(path) root = tree.getroot() external_paths = [] - for code_elem in root.findall( 'code' ): - external_path = code_elem.get( 'file' ) + for code_elem in root.findall('code'): + external_path = code_elem.get('file') if external_path: - external_paths.append( external_path ) - external_paths.extend( imported_macro_paths( root ) ) + external_paths.append(external_path) + external_paths.extend(imported_macro_paths(root)) # May also need to load external citation files as well at some point. return external_paths -class OutputParameterJSONTool( Tool ): +class OutputParameterJSONTool(Tool): """ Alternate implementation of Tool that provides parameters and other values JSONified within the contents of an output dataset """ tool_type = 'output_parameter_json' - def _prepare_json_list( self, param_list ): + def _prepare_json_list(self, param_list): rval = [] for value in param_list: - if isinstance( value, dict ): - rval.append( self._prepare_json_param_dict( value ) ) - elif isinstance( value, list ): - rval.append( self._prepare_json_list( value ) ) + if isinstance(value, dict): + rval.append(self._prepare_json_param_dict(value)) + elif isinstance(value, list): + rval.append(self._prepare_json_list(value)) else: - rval.append( str( value ) ) + rval.append(str(value)) return rval - def _prepare_json_param_dict( self, param_dict ): + def _prepare_json_param_dict(self, param_dict): rval = {} for key, value in param_dict.items(): - if isinstance( value, dict ): - rval[ key ] = self._prepare_json_param_dict( value ) - elif isinstance( value, list ): - rval[ key ] = self._prepare_json_list( value ) + if isinstance(value, dict): + rval[key] = self._prepare_json_param_dict(value) + elif isinstance(value, list): + rval[key] = self._prepare_json_list(value) else: - rval[ key ] = str( value ) + rval[key] = str(value) return rval - def exec_before_job( self, app, inp_data, out_data, param_dict=None ): + def exec_before_job(self, app, inp_data, out_data, param_dict=None): if param_dict is None: param_dict = {} json_params = {} - json_params[ 'param_dict' ] = self._prepare_json_param_dict( param_dict ) # it would probably be better to store the original incoming parameters here, instead of the Galaxy modified ones? - json_params[ 'output_data' ] = [] - json_params[ 'job_config' ] = dict( GALAXY_DATATYPES_CONF_FILE=param_dict.get( 'GALAXY_DATATYPES_CONF_FILE' ), GALAXY_ROOT_DIR=param_dict.get( 'GALAXY_ROOT_DIR' ), TOOL_PROVIDED_JOB_METADATA_FILE=galaxy.jobs.TOOL_PROVIDED_JOB_METADATA_FILE ) + json_params['param_dict'] = self._prepare_json_param_dict(param_dict) # it would probably be better to store the original incoming parameters here, instead of the Galaxy modified ones? + json_params['output_data'] = [] + json_params['job_config'] = dict(GALAXY_DATATYPES_CONF_FILE=param_dict.get('GALAXY_DATATYPES_CONF_FILE'), GALAXY_ROOT_DIR=param_dict.get('GALAXY_ROOT_DIR'), TOOL_PROVIDED_JOB_METADATA_FILE=self.provided_metadata_file) json_filename = None - for i, ( out_name, data ) in enumerate( out_data.items() ): + for i, (out_name, data) in enumerate(out_data.items()): # use wrapped dataset to access certain values - wrapped_data = param_dict.get( out_name ) + wrapped_data = param_dict.get(out_name) # allow multiple files to be created - file_name = str( wrapped_data ) - extra_files_path = str( wrapped_data.files_path ) - data_dict = dict( out_data_name=out_name, - ext=data.ext, - dataset_id=data.dataset.id, - hda_id=data.id, - file_name=file_name, - extra_files_path=extra_files_path ) - json_params[ 'output_data' ].append( data_dict ) + file_name = str(wrapped_data) + extra_files_path = str(wrapped_data.files_path) + data_dict = dict(out_data_name=out_name, + ext=data.ext, + dataset_id=data.dataset.id, + hda_id=data.id, + file_name=file_name, + extra_files_path=extra_files_path) + json_params['output_data'].append(data_dict) if json_filename is None: json_filename = file_name - out = open( json_filename, 'w' ) - out.write( json.dumps( json_params ) ) + out = open(json_filename, 'w') + out.write(json.dumps(json_params)) out.close() -class DataSourceTool( OutputParameterJSONTool ): +class DataSourceTool(OutputParameterJSONTool): """ Alternate implementation of Tool for data_source tools -- those that allow the user to query and extract data from another web site. @@ -2087,39 +2116,39 @@ class DataSourceTool( OutputParameterJSONTool ): tool_type = 'data_source' default_tool_action = DataSourceToolAction - def _build_GALAXY_URL_parameter( self ): - return ToolParameter.build( self, ElementTree.XML( '' % self.id ) ) + def _build_GALAXY_URL_parameter(self): + return ToolParameter.build(self, ElementTree.XML('' % self.id)) - def parse_inputs( self, tool_source ): - super( DataSourceTool, self ).parse_inputs( tool_source ) + def parse_inputs(self, tool_source): + super(DataSourceTool, self).parse_inputs(tool_source) # Open all data_source tools in _top. self.target = '_top' if 'GALAXY_URL' not in self.inputs: - self.inputs[ 'GALAXY_URL' ] = self._build_GALAXY_URL_parameter() - self.inputs_by_page[0][ 'GALAXY_URL' ] = self.inputs[ 'GALAXY_URL' ] + self.inputs['GALAXY_URL'] = self._build_GALAXY_URL_parameter() + self.inputs_by_page[0]['GALAXY_URL'] = self.inputs['GALAXY_URL'] - def exec_before_job( self, app, inp_data, out_data, param_dict=None ): + def exec_before_job(self, app, inp_data, out_data, param_dict=None): if param_dict is None: param_dict = {} - dbkey = param_dict.get( 'dbkey' ) - info = param_dict.get( 'info' ) - data_type = param_dict.get( 'data_type' ) - name = param_dict.get( 'name' ) + dbkey = param_dict.get('dbkey') + info = param_dict.get('info') + data_type = param_dict.get('data_type') + name = param_dict.get('name') json_params = {} - json_params[ 'param_dict' ] = self._prepare_json_param_dict( param_dict ) # it would probably be better to store the original incoming parameters here, instead of the Galaxy modified ones? - json_params[ 'output_data' ] = [] - json_params[ 'job_config' ] = dict( GALAXY_DATATYPES_CONF_FILE=param_dict.get( 'GALAXY_DATATYPES_CONF_FILE' ), GALAXY_ROOT_DIR=param_dict.get( 'GALAXY_ROOT_DIR' ), TOOL_PROVIDED_JOB_METADATA_FILE=galaxy.jobs.TOOL_PROVIDED_JOB_METADATA_FILE ) + json_params['param_dict'] = self._prepare_json_param_dict(param_dict) # it would probably be better to store the original incoming parameters here, instead of the Galaxy modified ones? + json_params['output_data'] = [] + json_params['job_config'] = dict(GALAXY_DATATYPES_CONF_FILE=param_dict.get('GALAXY_DATATYPES_CONF_FILE'), GALAXY_ROOT_DIR=param_dict.get('GALAXY_ROOT_DIR'), TOOL_PROVIDED_JOB_METADATA_FILE=self.provided_metadata_file) json_filename = None - for i, ( out_name, data ) in enumerate( out_data.items() ): + for i, (out_name, data) in enumerate(out_data.items()): # use wrapped dataset to access certain values - wrapped_data = param_dict.get( out_name ) + wrapped_data = param_dict.get(out_name) # allow multiple files to be created cur_base_param_name = 'GALAXY|%s|' % out_name - cur_name = param_dict.get( cur_base_param_name + 'name', name ) - cur_dbkey = param_dict.get( cur_base_param_name + 'dkey', dbkey ) - cur_info = param_dict.get( cur_base_param_name + 'info', info ) - cur_data_type = param_dict.get( cur_base_param_name + 'data_type', data_type ) + cur_name = param_dict.get(cur_base_param_name + 'name', name) + cur_dbkey = param_dict.get(cur_base_param_name + 'dkey', dbkey) + cur_info = param_dict.get(cur_base_param_name + 'info', info) + cur_data_type = param_dict.get(cur_base_param_name + 'data_type', data_type) if cur_name: data.name = cur_name if not data.info and cur_info: @@ -2128,34 +2157,34 @@ def exec_before_job( self, app, inp_data, out_data, param_dict=None ): data.dbkey = cur_dbkey if cur_data_type: data.extension = cur_data_type - file_name = str( wrapped_data ) - extra_files_path = str( wrapped_data.files_path ) - data_dict = dict( out_data_name=out_name, - ext=data.ext, - dataset_id=data.dataset.id, - hda_id=data.id, - file_name=file_name, - extra_files_path=extra_files_path ) - json_params[ 'output_data' ].append( data_dict ) + file_name = str(wrapped_data) + extra_files_path = str(wrapped_data.files_path) + data_dict = dict(out_data_name=out_name, + ext=data.ext, + dataset_id=data.dataset.id, + hda_id=data.id, + file_name=file_name, + extra_files_path=extra_files_path) + json_params['output_data'].append(data_dict) if json_filename is None: json_filename = file_name - out = open( json_filename, 'w' ) - out.write( json.dumps( json_params ) ) + out = open(json_filename, 'w') + out.write(json.dumps(json_params)) out.close() -class AsyncDataSourceTool( DataSourceTool ): +class AsyncDataSourceTool(DataSourceTool): tool_type = 'data_source_async' - def _build_GALAXY_URL_parameter( self ): - return ToolParameter.build( self, ElementTree.XML( '' % self.id ) ) + def _build_GALAXY_URL_parameter(self): + return ToolParameter.build(self, ElementTree.XML('' % self.id)) -class DataDestinationTool( Tool ): +class DataDestinationTool(Tool): tool_type = 'data_destination' -class SetMetadataTool( Tool ): +class SetMetadataTool(Tool): """ Tool implementation for special tool that sets metadata on an existing dataset. @@ -2163,61 +2192,61 @@ class SetMetadataTool( Tool ): tool_type = 'set_metadata' requires_setting_metadata = False - def exec_after_process( self, app, inp_data, out_data, param_dict, job=None ): + def exec_after_process(self, app, inp_data, out_data, param_dict, job=None): for name, dataset in inp_data.items(): - external_metadata = JobExternalOutputMetadataWrapper( job ) - if external_metadata.external_metadata_set_successfully( dataset, app.model.context ): - dataset.metadata.from_JSON_dict( external_metadata.get_output_filenames_by_dataset( dataset, app.model.context ).filename_out ) + external_metadata = JobExternalOutputMetadataWrapper(job) + if external_metadata.external_metadata_set_successfully(dataset, app.model.context): + dataset.metadata.from_JSON_dict(external_metadata.get_output_filenames_by_dataset(dataset, app.model.context).filename_out) else: dataset._state = model.Dataset.states.FAILED_METADATA - self.sa_session.add( dataset ) + self.sa_session.add(dataset) self.sa_session.flush() return # If setting external metadata has failed, how can we inform the # user? For now, we'll leave the default metadata and set the state # back to its original. - dataset.datatype.after_setting_metadata( dataset ) + dataset.datatype.after_setting_metadata(dataset) if job and job.tool_id == '1.0.0': - dataset.state = param_dict.get( '__ORIGINAL_DATASET_STATE__' ) + dataset.state = param_dict.get('__ORIGINAL_DATASET_STATE__') else: # Revert dataset.state to fall back to dataset.dataset.state dataset._state = None # Need to reset the peek, which may rely on metadata dataset.set_peek() - self.sa_session.add( dataset ) + self.sa_session.add(dataset) self.sa_session.flush() - def job_failed( self, job_wrapper, message, exception=False ): - job = job_wrapper.sa_session.query( model.Job ).get( job_wrapper.job_id ) + def job_failed(self, job_wrapper, message, exception=False): + job = job_wrapper.sa_session.query(model.Job).get(job_wrapper.job_id) if job: inp_data = {} for dataset_assoc in job.input_datasets: inp_data[dataset_assoc.name] = dataset_assoc.dataset - return self.exec_after_process( job_wrapper.app, inp_data, {}, job_wrapper.get_param_dict(), job=job ) + return self.exec_after_process(job_wrapper.app, inp_data, {}, job_wrapper.get_param_dict(), job=job) -class ExportHistoryTool( Tool ): +class ExportHistoryTool(Tool): tool_type = 'export_history' -class ImportHistoryTool( Tool ): +class ImportHistoryTool(Tool): tool_type = 'import_history' -class DataManagerTool( OutputParameterJSONTool ): +class DataManagerTool(OutputParameterJSONTool): tool_type = 'manage_data' default_tool_action = DataManagerToolAction - def __init__( self, config_file, root, app, guid=None, data_manager_id=None, **kwds ): + def __init__(self, config_file, root, app, guid=None, data_manager_id=None, **kwds): self.data_manager_id = data_manager_id - super( DataManagerTool, self ).__init__( config_file, root, app, guid=guid, **kwds ) + super(DataManagerTool, self).__init__(config_file, root, app, guid=guid, **kwds) if self.data_manager_id is None: self.data_manager_id = self.id - def exec_after_process( self, app, inp_data, out_data, param_dict, job=None, **kwds ): - assert self.allow_user_access( job.user ), "You must be an admin to access this tool." + def exec_after_process(self, app, inp_data, out_data, param_dict, job=None, **kwds): + assert self.allow_user_access(job.user), "You must be an admin to access this tool." # run original exec_after_process - super( DataManagerTool, self ).exec_after_process( app, inp_data, out_data, param_dict, job=job, **kwds ) + super(DataManagerTool, self).exec_after_process(app, inp_data, out_data, param_dict, job=job, **kwds) # process results of tool if job and job.state == job.states.ERROR: return @@ -2226,40 +2255,40 @@ def exec_after_process( self, app, inp_data, out_data, param_dict, job=None, **k if dataset.state != dataset.states.OK: return data_manager_id = job.data_manager_association.data_manager_id - data_manager = self.app.data_managers.get_manager( data_manager_id, None ) - assert data_manager is not None, "Invalid data manager (%s) requested. It may have been removed before the job completed." % ( data_manager_id ) - data_manager.process_result( out_data ) - - def get_default_history_by_trans( self, trans, create=False ): - def _create_data_manager_history( user ): - history = trans.app.model.History( name='Data Manager History (automatically created)', user=user ) - data_manager_association = trans.app.model.DataManagerHistoryAssociation( user=user, history=history ) - trans.sa_session.add_all( ( history, data_manager_association ) ) + data_manager = self.app.data_managers.get_manager(data_manager_id, None) + assert data_manager is not None, "Invalid data manager (%s) requested. It may have been removed before the job completed." % (data_manager_id) + data_manager.process_result(out_data) + + def get_default_history_by_trans(self, trans, create=False): + def _create_data_manager_history(user): + history = trans.app.model.History(name='Data Manager History (automatically created)', user=user) + data_manager_association = trans.app.model.DataManagerHistoryAssociation(user=user, history=history) + trans.sa_session.add_all((history, data_manager_association)) trans.sa_session.flush() return history user = trans.user assert user, 'You must be logged in to use this tool.' - assert self.allow_user_access( user ), "You must be an admin to access this tool." + assert self.allow_user_access(user), "You must be an admin to access this tool." history = user.data_manager_histories if not history: # create if create: - history = _create_data_manager_history( user ) + history = _create_data_manager_history(user) else: history = None else: - for history in reversed( history ): + for history in reversed(history): history = history.history if not history.deleted: break if history.deleted: if create: - history = _create_data_manager_history( user ) + history = _create_data_manager_history(user) else: history = None return history - def allow_user_access( self, user, attempting_access=True ): + def allow_user_access(self, user, attempting_access=True): """ :param user: model object representing user. :type user: galaxy.model.User @@ -2271,34 +2300,34 @@ def allow_user_access( self, user, attempting_access=True ): :returns: bool -- Whether the user is allowed to access the tool. Data Manager tools are only accessible to admins. """ - if super( DataManagerTool, self ).allow_user_access( user ) and self.app.config.is_admin_user( user ): + if super(DataManagerTool, self).allow_user_access(user) and self.app.config.is_admin_user(user): return True # If this is just an incidental check - do not log the scary message # about users attempting to do something problematic. if attempting_access: if user: user = user.id - log.debug( "User (%s) attempted to access a data manager tool (%s), but is not an admin.", user, self.id ) + log.debug("User (%s) attempted to access a data manager tool (%s), but is not an admin.", user, self.id) return False -class DatabaseOperationTool( Tool ): +class DatabaseOperationTool(Tool): default_tool_action = ModelOperationToolAction require_dataset_ok = True @property - def valid_input_states( self ): + def valid_input_states(self): if self.require_dataset_ok: return (model.Dataset.states.OK,) else: return model.Dataset.terminal_states @property - def allow_errored_inputs( self ): + def allow_errored_inputs(self): return not self.require_dataset_ok - def check_inputs_ready( self, input_datasets, input_dataset_collections ): - def check_dataset_instance( input_dataset ): + def check_inputs_ready(self, input_datasets, input_dataset_collections): + def check_dataset_instance(input_dataset): if input_dataset.is_pending: raise ToolInputsNotReadyException() @@ -2307,27 +2336,27 @@ def check_dataset_instance( input_dataset ): raise ValueError("Tool requires inputs to be in valid state.") for input_dataset in input_datasets.values(): - check_dataset_instance( input_dataset ) + check_dataset_instance(input_dataset) for input_dataset_collection_pairs in input_dataset_collections.values(): for input_dataset_collection, is_mapped in input_dataset_collection_pairs: if not input_dataset_collection.collection.populated: raise ToolInputsNotReadyException() - map( check_dataset_instance, input_dataset_collection.dataset_instances ) + map(check_dataset_instance, input_dataset_collection.dataset_instances) - def produce_outputs( self, trans, out_data, output_collections, incoming, history ): + def produce_outputs(self, trans, out_data, output_collections, incoming, history): return self._outputs_dict() - def _outputs_dict( self ): + def _outputs_dict(self): return odict() -class UnzipCollectionTool( DatabaseOperationTool ): +class UnzipCollectionTool(DatabaseOperationTool): tool_type = 'unzip_collection' - def produce_outputs( self, trans, out_data, output_collections, incoming, history ): - has_collection = incoming[ "input" ] + def produce_outputs(self, trans, out_data, output_collections, incoming, history): + has_collection = incoming["input"] if hasattr(has_collection, "element_type"): # It is a DCE collection = has_collection.element_object @@ -2339,38 +2368,38 @@ def produce_outputs( self, trans, out_data, output_collections, incoming, histor forward_o, reverse_o = collection.dataset_instances forward, reverse = forward_o.copy(), reverse_o.copy() # TODO: rename... - history.add_dataset( forward, set_hid=True ) - history.add_dataset( reverse, set_hid=True ) + history.add_dataset(forward, set_hid=True) + history.add_dataset(reverse, set_hid=True) out_data["forward"] = forward out_data["reverse"] = reverse -class ZipCollectionTool( DatabaseOperationTool ): +class ZipCollectionTool(DatabaseOperationTool): tool_type = 'zip_collection' - def produce_outputs( self, trans, out_data, output_collections, incoming, history ): - forward_o = incoming[ "input_forward" ] - reverse_o = incoming[ "input_reverse" ] + def produce_outputs(self, trans, out_data, output_collections, incoming, history): + forward_o = incoming["input_forward"] + reverse_o = incoming["input_reverse"] forward, reverse = forward_o.copy(), reverse_o.copy() new_elements = odict() new_elements["forward"] = forward new_elements["reverse"] = reverse - history.add_dataset( forward, set_hid=False ) - history.add_dataset( reverse, set_hid=False ) + history.add_dataset(forward, set_hid=False) + history.add_dataset(reverse, set_hid=False) output_collections.create_collection( next(iter(self.outputs.values())), "output", elements=new_elements ) -class MergeCollectionTool( DatabaseOperationTool ): +class MergeCollectionTool(DatabaseOperationTool): tool_type = 'merge_collection' - def produce_outputs( self, trans, out_data, output_collections, incoming, history ): + def produce_outputs(self, trans, out_data, output_collections, incoming, history): input_lists = [] - for incoming_repeat in incoming[ "inputs" ]: + for incoming_repeat in incoming["inputs"]: input_lists.append(incoming_repeat["input"]) advanced = incoming.get("advanced", None) @@ -2388,12 +2417,12 @@ def produce_outputs( self, trans, out_data, output_collections, incoming, histor identifiers_map = {} for input_num, input_list in enumerate(input_lists): for dce in input_list.collection.elements: - element_identifier = dce.element_identifier - if element_identifier not in identifiers_map: - identifiers_map[element_identifier] = [] - elif dupl_actions == "fail": - raise Exception("Duplicate collection element identifiers found for [%s]" % element_identifier) - identifiers_map[element_identifier].append(input_num) + element_identifier = dce.element_identifier + if element_identifier not in identifiers_map: + identifiers_map[element_identifier] = [] + elif dupl_actions == "fail": + raise Exception("Duplicate collection element identifiers found for [%s]" % element_identifier) + identifiers_map[element_identifier].append(input_num) for copy, input_list in enumerate(input_lists): for dce in input_list.collection.elements: @@ -2446,12 +2475,12 @@ def produce_outputs( self, trans, out_data, output_collections, incoming, histor ) -class FilterFailedDatasetsTool( DatabaseOperationTool ): +class FilterFailedDatasetsTool(DatabaseOperationTool): tool_type = 'filter_failed_datasets_collection' require_dataset_ok = False - def produce_outputs( self, trans, out_data, output_collections, incoming, history ): - hdca = incoming[ "input" ] + def produce_outputs(self, trans, out_data, output_collections, incoming, history): + hdca = incoming["input"] assert hdca.collection.collection_type == "list" or hdca.collection.collection_type == 'list:paired' @@ -2484,11 +2513,11 @@ def produce_outputs( self, trans, out_data, output_collections, incoming, histor ) -class FlattenTool( DatabaseOperationTool ): +class FlattenTool(DatabaseOperationTool): tool_type = 'flatten_collection' - def produce_outputs( self, trans, out_data, output_collections, incoming, history ): - hdca = incoming[ "input" ] + def produce_outputs(self, trans, out_data, output_collections, incoming, history): + hdca = incoming["input"] join_identifier = incoming["join_identifier"] new_elements = odict() @@ -2509,6 +2538,30 @@ def add_elements(collection, prefix=""): ) +class SortTool(DatabaseOperationTool): + tool_type = 'sort_collection' + + def produce_outputs(self, trans, out_data, output_collections, incoming, history): + hdca = incoming["input"] + sorttype = incoming["sort_type"] + new_elements = odict() + elements = hdca.collection.elements + if sorttype == 'alpha': + presort_elements = [(dce.element_identifier, dce) for dce in elements] + elif sorttype == 'numeric': + presort_elements = [(int(re.sub('[^0-9]', '', dce.element_identifier)), dce) for dce in elements] + sorted_elements = [x[1] for x in sorted(presort_elements, key=lambda x: x[0])] + + for dce in sorted_elements: + dce_object = dce.element_object + copied_dataset = dce_object.copy() + history.add_dataset(copied_dataset, set_hid=False) + new_elements[dce.element_identifier] = copied_dataset + output_collections.create_collection( + next(iter(self.outputs.values())), "output", elements=new_elements + ) + + class RelabelFromFileTool(DatabaseOperationTool): tool_type = 'relabel_from_file' @@ -2598,45 +2651,45 @@ def produce_outputs(self, trans, out_data, output_collections, incoming, history # Populate tool_type to ToolClass mappings tool_types = {} -for tool_class in [ Tool, SetMetadataTool, OutputParameterJSONTool, - DataManagerTool, DataSourceTool, AsyncDataSourceTool, - UnzipCollectionTool, ZipCollectionTool, MergeCollectionTool, RelabelFromFileTool, FilterFromFileTool, - DataDestinationTool ]: - tool_types[ tool_class.tool_type ] = tool_class +for tool_class in [Tool, SetMetadataTool, OutputParameterJSONTool, + DataManagerTool, DataSourceTool, AsyncDataSourceTool, + UnzipCollectionTool, ZipCollectionTool, MergeCollectionTool, RelabelFromFileTool, FilterFromFileTool, + DataDestinationTool]: + tool_types[tool_class.tool_type] = tool_class # ---- Utility classes to be factored out ----------------------------------- class TracksterConfig: """ Trackster configuration encapsulation. """ - def __init__( self, actions ): + def __init__(self, actions): self.actions = actions @staticmethod - def parse( root ): + def parse(root): actions = [] - for action_elt in root.findall( "action" ): - actions.append( SetParamAction.parse( action_elt ) ) - return TracksterConfig( actions ) + for action_elt in root.findall("action"): + actions.append(SetParamAction.parse(action_elt)) + return TracksterConfig(actions) class SetParamAction: """ Set parameter action. """ - def __init__( self, name, output_name ): + def __init__(self, name, output_name): self.name = name self.output_name = output_name @staticmethod - def parse( elt ): + def parse(elt): """ Parse action from element. """ - return SetParamAction( elt.get( "name" ), elt.get( "output_name" ) ) + return SetParamAction(elt.get("name"), elt.get("output_name")) -class BadValue( object ): - def __init__( self, value ): +class BadValue(object): + def __init__(self, value): self.value = value -class InterruptedUpload( Exception ): +class InterruptedUpload(Exception): pass diff --git a/lib/galaxy/tools/actions/__init__.py b/lib/galaxy/tools/actions/__init__.py index 78920a4c559c..13a5cc561ae8 100644 --- a/lib/galaxy/tools/actions/__init__.py +++ b/lib/galaxy/tools/actions/__init__.py @@ -17,31 +17,33 @@ from galaxy.util.template import fill_template from galaxy.web import url_for -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class ToolExecutionCache( object ): +class ToolExecutionCache(object): """ An object mean to cache calculation caused by repeatedly evaluting the same tool by the same user with slightly different parameters. """ + def __init__(self, trans): self.trans = trans self.current_user_roles = trans.get_current_user_roles() -class ToolAction( object ): +class ToolAction(object): """ The actions to be taken when a tool is run (after parameters have been converted and validated). """ - def execute( self, tool, trans, incoming={}, set_output_hid=True ): + + def execute(self, tool, trans, incoming={}, set_output_hid=True): raise TypeError("Abstract method") -class DefaultToolAction( object ): +class DefaultToolAction(object): """Default tool action is to run an external command""" - def _collect_input_datasets( self, tool, param_values, trans, history, current_user_roles=None ): + def _collect_input_datasets(self, tool, param_values, trans, history, current_user_roles=None): """ Collect any dataset inputs from incoming. Returns a mapping from parameter name to Dataset instance for each tool parameter that is @@ -51,111 +53,111 @@ def _collect_input_datasets( self, tool, param_values, trans, history, current_u current_user_roles = trans.get_current_user_roles() input_datasets = odict() - def visitor( input, value, prefix, parent=None, **kwargs ): + def visitor(input, value, prefix, parent=None, **kwargs): - def process_dataset( data, formats=None ): - if not data or isinstance( data, RuntimeValue ): + def process_dataset(data, formats=None): + if not data or isinstance(data, RuntimeValue): return None if formats is None: formats = input.formats - if not data.datatype.matches_any( formats ): + if not data.datatype.matches_any(formats): # Need to refresh in case this conversion just took place, i.e. input above in tool performed the same conversion - trans.sa_session.refresh( data ) - target_ext, converted_dataset = data.find_conversion_destination( formats ) + trans.sa_session.refresh(data) + target_ext, converted_dataset = data.find_conversion_destination(formats) if target_ext: if converted_dataset: data = converted_dataset else: - data = data.get_converted_dataset( trans, target_ext, target_context=parent, history=history ) + data = data.get_converted_dataset(trans, target_ext, target_context=parent, history=history) - if not trans.app.security_agent.can_access_dataset( current_user_roles, data.dataset ): - raise Exception( "User does not have permission to use a dataset (%s) provided for input." % data.id ) + if not trans.app.security_agent.can_access_dataset(current_user_roles, data.dataset): + raise Exception("User does not have permission to use a dataset (%s) provided for input." % data.id) return data - if isinstance( input, DataToolParameter ): - if isinstance( value, list ): + if isinstance(input, DataToolParameter): + if isinstance(value, list): # If there are multiple inputs with the same name, they # are stored as name1, name2, ... - for i, v in enumerate( value ): - processed_dataset = process_dataset( v ) + for i, v in enumerate(value): + processed_dataset = process_dataset(v) if i == 0: # Allow copying metadata to output, first item will be source. - input_datasets[ prefix + input.name ] = processed_dataset - input_datasets[ prefix + input.name + str( i + 1 ) ] = processed_dataset + input_datasets[prefix + input.name] = processed_dataset + input_datasets[prefix + input.name + str(i + 1)] = processed_dataset conversions = [] for conversion_name, conversion_extensions, conversion_datatypes in input.conversions: - new_data = process_dataset( input_datasets[ prefix + input.name + str( i + 1 ) ], conversion_datatypes ) - if not new_data or new_data.datatype.matches_any( conversion_datatypes ): - input_datasets[ prefix + conversion_name + str( i + 1 ) ] = new_data - conversions.append( ( conversion_name, new_data ) ) + new_data = process_dataset(input_datasets[prefix + input.name + str(i + 1)], conversion_datatypes) + if not new_data or new_data.datatype.matches_any(conversion_datatypes): + input_datasets[prefix + conversion_name + str(i + 1)] = new_data + conversions.append((conversion_name, new_data)) else: - raise Exception('A path for explicit datatype conversion has not been found: %s --/--> %s' % ( input_datasets[ prefix + input.name + str( i + 1 ) ].extension, conversion_extensions ) ) + raise Exception('A path for explicit datatype conversion has not been found: %s --/--> %s' % (input_datasets[prefix + input.name + str(i + 1)].extension, conversion_extensions)) if parent: - parent[ input.name ][ i ] = input_datasets[ prefix + input.name + str( i + 1 ) ] + parent[input.name][i] = input_datasets[prefix + input.name + str(i + 1)] for conversion_name, conversion_data in conversions: # allow explicit conversion to be stored in job_parameter table - parent[ conversion_name ][ i ] = conversion_data.id # a more robust way to determine JSONable value is desired + parent[conversion_name][i] = conversion_data.id # a more robust way to determine JSONable value is desired else: - param_values[ input.name ][ i ] = input_datasets[ prefix + input.name + str( i + 1 ) ] + param_values[input.name][i] = input_datasets[prefix + input.name + str(i + 1)] for conversion_name, conversion_data in conversions: # allow explicit conversion to be stored in job_parameter table - param_values[ conversion_name ][i] = conversion_data.id # a more robust way to determine JSONable value is desired + param_values[conversion_name][i] = conversion_data.id # a more robust way to determine JSONable value is desired else: - input_datasets[ prefix + input.name ] = process_dataset( value ) + input_datasets[prefix + input.name] = process_dataset(value) conversions = [] for conversion_name, conversion_extensions, conversion_datatypes in input.conversions: - new_data = process_dataset( input_datasets[ prefix + input.name ], conversion_datatypes ) - if not new_data or new_data.datatype.matches_any( conversion_datatypes ): - input_datasets[ prefix + conversion_name ] = new_data - conversions.append( ( conversion_name, new_data ) ) + new_data = process_dataset(input_datasets[prefix + input.name], conversion_datatypes) + if not new_data or new_data.datatype.matches_any(conversion_datatypes): + input_datasets[prefix + conversion_name] = new_data + conversions.append((conversion_name, new_data)) else: - raise Exception( 'A path for explicit datatype conversion has not been found: %s --/--> %s' % ( input_datasets[ prefix + input.name ].extension, conversion_extensions ) ) + raise Exception('A path for explicit datatype conversion has not been found: %s --/--> %s' % (input_datasets[prefix + input.name].extension, conversion_extensions)) target_dict = parent if not target_dict: target_dict = param_values - target_dict[ input.name ] = input_datasets[ prefix + input.name ] + target_dict[input.name] = input_datasets[prefix + input.name] for conversion_name, conversion_data in conversions: # allow explicit conversion to be stored in job_parameter table - target_dict[ conversion_name ] = conversion_data.id # a more robust way to determine JSONable value is desired - elif isinstance( input, DataCollectionToolParameter ): + target_dict[conversion_name] = conversion_data.id # a more robust way to determine JSONable value is desired + elif isinstance(input, DataCollectionToolParameter): if not value: return dataset_instances = [] - if hasattr( value, 'child_collection' ): + if hasattr(value, 'child_collection'): # if we are mapping a collection over a tool, we only require the child_collection dataset_instances = value.child_collection.dataset_instances else: # else the tool takes a collection as input so we need everything dataset_instances = value.collection.dataset_instances - for i, v in enumerate( dataset_instances ): + for i, v in enumerate(dataset_instances): data = v - if not trans.app.security_agent.can_access_dataset( current_user_roles, data.dataset ): - raise Exception( "User does not have permission to use a dataset (%s) provided for input." % data.id ) + if not trans.app.security_agent.can_access_dataset(current_user_roles, data.dataset): + raise Exception("User does not have permission to use a dataset (%s) provided for input." % data.id) # Skipping implicit conversion stuff for now, revisit at # some point and figure out if implicitly converting a # dataset collection makes senese. - input_datasets[ prefix + input.name + str( i + 1 ) ] = data + input_datasets[prefix + input.name + str(i + 1)] = data - tool.visit_inputs( param_values, visitor ) + tool.visit_inputs(param_values, visitor) return input_datasets - def collect_input_dataset_collections( self, tool, param_values ): - def append_to_key( the_dict, key, value ): + def collect_input_dataset_collections(self, tool, param_values): + def append_to_key(the_dict, key, value): if key not in the_dict: - the_dict[ key ] = [] - the_dict[ key ].append( value ) + the_dict[key] = [] + the_dict[key].append(value) input_dataset_collections = dict() - def visitor( input, value, prefix, parent=None, prefixed_name=None, **kwargs ): - if isinstance( input, DataToolParameter ): + def visitor(input, value, prefix, parent=None, prefixed_name=None, **kwargs): + if isinstance(input, DataToolParameter): values = value - if not isinstance( values, list ): - values = [ value ] + if not isinstance(values, list): + values = [value] for i, value in enumerate(values): - if isinstance( value, model.HistoryDatasetCollectionAssociation ): - append_to_key( input_dataset_collections, prefixed_name, ( value, True ) ) + if isinstance(value, model.HistoryDatasetCollectionAssociation): + append_to_key(input_dataset_collections, prefixed_name, (value, True)) target_dict = parent if not target_dict: target_dict = param_values @@ -165,41 +167,41 @@ def visitor( input, value, prefix, parent=None, prefixed_name=None, **kwargs ): # extraction and tool rerun. dataset_instances = value.collection.dataset_instances if i == 0: - target_dict[ input.name ] = [] - target_dict[ input.name ].extend( dataset_instances ) - elif isinstance( input, DataCollectionToolParameter ): - append_to_key( input_dataset_collections, prefix + input.name, ( value, False ) ) + target_dict[input.name] = [] + target_dict[input.name].extend(dataset_instances) + elif isinstance(input, DataCollectionToolParameter): + append_to_key(input_dataset_collections, prefix + input.name, (value, False)) - tool.visit_inputs( param_values, visitor ) + tool.visit_inputs(param_values, visitor) return input_dataset_collections - def _check_access( self, tool, trans ): - assert tool.allow_user_access( trans.user ), "User (%s) is not allowed to access this tool." % ( trans.user ) + def _check_access(self, tool, trans): + assert tool.allow_user_access(trans.user), "User (%s) is not allowed to access this tool." % (trans.user) - def _collect_inputs( self, tool, trans, incoming, history, current_user_roles ): + def _collect_inputs(self, tool, trans, incoming, history, current_user_roles): """ Collect history as well as input datasets and collections. """ app = trans.app # Set history. if not history: - history = tool.get_default_history_by_trans( trans, create=True ) + history = tool.get_default_history_by_trans(trans, create=True) if history not in trans.sa_session: - history = trans.sa_session.query( app.model.History ).get( history.id ) + history = trans.sa_session.query(app.model.History).get(history.id) # Track input dataset collections - but replace with simply lists so collect # input datasets can process these normally. - inp_dataset_collections = self.collect_input_dataset_collections( tool, incoming ) + inp_dataset_collections = self.collect_input_dataset_collections(tool, incoming) # Collect any input datasets from the incoming parameters - inp_data = self._collect_input_datasets( tool, incoming, trans, history=history, current_user_roles=current_user_roles ) + inp_data = self._collect_input_datasets(tool, incoming, trans, history=history, current_user_roles=current_user_roles) return history, inp_data, inp_dataset_collections - def execute(self, tool, trans, incoming={}, return_job=False, set_output_hid=True, history=None, job_params=None, rerun_remap_job_id=None, mapping_over_collection=False, execution_cache=None ): + def execute(self, tool, trans, incoming={}, return_job=False, set_output_hid=True, history=None, job_params=None, rerun_remap_job_id=None, mapping_over_collection=False, execution_cache=None): """ Executes a tool, creating job and tool outputs, associating them, and submitting the job to the job queue. If history is not specified, use trans.history as destination for tool's output datasets. """ - self._check_access( tool, trans ) + self._check_access(tool, trans) app = trans.app if execution_cache is None: execution_cache = ToolExecutionCache(trans) @@ -207,22 +209,22 @@ def execute(self, tool, trans, incoming={}, return_job=False, set_output_hid=Tru history, inp_data, inp_dataset_collections = self._collect_inputs(tool, trans, incoming, history, current_user_roles) # Build name for output datasets based on tool name and input names - on_text = self._get_on_text( inp_data ) + on_text = self._get_on_text(inp_data) # format='input" previously would give you a random extension from # the input extensions, now it should just give "input" as the output # format. input_ext = 'data' if tool.profile < 16.04 else "input" - input_dbkey = incoming.get( "dbkey", "?" ) + input_dbkey = incoming.get("dbkey", "?") preserved_tags = {} for name, data in reversed(inp_data.items()): if not data: - data = NoneDataset( datatypes_registry=app.datatypes_registry ) + data = NoneDataset(datatypes_registry=app.datatypes_registry) continue # Convert LDDA to an HDA. if isinstance(data, LibraryDatasetDatasetAssociation): - data = data.to_history_dataset_association( None ) + data = data.to_history_dataset_association(None) inp_data[name] = data if tool.profile < 16.04: @@ -231,34 +233,34 @@ def execute(self, tool, trans, incoming={}, return_job=False, set_output_hid=Tru if data.dbkey not in [None, '?']: input_dbkey = data.dbkey - identifier = getattr( data, "element_identifier", None ) + identifier = getattr(data, "element_identifier", None) if identifier is not None: - incoming[ "%s|__identifier__" % name ] = identifier + incoming["%s|__identifier__" % name] = identifier for tag in [t for t in data.tags if t.user_tname == 'name']: preserved_tags[tag.value] = tag # Collect chromInfo dataset and add as parameters to incoming - ( chrom_info, db_dataset ) = app.genome_builds.get_chrom_info( input_dbkey, trans=trans, custom_build_hack_get_len_from_fasta_conversion=tool.id != 'CONVERTER_fasta_to_len' ) + (chrom_info, db_dataset) = app.genome_builds.get_chrom_info(input_dbkey, trans=trans, custom_build_hack_get_len_from_fasta_conversion=tool.id != 'CONVERTER_fasta_to_len') if db_dataset: - inp_data.update( { "chromInfo": db_dataset } ) - incoming[ "chromInfo" ] = chrom_info + inp_data.update({"chromInfo": db_dataset}) + incoming["chromInfo"] = chrom_info # Determine output dataset permission/roles list - existing_datasets = [ inp for inp in inp_data.values() if inp ] + existing_datasets = [inp for inp in inp_data.values() if inp] if existing_datasets: - output_permissions = app.security_agent.guess_derived_permissions_for_datasets( existing_datasets ) + output_permissions = app.security_agent.guess_derived_permissions_for_datasets(existing_datasets) else: # No valid inputs, we will use history defaults - output_permissions = app.security_agent.history_get_default_permissions( history ) + output_permissions = app.security_agent.history_get_default_permissions(history) # Add the dbkey to the incoming parameters - incoming[ "dbkey" ] = input_dbkey + incoming["dbkey"] = input_dbkey # wrapped params are used by change_format action and by output.label; only perform this wrapping once, as needed - wrapped_params = self._wrapped_params( trans, tool, incoming ) + wrapped_params = self._wrapped_params(trans, tool, incoming) out_data = odict() - input_collections = dict( (k, v[0][0]) for k, v in inp_dataset_collections.items() ) + input_collections = dict((k, v[0][0]) for k, v in inp_dataset_collections.items()) output_collections = OutputCollections( trans, history, @@ -276,19 +278,19 @@ def execute(self, tool, trans, incoming={}, return_job=False, set_output_hid=Tru # datasets first, then create the associations parent_to_child_pairs = [] child_dataset_names = set() - object_store_populator = ObjectStorePopulator( app ) + object_store_populator = ObjectStorePopulator(app) - def handle_output( name, output, hidden=None ): + def handle_output(name, output, hidden=None): if output.parent: - parent_to_child_pairs.append( ( output.parent, name ) ) - child_dataset_names.add( name ) + parent_to_child_pairs.append((output.parent, name)) + child_dataset_names.add(name) # What is the following hack for? Need to document under what # conditions can the following occur? (james@bx.psu.edu) # HACK: the output data has already been created # this happens i.e. as a result of the async controller if name in incoming: dataid = incoming[name] - data = trans.sa_session.query( app.model.HistoryDatasetAssociation ).get( dataid ) + data = trans.sa_session.query(app.model.HistoryDatasetAssociation).get(dataid) assert data is not None out_data[name] = data else: @@ -299,20 +301,20 @@ def handle_output( name, output, hidden=None ): inp_dataset_collections, input_ext ) - data = app.model.HistoryDatasetAssociation( extension=ext, create_dataset=True, flush=False ) + data = app.model.HistoryDatasetAssociation(extension=ext, create_dataset=True, flush=False) if hidden is None: hidden = output.hidden if hidden: data.visible = False - trans.sa_session.add( data ) - trans.app.security_agent.set_all_dataset_permissions( data.dataset, output_permissions, new=True ) + trans.sa_session.add(data) + trans.app.security_agent.set_all_dataset_permissions(data.dataset, output_permissions, new=True) for _, tag in preserved_tags.items(): data.tags.append(tag.copy()) # Must flush before setting object store id currently. # TODO: optimize this. trans.sa_session.flush() - object_store_populator.set_object_store_id( data ) + object_store_populator.set_object_store_id(data) # This may not be neccesary with the new parent/child associations data.designation = name @@ -322,11 +324,11 @@ def handle_output( name, output, hidden=None ): # or an actual object to copy. metadata_source = output.metadata_source if metadata_source: - if isinstance( metadata_source, string_types ): - metadata_source = inp_data.get( metadata_source ) + if isinstance(metadata_source, string_types): + metadata_source = inp_data.get(metadata_source) if metadata_source is not None: - data.init_meta( copy_from=metadata_source ) + data.init_meta(copy_from=metadata_source) else: data.init_meta() # Take dbkey from LAST input @@ -334,16 +336,16 @@ def handle_output( name, output, hidden=None ): # Set state data.blurb = "queued" # Set output label - data.name = self.get_output_name( output, data, tool, on_text, trans, incoming, history, wrapped_params.params, job_params ) + data.name = self.get_output_name(output, data, tool, on_text, trans, incoming, history, wrapped_params.params, job_params) # Store output - out_data[ name ] = data + out_data[name] = data if output.actions: # Apply pre-job tool-output-dataset actions; e.g. setting metadata, changing format - output_action_params = dict( out_data ) - output_action_params.update( incoming ) - output.actions.apply_action( data, output_action_params ) + output_action_params = dict(out_data) + output_action_params.update(incoming) + output.actions.apply_action(data, output_action_params) # Also set the default values of actions of type metadata - self.set_metadata_defaults( output, data, tool, on_text, trans, incoming, history, wrapped_params.params, job_params ) + self.set_metadata_defaults(output, data, tool, on_text, trans, incoming, history, wrapped_params.params, job_params) # Flush all datasets at once. return data @@ -352,7 +354,7 @@ def handle_output( name, output, hidden=None ): if output.collection: collections_manager = app.dataset_collections_service element_identifiers = [] - known_outputs = output.known_outputs( input_collections, collections_manager.type_registry ) + known_outputs = output.known_outputs(input_collections, collections_manager.type_registry) # Just to echo TODO elsewhere - this should be restructured to allow # nested collections. for output_part_def in known_outputs: @@ -375,18 +377,18 @@ def handle_output( name, output, hidden=None ): )) else: index = name_to_index[parent_id] - current_element_identifiers = current_element_identifiers[ index ][ "element_identifiers" ] + current_element_identifiers = current_element_identifiers[index]["element_identifiers"] effective_output_name = output_part_def.effective_output_name - element = handle_output( effective_output_name, output_part_def.output_def, hidden=True ) + element = handle_output(effective_output_name, output_part_def.output_def, hidden=True) # TODO: this shouldn't exist in the top-level of the history at all # but for now we are still working around that by hiding the contents # there. # Following hack causes dataset to no be added to history... - child_dataset_names.add( effective_output_name ) + child_dataset_names.add(effective_output_name) - history.add_dataset( element, set_hid=set_output_hid, quota=False ) - trans.sa_session.add( element ) + history.add_dataset(element, set_hid=set_output_hid, quota=False) + trans.sa_session.add(element) trans.sa_session.flush() current_element_identifiers.append({ @@ -409,7 +411,7 @@ def handle_output( name, output, hidden=None ): ) else: handle_output_timer = ExecutionTimer() - handle_output( name, output ) + handle_output(name, output) log.info("Handled output named %s for tool %s %s" % (name, tool.id, handle_output_timer)) add_datasets_timer = ExecutionTimer() @@ -417,41 +419,41 @@ def handle_output( name, output, hidden=None ): datasets_to_persist = [] for name in out_data.keys(): if name not in child_dataset_names and name not in incoming: # don't add children; or already existing datasets, i.e. async created - data = out_data[ name ] - datasets_to_persist.append( data ) + data = out_data[name] + datasets_to_persist.append(data) # Set HID and add to history. # This is brand new and certainly empty so don't worry about quota. # TOOL OPTIMIZATION NOTE - from above loop to the job create below 99%+ # of execution time happens within in history.add_datasets. - history.add_datasets( trans.sa_session, datasets_to_persist, set_hid=set_output_hid, quota=False, flush=False ) + history.add_datasets(trans.sa_session, datasets_to_persist, set_hid=set_output_hid, quota=False, flush=False) # Add all the children to their parents for parent_name, child_name in parent_to_child_pairs: - parent_dataset = out_data[ parent_name ] - child_dataset = out_data[ child_name ] - parent_dataset.children.append( child_dataset ) + parent_dataset = out_data[parent_name] + child_dataset = out_data[child_name] + parent_dataset.children.append(child_dataset) log.info("Added output datasets to history %s" % add_datasets_timer) job_setup_timer = ExecutionTimer() # Create the job object - job, galaxy_session = self._new_job_for_session( trans, tool, history ) - self._record_inputs( trans, tool, job, incoming, inp_data, inp_dataset_collections, current_user_roles ) - self._record_outputs( job, out_data, output_collections ) + job, galaxy_session = self._new_job_for_session(trans, tool, history) + self._record_inputs(trans, tool, job, incoming, inp_data, inp_dataset_collections, current_user_roles) + self._record_outputs(job, out_data, output_collections) job.object_store_id = object_store_populator.object_store_id if job_params: - job.params = dumps( job_params ) + job.params = dumps(job_params) job.set_handler(tool.get_job_handler(job_params)) - trans.sa_session.add( job ) + trans.sa_session.add(job) # Now that we have a job id, we can remap any outputs if this is a rerun and the user chose to continue dependent jobs # This functionality requires tracking jobs in the database. if app.config.track_jobs_in_database and rerun_remap_job_id is not None: try: - old_job = trans.sa_session.query( app.model.Job ).get(rerun_remap_job_id) + old_job = trans.sa_session.query(app.model.Job).get(rerun_remap_job_id) assert old_job is not None, '(%s/%s): Old job id is invalid' % (rerun_remap_job_id, job.id) assert old_job.tool_id == job.tool_id, '(%s/%s): Old tool id (%s) does not match rerun tool id (%s)' % (old_job.id, job.id, old_job.tool_id, job.tool_id) if trans.user is not None: assert old_job.user_id == trans.user.id, '(%s/%s): Old user id (%s) does not match rerun user id (%s)' % (old_job.id, job.id, old_job.user_id, trans.user.id) - elif trans.user is None and type( galaxy_session ) == trans.model.GalaxySession: + elif trans.user is None and type(galaxy_session) == trans.model.GalaxySession: assert old_job.session_id == galaxy_session.id, '(%s/%s): Old session id (%s) does not match rerun session id (%s)' % (old_job.id, job.id, old_job.session_id, galaxy_session.id) else: raise Exception('(%s/%s): Remapping via the API is not (yet) supported' % (old_job.id, job.id)) @@ -463,14 +465,14 @@ def handle_output( name, output, hidden=None ): if (trans.user is not None and job_to_remap.user_id == trans.user.id) or (trans.user is None and job_to_remap.session_id == galaxy_session.id): if job_to_remap.state == job_to_remap.states.PAUSED: job_to_remap.state = job_to_remap.states.NEW - for hda in [ dep_jtod.dataset for dep_jtod in job_to_remap.output_datasets ]: + for hda in [dep_jtod.dataset for dep_jtod in job_to_remap.output_datasets]: if hda.state == hda.states.PAUSED: hda.state = hda.states.NEW hda.info = None - input_values = dict( [ ( p.name, json.loads( p.value ) ) for p in job_to_remap.parameters ] ) - update_param( jtid.name, input_values, str( out_data[ jtod.name ].id ) ) + input_values = dict([(p.name, json.loads(p.value)) for p in job_to_remap.parameters]) + update_param(jtid.name, input_values, str(out_data[jtod.name].id)) for p in job_to_remap.parameters: - p.value = json.dumps( input_values[ p.name ] ) + p.value = json.dumps(input_values[p.name]) jtid.dataset = out_data[jtod.name] jtid.dataset.hid = jtod.dataset.hid log.info('Job %s input HDA %s remapped to new HDA %s' % (job_to_remap.id, jtod.dataset.id, jtid.dataset.id)) @@ -493,45 +495,45 @@ def handle_output( name, output, hidden=None ): if 'REDIRECT_URL' in incoming: # Get the dataset - there should only be 1 for name in inp_data.keys(): - dataset = inp_data[ name ] - redirect_url = tool.parse_redirect_url( dataset, incoming ) + dataset = inp_data[name] + redirect_url = tool.parse_redirect_url(dataset, incoming) # GALAXY_URL should be include in the tool params to enable the external application # to send back to the current Galaxy instance - GALAXY_URL = incoming.get( 'GALAXY_URL', None ) + GALAXY_URL = incoming.get('GALAXY_URL', None) assert GALAXY_URL is not None, "GALAXY_URL parameter missing in tool config." redirect_url += "&GALAXY_URL=%s" % GALAXY_URL # Job should not be queued, so set state to ok - job.set_state( app.model.Job.states.OK ) + job.set_state(app.model.Job.states.OK) job.info = "Redirected to: %s" % redirect_url - trans.sa_session.add( job ) + trans.sa_session.add(job) trans.sa_session.flush() - trans.response.send_redirect( url_for( controller='tool_runner', action='redirect', redirect_url=redirect_url ) ) + trans.response.send_redirect(url_for(controller='tool_runner', action='redirect', redirect_url=redirect_url)) else: # Put the job in the queue if tracking in memory - app.job_queue.put( job.id, job.tool_id ) - trans.log_event( "Added job to the job queue, id: %s" % str(job.id), tool_id=job.tool_id ) + app.job_queue.put(job.id, job.tool_id) + trans.log_event("Added job to the job queue, id: %s" % str(job.id), tool_id=job.tool_id) return job, out_data - def _wrapped_params( self, trans, tool, incoming ): - wrapped_params = WrappedParameters( trans, tool, incoming ) + def _wrapped_params(self, trans, tool, incoming): + wrapped_params = WrappedParameters(trans, tool, incoming) return wrapped_params - def _get_on_text( self, inp_data ): + def _get_on_text(self, inp_data): input_names = [] - for name, data in reversed( inp_data.items() ): - if getattr( data, "hid", None ): - input_names.append( 'data %s' % data.hid ) + for name, data in reversed(inp_data.items()): + if getattr(data, "hid", None): + input_names.append('data %s' % data.hid) - return on_text_for_names( input_names ) + return on_text_for_names(input_names) - def _new_job_for_session( self, trans, tool, history ): + def _new_job_for_session(self, trans, tool, history): job = trans.app.model.Job() galaxy_session = None - if hasattr( trans, "get_galaxy_session" ): + if hasattr(trans, "get_galaxy_session"): galaxy_session = trans.get_galaxy_session() # If we're submitting from the API, there won't be a session. - if type( galaxy_session ) == trans.model.GalaxySession: + if type(galaxy_session) == trans.model.GalaxySession: job.session_id = galaxy_session.id if trans.user is not None: job.user_id = trans.user.id @@ -544,75 +546,75 @@ def _new_job_for_session( self, trans, tool, history ): job.tool_version = "1.0.0" return job, galaxy_session - def _record_inputs( self, trans, tool, job, incoming, inp_data, inp_dataset_collections, current_user_roles ): + def _record_inputs(self, trans, tool, job, incoming, inp_data, inp_dataset_collections, current_user_roles): # FIXME: Don't need all of incoming here, just the defined parameters # from the tool. We need to deal with tools that pass all post # parameters to the command as a special case. reductions = {} for name, dataset_collection_info_pairs in inp_dataset_collections.items(): - for ( dataset_collection, reduced ) in dataset_collection_info_pairs: + for (dataset_collection, reduced) in dataset_collection_info_pairs: if reduced: if name not in reductions: reductions[name] = [] reductions[name].append(dataset_collection) # TODO: verify can have multiple with same name, don't want to loose tracability - job.add_input_dataset_collection( name, dataset_collection ) + job.add_input_dataset_collection(name, dataset_collection) # If this an input collection is a reduction, we expanded it for dataset security, type # checking, and such, but the persisted input must be the original collection # so we can recover things like element identifier during tool command evaluation. - def restore_reduction_visitor( input, value, prefix, parent=None, prefixed_name=None, **kwargs ): - if prefixed_name in reductions and isinstance( input, DataToolParameter ): + def restore_reduction_visitor(input, value, prefix, parent=None, prefixed_name=None, **kwargs): + if prefixed_name in reductions and isinstance(input, DataToolParameter): target_dict = parent if not target_dict: target_dict = incoming - target_dict[ input.name ] = [] + target_dict[input.name] = [] for reduced_collection in reductions[prefixed_name]: - target_dict[ input.name ].append( { 'id': reduced_collection.id, 'src': 'hdca' } ) + target_dict[input.name].append({'id': reduced_collection.id, 'src': 'hdca'}) if reductions: - tool.visit_inputs( incoming, restore_reduction_visitor ) + tool.visit_inputs(incoming, restore_reduction_visitor) - for name, value in tool.params_to_strings( incoming, trans.app ).items(): - job.add_parameter( name, value ) - self._check_input_data_access( trans, job, inp_data, current_user_roles ) + for name, value in tool.params_to_strings(incoming, trans.app).items(): + job.add_parameter(name, value) + self._check_input_data_access(trans, job, inp_data, current_user_roles) - def _record_outputs( self, job, out_data, output_collections ): + def _record_outputs(self, job, out_data, output_collections): out_collections = output_collections.out_collections out_collection_instances = output_collections.out_collection_instances for name, dataset in out_data.items(): - job.add_output_dataset( name, dataset ) + job.add_output_dataset(name, dataset) for name, dataset_collection in out_collections.items(): - job.add_implicit_output_dataset_collection( name, dataset_collection ) + job.add_implicit_output_dataset_collection(name, dataset_collection) for name, dataset_collection_instance in out_collection_instances.items(): - job.add_output_dataset_collection( name, dataset_collection_instance ) + job.add_output_dataset_collection(name, dataset_collection_instance) - def _check_input_data_access( self, trans, job, inp_data, current_user_roles ): + def _check_input_data_access(self, trans, job, inp_data, current_user_roles): access_timer = ExecutionTimer() for name, dataset in inp_data.items(): if dataset: - if not trans.app.security_agent.can_access_dataset( current_user_roles, dataset.dataset ): + if not trans.app.security_agent.can_access_dataset(current_user_roles, dataset.dataset): raise Exception("User does not have permission to use a dataset (%s) provided for input." % dataset.id) if dataset in trans.sa_session: - job.add_input_dataset( name, dataset=dataset ) + job.add_input_dataset(name, dataset=dataset) else: - job.add_input_dataset( name, dataset_id=dataset.id ) + job.add_input_dataset(name, dataset_id=dataset.id) else: - job.add_input_dataset( name, None ) + job.add_input_dataset(name, None) job_str = job.log_str() log.info("Verified access to datasets for %s %s" % (job_str, access_timer)) - def get_output_name( self, output, dataset, tool, on_text, trans, incoming, history, params, job_params ): + def get_output_name(self, output, dataset, tool, on_text, trans, incoming, history, params, job_params): if output.label: params['tool'] = tool params['on_string'] = on_text - return fill_template( output.label, context=params ) + return fill_template(output.label, context=params) else: - return self._get_default_data_name( dataset, tool, on_text=on_text, trans=trans, incoming=incoming, history=history, params=params, job_params=job_params ) + return self._get_default_data_name(dataset, tool, on_text=on_text, trans=trans, incoming=incoming, history=history, params=params, job_params=job_params) - def set_metadata_defaults( self, output, dataset, tool, on_text, trans, incoming, history, params, job_params ): + def set_metadata_defaults(self, output, dataset, tool, on_text, trans, incoming, history, params, job_params): """ This allows to map names of input files to metadata default values. Example: @@ -625,32 +627,32 @@ def set_metadata_defaults( self, output, dataset, tool, on_text, trans, incoming if output.actions: for action in output.actions.actions: if action.tag == "metadata" and action.default: - metadata_new_value = fill_template( action.default, context=params ).split(",") + metadata_new_value = fill_template(action.default, context=params).split(",") dataset.metadata.__setattr__(str(action.name), metadata_new_value) - def _get_default_data_name( self, dataset, tool, on_text=None, trans=None, incoming=None, history=None, params=None, job_params=None, **kwd ): + def _get_default_data_name(self, dataset, tool, on_text=None, trans=None, incoming=None, history=None, params=None, job_params=None, **kwd): name = tool.name if on_text: - name += ( " on " + on_text ) + name += (" on " + on_text) return name -class ObjectStorePopulator( object ): +class ObjectStorePopulator(object): """ Small helper for interacting with the object store and making sure all datasets from a job end up with the same object_store_id. """ - def __init__( self, app ): + def __init__(self, app): self.object_store = app.object_store self.object_store_id = None - def set_object_store_id( self, data ): + def set_object_store_id(self, data): # Create an empty file immediately. The first dataset will be # created in the "default" store, all others will be created in # the same store as the first. data.dataset.object_store_id = self.object_store_id try: - self.object_store.create( data.dataset ) + self.object_store.create(data.dataset) except ObjectInvalid: raise Exception('Unable to create output dataset: object store is full') self.object_store_id = data.dataset.object_store_id # these will be the same thing after the first output @@ -710,7 +712,7 @@ def create_collection(self, output, name, tags=None, **element_kwds): collection_type=collection_type, **element_kwds ) - self.out_collections[ name ] = dc + self.out_collections[name] = dc else: hdca_name = self.tool_action.get_output_name( output, @@ -734,27 +736,27 @@ def create_collection(self, output, name, tags=None, **element_kwds): ) # name here is name of the output element - not name # of the hdca. - self.out_collection_instances[ name ] = hdca + self.out_collection_instances[name] = hdca -def on_text_for_names( input_names ): +def on_text_for_names(input_names): # input_names may contain duplicates... this is because the first value in # multiple input dataset parameters will appear twice once as param_name # and once as param_name1. unique_names = [] for name in input_names: if name not in unique_names: - unique_names.append( name ) + unique_names.append(name) input_names = unique_names # Build name for output datasets based on tool name and input names - if len( input_names ) == 1: + if len(input_names) == 1: on_text = input_names[0] - elif len( input_names ) == 2: + elif len(input_names) == 2: on_text = '%s and %s' % tuple(input_names[0:2]) - elif len( input_names ) == 3: + elif len(input_names) == 3: on_text = '%s, %s, and %s' % tuple(input_names[0:3]) - elif len( input_names ) > 3: + elif len(input_names) > 3: on_text = '%s, %s, and others' % tuple(input_names[0:2]) else: on_text = "" @@ -764,10 +766,10 @@ def on_text_for_names( input_names ): def filter_output(output, incoming): for filter in output.filters: try: - if not eval( filter.text.strip(), globals(), incoming ): + if not eval(filter.text.strip(), globals(), incoming): return True # do not create this dataset except Exception as e: - log.debug( 'Dataset output filter failed: %s' % e ) + log.debug('Dataset output filter failed: %s' % e) return False @@ -822,38 +824,38 @@ def determine_output_format(output, parameter_context, input_datasets, input_dat if output.change_format is not None: new_format_set = False for change_elem in output.change_format: - for when_elem in change_elem.findall( 'when' ): - check = when_elem.get( 'input', None ) + for when_elem in change_elem.findall('when'): + check = when_elem.get('input', None) if check is not None: try: if '$' not in check: # allow a simple name or more complex specifications check = '${%s}' % check - if str( fill_template( check, context=parameter_context ) ) == when_elem.get( 'value', None ): - ext = when_elem.get( 'format', ext ) + if str(fill_template(check, context=parameter_context)) == when_elem.get('value', None): + ext = when_elem.get('format', ext) except: # bad tag input value; possibly referencing a param within a different conditional when block or other nonexistent grouping construct continue else: - check = when_elem.get( 'input_dataset', None ) + check = when_elem.get('input_dataset', None) if check is not None: - check = input_datasets.get( check, None ) + check = input_datasets.get(check, None) # At this point check is a HistoryDatasetAssociation object. - check_format = when_elem.get( 'format', ext ) - check_value = when_elem.get( 'value', None ) - check_attribute = when_elem.get( 'attribute', None ) + check_format = when_elem.get('format', ext) + check_value = when_elem.get('value', None) + check_attribute = when_elem.get('attribute', None) if check is not None and check_value is not None and check_attribute is not None: # See if the attribute to be checked belongs to the HistoryDatasetAssociation object. - if hasattr( check, check_attribute ): - if str( getattr( check, check_attribute ) ) == str( check_value ): + if hasattr(check, check_attribute): + if str(getattr(check, check_attribute)) == str(check_value): ext = check_format new_format_set = True break # See if the attribute to be checked belongs to the metadata associated with the # HistoryDatasetAssociation object. if check.metadata is not None: - metadata_value = check.metadata.get( check_attribute, None ) + metadata_value = check.metadata.get(check_attribute, None) if metadata_value is not None: - if str( metadata_value ) == str( check_value ): + if str(metadata_value) == str(check_value): ext = check_format new_format_set = True break diff --git a/lib/galaxy/tools/actions/data_manager.py b/lib/galaxy/tools/actions/data_manager.py index d29bd4e828ac..bd26cdf3f7bf 100644 --- a/lib/galaxy/tools/actions/data_manager.py +++ b/lib/galaxy/tools/actions/data_manager.py @@ -2,18 +2,18 @@ from . import DefaultToolAction -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class DataManagerToolAction( DefaultToolAction ): +class DataManagerToolAction(DefaultToolAction): """Tool action used for Data Manager Tools""" - def execute( self, tool, trans, **kwds ): - rval = super( DataManagerToolAction, self ).execute( tool, trans, **kwds ) - if isinstance( rval, tuple ) and len( rval ) == 2 and isinstance( rval[0], trans.app.model.Job ): - assoc = trans.app.model.DataManagerJobAssociation( job=rval[0], data_manager_id=tool.data_manager_id ) - trans.sa_session.add( assoc ) + def execute(self, tool, trans, **kwds): + rval = super(DataManagerToolAction, self).execute(tool, trans, **kwds) + if isinstance(rval, tuple) and len(rval) == 2 and isinstance(rval[0], trans.app.model.Job): + assoc = trans.app.model.DataManagerJobAssociation(job=rval[0], data_manager_id=tool.data_manager_id) + trans.sa_session.add(assoc) trans.sa_session.flush() else: - log.error( "Got bad return value from DefaultToolAction.execute(): %s" % ( rval ) ) + log.error("Got bad return value from DefaultToolAction.execute(): %s" % (rval)) return rval diff --git a/lib/galaxy/tools/actions/data_source.py b/lib/galaxy/tools/actions/data_source.py index 9af06ea9e892..31ad51b48225 100644 --- a/lib/galaxy/tools/actions/data_source.py +++ b/lib/galaxy/tools/actions/data_source.py @@ -2,13 +2,13 @@ from . import DefaultToolAction -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class DataSourceToolAction( DefaultToolAction ): +class DataSourceToolAction(DefaultToolAction): """Tool action used for Data Source Tools""" - def _get_default_data_name( self, dataset, tool, on_text=None, trans=None, incoming=None, history=None, params=None, job_params=None, **kwd ): + def _get_default_data_name(self, dataset, tool, on_text=None, trans=None, incoming=None, history=None, params=None, job_params=None, **kwd): if incoming and 'name' in incoming: - return incoming[ 'name' ] - return super( DataSourceToolAction, self )._get_default_data_name( dataset, tool, on_text=on_text, trans=trans, incoming=incoming, history=history, params=params, job_params=job_params ) + return incoming['name'] + return super(DataSourceToolAction, self)._get_default_data_name(dataset, tool, on_text=on_text, trans=trans, incoming=incoming, history=history, params=params, job_params=job_params) diff --git a/lib/galaxy/tools/actions/history_imp_exp.py b/lib/galaxy/tools/actions/history_imp_exp.py index edd9aa9c67b9..a67b1b509ee1 100644 --- a/lib/galaxy/tools/actions/history_imp_exp.py +++ b/lib/galaxy/tools/actions/history_imp_exp.py @@ -6,13 +6,13 @@ from galaxy.tools.imp_exp import JobExportHistoryArchiveWrapper from galaxy.util.odict import odict -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class ImportHistoryToolAction( ToolAction ): +class ImportHistoryToolAction(ToolAction): """Tool action used for importing a history to an archive. """ - def execute( self, tool, trans, incoming={}, set_output_hid=False, overwrite=True, history=None, **kwargs ): + def execute(self, tool, trans, incoming={}, set_output_hid=False, overwrite=True, history=None, **kwargs): # # Create job. # @@ -30,7 +30,7 @@ def execute( self, tool, trans, incoming={}, set_output_hid=False, overwrite=Tru job.user_id = trans.user.id start_job_state = job.state # should be job.states.NEW job.state = job.states.WAITING # we need to set job state to something other than NEW, or else when tracking jobs in db it will be picked up before we have added input / output parameters - trans.sa_session.add( job ) + trans.sa_session.add(job) trans.sa_session.flush() # ensure job.id are available # @@ -41,47 +41,47 @@ def execute( self, tool, trans, incoming={}, set_output_hid=False, overwrite=Tru # Use abspath because mkdtemp() does not, contrary to the documentation, # always return an absolute path. - archive_dir = os.path.abspath( tempfile.mkdtemp() ) - jiha = trans.app.model.JobImportHistoryArchive( job=job, archive_dir=archive_dir ) - trans.sa_session.add( jiha ) + archive_dir = os.path.abspath(tempfile.mkdtemp()) + jiha = trans.app.model.JobImportHistoryArchive(job=job, archive_dir=archive_dir) + trans.sa_session.add(jiha) # # Add parameters to job_parameter table. # # Set additional parameters. - incoming[ '__DEST_DIR__' ] = jiha.archive_dir - for name, value in tool.params_to_strings( incoming, trans.app ).items(): - job.add_parameter( name, value ) + incoming['__DEST_DIR__'] = jiha.archive_dir + for name, value in tool.params_to_strings(incoming, trans.app).items(): + job.add_parameter(name, value) job.state = start_job_state # job inputs have been configured, restore initial job state job.set_handler(tool.get_job_handler(None)) trans.sa_session.flush() # Queue the job for execution - trans.app.job_queue.put( job.id, tool.id ) - trans.log_event( "Added import history job to the job queue, id: %s" % str(job.id), tool_id=job.tool_id ) + trans.app.job_queue.put(job.id, tool.id) + trans.log_event("Added import history job to the job queue, id: %s" % str(job.id), tool_id=job.tool_id) return job, odict() -class ExportHistoryToolAction( ToolAction ): +class ExportHistoryToolAction(ToolAction): """Tool action used for exporting a history to an archive. """ - def execute( self, tool, trans, incoming={}, set_output_hid=False, overwrite=True, history=None, **kwargs ): + def execute(self, tool, trans, incoming={}, set_output_hid=False, overwrite=True, history=None, **kwargs): # # Get history to export. # history = None for name, value in incoming.items(): - if isinstance( value, trans.app.model.History ): + if isinstance(value, trans.app.model.History): history_param_name = name history = value - del incoming[ history_param_name ] + del incoming[history_param_name] break if not history: - raise Exception( 'There is no history to export.' ) + raise Exception('There is no history to export.') # # Create the job and output dataset objects @@ -100,45 +100,45 @@ def execute( self, tool, trans, incoming={}, set_output_hid=False, overwrite=Tru job.user_id = trans.user.id start_job_state = job.state # should be job.states.NEW job.state = job.states.WAITING # we need to set job state to something other than NEW, or else when tracking jobs in db it will be picked up before we have added input / output parameters - trans.sa_session.add( job ) + trans.sa_session.add(job) # Create dataset that will serve as archive. archive_dataset = trans.app.model.Dataset() - trans.sa_session.add( archive_dataset ) + trans.sa_session.add(archive_dataset) trans.sa_session.flush() # ensure job.id and archive_dataset.id are available - trans.app.object_store.create( archive_dataset ) # set the object store id, create dataset (if applicable) + trans.app.object_store.create(archive_dataset) # set the object store id, create dataset (if applicable) # # Setup job and job wrapper. # # Add association for keeping track of job, history, archive relationship. - jeha = trans.app.model.JobExportHistoryArchive( job=job, history=history, - dataset=archive_dataset, - compressed=incoming[ 'compress' ] ) - trans.sa_session.add( jeha ) + jeha = trans.app.model.JobExportHistoryArchive(job=job, history=history, + dataset=archive_dataset, + compressed=incoming['compress']) + trans.sa_session.add(jeha) - job_wrapper = JobExportHistoryArchiveWrapper( job ) - cmd_line = job_wrapper.setup_job( trans, jeha, include_hidden=incoming[ 'include_hidden' ], - include_deleted=incoming[ 'include_deleted' ] ) + job_wrapper = JobExportHistoryArchiveWrapper(job) + cmd_line = job_wrapper.setup_job(trans, jeha, include_hidden=incoming['include_hidden'], + include_deleted=incoming['include_deleted']) # # Add parameters to job_parameter table. # # Set additional parameters. - incoming[ '__HISTORY_TO_EXPORT__' ] = history.id - incoming[ '__EXPORT_HISTORY_COMMAND_INPUTS_OPTIONS__' ] = cmd_line - for name, value in tool.params_to_strings( incoming, trans.app ).items(): - job.add_parameter( name, value ) + incoming['__HISTORY_TO_EXPORT__'] = history.id + incoming['__EXPORT_HISTORY_COMMAND_INPUTS_OPTIONS__'] = cmd_line + for name, value in tool.params_to_strings(incoming, trans.app).items(): + job.add_parameter(name, value) job.state = start_job_state # job inputs have been configured, restore initial job state job.set_handler(tool.get_job_handler(None)) trans.sa_session.flush() # Queue the job for execution - trans.app.job_queue.put( job.id, tool.id ) - trans.log_event( "Added export history job to the job queue, id: %s" % str(job.id), tool_id=job.tool_id ) + trans.app.job_queue.put(job.id, tool.id) + trans.log_event("Added export history job to the job queue, id: %s" % str(job.id), tool_id=job.tool_id) return job, odict() diff --git a/lib/galaxy/tools/actions/metadata.py b/lib/galaxy/tools/actions/metadata.py index c1c29a9cc62f..a86dee634da2 100644 --- a/lib/galaxy/tools/actions/metadata.py +++ b/lib/galaxy/tools/actions/metadata.py @@ -7,42 +7,42 @@ from . import ToolAction -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class SetMetadataToolAction( ToolAction ): +class SetMetadataToolAction(ToolAction): """Tool action used for setting external metadata on an existing dataset""" - def execute( self, tool, trans, incoming={}, set_output_hid=False, overwrite=True, history=None, job_params=None, **kwargs ): + def execute(self, tool, trans, incoming={}, set_output_hid=False, overwrite=True, history=None, job_params=None, **kwargs): """ Execute using a web transaction. """ - job, odict = self.execute_via_app( tool, trans.app, trans.get_galaxy_session().id, - trans.history.id, trans.user, incoming, set_output_hid, - overwrite, history, job_params ) + job, odict = self.execute_via_app(tool, trans.app, trans.get_galaxy_session().id, + trans.history.id, trans.user, incoming, set_output_hid, + overwrite, history, job_params) # FIXME: can remove this when logging in execute_via_app method. - trans.log_event( "Added set external metadata job to the job queue, id: %s" % str(job.id), tool_id=job.tool_id ) + trans.log_event("Added set external metadata job to the job queue, id: %s" % str(job.id), tool_id=job.tool_id) return job, odict - def execute_via_app( self, tool, app, session_id, history_id, user=None, - incoming={}, set_output_hid=False, overwrite=True, - history=None, job_params=None ): + def execute_via_app(self, tool, app, session_id, history_id, user=None, + incoming={}, set_output_hid=False, overwrite=True, + history=None, job_params=None): """ Execute using application. """ for name, value in incoming.items(): - if isinstance( value, app.model.HistoryDatasetAssociation ): + if isinstance(value, app.model.HistoryDatasetAssociation): dataset = value dataset_name = name type = 'hda' break - elif isinstance( value, app.model.LibraryDatasetDatasetAssociation ): + elif isinstance(value, app.model.LibraryDatasetDatasetAssociation): dataset = value dataset_name = name type = 'ldda' break else: - raise Exception( 'The dataset to set metadata on could not be determined.' ) + raise Exception('The dataset to set metadata on could not be determined.') sa_session = app.model.context @@ -54,7 +54,7 @@ def execute_via_app( self, tool, app, session_id, history_id, user=None, if user: job.user_id = user.id if job_params: - job.params = dumps( job_params ) + job.params = dumps(job_params) start_job_state = job.state # should be job.states.NEW try: # For backward compatibility, some tools may not have versions yet. @@ -62,38 +62,38 @@ def execute_via_app( self, tool, app, session_id, history_id, user=None, except: job.tool_version = "1.0.1" job.state = job.states.WAITING # we need to set job state to something other than NEW, or else when tracking jobs in db it will be picked up before we have added input / output parameters - job.set_handler(tool.get_job_handler( job_params )) - sa_session.add( job ) + job.set_handler(tool.get_job_handler(job_params)) + sa_session.add(job) sa_session.flush() # ensure job.id is available # add parameters to job_parameter table # Store original dataset state, so we can restore it. A separate table might be better (no chance of 'losing' the original state)? - incoming[ '__ORIGINAL_DATASET_STATE__' ] = dataset.state - input_paths = [DatasetPath( dataset.id, real_path=dataset.file_name, mutable=False )] + incoming['__ORIGINAL_DATASET_STATE__'] = dataset.state + input_paths = [DatasetPath(dataset.id, real_path=dataset.file_name, mutable=False)] app.object_store.create(job, base_dir='job_work', dir_only=True, extra_dir=str(job.id)) job_working_dir = app.object_store.get_filename(job, base_dir='job_work', dir_only=True, extra_dir=str(job.id)) - external_metadata_wrapper = JobExternalOutputMetadataWrapper( job ) - cmd_line = external_metadata_wrapper.setup_external_metadata( dataset, - sa_session, - exec_dir=None, - tmp_dir=job_working_dir, - dataset_files_path=app.model.Dataset.file_path, - output_fnames=input_paths, - config_root=app.config.root, - config_file=app.config.config_file, - datatypes_config=app.datatypes_registry.integrated_datatypes_configs, - job_metadata=None, - include_command=False, - max_metadata_value_size=app.config.max_metadata_value_size, - kwds={ 'overwrite' : overwrite } ) - incoming[ '__SET_EXTERNAL_METADATA_COMMAND_LINE__' ] = cmd_line - for name, value in tool.params_to_strings( incoming, app ).items(): - job.add_parameter( name, value ) + external_metadata_wrapper = JobExternalOutputMetadataWrapper(job) + cmd_line = external_metadata_wrapper.setup_external_metadata(dataset, + sa_session, + exec_dir=None, + tmp_dir=job_working_dir, + dataset_files_path=app.model.Dataset.file_path, + output_fnames=input_paths, + config_root=app.config.root, + config_file=app.config.config_file, + datatypes_config=app.datatypes_registry.integrated_datatypes_configs, + job_metadata=None, + include_command=False, + max_metadata_value_size=app.config.max_metadata_value_size, + kwds={'overwrite' : overwrite}) + incoming['__SET_EXTERNAL_METADATA_COMMAND_LINE__'] = cmd_line + for name, value in tool.params_to_strings(incoming, app).items(): + job.add_parameter(name, value) # add the dataset to job_to_input_dataset table if type == 'hda': - job.add_input_dataset( dataset_name, dataset ) + job.add_input_dataset(dataset_name, dataset) elif type == 'ldda': - job.add_input_library_dataset( dataset_name, dataset ) + job.add_input_library_dataset(dataset_name, dataset) # Need a special state here to show that metadata is being set and also allow the job to run # i.e. if state was set to 'running' the set metadata job would never run, as it would wait for input (the dataset to set metadata on) to be in a ready state dataset._state = dataset.states.SETTING_METADATA @@ -101,11 +101,11 @@ def execute_via_app( self, tool, app, session_id, history_id, user=None, sa_session.flush() # Queue the job for execution - app.job_queue.put( job.id, tool.id ) + app.job_queue.put(job.id, tool.id) # FIXME: need to add event logging to app and log events there rather than trans. # trans.log_event( "Added set external metadata job to the job queue, id: %s" % str(job.id), tool_id=job.tool_id ) # clear e.g. converted files - dataset.datatype.before_setting_metadata( dataset ) + dataset.datatype.before_setting_metadata(dataset) return job, odict() diff --git a/lib/galaxy/tools/actions/model_operations.py b/lib/galaxy/tools/actions/model_operations.py index eb10ae9959c3..d87c44e33a6f 100644 --- a/lib/galaxy/tools/actions/model_operations.py +++ b/lib/galaxy/tools/actions/model_operations.py @@ -7,21 +7,21 @@ ) from galaxy.util.odict import odict -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class ModelOperationToolAction( DefaultToolAction ): +class ModelOperationToolAction(DefaultToolAction): - def check_inputs_ready( self, tool, trans, incoming, history, execution_cache=None ): + def check_inputs_ready(self, tool, trans, incoming, history, execution_cache=None): if execution_cache is None: execution_cache = ToolExecutionCache(trans) current_user_roles = execution_cache.current_user_roles history, inp_data, inp_dataset_collections = self._collect_inputs(tool, trans, incoming, history, current_user_roles) - tool.check_inputs_ready( inp_data, inp_dataset_collections ) + tool.check_inputs_ready(inp_data, inp_dataset_collections) - def execute( self, tool, trans, incoming={}, set_output_hid=False, overwrite=True, history=None, job_params=None, mapping_over_collection=False, execution_cache=None, **kwargs ): + def execute(self, tool, trans, incoming={}, set_output_hid=False, overwrite=True, history=None, job_params=None, mapping_over_collection=False, execution_cache=None, **kwargs): if execution_cache is None: execution_cache = ToolExecutionCache(trans) @@ -29,13 +29,13 @@ def execute( self, tool, trans, incoming={}, set_output_hid=False, overwrite=Tru history, inp_data, inp_dataset_collections = self._collect_inputs(tool, trans, incoming, history, current_user_roles) # Build name for output datasets based on tool name and input names - on_text = self._get_on_text( inp_data ) + on_text = self._get_on_text(inp_data) # wrapped params are used by change_format action and by output.label; only perform this wrapping once, as needed - wrapped_params = self._wrapped_params( trans, tool, incoming ) + wrapped_params = self._wrapped_params(trans, tool, incoming) out_data = odict() - input_collections = dict( (k, v[0][0]) for k, v in inp_dataset_collections.items() ) + input_collections = dict((k, v[0][0]) for k, v in inp_dataset_collections.items()) output_collections = OutputCollections( trans, history, @@ -52,12 +52,12 @@ def execute( self, tool, trans, incoming={}, set_output_hid=False, overwrite=Tru # # Create job. # - job, galaxy_session = self._new_job_for_session( trans, tool, history ) - self._produce_outputs( trans, tool, out_data, output_collections, incoming=incoming, history=history ) - self._record_inputs( trans, tool, job, incoming, inp_data, inp_dataset_collections, current_user_roles ) - self._record_outputs( job, out_data, output_collections ) + job, galaxy_session = self._new_job_for_session(trans, tool, history) + self._produce_outputs(trans, tool, out_data, output_collections, incoming=incoming, history=history) + self._record_inputs(trans, tool, job, incoming, inp_data, inp_dataset_collections, current_user_roles) + self._record_outputs(job, out_data, output_collections) job.state = job.states.OK - trans.sa_session.add( job ) + trans.sa_session.add(job) trans.sa_session.flush() # ensure job.id are available # Queue the job for execution @@ -66,7 +66,7 @@ def execute( self, tool, trans, incoming={}, set_output_hid=False, overwrite=Tru log.info("Calling produce_outputs, tool is %s" % tool) return job, out_data - def _produce_outputs( self, trans, tool, out_data, output_collections, incoming, history, **kwargs ): - tool.produce_outputs( trans, out_data, output_collections, incoming, history=history ) - trans.sa_session.add_all( out_data.values() ) + def _produce_outputs(self, trans, tool, out_data, output_collections, incoming, history, **kwargs): + tool.produce_outputs(trans, out_data, output_collections, incoming, history=history) + trans.sa_session.add_all(out_data.values()) trans.sa_session.flush() diff --git a/lib/galaxy/tools/actions/upload.py b/lib/galaxy/tools/actions/upload.py index 41679939238c..4ce327067953 100644 --- a/lib/galaxy/tools/actions/upload.py +++ b/lib/galaxy/tools/actions/upload.py @@ -5,35 +5,35 @@ from . import ToolAction -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class UploadToolAction( ToolAction ): +class UploadToolAction(ToolAction): - def execute( self, tool, trans, incoming={}, set_output_hid=True, history=None, **kwargs ): + def execute(self, tool, trans, incoming={}, set_output_hid=True, history=None, **kwargs): dataset_upload_inputs = [] for input_name, input in tool.inputs.items(): if input.type == "upload_dataset": - dataset_upload_inputs.append( input ) - assert dataset_upload_inputs, Exception( "No dataset upload groups were found." ) + dataset_upload_inputs.append(input) + assert dataset_upload_inputs, Exception("No dataset upload groups were found.") persisting_uploads_timer = ExecutionTimer() - precreated_datasets = upload_common.get_precreated_datasets( trans, incoming, trans.app.model.HistoryDatasetAssociation ) - incoming = upload_common.persist_uploads( incoming ) + precreated_datasets = upload_common.get_precreated_datasets(trans, incoming, trans.app.model.HistoryDatasetAssociation) + incoming = upload_common.persist_uploads(incoming) log.debug("Persisted uploads %s" % persisting_uploads_timer) # We can pass an empty string as the cntrller here since it is used to check whether we # are in an admin view, and this tool is currently not used there. check_and_cleanup_timer = ExecutionTimer() - uploaded_datasets = upload_common.get_uploaded_datasets( trans, '', incoming, precreated_datasets, dataset_upload_inputs, history=history ) - upload_common.cleanup_unused_precreated_datasets( precreated_datasets ) + uploaded_datasets = upload_common.get_uploaded_datasets(trans, '', incoming, precreated_datasets, dataset_upload_inputs, history=history) + upload_common.cleanup_unused_precreated_datasets(precreated_datasets) if not uploaded_datasets: return None, 'No data was entered in the upload form, please go back and choose data to upload.' log.debug("Checked and cleaned uploads %s" % check_and_cleanup_timer) create_job_timer = ExecutionTimer() - json_file_path = upload_common.create_paramfile( trans, uploaded_datasets ) - data_list = [ ud.data for ud in uploaded_datasets ] - rval = upload_common.create_job( trans, incoming, tool, json_file_path, data_list, history=history ) + json_file_path = upload_common.create_paramfile(trans, uploaded_datasets) + data_list = [ud.data for ud in uploaded_datasets] + rval = upload_common.create_job(trans, incoming, tool, json_file_path, data_list, history=history) log.debug("Created upload job %s" % create_job_timer) return rval diff --git a/lib/galaxy/tools/actions/upload_common.py b/lib/galaxy/tools/actions/upload_common.py index 857acbcc2169..16f1a8982f65 100644 --- a/lib/galaxy/tools/actions/upload_common.py +++ b/lib/galaxy/tools/actions/upload_common.py @@ -11,12 +11,13 @@ from galaxy import datatypes, util from galaxy.exceptions import ObjectInvalid +from galaxy.managers import tags from galaxy.util.odict import odict -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -def persist_uploads( params ): +def persist_uploads(params): """ Turn any uploads in the submitted form to persisted files. """ @@ -24,161 +25,166 @@ def persist_uploads( params ): new_files = [] for upload_dataset in params['files']: f = upload_dataset['file_data'] - if isinstance( f, FieldStorage ): - assert not isinstance( f.file, StringIO ) + if isinstance(f, FieldStorage): + assert not isinstance(f.file, StringIO) assert f.file.name != '' - local_filename = util.mkstemp_ln( f.file.name, 'upload_file_data_' ) + local_filename = util.mkstemp_ln(f.file.name, 'upload_file_data_') f.file.close() - upload_dataset['file_data'] = dict( filename=f.filename, - local_filename=local_filename ) - elif type( f ) == dict and 'local_filename' not in f: - raise Exception( 'Uploaded file was encoded in a way not understood by Galaxy.' ) + upload_dataset['file_data'] = dict(filename=f.filename, + local_filename=local_filename) + elif type(f) == dict and 'local_filename' not in f: + raise Exception('Uploaded file was encoded in a way not understood by Galaxy.') if upload_dataset['url_paste'] and upload_dataset['url_paste'].strip() != '': - upload_dataset['url_paste'], is_multi_byte = datatypes.sniff.stream_to_file( StringIO( upload_dataset['url_paste'] ), prefix="strio_url_paste_" ) + upload_dataset['url_paste'], is_multi_byte = datatypes.sniff.stream_to_file(StringIO(upload_dataset['url_paste']), prefix="strio_url_paste_") else: upload_dataset['url_paste'] = None - new_files.append( upload_dataset ) + new_files.append(upload_dataset) params['files'] = new_files return params -def handle_library_params( trans, params, folder_id, replace_dataset=None ): +def handle_library_params(trans, params, folder_id, replace_dataset=None): # FIXME: the received params has already been parsed by util.Params() by the time it reaches here, # so no complex objects remain. This is not good because it does not allow for those objects to be # manipulated here. The received params should be the original kwd from the initial request. library_bunch = util.bunch.Bunch() library_bunch.replace_dataset = replace_dataset - library_bunch.message = params.get( 'ldda_message', '' ) + library_bunch.message = params.get('ldda_message', '') # See if we have any template field contents library_bunch.template_field_contents = {} - template_id = params.get( 'template_id', None ) - library_bunch.folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( trans.security.decode_id( folder_id ) ) + template_id = params.get('template_id', None) + library_bunch.folder = trans.sa_session.query(trans.app.model.LibraryFolder).get(trans.security.decode_id(folder_id)) # We are inheriting the folder's info_association, so we may have received inherited contents or we may have redirected # here after the user entered template contents ( due to errors ). - if template_id not in [ None, 'None' ]: - library_bunch.template = trans.sa_session.query( trans.app.model.FormDefinition ).get( template_id ) + if template_id not in [None, 'None']: + library_bunch.template = trans.sa_session.query(trans.app.model.FormDefinition).get(template_id) for field in library_bunch.template.fields: - field_name = field[ 'name' ] - if params.get( field_name, False ): - field_value = util.restore_text( params.get( field_name, '' ) ) - library_bunch.template_field_contents[ field_name ] = field_value + field_name = field['name'] + if params.get(field_name, False): + field_value = util.restore_text(params.get(field_name, '')) + library_bunch.template_field_contents[field_name] = field_value else: library_bunch.template = None library_bunch.roles = [] - for role_id in util.listify( params.get( 'roles', [] ) ): - role = trans.sa_session.query( trans.app.model.Role ).get( role_id ) - library_bunch.roles.append( role ) + for role_id in util.listify(params.get('roles', [])): + role = trans.sa_session.query(trans.app.model.Role).get(role_id) + library_bunch.roles.append(role) return library_bunch -def get_precreated_datasets( trans, params, data_obj, controller='root' ): +def get_precreated_datasets(trans, params, data_obj, controller='root'): """ Get any precreated datasets (when using asynchronous uploads). """ rval = [] async_datasets = [] - if params.get( 'async_datasets', None ) not in ["None", "", None]: + if params.get('async_datasets', None) not in ["None", "", None]: async_datasets = params['async_datasets'].split(',') current_user_roles = trans.get_current_user_roles() for id in async_datasets: try: - data = trans.sa_session.query( data_obj ).get( int( id ) ) + data = trans.sa_session.query(data_obj).get(int(id)) except: - log.exception( 'Unable to load precreated dataset (%s) sent in upload form' % id ) + log.exception('Unable to load precreated dataset (%s) sent in upload form' % id) continue if data_obj is trans.app.model.HistoryDatasetAssociation: if trans.user is None and trans.galaxy_session.current_history != data.history: - log.error( 'Got a precreated dataset (%s) but it does not belong to anonymous user\'s current session (%s)' % ( data.id, trans.galaxy_session.id ) ) + log.error('Got a precreated dataset (%s) but it does not belong to anonymous user\'s current session (%s)' % (data.id, trans.galaxy_session.id)) elif data.history.user != trans.user: - log.error( 'Got a precreated dataset (%s) but it does not belong to current user (%s)' % ( data.id, trans.user.id ) ) + log.error('Got a precreated dataset (%s) but it does not belong to current user (%s)' % (data.id, trans.user.id)) else: - rval.append( data ) + rval.append(data) elif data_obj is trans.app.model.LibraryDatasetDatasetAssociation: - if controller == 'library' and not trans.app.security_agent.can_add_library_item( current_user_roles, data.library_dataset.folder ): - log.error( 'Got a precreated dataset (%s) but this user (%s) is not allowed to write to it' % ( data.id, trans.user.id ) ) + if controller == 'library' and not trans.app.security_agent.can_add_library_item(current_user_roles, data.library_dataset.folder): + log.error('Got a precreated dataset (%s) but this user (%s) is not allowed to write to it' % (data.id, trans.user.id)) else: - rval.append( data ) + rval.append(data) return rval -def get_precreated_dataset( precreated_datasets, name ): +def get_precreated_dataset(precreated_datasets, name): """ Return a dataset matching a name from the list of precreated (via async upload) datasets. If there's more than one upload with the exact same name, we need to pop one (the first) so it isn't chosen next time. """ - names = [ d.name for d in precreated_datasets ] - if names.count( name ) > 0: - return precreated_datasets.pop( names.index( name ) ) + names = [d.name for d in precreated_datasets] + if names.count(name) > 0: + return precreated_datasets.pop(names.index(name)) else: return None -def cleanup_unused_precreated_datasets( precreated_datasets ): +def cleanup_unused_precreated_datasets(precreated_datasets): for data in precreated_datasets: - log.info( 'Cleaned up unclaimed precreated dataset (%s).' % ( data.id ) ) + log.info('Cleaned up unclaimed precreated dataset (%s).' % (data.id)) data.state = data.states.ERROR data.info = 'No file contents were available.' -def __new_history_upload( trans, uploaded_dataset, history=None, state=None ): +def __new_history_upload(trans, uploaded_dataset, history=None, state=None): if not history: history = trans.history - hda = trans.app.model.HistoryDatasetAssociation( name=uploaded_dataset.name, - extension=uploaded_dataset.file_type, - dbkey=uploaded_dataset.dbkey, - history=history, - create_dataset=True, - sa_session=trans.sa_session ) + hda = trans.app.model.HistoryDatasetAssociation(name=uploaded_dataset.name, + extension=uploaded_dataset.file_type, + dbkey=uploaded_dataset.dbkey, + history=history, + create_dataset=True, + sa_session=trans.sa_session) if state: hda.state = state else: hda.state = hda.states.QUEUED - trans.sa_session.add( hda ) + trans.sa_session.add(hda) trans.sa_session.flush() - history.add_dataset( hda, genome_build=uploaded_dataset.dbkey ) - permissions = trans.app.security_agent.history_get_default_permissions( history ) - trans.app.security_agent.set_all_dataset_permissions( hda.dataset, permissions ) + history.add_dataset(hda, genome_build=uploaded_dataset.dbkey) + permissions = trans.app.security_agent.history_get_default_permissions(history) + trans.app.security_agent.set_all_dataset_permissions(hda.dataset, permissions) trans.sa_session.flush() return hda -def __new_library_upload( trans, cntrller, uploaded_dataset, library_bunch, state=None ): +def __new_library_upload(trans, cntrller, uploaded_dataset, library_bunch, state=None): current_user_roles = trans.get_current_user_roles() - if not ( ( trans.user_is_admin() and cntrller in [ 'library_admin', 'api' ] ) or trans.app.security_agent.can_add_library_item( current_user_roles, library_bunch.folder ) ): + if not ((trans.user_is_admin() and cntrller in ['library_admin', 'api']) or trans.app.security_agent.can_add_library_item(current_user_roles, library_bunch.folder)): # This doesn't have to be pretty - the only time this should happen is if someone's being malicious. - raise Exception( "User is not authorized to add datasets to this library." ) + raise Exception("User is not authorized to add datasets to this library.") folder = library_bunch.folder - if uploaded_dataset.get( 'in_folder', False ): + if uploaded_dataset.get('in_folder', False): # Create subfolders if desired - for name in uploaded_dataset.in_folder.split( os.path.sep ): - trans.sa_session.refresh( folder ) - matches = [x for x in active_folders( trans, folder ) if x.name == name] + for name in uploaded_dataset.in_folder.split(os.path.sep): + trans.sa_session.refresh(folder) + matches = [x for x in active_folders(trans, folder) if x.name == name] if matches: folder = matches[0] else: - new_folder = trans.app.model.LibraryFolder( name=name, description='Automatically created by upload tool' ) + new_folder = trans.app.model.LibraryFolder(name=name, description='Automatically created by upload tool') new_folder.genome_build = trans.app.genome_builds.default_value - folder.add_folder( new_folder ) - trans.sa_session.add( new_folder ) + folder.add_folder(new_folder) + trans.sa_session.add(new_folder) trans.sa_session.flush() - trans.app.security_agent.copy_library_permissions( trans, folder, new_folder ) + trans.app.security_agent.copy_library_permissions(trans, folder, new_folder) folder = new_folder if library_bunch.replace_dataset: ld = library_bunch.replace_dataset else: - ld = trans.app.model.LibraryDataset( folder=folder, name=uploaded_dataset.name ) - trans.sa_session.add( ld ) + ld = trans.app.model.LibraryDataset(folder=folder, name=uploaded_dataset.name) + trans.sa_session.add(ld) trans.sa_session.flush() - trans.app.security_agent.copy_library_permissions( trans, folder, ld ) - ldda = trans.app.model.LibraryDatasetDatasetAssociation( name=uploaded_dataset.name, - extension=uploaded_dataset.file_type, - dbkey=uploaded_dataset.dbkey, - library_dataset=ld, - user=trans.user, - create_dataset=True, - sa_session=trans.sa_session ) - trans.sa_session.add( ldda ) + trans.app.security_agent.copy_library_permissions(trans, folder, ld) + ldda = trans.app.model.LibraryDatasetDatasetAssociation(name=uploaded_dataset.name, + extension=uploaded_dataset.file_type, + dbkey=uploaded_dataset.dbkey, + library_dataset=ld, + user=trans.user, + create_dataset=True, + sa_session=trans.sa_session) + if uploaded_dataset.get('tag_using_filenames', False): + tag_from_filename = os.path.splitext(os.path.basename(uploaded_dataset.name))[0] + tag_manager = tags.GalaxyTagManager(trans.sa_session) + tag_manager.apply_item_tag(item=ldda, user=trans.user, name='name', value=tag_from_filename) + + trans.sa_session.add(ldda) if state: ldda.state = state else: @@ -186,18 +192,18 @@ def __new_library_upload( trans, cntrller, uploaded_dataset, library_bunch, stat ldda.message = library_bunch.message trans.sa_session.flush() # Permissions must be the same on the LibraryDatasetDatasetAssociation and the associated LibraryDataset - trans.app.security_agent.copy_library_permissions( trans, ld, ldda ) + trans.app.security_agent.copy_library_permissions(trans, ld, ldda) if library_bunch.replace_dataset: # Copy the Dataset level permissions from replace_dataset to the new LibraryDatasetDatasetAssociation.dataset - trans.app.security_agent.copy_dataset_permissions( library_bunch.replace_dataset.library_dataset_dataset_association.dataset, ldda.dataset ) + trans.app.security_agent.copy_dataset_permissions(library_bunch.replace_dataset.library_dataset_dataset_association.dataset, ldda.dataset) else: # Copy the current user's DefaultUserPermissions to the new LibraryDatasetDatasetAssociation.dataset - trans.app.security_agent.set_all_dataset_permissions( ldda.dataset, trans.app.security_agent.user_get_default_permissions( trans.user ) ) - folder.add_library_dataset( ld, genome_build=uploaded_dataset.dbkey ) - trans.sa_session.add( folder ) + trans.app.security_agent.set_all_dataset_permissions(ldda.dataset, trans.app.security_agent.user_get_default_permissions(trans.user)) + folder.add_library_dataset(ld, genome_build=uploaded_dataset.dbkey) + trans.sa_session.add(folder) trans.sa_session.flush() ld.library_dataset_dataset_association_id = ldda.id - trans.sa_session.add( ld ) + trans.sa_session.add(ld) trans.sa_session.flush() # Handle template included in the upload form, if any. If the upload is not asynchronous ( e.g., URL paste ), # then the template and contents will be included in the library_bunch at this point. If the upload is @@ -208,48 +214,48 @@ def __new_library_upload( trans, cntrller, uploaded_dataset, library_bunch, stat # If the user has added field contents, we'll need to create a new form_values and info_association # for the new library_dataset_dataset_association object. # Create a new FormValues object, using the template we previously retrieved - form_values = trans.app.model.FormValues( library_bunch.template, library_bunch.template_field_contents ) - trans.sa_session.add( form_values ) + form_values = trans.app.model.FormValues(library_bunch.template, library_bunch.template_field_contents) + trans.sa_session.add(form_values) trans.sa_session.flush() # Create a new info_association between the current ldda and form_values # TODO: Currently info_associations at the ldda level are not inheritable to the associated LibraryDataset, # we need to figure out if this is optimal - info_association = trans.app.model.LibraryDatasetDatasetInfoAssociation( ldda, library_bunch.template, form_values ) - trans.sa_session.add( info_association ) + info_association = trans.app.model.LibraryDatasetDatasetInfoAssociation(ldda, library_bunch.template, form_values) + trans.sa_session.add(info_association) trans.sa_session.flush() # If roles were selected upon upload, restrict access to the Dataset to those roles if library_bunch.roles: for role in library_bunch.roles: - dp = trans.app.model.DatasetPermissions( trans.app.security_agent.permitted_actions.DATASET_ACCESS.action, ldda.dataset, role ) - trans.sa_session.add( dp ) + dp = trans.app.model.DatasetPermissions(trans.app.security_agent.permitted_actions.DATASET_ACCESS.action, ldda.dataset, role) + trans.sa_session.add(dp) trans.sa_session.flush() return ldda -def new_upload( trans, cntrller, uploaded_dataset, library_bunch=None, history=None, state=None ): +def new_upload(trans, cntrller, uploaded_dataset, library_bunch=None, history=None, state=None): if library_bunch: - return __new_library_upload( trans, cntrller, uploaded_dataset, library_bunch, state ) + return __new_library_upload(trans, cntrller, uploaded_dataset, library_bunch, state) else: - return __new_history_upload( trans, uploaded_dataset, history=history, state=state ) + return __new_history_upload(trans, uploaded_dataset, history=history, state=state) -def get_uploaded_datasets( trans, cntrller, params, precreated_datasets, dataset_upload_inputs, library_bunch=None, history=None ): +def get_uploaded_datasets(trans, cntrller, params, precreated_datasets, dataset_upload_inputs, library_bunch=None, history=None): uploaded_datasets = [] for dataset_upload_input in dataset_upload_inputs: - uploaded_datasets.extend( dataset_upload_input.get_uploaded_datasets( trans, params ) ) + uploaded_datasets.extend(dataset_upload_input.get_uploaded_datasets(trans, params)) for uploaded_dataset in uploaded_datasets: - data = get_precreated_dataset( precreated_datasets, uploaded_dataset.name ) + data = get_precreated_dataset(precreated_datasets, uploaded_dataset.name) if not data: - data = new_upload( trans, cntrller, uploaded_dataset, library_bunch=library_bunch, history=history ) + data = new_upload(trans, cntrller, uploaded_dataset, library_bunch=library_bunch, history=history) else: data.extension = uploaded_dataset.file_type data.dbkey = uploaded_dataset.dbkey data.uuid = uploaded_dataset.uuid - trans.sa_session.add( data ) + trans.sa_session.add(data) trans.sa_session.flush() if library_bunch: library_bunch.folder.genome_build = uploaded_dataset.dbkey - trans.sa_session.add( library_bunch.folder ) + trans.sa_session.add(library_bunch.folder) # Handle template included in the upload form, if any. If the upload is asynchronous ( e.g., file upload ), # then the template and contents will be included in the library_bunch at this point. If the upload is # not asynchronous ( e.g., URL paste ), then the template and contents will be included in the library_bunch @@ -259,14 +265,14 @@ def get_uploaded_datasets( trans, cntrller, params, precreated_datasets, dataset # If the user has added field contents, we'll need to create a new form_values and info_association # for the new library_dataset_dataset_association object. # Create a new FormValues object, using the template we previously retrieved - form_values = trans.app.model.FormValues( library_bunch.template, library_bunch.template_field_contents ) - trans.sa_session.add( form_values ) + form_values = trans.app.model.FormValues(library_bunch.template, library_bunch.template_field_contents) + trans.sa_session.add(form_values) trans.sa_session.flush() # Create a new info_association between the current ldda and form_values # TODO: Currently info_associations at the ldda level are not inheritable to the associated LibraryDataset, # we need to figure out if this is optimal - info_association = trans.app.model.LibraryDatasetDatasetInfoAssociation( data, library_bunch.template, form_values ) - trans.sa_session.add( info_association ) + info_association = trans.app.model.LibraryDatasetDatasetInfoAssociation(data, library_bunch.template, form_values) + trans.sa_session.add(info_association) trans.sa_session.flush() else: if not history: @@ -276,44 +282,44 @@ def get_uploaded_datasets( trans, cntrller, params, precreated_datasets, dataset return uploaded_datasets -def create_paramfile( trans, uploaded_datasets ): +def create_paramfile(trans, uploaded_datasets): """ Create the upload tool's JSON "param" file. """ - def _chown( path ): + def _chown(path): try: # get username from email/username pwent = trans.user.system_user_pwent(trans.app.config.real_system_username) cmd = shlex.split(trans.app.config.external_chown_script) - cmd.extend( [ path, pwent[0], str( pwent[3] ) ] ) - log.debug( 'Changing ownership of %s with: %s' % ( path, ' '.join( cmd ) ) ) - p = subprocess.Popen( cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) + cmd.extend([path, pwent[0], str(pwent[3])]) + log.debug('Changing ownership of %s with: %s' % (path, ' '.join(cmd))) + p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = p.communicate() assert p.returncode == 0, stderr except Exception as e: - log.warning( 'Changing ownership of uploaded file %s failed: %s' % ( path, str( e ) ) ) + log.warning('Changing ownership of uploaded file %s failed: %s' % (path, str(e))) # TODO: json_file should go in the working directory json_file = tempfile.mkstemp() json_file_path = json_file[1] - json_file = os.fdopen( json_file[0], 'w' ) + json_file = os.fdopen(json_file[0], 'w') for uploaded_dataset in uploaded_datasets: data = uploaded_dataset.data if uploaded_dataset.type == 'composite': # we need to init metadata before the job is dispatched data.init_meta() for meta_name, meta_value in uploaded_dataset.metadata.items(): - setattr( data.metadata, meta_name, meta_value ) - trans.sa_session.add( data ) + setattr(data.metadata, meta_name, meta_value) + trans.sa_session.add(data) trans.sa_session.flush() - json = dict( file_type=uploaded_dataset.file_type, - dataset_id=data.dataset.id, - dbkey=uploaded_dataset.dbkey, - type=uploaded_dataset.type, - metadata=uploaded_dataset.metadata, - primary_file=uploaded_dataset.primary_file, - composite_file_paths=uploaded_dataset.composite_files, - composite_files=dict( ( k, v.__dict__ ) for k, v in data.datatype.get_composite_files( data ).items() ) ) + json = dict(file_type=uploaded_dataset.file_type, + dataset_id=data.dataset.id, + dbkey=uploaded_dataset.dbkey, + type=uploaded_dataset.type, + metadata=uploaded_dataset.metadata, + primary_file=uploaded_dataset.primary_file, + composite_file_paths=uploaded_dataset.composite_files, + composite_files=dict((k, v.__dict__) for k, v in data.datatype.get_composite_files(data).items())) else: try: is_binary = uploaded_dataset.datatype.is_binary @@ -331,39 +337,41 @@ def _chown( path ): purge_source = uploaded_dataset.purge_source except: purge_source = True - json = dict( file_type=uploaded_dataset.file_type, - ext=uploaded_dataset.ext, - name=uploaded_dataset.name, - dataset_id=data.dataset.id, - dbkey=uploaded_dataset.dbkey, - type=uploaded_dataset.type, - is_binary=is_binary, - link_data_only=link_data_only, - uuid=uuid_str, - to_posix_lines=getattr(uploaded_dataset, "to_posix_lines", True), - purge_source=purge_source, - space_to_tab=uploaded_dataset.space_to_tab, - in_place=trans.app.config.external_chown_script is None, - path=uploaded_dataset.path ) + json = dict(file_type=uploaded_dataset.file_type, + ext=uploaded_dataset.ext, + name=uploaded_dataset.name, + dataset_id=data.dataset.id, + dbkey=uploaded_dataset.dbkey, + type=uploaded_dataset.type, + is_binary=is_binary, + link_data_only=link_data_only, + uuid=uuid_str, + to_posix_lines=getattr(uploaded_dataset, "to_posix_lines", True), + auto_decompress=getattr(uploaded_dataset, "auto_decompress", True), + purge_source=purge_source, + space_to_tab=uploaded_dataset.space_to_tab, + in_place=trans.app.config.external_chown_script is None, + check_content=trans.app.config.check_upload_content, + path=uploaded_dataset.path) # TODO: This will have to change when we start bundling inputs. # Also, in_place above causes the file to be left behind since the # user cannot remove it unless the parent directory is writable. if link_data_only == 'copy_files' and trans.app.config.external_chown_script: - _chown( uploaded_dataset.path ) - json_file.write( dumps( json ) + '\n' ) + _chown(uploaded_dataset.path) + json_file.write(dumps(json) + '\n') json_file.close() if trans.app.config.external_chown_script: - _chown( json_file_path ) + _chown(json_file_path) return json_file_path -def create_job( trans, params, tool, json_file_path, data_list, folder=None, history=None, job_params=None ): +def create_job(trans, params, tool, json_file_path, data_list, folder=None, history=None, job_params=None): """ Create the upload job. """ job = trans.app.model.Job() galaxy_session = trans.get_galaxy_session() - if type( galaxy_session ) == trans.model.GalaxySession: + if type(galaxy_session) == trans.model.GalaxySession: job.session_id = galaxy_session.id if trans.user is not None: job.user_id = trans.user.id @@ -375,55 +383,55 @@ def create_job( trans, params, tool, json_file_path, data_list, folder=None, his job.history_id = history.id job.tool_id = tool.id job.tool_version = tool.version - job.set_state( job.states.UPLOAD ) - trans.sa_session.add( job ) + job.set_state(job.states.UPLOAD) + trans.sa_session.add(job) trans.sa_session.flush() - log.info( 'tool %s created job id %d' % ( tool.id, job.id ) ) - trans.log_event( 'created job id %d' % job.id, tool_id=tool.id ) + log.info('tool %s created job id %d' % (tool.id, job.id)) + trans.log_event('created job id %d' % job.id, tool_id=tool.id) - for name, value in tool.params_to_strings( params, trans.app ).items(): - job.add_parameter( name, value ) - job.add_parameter( 'paramfile', dumps( json_file_path ) ) + for name, value in tool.params_to_strings(params, trans.app).items(): + job.add_parameter(name, value) + job.add_parameter('paramfile', dumps(json_file_path)) object_store_id = None - for i, dataset in enumerate( data_list ): + for i, dataset in enumerate(data_list): if folder: - job.add_output_library_dataset( 'output%i' % i, dataset ) + job.add_output_library_dataset('output%i' % i, dataset) else: - job.add_output_dataset( 'output%i' % i, dataset ) + job.add_output_dataset('output%i' % i, dataset) # Create an empty file immediately if not dataset.dataset.external_filename: dataset.dataset.object_store_id = object_store_id try: - trans.app.object_store.create( dataset.dataset ) + trans.app.object_store.create(dataset.dataset) except ObjectInvalid: raise Exception('Unable to create output dataset: object store is full') object_store_id = dataset.dataset.object_store_id - trans.sa_session.add( dataset ) + trans.sa_session.add(dataset) # open( dataset.file_name, "w" ).close() job.object_store_id = object_store_id - job.set_state( job.states.NEW ) - job.set_handler( tool.get_job_handler( None ) ) + job.set_state(job.states.NEW) + job.set_handler(tool.get_job_handler(None)) if job_params: for name, value in job_params.items(): - job.add_parameter( name, value ) - trans.sa_session.add( job ) + job.add_parameter(name, value) + trans.sa_session.add(job) trans.sa_session.flush() # Queue the job for execution - trans.app.job_queue.put( job.id, job.tool_id ) - trans.log_event( "Added job to the job queue, id: %s" % str(job.id), tool_id=job.tool_id ) + trans.app.job_queue.put(job.id, job.tool_id) + trans.log_event("Added job to the job queue, id: %s" % str(job.id), tool_id=job.tool_id) output = odict() - for i, v in enumerate( data_list ): - output[ 'output%i' % i ] = v + for i, v in enumerate(data_list): + output['output%i' % i] = v return job, output -def active_folders( trans, folder ): +def active_folders(trans, folder): # Stolen from galaxy.web.controllers.library_common (importing from which causes a circular issues). # Much faster way of retrieving all active sub-folders within a given folder than the # performance of the mapper. This query also eagerloads the permissions on each folder. - return trans.sa_session.query( trans.app.model.LibraryFolder ) \ - .filter_by( parent=folder, deleted=False ) \ - .options( eagerload_all( "actions" ) ) \ - .order_by( trans.app.model.LibraryFolder.table.c.name ) \ + return trans.sa_session.query(trans.app.model.LibraryFolder) \ + .filter_by(parent=folder, deleted=False) \ + .options(eagerload_all("actions")) \ + .order_by(trans.app.model.LibraryFolder.table.c.name) \ .all() diff --git a/lib/galaxy/tools/cache.py b/lib/galaxy/tools/cache.py index 87e6b6e18c77..527f0b583576 100644 --- a/lib/galaxy/tools/cache.py +++ b/lib/galaxy/tools/cache.py @@ -76,7 +76,7 @@ def expire_tool(self, tool_id): def cache_tool(self, config_filename, tool): tool_hash = md5_hash_file(config_filename) - tool_id = str( tool.id ) + tool_id = str(tool.id) self._hash_by_tool_paths[config_filename] = tool_hash self._mod_time_by_path[config_filename] = os.path.getmtime(config_filename) self._tool_paths_by_id[tool_id] = config_filename diff --git a/lib/galaxy/tools/cwl/parser.py b/lib/galaxy/tools/cwl/parser.py index 195fdf28e5ed..cb9a78d8e27f 100644 --- a/lib/galaxy/tools/cwl/parser.py +++ b/lib/galaxy/tools/cwl/parser.py @@ -125,7 +125,7 @@ def check_requirements(rec, tool=True): @six.add_metaclass(ABCMeta) -class ToolProxy( object ): +class ToolProxy(object): def __init__(self, tool, tool_path): self._tool = tool diff --git a/lib/galaxy/tools/cwl/representation.py b/lib/galaxy/tools/cwl/representation.py index fde19ffbf1fa..a67574cac7a1 100644 --- a/lib/galaxy/tools/cwl/representation.py +++ b/lib/galaxy/tools/cwl/representation.py @@ -89,8 +89,8 @@ def simple_value(input, param_dict_value, cwl_type=None): assert input_name in param_dict, "No value for %s in %s" % (input_name, param_dict) current_case = param_dict[input_name]["_cwl__type_"] if str(current_case) != "null": # str because it is a wrapped... - case_index = input.get_current_case( current_case ) - case_input = input.cases[ case_index ].inputs["_cwl__value_"] + case_index = input.get_current_case(current_case) + case_input = input.cases[case_index].inputs["_cwl__value_"] case_value = param_dict[input_name]["_cwl__value_"] input_json[input_name] = simple_value(case_input, case_value, cwl_type=current_case) else: @@ -162,8 +162,8 @@ def from_simple_value(input, param_dict_value, cwl_type=None): galaxy_request["%s|_cwl__type_" % input_name] = cwl_type if cwl_type != "null": current_case_index = input.get_current_case(cwl_type) - current_case_inputs = input.cases[ current_case_index ].inputs - current_case_input = current_case_inputs[ "_cwl__value_" ] + current_case_inputs = input.cases[current_case_index].inputs + current_case_input = current_case_inputs["_cwl__value_"] galaxy_value = from_simple_value(current_case_input, as_dict_value, cwl_type) galaxy_request["%s|_cwl__value_" % input_name] = galaxy_value elif as_dict_value is NOT_PRESENT: diff --git a/lib/galaxy/tools/data/__init__.py b/lib/galaxy/tools/data/__init__.py index 5da006e5fc7a..92aa6c995ed5 100644 --- a/lib/galaxy/tools/data/__init__.py +++ b/lib/galaxy/tools/data/__init__.py @@ -22,7 +22,7 @@ from galaxy.util.dictifiable import Dictifiable from galaxy.util.odict import odict -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) DEFAULT_TABLE_TYPE = 'tabular' @@ -58,43 +58,43 @@ def exists(self, path): return False -class ToolDataTableManager( object ): +class ToolDataTableManager(object): """Manages a collection of tool data tables""" - def __init__( self, tool_data_path, config_filename=None ): + def __init__(self, tool_data_path, config_filename=None): self.tool_data_path = tool_data_path # This stores all defined data table entries from both the tool_data_table_conf.xml file and the shed_tool_data_table_conf.xml file # at server startup. If tool shed repositories are installed that contain a valid file named tool_data_table_conf.xml.sample, entries # from that file are inserted into this dict at the time of installation. self.data_tables = {} self.tool_data_path_files = ToolDataPathFiles(self.tool_data_path) - for single_config_filename in util.listify( config_filename ): + for single_config_filename in util.listify(config_filename): if not single_config_filename: continue - self.load_from_config_file( single_config_filename, self.tool_data_path, from_shed_config=False ) + self.load_from_config_file(single_config_filename, self.tool_data_path, from_shed_config=False) - def __getitem__( self, key ): - return self.data_tables.__getitem__( key ) + def __getitem__(self, key): + return self.data_tables.__getitem__(key) - def __setitem__( self, key, value ): - return self.data_tables.__setitem__( key, value ) + def __setitem__(self, key, value): + return self.data_tables.__setitem__(key, value) - def __contains__( self, key ): - return self.data_tables.__contains__( key ) + def __contains__(self, key): + return self.data_tables.__contains__(key) - def get( self, name, default=None ): + def get(self, name, default=None): try: - return self[ name ] + return self[name] except KeyError: return default - def set( self, name, value ): - self[ name ] = value + def set(self, name, value): + self[name] = value - def get_tables( self ): + def get_tables(self): return self.data_tables - def load_from_config_file( self, config_filename, tool_data_path, from_shed_config=False): + def load_from_config_file(self, config_filename, tool_data_path, from_shed_config=False): """ This method is called under 3 conditions: @@ -105,24 +105,24 @@ def load_from_config_file( self, config_filename, tool_data_path, from_shed_conf Galaxy instance. In this case, we have 2 entry types to handle, files whose root tag is , for example: """ table_elems = [] - if not isinstance( config_filename, list ): - config_filename = [ config_filename ] + if not isinstance(config_filename, list): + config_filename = [config_filename] for filename in config_filename: - tree = util.parse_xml( filename ) + tree = util.parse_xml(filename) root = tree.getroot() - for table_elem in root.findall( 'table' ): - table = ToolDataTable.from_elem( table_elem, tool_data_path, from_shed_config, filename=filename, tool_data_path_files=self.tool_data_path_files ) - table_elems.append( table_elem ) + for table_elem in root.findall('table'): + table = ToolDataTable.from_elem(table_elem, tool_data_path, from_shed_config, filename=filename, tool_data_path_files=self.tool_data_path_files) + table_elems.append(table_elem) if table.name not in self.data_tables: - self.data_tables[ table.name ] = table - log.debug( "Loaded tool data table '%s' from file '%s'", table.name, filename ) + self.data_tables[table.name] = table + log.debug("Loaded tool data table '%s' from file '%s'", table.name, filename) else: - log.debug( "Loading another instance of data table '%s' from file '%s', attempting to merge content.", table.name, filename ) - self.data_tables[ table.name ].merge_tool_data_table( table, allow_duplicates=False ) # only merge content, do not persist to disk, do not allow duplicate rows when merging + log.debug("Loading another instance of data table '%s' from file '%s', attempting to merge content.", table.name, filename) + self.data_tables[table.name].merge_tool_data_table(table, allow_duplicates=False) # only merge content, do not persist to disk, do not allow duplicate rows when merging # FIXME: This does not account for an entry with the same unique build ID, but a different path. return table_elems - def add_new_entries_from_config_file( self, config_filename, tool_data_path, shed_tool_data_table_config, persist=False ): + def add_new_entries_from_config_file(self, config_filename, tool_data_path, shed_tool_data_table_config, persist=False): """ This method is called when a tool shed repository that includes a tool_data_table_conf.xml.sample file is being installed into a local galaxy instance. We have 2 cases to handle, files whose root tag is , for example:: @@ -146,56 +146,56 @@ def add_new_entries_from_config_file( self, config_filename, tool_data_path, she """ error_message = '' try: - table_elems = self.load_from_config_file( config_filename=config_filename, - tool_data_path=tool_data_path, - from_shed_config=True ) + table_elems = self.load_from_config_file(config_filename=config_filename, + tool_data_path=tool_data_path, + from_shed_config=True) except Exception as e: - error_message = 'Error attempting to parse file %s: %s' % ( str( os.path.split( config_filename )[ 1 ] ), str( e ) ) - log.debug( error_message ) + error_message = 'Error attempting to parse file %s: %s' % (str(os.path.split(config_filename)[1]), str(e)) + log.debug(error_message) table_elems = [] if persist: # Persist Galaxy's version of the changed tool_data_table_conf.xml file. - self.to_xml_file( shed_tool_data_table_config, table_elems ) + self.to_xml_file(shed_tool_data_table_config, table_elems) return table_elems, error_message - def to_xml_file( self, shed_tool_data_table_config, new_elems=None, remove_elems=None ): + def to_xml_file(self, shed_tool_data_table_config, new_elems=None, remove_elems=None): """ Write the current in-memory version of the shed_tool_data_table_conf.xml file to disk. remove_elems are removed before new_elems are added. """ - if not ( new_elems or remove_elems ): - log.debug( 'ToolDataTableManager.to_xml_file called without any elements to add or remove.' ) + if not (new_elems or remove_elems): + log.debug('ToolDataTableManager.to_xml_file called without any elements to add or remove.') return # no changes provided, no need to persist any changes if not new_elems: new_elems = [] if not remove_elems: remove_elems = [] - full_path = os.path.abspath( shed_tool_data_table_config ) + full_path = os.path.abspath(shed_tool_data_table_config) # FIXME: we should lock changing this file by other threads / head nodes try: - tree = util.parse_xml( full_path ) + tree = util.parse_xml(full_path) root = tree.getroot() - out_elems = [ elem for elem in root ] + out_elems = [elem for elem in root] except Exception as e: out_elems = [] - log.debug( 'Could not parse existing tool data table config, assume no existing elements: %s', e ) + log.debug('Could not parse existing tool data table config, assume no existing elements: %s', e) for elem in remove_elems: # handle multiple occurrences of remove elem in existing elems while elem in out_elems: - remove_elems.remove( elem ) + remove_elems.remove(elem) # add new elems - out_elems.extend( new_elems ) + out_elems.extend(new_elems) out_path_is_new = not os.path.exists(full_path) - with open( full_path, 'wb' ) as out: - out.write( '\n\n' ) + with open(full_path, 'wb') as out: + out.write('\n\n') for elem in out_elems: - out.write( util.xml_to_string( elem, pretty=True ) ) - out.write( '\n' ) - os.chmod( full_path, 0o644 ) + out.write(util.xml_to_string(elem, pretty=True)) + out.write('\n') + os.chmod(full_path, 0o644) if out_path_is_new: self.tool_data_path_files.update_files() - def reload_tables( self, table_names=None, path=None ): + def reload_tables(self, table_names=None, path=None): """ Reload tool data tables. If neither table_names nor path is given, reloads all tool data tables. """ @@ -205,11 +205,11 @@ def reload_tables( self, table_names=None, path=None ): table_names = self.get_table_names_by_path(path) else: table_names = list(tables.keys()) - elif not isinstance( table_names, list ): - table_names = [ table_names ] + elif not isinstance(table_names, list): + table_names = [table_names] for table_name in table_names: - tables[ table_name ].reload_from_files() - log.debug( "Reloaded tool data table '%s' from files.", table_name ) + tables[table_name].reload_from_files() + log.debug("Reloaded tool data table '%s' from files.", table_name) return table_names def get_table_names_by_path(self, path): @@ -221,20 +221,20 @@ def get_table_names_by_path(self, path): return list(table_names) -class ToolDataTable( object ): +class ToolDataTable(object): @classmethod - def from_elem( cls, table_elem, tool_data_path, from_shed_config, filename, tool_data_path_files ): - table_type = table_elem.get( 'type', 'tabular' ) + def from_elem(cls, table_elem, tool_data_path, from_shed_config, filename, tool_data_path_files): + table_type = table_elem.get('type', 'tabular') assert table_type in tool_data_table_types, "Unknown data table type '%s'" % type - return tool_data_table_types[ table_type ]( table_elem, tool_data_path, from_shed_config=from_shed_config, filename=filename, tool_data_path_files=tool_data_path_files ) + return tool_data_table_types[table_type](table_elem, tool_data_path, from_shed_config=from_shed_config, filename=filename, tool_data_path_files=tool_data_path_files) - def __init__( self, config_element, tool_data_path, from_shed_config=False, filename=None, tool_data_path_files=None ): - self.name = config_element.get( 'name' ) - self.comment_char = config_element.get( 'comment_char' ) - self.empty_field_value = config_element.get( 'empty_field_value', '' ) + def __init__(self, config_element, tool_data_path, from_shed_config=False, filename=None, tool_data_path_files=None): + self.name = config_element.get('name') + self.comment_char = config_element.get('comment_char') + self.empty_field_value = config_element.get('empty_field_value', '') self.empty_field_values = {} - self.allow_duplicate_entries = util.asbool( config_element.get( 'allow_duplicate_entries', True ) ) + self.allow_duplicate_entries = util.asbool(config_element.get('allow_duplicate_entries', True)) self.here = filename and os.path.dirname(filename) self.filenames = odict() self.tool_data_path = tool_data_path @@ -243,56 +243,56 @@ def __init__( self, config_element, tool_data_path, from_shed_config=False, file # increment this variable any time a new entry is added, or when the table is totally reloaded # This value has no external meaning, and does not represent an abstract version of the underlying data self._loaded_content_version = 1 - self._load_info = ( [ config_element, tool_data_path ], { 'from_shed_config': from_shed_config, 'tool_data_path_files': self.tool_data_path_files } ) + self._load_info = ([config_element, tool_data_path], {'from_shed_config': from_shed_config, 'tool_data_path_files': self.tool_data_path_files}) self._merged_load_info = [] - def _update_version( self, version=None ): + def _update_version(self, version=None): if version is not None: self._loaded_content_version = version else: self._loaded_content_version += 1 return self._loaded_content_version - def get_empty_field_by_name( self, name ): - return self.empty_field_values.get( name, self.empty_field_value ) + def get_empty_field_by_name(self, name): + return self.empty_field_values.get(name, self.empty_field_value) - def _add_entry( self, entry, allow_duplicates=True, persist=False, persist_on_error=False, entry_source=None, **kwd ): - raise NotImplementedError( "Abstract method" ) + def _add_entry(self, entry, allow_duplicates=True, persist=False, persist_on_error=False, entry_source=None, **kwd): + raise NotImplementedError("Abstract method") - def add_entry( self, entry, allow_duplicates=True, persist=False, persist_on_error=False, entry_source=None, **kwd ): - self._add_entry( entry, allow_duplicates=allow_duplicates, persist=persist, persist_on_error=persist_on_error, entry_source=entry_source, **kwd ) + def add_entry(self, entry, allow_duplicates=True, persist=False, persist_on_error=False, entry_source=None, **kwd): + self._add_entry(entry, allow_duplicates=allow_duplicates, persist=persist, persist_on_error=persist_on_error, entry_source=entry_source, **kwd) return self._update_version() - def add_entries( self, entries, allow_duplicates=True, persist=False, persist_on_error=False, entry_source=None, **kwd ): + def add_entries(self, entries, allow_duplicates=True, persist=False, persist_on_error=False, entry_source=None, **kwd): if entries: for entry in entries: - self.add_entry( entry, allow_duplicates=allow_duplicates, persist=persist, persist_on_error=persist_on_error, entry_source=entry_source, **kwd ) + self.add_entry(entry, allow_duplicates=allow_duplicates, persist=persist, persist_on_error=persist_on_error, entry_source=entry_source, **kwd) return self._loaded_content_version def _remove_entry(self, values): - raise NotImplementedError( "Abstract method" ) + raise NotImplementedError("Abstract method") def remove_entry(self, values): - self._remove_entry( values ) + self._remove_entry(values) return self._update_version() - def is_current_version( self, other_version ): + def is_current_version(self, other_version): return self._loaded_content_version == other_version - def merge_tool_data_table( self, other_table, allow_duplicates=True, persist=False, persist_on_error=False, entry_source=None, **kwd ): - raise NotImplementedError( "Abstract method" ) + def merge_tool_data_table(self, other_table, allow_duplicates=True, persist=False, persist_on_error=False, entry_source=None, **kwd): + raise NotImplementedError("Abstract method") - def reload_from_files( self ): + def reload_from_files(self): new_version = self._update_version() merged_info = self._merged_load_info - self.__init__( *self._load_info[0], **self._load_info[1] ) - self._update_version( version=new_version ) - for ( tool_data_table_class, load_info ) in merged_info: - self.merge_tool_data_table( tool_data_table_class( *load_info[0], **load_info[1] ), allow_duplicates=False ) + self.__init__(*self._load_info[0], **self._load_info[1]) + self._update_version(version=new_version) + for (tool_data_table_class, load_info) in merged_info: + self.merge_tool_data_table(tool_data_table_class(*load_info[0], **load_info[1]), allow_duplicates=False) return self._update_version() -class TabularToolDataTable( ToolDataTable, Dictifiable ): +class TabularToolDataTable(ToolDataTable, Dictifiable): """ Data stored in a tabular / separated value format on disk, allows multiple files to be merged but all must have the same column definitions:: @@ -304,53 +304,53 @@ class TabularToolDataTable( ToolDataTable, Dictifiable ): """ - dict_collection_visible_keys = [ 'name' ] + dict_collection_visible_keys = ['name'] type_key = 'tabular' - def __init__( self, config_element, tool_data_path, from_shed_config=False, filename=None, tool_data_path_files=None ): - super( TabularToolDataTable, self ).__init__( config_element, tool_data_path, from_shed_config, filename, tool_data_path_files) + def __init__(self, config_element, tool_data_path, from_shed_config=False, filename=None, tool_data_path_files=None): + super(TabularToolDataTable, self).__init__(config_element, tool_data_path, from_shed_config, filename, tool_data_path_files) self.config_element = config_element self.data = [] - self.configure_and_load( config_element, tool_data_path, from_shed_config ) + self.configure_and_load(config_element, tool_data_path, from_shed_config) - def configure_and_load( self, config_element, tool_data_path, from_shed_config=False, url_timeout=10 ): + def configure_and_load(self, config_element, tool_data_path, from_shed_config=False, url_timeout=10): """ Configure and load table from an XML element. """ - self.separator = config_element.get( 'separator', '\t' ) - self.comment_char = config_element.get( 'comment_char', '#' ) + self.separator = config_element.get('separator', '\t') + self.comment_char = config_element.get('comment_char', '#') # Configure columns - self.parse_column_spec( config_element ) + self.parse_column_spec(config_element) # store repo info if available: - repo_elem = config_element.find( 'tool_shed_repository' ) + repo_elem = config_element.find('tool_shed_repository') if repo_elem is not None: - repo_info = dict( tool_shed=repo_elem.find( 'tool_shed' ).text, name=repo_elem.find( 'repository_name' ).text, - owner=repo_elem.find( 'repository_owner' ).text, installed_changeset_revision=repo_elem.find( 'installed_changeset_revision' ).text ) + repo_info = dict(tool_shed=repo_elem.find('tool_shed').text, name=repo_elem.find('repository_name').text, + owner=repo_elem.find('repository_owner').text, installed_changeset_revision=repo_elem.find('installed_changeset_revision').text) else: repo_info = None # Read every file - for file_element in config_element.findall( 'file' ): + for file_element in config_element.findall('file'): tmp_file = None - filename = file_element.get( 'path', None ) + filename = file_element.get('path', None) if filename is None: # Handle URLs as files - filename = file_element.get( 'url', None ) + filename = file_element.get('url', None) if filename: - tmp_file = NamedTemporaryFile( prefix='TTDT_URL_%s-' % self.name ) + tmp_file = NamedTemporaryFile(prefix='TTDT_URL_%s-' % self.name) try: - tmp_file.write( urlopen( filename, timeout=url_timeout ).read() ) + tmp_file.write(urlopen(filename, timeout=url_timeout).read()) except Exception as e: - log.error( 'Error loading Data Table URL "%s": %s', filename, e ) + log.error('Error loading Data Table URL "%s": %s', filename, e) continue - log.debug( 'Loading Data Table URL "%s" as filename "%s".', filename, tmp_file.name ) + log.debug('Loading Data Table URL "%s" as filename "%s".', filename, tmp_file.name) filename = tmp_file.name tmp_file.flush() - filename = file_path = expand_here_template( filename, here=self.here ) + filename = file_path = expand_here_template(filename, here=self.here) found = False if file_path is None: - log.debug( "Encountered a file element (%s) that does not contain a path value when loading tool data table '%s'.", util.xml_to_string( file_element ), self.name ) + log.debug("Encountered a file element (%s) that does not contain a path value when loading tool data table '%s'.", util.xml_to_string(file_element), self.name) continue # FIXME: splitting on and merging paths from a configuration file when loading is wonky @@ -364,11 +364,11 @@ def configure_and_load( self, config_element, tool_data_path, from_shed_config=F # regular galaxy app has and uses tool_data_path. # We're loading a tool in the tool shed, so we cannot use the Galaxy tool-data # directory which is hard-coded into the tool_data_table_conf.xml entries. - filename = os.path.split( file_path )[ 1 ] - filename = os.path.join( tool_data_path, filename ) - if self.tool_data_path_files.exists( filename ): + filename = os.path.split(file_path)[1] + filename = os.path.join(tool_data_path, filename) + if self.tool_data_path_files.exists(filename): found = True - elif self.tool_data_path_files.exists( "%s.sample" % filename ) and not from_shed_config: + elif self.tool_data_path_files.exists("%s.sample" % filename) and not from_shed_config: log.info("Could not find tool data %s, reading sample" % filename) filename = "%s.sample" % filename found = True @@ -377,51 +377,51 @@ def configure_and_load( self, config_element, tool_data_path, from_shed_config=F # (e.g., ) which may not be the same value # as self.tool_data_path, we'll parse the path to get the filename and see if it is # in self.tool_data_path. - file_path, file_name = os.path.split( filename ) + file_path, file_name = os.path.split(filename) if file_path and file_path != self.tool_data_path: - corrected_filename = os.path.join( self.tool_data_path, file_name ) - if self.tool_data_path_files.exists( corrected_filename ): + corrected_filename = os.path.join(self.tool_data_path, file_name) + if self.tool_data_path_files.exists(corrected_filename): filename = corrected_filename found = True errors = [] if found: - self.extend_data_with( filename, errors=errors ) + self.extend_data_with(filename, errors=errors) self._update_version() else: self.missing_index_file = filename - log.warning( "Cannot find index file '%s' for tool data table '%s'" % ( filename, self.name ) ) + log.warning("Cannot find index file '%s' for tool data table '%s'" % (filename, self.name)) - if filename not in self.filenames or not self.filenames[ filename ][ 'found' ]: - self.filenames[ filename ] = dict( found=found, filename=filename, from_shed_config=from_shed_config, tool_data_path=tool_data_path, - config_element=config_element, tool_shed_repository=repo_info, errors=errors ) + if filename not in self.filenames or not self.filenames[filename]['found']: + self.filenames[filename] = dict(found=found, filename=filename, from_shed_config=from_shed_config, tool_data_path=tool_data_path, + config_element=config_element, tool_shed_repository=repo_info, errors=errors) else: - log.debug( "Filename '%s' already exists in filenames (%s), not adding", filename, list(self.filenames.keys()) ) + log.debug("Filename '%s' already exists in filenames (%s), not adding", filename, list(self.filenames.keys())) # Remove URL tmp file if tmp_file is not None: tmp_file.close() - def merge_tool_data_table( self, other_table, allow_duplicates=True, persist=False, persist_on_error=False, entry_source=None, **kwd ): - assert self.columns == other_table.columns, "Merging tabular data tables with non matching columns is not allowed: %s:%s != %s:%s" % ( self.name, self.columns, other_table.name, other_table.columns ) + def merge_tool_data_table(self, other_table, allow_duplicates=True, persist=False, persist_on_error=False, entry_source=None, **kwd): + assert self.columns == other_table.columns, "Merging tabular data tables with non matching columns is not allowed: %s:%s != %s:%s" % (self.name, self.columns, other_table.name, other_table.columns) # merge filename info for filename, info in other_table.filenames.items(): if filename not in self.filenames: - self.filenames[ filename ] = info + self.filenames[filename] = info # save info about table - self._merged_load_info.append( ( other_table.__class__, other_table._load_info ) ) + self._merged_load_info.append((other_table.__class__, other_table._load_info)) # If we are merging in a data table that does not allow duplicates, enforce that upon the data table if self.allow_duplicate_entries and not other_table.allow_duplicate_entries: - log.debug( 'While attempting to merge tool data table "%s", the other instance of the table specified that duplicate entries are not allowed, now deduplicating all previous entries.', self.name ) + log.debug('While attempting to merge tool data table "%s", the other instance of the table specified that duplicate entries are not allowed, now deduplicating all previous entries.', self.name) self.allow_duplicate_entries = False self._deduplicate_data() # add data entries and return current data table version - return self.add_entries( other_table.data, allow_duplicates=allow_duplicates, persist=persist, persist_on_error=persist_on_error, entry_source=entry_source, **kwd ) + return self.add_entries(other_table.data, allow_duplicates=allow_duplicates, persist=persist, persist_on_error=persist_on_error, entry_source=entry_source, **kwd) - def handle_found_index_file( self, filename ): + def handle_found_index_file(self, filename): self.missing_index_file = None - self.extend_data_with( filename ) + self.extend_data_with(filename) - def get_fields( self ): + def get_fields(self): return self.data def get_field(self, value): @@ -431,25 +431,25 @@ def get_field(self, value): rval = TabularToolDataField(i) return rval - def get_named_fields_list( self ): + def get_named_fields_list(self): rval = [] named_columns = self.get_column_name_list() for fields in self.get_fields(): field_dict = {} - for i, field in enumerate( fields ): - if i == len( named_columns ): + for i, field in enumerate(fields): + if i == len(named_columns): break field_name = named_columns[i] if field_name is None: field_name = i # check that this is supposed to be 0 based. - field_dict[ field_name ] = field - rval.append( field_dict ) + field_dict[field_name] = field + rval.append(field_dict) return rval - def get_version_fields( self ): - return ( self._loaded_content_version, self.get_fields() ) + def get_version_fields(self): + return (self._loaded_content_version, self.get_fields()) - def parse_column_spec( self, config_element ): + def parse_column_spec(self, config_element): """ Parse column definitions, which can either be a set of 'column' elements with a name and index (as in dynamic options config), or a shorthand @@ -459,37 +459,37 @@ def parse_column_spec( self, config_element ): A column named 'value' is required. """ self.columns = {} - if config_element.find( 'columns' ) is not None: - column_names = util.xml_text( config_element.find( 'columns' ) ) - column_names = [ n.strip() for n in column_names.split( ',' ) ] - for index, name in enumerate( column_names ): - self.columns[ name ] = index + if config_element.find('columns') is not None: + column_names = util.xml_text(config_element.find('columns')) + column_names = [n.strip() for n in column_names.split(',')] + for index, name in enumerate(column_names): + self.columns[name] = index self.largest_index = index else: self.largest_index = 0 - for column_elem in config_element.findall( 'column' ): - name = column_elem.get( 'name', None ) + for column_elem in config_element.findall('column'): + name = column_elem.get('name', None) assert name is not None, "Required 'name' attribute missing from column def" - index = column_elem.get( 'index', None ) + index = column_elem.get('index', None) assert index is not None, "Required 'index' attribute missing from column def" - index = int( index ) + index = int(index) self.columns[name] = index if index > self.largest_index: self.largest_index = index - empty_field_value = column_elem.get( 'empty_field_value', None ) + empty_field_value = column_elem.get('empty_field_value', None) if empty_field_value is not None: - self.empty_field_values[ name ] = empty_field_value + self.empty_field_values[name] = empty_field_value assert 'value' in self.columns, "Required 'value' column missing from column def" if 'name' not in self.columns: self.columns['name'] = self.columns['value'] - def extend_data_with( self, filename, errors=None ): + def extend_data_with(self, filename, errors=None): here = os.path.dirname(os.path.abspath(filename)) - self.data.extend( self.parse_file_fields( open( filename ), errors=errors, here=here ) ) + self.data.extend(self.parse_file_fields(open(filename), errors=errors, here=here)) if not self.allow_duplicate_entries: self._deduplicate_data() - def parse_file_fields( self, reader, errors=None, here="__HERE__" ): + def parse_file_fields(self, reader, errors=None, here="__HERE__"): """ Parse separated lines from file and return a list of tuples. @@ -498,79 +498,79 @@ def parse_file_fields( self, reader, errors=None, here="__HERE__" ): separator_char = "" if self.separator == "\t" else self.separator rval = [] - for i, line in enumerate( reader ): - if line.lstrip().startswith( self.comment_char ): + for i, line in enumerate(reader): + if line.lstrip().startswith(self.comment_char): continue - line = line.rstrip( "\n\r" ) + line = line.rstrip("\n\r") if line: - line = expand_here_template( line, here=here ) - fields = line.split( self.separator ) - if self.largest_index < len( fields ): - rval.append( fields ) + line = expand_here_template(line, here=here) + fields = line.split(self.separator) + if self.largest_index < len(fields): + rval.append(fields) else: - line_error = "Line %i in tool data table '%s' is invalid (HINT: '%s' characters must be used to separate fields):\n%s" % ( ( i + 1 ), self.name, separator_char, line ) + line_error = "Line %i in tool data table '%s' is invalid (HINT: '%s' characters must be used to separate fields):\n%s" % ((i + 1), self.name, separator_char, line) if errors is not None: - errors.append( line_error ) - log.warning( line_error ) + errors.append(line_error) + log.warning(line_error) if hasattr(reader, "name"): log.debug("Loaded %i lines from '%s' for '%s'", len(rval), reader.name, self.name) return rval - def get_column_name_list( self ): + def get_column_name_list(self): rval = [] - for i in range( self.largest_index + 1 ): + for i in range(self.largest_index + 1): found_column = False for name, index in self.columns.items(): if index == i: if not found_column: - rval.append( name ) + rval.append(name) elif name == 'value': # the column named 'value' always has priority over other named columns - rval[ -1 ] = name + rval[-1] = name found_column = True if not found_column: - rval.append( None ) + rval.append(None) return rval - def get_entry( self, query_attr, query_val, return_attr, default=None ): + def get_entry(self, query_attr, query_val, return_attr, default=None): """ Returns table entry associated with a col/val pair. """ - rval = self.get_entries( query_attr, query_val, return_attr, default=default, limit=1 ) + rval = self.get_entries(query_attr, query_val, return_attr, default=default, limit=1) if rval: return rval[0] return default - def get_entries( self, query_attr, query_val, return_attr, default=None, limit=None ): + def get_entries(self, query_attr, query_val, return_attr, default=None, limit=None): """ Returns table entry associated with a col/val pair. """ - query_col = self.columns.get( query_attr, None ) + query_col = self.columns.get(query_attr, None) if query_col is None: return default if return_attr is not None: - return_col = self.columns.get( return_attr, None ) + return_col = self.columns.get(return_attr, None) if return_col is None: return default rval = [] # Look for table entry. for fields in self.get_fields(): - if fields[ query_col ] == query_val: + if fields[query_col] == query_val: if return_attr is None: field_dict = {} - for i, col_name in enumerate( self.get_column_name_list() ): - field_dict[ col_name or i ] = fields[i] - rval.append( field_dict ) + for i, col_name in enumerate(self.get_column_name_list()): + field_dict[col_name or i] = fields[i] + rval.append(field_dict) else: - rval.append( fields[ return_col ] ) - if limit is not None and len( rval ) == limit: + rval.append(fields[return_col]) + if limit is not None and len(rval) == limit: break return rval or default - def get_filename_for_source( self, source, default=None ): + def get_filename_for_source(self, source, default=None): if source: # if dict, assume is compatible info dict, otherwise call method - if isinstance( source, dict ): + if isinstance(source, dict): source_repo_info = source else: source_repo_info = source.get_tool_shed_repository_info_dict() @@ -578,87 +578,87 @@ def get_filename_for_source( self, source, default=None ): source_repo_info = None filename = default for name, value in self.filenames.items(): - repo_info = value.get( 'tool_shed_repository', None ) - if ( not source_repo_info and not repo_info ) or ( source_repo_info and repo_info and source_repo_info == repo_info ): + repo_info = value.get('tool_shed_repository', None) + if (not source_repo_info and not repo_info) or (source_repo_info and repo_info and source_repo_info == repo_info): filename = name break return filename - def _add_entry( self, entry, allow_duplicates=True, persist=False, persist_on_error=False, entry_source=None, **kwd ): + def _add_entry(self, entry, allow_duplicates=True, persist=False, persist_on_error=False, entry_source=None, **kwd): # accepts dict or list of columns - if isinstance( entry, dict ): + if isinstance(entry, dict): fields = [] for column_name in self.get_column_name_list(): if column_name not in entry: - log.debug( "Using default column value for column '%s' when adding data table entry (%s) to table '%s'.", column_name, entry, self.name ) - field_value = self.get_empty_field_by_name( column_name ) + log.debug("Using default column value for column '%s' when adding data table entry (%s) to table '%s'.", column_name, entry, self.name) + field_value = self.get_empty_field_by_name(column_name) else: - field_value = entry[ column_name ] - fields.append( field_value ) + field_value = entry[column_name] + fields.append(field_value) else: fields = entry is_error = False - if self.largest_index < len( fields ): - fields = self._replace_field_separators( fields ) - if fields not in self.get_fields() or ( allow_duplicates and self.allow_duplicate_entries ): - self.data.append( fields ) + if self.largest_index < len(fields): + fields = self._replace_field_separators(fields) + if fields not in self.get_fields() or (allow_duplicates and self.allow_duplicate_entries): + self.data.append(fields) else: - log.debug( "Attempted to add fields (%s) to data table '%s', but this entry already exists and allow_duplicates is False.", fields, self.name ) + log.debug("Attempted to add fields (%s) to data table '%s', but this entry already exists and allow_duplicates is False.", fields, self.name) is_error = True else: - log.error( "Attempted to add fields (%s) to data table '%s', but there were not enough fields specified ( %i < %i ).", fields, self.name, len( fields ), self.largest_index + 1 ) + log.error("Attempted to add fields (%s) to data table '%s', but there were not enough fields specified ( %i < %i ).", fields, self.name, len(fields), self.largest_index + 1) is_error = True filename = None - if persist and ( not is_error or persist_on_error ): - filename = self.get_filename_for_source( entry_source ) + if persist and (not is_error or persist_on_error): + filename = self.get_filename_for_source(entry_source) if filename is None: # should we default to using any filename here instead? - log.error( "Unable to determine filename for persisting data table '%s' values: '%s'.", self.name, fields ) + log.error("Unable to determine filename for persisting data table '%s' values: '%s'.", self.name, fields) is_error = True else: # FIXME: Need to lock these files for editing - log.debug( "Persisting changes to file: %s", filename ) + log.debug("Persisting changes to file: %s", filename) try: - data_table_fh = open( filename, 'r+b' ) + data_table_fh = open(filename, 'r+b') except IOError as e: - log.warning( 'Error opening data table file (%s) with r+b, assuming file does not exist and will open as wb: %s', filename, e ) - data_table_fh = open( filename, 'wb' ) - if os.stat( filename )[6] != 0: + log.warning('Error opening data table file (%s) with r+b, assuming file does not exist and will open as wb: %s', filename, e) + data_table_fh = open(filename, 'wb') + if os.stat(filename)[6] != 0: # ensure last existing line ends with new line - data_table_fh.seek( -1, 2 ) # last char in file - last_char = data_table_fh.read( 1 ) - if last_char not in [ '\n', '\r' ]: - data_table_fh.write( '\n' ) - data_table_fh.write( "%s\n" % ( self.separator.join( fields ) ) ) + data_table_fh.seek(-1, 2) # last char in file + last_char = data_table_fh.read(1) + if last_char not in ['\n', '\r']: + data_table_fh.write('\n') + data_table_fh.write("%s\n" % (self.separator.join(fields))) return not is_error - def _remove_entry( self, values): + def _remove_entry(self, values): # update every file for filename in self.filenames: - if os.path.exists( filename ): - values = self._replace_field_separators( values ) - self.filter_file_fields( filename, values ) + if os.path.exists(filename): + values = self._replace_field_separators(values) + self.filter_file_fields(filename, values) else: - log.warning( "Cannot find index file '%s' for tool data table '%s'" % ( filename, self.name ) ) + log.warning("Cannot find index file '%s' for tool data table '%s'" % (filename, self.name)) self.reload_from_files() - def filter_file_fields( self, loc_file, values ): + def filter_file_fields(self, loc_file, values): """ Reads separated lines from file and print back only the lines that pass a filter. """ with open(loc_file) as reader: rval = "" for line in reader: - if line.lstrip().startswith( self.comment_char ): + if line.lstrip().startswith(self.comment_char): rval += line else: - line_s = line.rstrip( "\n\r" ) + line_s = line.rstrip("\n\r") if line_s: - fields = line_s.split( self.separator ) + fields = line_s.split(self.separator) if fields != values: rval += line @@ -667,7 +667,7 @@ def filter_file_fields( self, loc_file, values ): return rval - def _replace_field_separators( self, fields, separator=None, replace=None, comment_char=None ): + def _replace_field_separators(self, fields, separator=None, replace=None, comment_char=None): # make sure none of the fields contain separator # make sure separator replace is different from comment_char, # due to possible leading replace @@ -684,25 +684,25 @@ def _replace_field_separators( self, fields, separator=None, replace=None, comme replace = "_" else: replace = " " - return [x.replace( separator, replace ) for x in fields] + return [x.replace(separator, replace) for x in fields] - def _deduplicate_data( self ): + def _deduplicate_data(self): # Remove duplicate entries, without recreating self.data object dup_lines = [] hash_list = [] - for i, fields in enumerate( self.data ): - fields_hash = hash( self.separator.join( fields ) ) + for i, fields in enumerate(self.data): + fields_hash = hash(self.separator.join(fields)) if fields_hash in hash_list: - dup_lines.append( i ) - log.debug( 'Found duplicate entry in tool data table "%s", but duplicates are not allowed, removing additional entry for: "%s"', self.name, fields ) + dup_lines.append(i) + log.debug('Found duplicate entry in tool data table "%s", but duplicates are not allowed, removing additional entry for: "%s"', self.name, fields) else: - hash_list.append( fields_hash ) - for i in reversed( dup_lines ): - self.data.pop( i ) + hash_list.append(fields_hash) + for i in reversed(dup_lines): + self.data.pop(i) @property - def xml_string( self ): - return util.xml_to_string( self.config_element ) + def xml_string(self): + return util.xml_to_string(self.config_element) def to_dict(self, view='collection'): rval = super(TabularToolDataTable, self).to_dict() @@ -723,7 +723,7 @@ def __getitem__(self, key): return self.data[key] def get_base_path(self): - return os.path.normpath(os.path.abspath( self.data['path'] )) + return os.path.normpath(os.path.abspath(self.data['path'])) def get_base_dir(self): path = self.get_base_path() @@ -732,10 +732,10 @@ def get_base_dir(self): return path def clean_base_dir(self, path): - return re.sub( "^" + self.get_base_dir() + r"/*", "", path ) + return re.sub("^" + self.get_base_dir() + r"/*", "", path) def get_files(self): - return glob( self.get_base_path() + "*" ) + return glob(self.get_base_path() + "*") def get_filesize_map(self, rm_base_dir=False): out = {} @@ -766,9 +766,9 @@ def to_dict(self): def expand_here_template(content, here=None): if here and content: - content = string.Template(content).safe_substitute( { "__HERE__": here }) + content = string.Template(content).safe_substitute({"__HERE__": here}) return content # Registry of tool data types by type_key -tool_data_table_types = dict( [ ( cls.type_key, cls ) for cls in [ TabularToolDataTable ] ] ) +tool_data_table_types = dict([(cls.type_key, cls) for cls in [TabularToolDataTable]]) diff --git a/lib/galaxy/tools/data_manager/manager.py b/lib/galaxy/tools/data_manager/manager.py index 14f9bb966037..05d3a9b3e1ba 100644 --- a/lib/galaxy/tools/data_manager/manager.py +++ b/lib/galaxy/tools/data_manager/manager.py @@ -17,81 +17,81 @@ repository_util ) -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -SUPPORTED_DATA_TABLE_TYPES = ( TabularToolDataTable ) -VALUE_TRANSLATION_FUNCTIONS = dict( abspath=os.path.abspath ) +SUPPORTED_DATA_TABLE_TYPES = (TabularToolDataTable) +VALUE_TRANSLATION_FUNCTIONS = dict(abspath=os.path.abspath) DEFAULT_VALUE_TRANSLATION_TYPE = 'template' -class DataManagers( object ): - def __init__( self, app, xml_filename=None): +class DataManagers(object): + def __init__(self, app, xml_filename=None): self.app = app self.data_managers = odict() self.managed_data_tables = odict() self.tool_path = None self._reload_count = 0 self.filename = xml_filename or self.app.config.data_manager_config_file - for filename in util.listify( self.filename ): + for filename in util.listify(self.filename): if not filename: continue - self.load_from_xml( filename ) + self.load_from_xml(filename) if self.app.config.shed_data_manager_config_file: - self.load_from_xml( self.app.config.shed_data_manager_config_file, store_tool_path=True ) + self.load_from_xml(self.app.config.shed_data_manager_config_file, store_tool_path=True) - def load_from_xml( self, xml_filename, store_tool_path=True ): + def load_from_xml(self, xml_filename, store_tool_path=True): try: - tree = util.parse_xml( xml_filename ) + tree = util.parse_xml(xml_filename) except Exception as e: - log.error( 'There was an error parsing your Data Manager config file "%s": %s' % ( xml_filename, e ) ) + log.error('There was an error parsing your Data Manager config file "%s": %s' % (xml_filename, e)) return # we are not able to load any data managers root = tree.getroot() if root.tag != 'data_managers': - log.error( 'A data managers configuration must have a "data_managers" tag as the root. "%s" is present' % ( root.tag ) ) + log.error('A data managers configuration must have a "data_managers" tag as the root. "%s" is present' % (root.tag)) return if store_tool_path: - tool_path = root.get( 'tool_path', None ) + tool_path = root.get('tool_path', None) if tool_path is None: tool_path = self.app.config.tool_path if not tool_path: tool_path = '.' self.tool_path = tool_path - for data_manager_elem in root.findall( 'data_manager' ): - self.load_manager_from_elem( data_manager_elem, tool_path=self.tool_path ) + for data_manager_elem in root.findall('data_manager'): + self.load_manager_from_elem(data_manager_elem, tool_path=self.tool_path) - def load_manager_from_elem( self, data_manager_elem, tool_path=None, add_manager=True ): + def load_manager_from_elem(self, data_manager_elem, tool_path=None, add_manager=True): try: - data_manager = DataManager( self, data_manager_elem, tool_path=tool_path ) + data_manager = DataManager(self, data_manager_elem, tool_path=tool_path) except Exception as e: - log.error( "Error loading data_manager '%s':\n%s" % ( e, util.xml_to_string( data_manager_elem ) ) ) + log.error("Error loading data_manager '%s':\n%s" % (e, util.xml_to_string(data_manager_elem))) return None if add_manager: - self.add_manager( data_manager ) - log.debug( 'Loaded Data Manager: %s' % ( data_manager.id ) ) + self.add_manager(data_manager) + log.debug('Loaded Data Manager: %s' % (data_manager.id)) return data_manager - def add_manager( self, data_manager ): + def add_manager(self, data_manager): if data_manager.id in self.data_managers: - log.warning( "A data manager has been defined twice: %s " % ( data_manager.id ) ) - self.data_managers[ data_manager.id ] = data_manager + log.warning("A data manager has been defined twice: %s " % (data_manager.id)) + self.data_managers[data_manager.id] = data_manager for data_table_name in data_manager.data_tables.keys(): if data_table_name not in self.managed_data_tables: - self.managed_data_tables[ data_table_name ] = [] - self.managed_data_tables[ data_table_name ].append( data_manager ) + self.managed_data_tables[data_table_name] = [] + self.managed_data_tables[data_table_name].append(data_manager) - def get_manager( self, *args, **kwds ): - return self.data_managers.get( *args, **kwds ) + def get_manager(self, *args, **kwds): + return self.data_managers.get(*args, **kwds) - def remove_manager( self, manager_ids ): - if not isinstance( manager_ids, list ): - manager_ids = [ manager_ids ] + def remove_manager(self, manager_ids): + if not isinstance(manager_ids, list): + manager_ids = [manager_ids] for manager_id in manager_ids: - data_manager = self.get_manager( manager_id, None ) + data_manager = self.get_manager(manager_id, None) if data_manager is not None: - del self.data_managers[ manager_id ] + del self.data_managers[manager_id] # remove tool from toolbox if data_manager.tool: - self.app.toolbox.remove_tool_by_id( data_manager.tool.id ) + self.app.toolbox.remove_tool_by_id(data_manager.tool.id) # determine if any data_tables are no longer tracked for data_table_name in data_manager.data_tables.keys(): remove_data_table_tracking = True @@ -100,14 +100,14 @@ def remove_manager( self, manager_ids ): remove_data_table_tracking = False break if remove_data_table_tracking and data_table_name in self.managed_data_tables: - del self.managed_data_tables[ data_table_name ] + del self.managed_data_tables[data_table_name] -class DataManager( object ): +class DataManager(object): GUID_TYPE = 'data_manager' DEFAULT_VERSION = "0.0.1" - def __init__( self, data_managers, elem=None, tool_path=None ): + def __init__(self, data_managers, elem=None, tool_path=None): self.data_managers = data_managers self.declared_id = None self.name = None @@ -122,22 +122,22 @@ def __init__( self, data_managers, elem=None, tool_path=None ): self.tool_shed_repository_info_dict = None self.undeclared_tables = False if elem is not None: - self.load_from_element( elem, tool_path or self.data_managers.tool_path ) + self.load_from_element(elem, tool_path or self.data_managers.tool_path) - def load_from_element( self, elem, tool_path ): - assert elem.tag == 'data_manager', 'A data manager configuration must have a "data_manager" tag as the root. "%s" is present' % ( elem.tag ) - self.declared_id = elem.get( 'id', None ) - self.guid = elem.get( 'guid', None ) - path = elem.get( 'tool_file', None ) - self.version = elem.get( 'version', self.version ) + def load_from_element(self, elem, tool_path): + assert elem.tag == 'data_manager', 'A data manager configuration must have a "data_manager" tag as the root. "%s" is present' % (elem.tag) + self.declared_id = elem.get('id', None) + self.guid = elem.get('guid', None) + path = elem.get('tool_file', None) + self.version = elem.get('version', self.version) tool_shed_repository_id = None tool_guid = None if path is None: - tool_elem = elem.find( 'tool' ) - assert tool_elem is not None, "Error loading tool for data manager. Make sure that a tool_file attribute or a tool tag set has been defined:\n%s" % ( util.xml_to_string( elem ) ) - path = tool_elem.get( "file", None ) - tool_guid = tool_elem.get( "guid", None ) + tool_elem = elem.find('tool') + assert tool_elem is not None, "Error loading tool for data manager. Make sure that a tool_file attribute or a tool tag set has been defined:\n%s" % (util.xml_to_string(elem)) + path = tool_elem.get("file", None) + tool_guid = tool_elem.get("guid", None) # need to determine repository info so that dependencies will work correctly if hasattr(self.data_managers.app, 'tool_cache') and tool_guid in self.data_managers.app.tool_cache._tool_paths_by_id: path = self.data_managers.app.tool_cache._tool_paths_by_id[tool_guid] @@ -150,246 +150,246 @@ def load_from_element( self, elem, tool_path ): tool_shed_repository_id = self.data_managers.app.security.encode_id(tool_shed_repository.id) tool_path = "" else: - tool_shed_url = tool_elem.find( 'tool_shed' ).text + tool_shed_url = tool_elem.find('tool_shed').text # Handle protocol changes. - tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry( self.data_managers.app, tool_shed_url ) + tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry(self.data_managers.app, tool_shed_url) # The protocol is not stored in the database. - tool_shed = common_util.remove_protocol_from_tool_shed_url( tool_shed_url ) - repository_name = tool_elem.find( 'repository_name' ).text - repository_owner = tool_elem.find( 'repository_owner' ).text - installed_changeset_revision = tool_elem.find( 'installed_changeset_revision' ).text - self.tool_shed_repository_info_dict = dict( tool_shed=tool_shed, - name=repository_name, - owner=repository_owner, - installed_changeset_revision=installed_changeset_revision ) + tool_shed = common_util.remove_protocol_from_tool_shed_url(tool_shed_url) + repository_name = tool_elem.find('repository_name').text + repository_owner = tool_elem.find('repository_owner').text + installed_changeset_revision = tool_elem.find('installed_changeset_revision').text + self.tool_shed_repository_info_dict = dict(tool_shed=tool_shed, + name=repository_name, + owner=repository_owner, + installed_changeset_revision=installed_changeset_revision) tool_shed_repository = \ - repository_util.get_installed_repository( self.data_managers.app, - tool_shed=tool_shed, - name=repository_name, - owner=repository_owner, - installed_changeset_revision=installed_changeset_revision ) + repository_util.get_installed_repository(self.data_managers.app, + tool_shed=tool_shed, + name=repository_name, + owner=repository_owner, + installed_changeset_revision=installed_changeset_revision) if tool_shed_repository is None: - log.warning( 'Could not determine tool shed repository from database. This should only ever happen when running tests.' ) + log.warning('Could not determine tool shed repository from database. This should only ever happen when running tests.') # we'll set tool_path manually here from shed_conf_file tool_shed_repository_id = None try: - tool_path = util.parse_xml( elem.get( 'shed_conf_file' ) ).getroot().get( 'tool_path', tool_path ) + tool_path = util.parse_xml(elem.get('shed_conf_file')).getroot().get('tool_path', tool_path) except Exception as e: - log.error( 'Error determining tool_path for Data Manager during testing: %s', e ) + log.error('Error determining tool_path for Data Manager during testing: %s', e) else: - tool_shed_repository_id = self.data_managers.app.security.encode_id( tool_shed_repository.id ) + tool_shed_repository_id = self.data_managers.app.security.encode_id(tool_shed_repository.id) # use shed_conf_file to determine tool_path - shed_conf_file = elem.get( "shed_conf_file", None ) + shed_conf_file = elem.get("shed_conf_file", None) if shed_conf_file: - shed_conf = self.data_managers.app.toolbox.get_shed_config_dict_by_filename( shed_conf_file, None ) + shed_conf = self.data_managers.app.toolbox.get_shed_config_dict_by_filename(shed_conf_file, None) if shed_conf: - tool_path = shed_conf.get( "tool_path", tool_path ) - assert path is not None, "A tool file path could not be determined:\n%s" % ( util.xml_to_string( elem ) ) - self.load_tool( os.path.join( tool_path, path ), - guid=tool_guid, - data_manager_id=self.id, - tool_shed_repository_id=tool_shed_repository_id ) - self.name = elem.get( 'name', self.tool.name ) - self.description = elem.get( 'description', self.tool.description ) - self.undeclared_tables = util.asbool( elem.get( 'undeclared_tables', self.undeclared_tables ) ) + tool_path = shed_conf.get("tool_path", tool_path) + assert path is not None, "A tool file path could not be determined:\n%s" % (util.xml_to_string(elem)) + self.load_tool(os.path.join(tool_path, path), + guid=tool_guid, + data_manager_id=self.id, + tool_shed_repository_id=tool_shed_repository_id) + self.name = elem.get('name', self.tool.name) + self.description = elem.get('description', self.tool.description) + self.undeclared_tables = util.asbool(elem.get('undeclared_tables', self.undeclared_tables)) - for data_table_elem in elem.findall( 'data_table' ): - data_table_name = data_table_elem.get( "name" ) + for data_table_elem in elem.findall('data_table'): + data_table_name = data_table_elem.get("name") assert data_table_name is not None, "A name is required for a data table entry" if data_table_name not in self.data_tables: - self.data_tables[ data_table_name ] = odict() - output_elem = data_table_elem.find( 'output' ) + self.data_tables[data_table_name] = odict() + output_elem = data_table_elem.find('output') if output_elem is not None: - for column_elem in output_elem.findall( 'column' ): - column_name = column_elem.get( 'name', None ) + for column_elem in output_elem.findall('column'): + column_name = column_elem.get('name', None) assert column_name is not None, "Name is required for column entry" - data_table_coumn_name = column_elem.get( 'data_table_name', column_name ) - self.data_tables[ data_table_name ][ data_table_coumn_name ] = column_name - output_ref = column_elem.get( 'output_ref', None ) + data_table_coumn_name = column_elem.get('data_table_name', column_name) + self.data_tables[data_table_name][data_table_coumn_name] = column_name + output_ref = column_elem.get('output_ref', None) if output_ref is not None: if data_table_name not in self.output_ref_by_data_table: - self.output_ref_by_data_table[ data_table_name ] = {} - self.output_ref_by_data_table[ data_table_name ][ data_table_coumn_name ] = output_ref - value_translation_elems = column_elem.findall( 'value_translation' ) + self.output_ref_by_data_table[data_table_name] = {} + self.output_ref_by_data_table[data_table_name][data_table_coumn_name] = output_ref + value_translation_elems = column_elem.findall('value_translation') if value_translation_elems is not None: for value_translation_elem in value_translation_elems: value_translation = value_translation_elem.text if value_translation is not None: - value_translation_type = value_translation_elem.get( 'type', DEFAULT_VALUE_TRANSLATION_TYPE ) + value_translation_type = value_translation_elem.get('type', DEFAULT_VALUE_TRANSLATION_TYPE) if data_table_name not in self.value_translation_by_data_table_column: - self.value_translation_by_data_table_column[ data_table_name ] = {} - if data_table_coumn_name not in self.value_translation_by_data_table_column[ data_table_name ]: - self.value_translation_by_data_table_column[ data_table_name ][ data_table_coumn_name ] = [] + self.value_translation_by_data_table_column[data_table_name] = {} + if data_table_coumn_name not in self.value_translation_by_data_table_column[data_table_name]: + self.value_translation_by_data_table_column[data_table_name][data_table_coumn_name] = [] if value_translation_type == 'function': if value_translation in VALUE_TRANSLATION_FUNCTIONS: - value_translation = VALUE_TRANSLATION_FUNCTIONS[ value_translation ] + value_translation = VALUE_TRANSLATION_FUNCTIONS[value_translation] else: - raise ValueError( "Unsupported value translation function: '%s'" % ( value_translation ) ) + raise ValueError("Unsupported value translation function: '%s'" % (value_translation)) else: - assert value_translation_type == DEFAULT_VALUE_TRANSLATION_TYPE, ValueError( "Unsupported value translation type: '%s'" % ( value_translation_type ) ) - self.value_translation_by_data_table_column[ data_table_name ][ data_table_coumn_name ].append( value_translation ) + assert value_translation_type == DEFAULT_VALUE_TRANSLATION_TYPE, ValueError("Unsupported value translation type: '%s'" % (value_translation_type)) + self.value_translation_by_data_table_column[data_table_name][data_table_coumn_name].append(value_translation) - for move_elem in column_elem.findall( 'move' ): - move_type = move_elem.get( 'type', 'directory' ) - relativize_symlinks = move_elem.get( 'relativize_symlinks', False ) # TODO: should we instead always relativize links? - source_elem = move_elem.find( 'source' ) + for move_elem in column_elem.findall('move'): + move_type = move_elem.get('type', 'directory') + relativize_symlinks = move_elem.get('relativize_symlinks', False) # TODO: should we instead always relativize links? + source_elem = move_elem.find('source') if source_elem is None: source_base = None source_value = '' else: - source_base = source_elem.get( 'base', None ) + source_base = source_elem.get('base', None) source_value = source_elem.text - target_elem = move_elem.find( 'target' ) + target_elem = move_elem.find('target') if target_elem is None: target_base = None target_value = '' else: - target_base = target_elem.get( 'base', None ) + target_base = target_elem.get('base', None) target_value = target_elem.text if data_table_name not in self.move_by_data_table_column: - self.move_by_data_table_column[ data_table_name ] = {} - self.move_by_data_table_column[ data_table_name ][ data_table_coumn_name ] = \ - dict( type=move_type, - source_base=source_base, - source_value=source_value, - target_base=target_base, - target_value=target_value, - relativize_symlinks=relativize_symlinks ) + self.move_by_data_table_column[data_table_name] = {} + self.move_by_data_table_column[data_table_name][data_table_coumn_name] = \ + dict(type=move_type, + source_base=source_base, + source_value=source_value, + target_base=target_base, + target_value=target_value, + relativize_symlinks=relativize_symlinks) @property - def id( self ): + def id(self): return self.guid or self.declared_id # if we have a guid, we will use that as the data_manager id - def load_tool( self, tool_filename, guid=None, data_manager_id=None, tool_shed_repository_id=None ): + def load_tool(self, tool_filename, guid=None, data_manager_id=None, tool_shed_repository_id=None): toolbox = self.data_managers.app.toolbox - tool = toolbox.load_hidden_tool( tool_filename, - guid=guid, - data_manager_id=data_manager_id, - repository_id=tool_shed_repository_id, - use_cached=True ) - self.data_managers.app.toolbox.data_manager_tools[ tool.id ] = tool + tool = toolbox.load_hidden_tool(tool_filename, + guid=guid, + data_manager_id=data_manager_id, + repository_id=tool_shed_repository_id, + use_cached=True) + self.data_managers.app.toolbox.data_manager_tools[tool.id] = tool self.tool = tool return tool - def process_result( self, out_data ): + def process_result(self, out_data): data_manager_dicts = {} data_manager_dict = {} # TODO: fix this merging below for output_name, output_dataset in out_data.items(): try: - output_dict = json.loads( open( output_dataset.file_name ).read() ) + output_dict = json.loads(open(output_dataset.file_name).read()) except Exception as e: - log.warning( 'Error reading DataManagerTool json for "%s": %s' % ( output_name, e ) ) + log.warning('Error reading DataManagerTool json for "%s": %s' % (output_name, e)) continue - data_manager_dicts[ output_name ] = output_dict + data_manager_dicts[output_name] = output_dict for key, value in output_dict.items(): if key not in data_manager_dict: - data_manager_dict[ key ] = {} - data_manager_dict[ key ].update( value ) - data_manager_dict.update( output_dict ) + data_manager_dict[key] = {} + data_manager_dict[key].update(value) + data_manager_dict.update(output_dict) - data_tables_dict = data_manager_dict.get( 'data_tables', {} ) + data_tables_dict = data_manager_dict.get('data_tables', {}) for data_table_name in self.data_tables.keys(): - data_table_values = data_tables_dict.pop( data_table_name, None ) + data_table_values = data_tables_dict.pop(data_table_name, None) if not data_table_values: - log.warning( 'No values for data table "%s" were returned by the data manager "%s".' % ( data_table_name, self.id ) ) + log.warning('No values for data table "%s" were returned by the data manager "%s".' % (data_table_name, self.id)) continue # next data table - data_table = self.data_managers.app.tool_data_tables.get( data_table_name, None ) + data_table = self.data_managers.app.tool_data_tables.get(data_table_name, None) if data_table is None: - log.error( 'The data manager "%s" returned an unknown data table "%s" with new entries "%s". These entries will not be created. Please confirm that an entry for "%s" exists in your "%s" file.' % ( self.id, data_table_name, data_table_values, data_table_name, 'tool_data_table_conf.xml' ) ) + log.error('The data manager "%s" returned an unknown data table "%s" with new entries "%s". These entries will not be created. Please confirm that an entry for "%s" exists in your "%s" file.' % (self.id, data_table_name, data_table_values, data_table_name, 'tool_data_table_conf.xml')) continue # next table name - if not isinstance( data_table, SUPPORTED_DATA_TABLE_TYPES ): - log.error( 'The data manager "%s" returned an unsupported data table "%s" with type "%s" with new entries "%s". These entries will not be created. Please confirm that the data table is of a supported type (%s).' % ( self.id, data_table_name, type( data_table ), data_table_values, SUPPORTED_DATA_TABLE_TYPES ) ) + if not isinstance(data_table, SUPPORTED_DATA_TABLE_TYPES): + log.error('The data manager "%s" returned an unsupported data table "%s" with type "%s" with new entries "%s". These entries will not be created. Please confirm that the data table is of a supported type (%s).' % (self.id, data_table_name, type(data_table), data_table_values, SUPPORTED_DATA_TABLE_TYPES)) continue # next table name output_ref_values = {} if data_table_name in self.output_ref_by_data_table: - for data_table_column, output_ref in self.output_ref_by_data_table[ data_table_name ].items(): - output_ref_dataset = out_data.get( output_ref, None ) + for data_table_column, output_ref in self.output_ref_by_data_table[data_table_name].items(): + output_ref_dataset = out_data.get(output_ref, None) assert output_ref_dataset is not None, "Referenced output was not found." - output_ref_values[ data_table_column ] = output_ref_dataset + output_ref_values[data_table_column] = output_ref_dataset - if not isinstance( data_table_values, list ): - data_table_values = [ data_table_values ] + if not isinstance(data_table_values, list): + data_table_values = [data_table_values] for data_table_row in data_table_values: - data_table_value = dict( **data_table_row ) # keep original values here + data_table_value = dict(**data_table_row) # keep original values here for name, value in data_table_row.items(): # FIXME: need to loop through here based upon order listed in data_manager config if name in output_ref_values: - self.process_move( data_table_name, name, output_ref_values[ name ].extra_files_path, **data_table_value ) - data_table_value[ name ] = self.process_value_translation( data_table_name, name, **data_table_value ) - data_table.add_entry( data_table_value, persist=True, entry_source=self ) + self.process_move(data_table_name, name, output_ref_values[name].extra_files_path, **data_table_value) + data_table_value[name] = self.process_value_translation(data_table_name, name, **data_table_value) + data_table.add_entry(data_table_value, persist=True, entry_source=self) send_control_task(self.data_managers.app, 'reload_tool_data_tables', noop_self=True, - kwargs={'table_name': data_table_name} ) + kwargs={'table_name': data_table_name}) if self.undeclared_tables and data_tables_dict: # We handle the data move, by just moving all the data out of the extra files path # moving a directory and the target already exists, we move the contents instead - log.debug( 'Attempting to add entries for undeclared tables: %s.', ', '.join( data_tables_dict.keys() ) ) + log.debug('Attempting to add entries for undeclared tables: %s.', ', '.join(data_tables_dict.keys())) for ref_file in out_data.values(): - util.move_merge( ref_file.extra_files_path, self.data_managers.app.config.galaxy_data_manager_data_path ) - path_column_names = [ 'path' ] + util.move_merge(ref_file.extra_files_path, self.data_managers.app.config.galaxy_data_manager_data_path) + path_column_names = ['path'] for data_table_name, data_table_values in data_tables_dict.items(): - data_table = self.data_managers.app.tool_data_tables.get( data_table_name, None ) - if not isinstance( data_table_values, list ): - data_table_values = [ data_table_values ] + data_table = self.data_managers.app.tool_data_tables.get(data_table_name, None) + if not isinstance(data_table_values, list): + data_table_values = [data_table_values] for data_table_row in data_table_values: - data_table_value = dict( **data_table_row ) # keep original values here + data_table_value = dict(**data_table_row) # keep original values here for name, value in data_table_row.items(): if name in path_column_names: - data_table_value[ name ] = os.path.abspath( os.path.join( self.data_managers.app.config.galaxy_data_manager_data_path, value ) ) - data_table.add_entry( data_table_value, persist=True, entry_source=self ) + data_table_value[name] = os.path.abspath(os.path.join(self.data_managers.app.config.galaxy_data_manager_data_path, value)) + data_table.add_entry(data_table_value, persist=True, entry_source=self) send_control_task(self.data_managers.app, 'reload_tool_data_tables', noop_self=True, - kwargs={'table_name': data_table_name} ) + kwargs={'table_name': data_table_name}) else: for data_table_name, data_table_values in data_tables_dict.items(): # tool returned extra data table entries, but data table was not declared in data manager # do not add these values, but do provide messages - log.warning( 'The data manager "%s" returned an undeclared data table "%s" with new entries "%s". These entries will not be created. Please confirm that an entry for "%s" exists in your "%s" file.' % ( self.id, data_table_name, data_table_values, data_table_name, self.data_managers.filename ) ) + log.warning('The data manager "%s" returned an undeclared data table "%s" with new entries "%s". These entries will not be created. Please confirm that an entry for "%s" exists in your "%s" file.' % (self.id, data_table_name, data_table_values, data_table_name, self.data_managers.filename)) - def process_move( self, data_table_name, column_name, source_base_path, relative_symlinks=False, **kwd ): - if data_table_name in self.move_by_data_table_column and column_name in self.move_by_data_table_column[ data_table_name ]: - move_dict = self.move_by_data_table_column[ data_table_name ][ column_name ] - source = move_dict[ 'source_base' ] + def process_move(self, data_table_name, column_name, source_base_path, relative_symlinks=False, **kwd): + if data_table_name in self.move_by_data_table_column and column_name in self.move_by_data_table_column[data_table_name]: + move_dict = self.move_by_data_table_column[data_table_name][column_name] + source = move_dict['source_base'] if source is None: source = source_base_path else: - source = fill_template( source, GALAXY_DATA_MANAGER_DATA_PATH=self.data_managers.app.config.galaxy_data_manager_data_path, **kwd ) - if move_dict[ 'source_value' ]: - source = os.path.join( source, fill_template( move_dict[ 'source_value' ], GALAXY_DATA_MANAGER_DATA_PATH=self.data_managers.app.config.galaxy_data_manager_data_path, **kwd ) ) - target = move_dict[ 'target_base' ] + source = fill_template(source, GALAXY_DATA_MANAGER_DATA_PATH=self.data_managers.app.config.galaxy_data_manager_data_path, **kwd) + if move_dict['source_value']: + source = os.path.join(source, fill_template(move_dict['source_value'], GALAXY_DATA_MANAGER_DATA_PATH=self.data_managers.app.config.galaxy_data_manager_data_path, **kwd)) + target = move_dict['target_base'] if target is None: target = self.data_managers.app.config.galaxy_data_manager_data_path else: - target = fill_template( target, GALAXY_DATA_MANAGER_DATA_PATH=self.data_managers.app.config.galaxy_data_manager_data_path, **kwd ) - if move_dict[ 'target_value' ]: - target = os.path.join( target, fill_template( move_dict[ 'target_value' ], GALAXY_DATA_MANAGER_DATA_PATH=self.data_managers.app.config.galaxy_data_manager_data_path, **kwd ) ) + target = fill_template(target, GALAXY_DATA_MANAGER_DATA_PATH=self.data_managers.app.config.galaxy_data_manager_data_path, **kwd) + if move_dict['target_value']: + target = os.path.join(target, fill_template(move_dict['target_value'], GALAXY_DATA_MANAGER_DATA_PATH=self.data_managers.app.config.galaxy_data_manager_data_path, **kwd)) - if move_dict[ 'type' ] == 'file': - dirs = os.path.split( target )[0] + if move_dict['type'] == 'file': + dirs = os.path.split(target)[0] try: - os.makedirs( dirs ) + os.makedirs(dirs) except OSError as e: if e.errno != errno.EEXIST: raise e # moving a directory and the target already exists, we move the contents instead - util.move_merge( source, target ) + util.move_merge(source, target) - if move_dict.get( 'relativize_symlinks', False ): - util.relativize_symlinks( target ) + if move_dict.get('relativize_symlinks', False): + util.relativize_symlinks(target) return True return False - def process_value_translation( self, data_table_name, column_name, **kwd ): - value = kwd.get( column_name ) - if data_table_name in self.value_translation_by_data_table_column and column_name in self.value_translation_by_data_table_column[ data_table_name ]: - for value_translation in self.value_translation_by_data_table_column[ data_table_name ][ column_name ]: - if isinstance( value_translation, string_types ): - value = fill_template( value_translation, GALAXY_DATA_MANAGER_DATA_PATH=self.data_managers.app.config.galaxy_data_manager_data_path, **kwd ) + def process_value_translation(self, data_table_name, column_name, **kwd): + value = kwd.get(column_name) + if data_table_name in self.value_translation_by_data_table_column and column_name in self.value_translation_by_data_table_column[data_table_name]: + for value_translation in self.value_translation_by_data_table_column[data_table_name][column_name]: + if isinstance(value_translation, string_types): + value = fill_template(value_translation, GALAXY_DATA_MANAGER_DATA_PATH=self.data_managers.app.config.galaxy_data_manager_data_path, **kwd) else: - value = value_translation( value ) + value = value_translation(value) return value - def get_tool_shed_repository_info_dict( self ): + def get_tool_shed_repository_info_dict(self): return self.tool_shed_repository_info_dict diff --git a/lib/galaxy/tools/deps/__init__.py b/lib/galaxy/tools/deps/__init__.py index 0c568ea2fb6c..50b04bd28c24 100644 --- a/lib/galaxy/tools/deps/__init__.py +++ b/lib/galaxy/tools/deps/__init__.py @@ -23,13 +23,13 @@ from .resolvers.galaxy_packages import GalaxyPackageDependencyResolver from .resolvers.tool_shed_packages import ToolShedPackageDependencyResolver -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) CONFIG_VAL_NOT_FOUND = object() -def build_dependency_manager( config ): - if getattr( config, "use_tool_dependencies", False ): +def build_dependency_manager(config): + if getattr(config, "use_tool_dependencies", False): dependency_manager_kwds = { 'default_base_path': config.tool_dependency_dir, 'conf_file': config.dependency_resolvers_config_file, @@ -38,27 +38,27 @@ def build_dependency_manager( config ): if getattr(config, "use_cached_dependency_manager", False): dependency_manager = CachedDependencyManager(**dependency_manager_kwds) else: - dependency_manager = DependencyManager( **dependency_manager_kwds ) + dependency_manager = DependencyManager(**dependency_manager_kwds) else: dependency_manager = NullDependencyManager() return dependency_manager -class NullDependencyManager( object ): +class NullDependencyManager(object): dependency_resolvers = [] def uses_tool_shed_dependencies(self): return False - def dependency_shell_commands( self, requirements, **kwds ): + def dependency_shell_commands(self, requirements, **kwds): return [] - def find_dep( self, name, version=None, type='package', **kwds ): + def find_dep(self, name, version=None, type='package', **kwds): return NullDependency(version=version, name=name) -class DependencyManager( object ): +class DependencyManager(object): """ A DependencyManager attempts to resolve named and versioned dependencies by searching for them under a list of directories. Directories should be @@ -69,19 +69,20 @@ class DependencyManager( object ): and should each contain a file 'env.sh' which can be sourced to make the dependency available in the current shell environment. """ - def __init__( self, default_base_path, conf_file=None, app_config={} ): + + def __init__(self, default_base_path, conf_file=None, app_config={}): """ Create a new dependency manager looking for packages under the paths listed in `base_paths`. The default base path is app.config.tool_dependency_dir. """ - if not os.path.exists( default_base_path ): - log.warning( "Path '%s' does not exist, ignoring", default_base_path ) - if not os.path.isdir( default_base_path ): - log.warning( "Path '%s' is not directory, ignoring", default_base_path ) + if not os.path.exists(default_base_path): + log.warning("Path '%s' does not exist, ignoring", default_base_path) + if not os.path.isdir(default_base_path): + log.warning("Path '%s' is not directory, ignoring", default_base_path) self.__app_config = app_config - self.default_base_path = os.path.abspath( default_base_path ) + self.default_base_path = os.path.abspath(default_base_path) self.resolver_classes = self.__resolvers_dict() - self.dependency_resolvers = self.__build_dependency_resolvers( conf_file ) + self.dependency_resolvers = self.__build_dependency_resolvers(conf_file) def get_resolver_option(self, resolver, key, explicit_resolver_options={}): """Look in resolver-specific settings for option and then fallback to global settings. @@ -107,7 +108,7 @@ def get_app_option(self, key, default=None): value = default return value - def dependency_shell_commands( self, requirements, **kwds ): + def dependency_shell_commands(self, requirements, **kwds): requirement_to_dependency = self.requirements_to_dependencies(requirements, **kwds) return [dependency.shell_commands(requirement) for requirement, dependency in requirement_to_dependency.items()] @@ -160,7 +161,7 @@ def _requirements_to_dependencies_dict(self, requirements, **kwds): if requirement in requirement_to_dependency: continue - dependency = resolver.resolve( requirement, **kwds ) + dependency = resolver.resolve(requirement, **kwds) if require_exact and not dependency.exact: continue @@ -174,9 +175,9 @@ def _requirements_to_dependencies_dict(self, requirements, **kwds): return requirement_to_dependency def uses_tool_shed_dependencies(self): - return any( map( lambda r: isinstance( r, ToolShedPackageDependencyResolver ), self.dependency_resolvers ) ) + return any(map(lambda r: isinstance(r, ToolShedPackageDependencyResolver), self.dependency_resolvers)) - def find_dep( self, name, version=None, type='package', **kwds ): + def find_dep(self, name, version=None, type='package', **kwds): log.debug('Find dependency %s version %s' % (name, version)) requirements = ToolRequirements([ToolRequirement(name=name, version=version, type=type)]) dep_dict = self._requirements_to_dependencies_dict(requirements, **kwds) @@ -185,16 +186,16 @@ def find_dep( self, name, version=None, type='package', **kwds ): else: return NullDependency(name=name, version=version) - def __build_dependency_resolvers( self, conf_file ): + def __build_dependency_resolvers(self, conf_file): if not conf_file: return self.__default_dependency_resolvers() - if not os.path.exists( conf_file ): - log.debug( "Unable to find config file '%s'", conf_file) + if not os.path.exists(conf_file): + log.debug("Unable to find config file '%s'", conf_file) return self.__default_dependency_resolvers() - plugin_source = plugin_config.plugin_source_from_path( conf_file ) - return self.__parse_resolver_conf_xml( plugin_source ) + plugin_source = plugin_config.plugin_source_from_path(conf_file) + return self.__parse_resolver_conf_xml(plugin_source) - def __default_dependency_resolvers( self ): + def __default_dependency_resolvers(self): return [ ToolShedPackageDependencyResolver(self), GalaxyPackageDependencyResolver(self), @@ -206,12 +207,12 @@ def __default_dependency_resolvers( self ): def __parse_resolver_conf_xml(self, plugin_source): """ """ - extra_kwds = dict( dependency_manager=self ) - return plugin_config.load_plugins( self.resolver_classes, plugin_source, extra_kwds ) + extra_kwds = dict(dependency_manager=self) + return plugin_config.load_plugins(self.resolver_classes, plugin_source, extra_kwds) - def __resolvers_dict( self ): + def __resolvers_dict(self): import galaxy.tools.deps.resolvers - return plugin_config.plugins_dict( galaxy.tools.deps.resolvers, 'resolver_type' ) + return plugin_config.plugins_dict(galaxy.tools.deps.resolvers, 'resolver_type') class CachedDependencyManager(DependencyManager): @@ -235,7 +236,7 @@ def build_cache(self, requirements, **kwds): return [dep.build_cache(hashed_dependencies_dir) for dep in cacheable_dependencies] - def dependency_shell_commands( self, requirements, **kwds ): + def dependency_shell_commands(self, requirements, **kwds): """ Runs a set of requirements through the dependency resolvers and returns a list of commands required to activate the dependencies. If dependencies diff --git a/lib/galaxy/tools/deps/brew_exts.py b/lib/galaxy/tools/deps/brew_exts.py index 37ce6474e102..9f6c0985c294 100755 --- a/lib/galaxy/tools/deps/brew_exts.py +++ b/lib/galaxy/tools/deps/brew_exts.py @@ -224,7 +224,7 @@ def versioned_install(recipe_context, package=None, version=None, installed_deps 'deps': deps_metadata } cellar_root = recipe_context.brew_context.homebrew_cellar - cellar_path = recipe_cellar_path( cellar_root, package, version ) + cellar_path = recipe_cellar_path(cellar_root, package, version) v_metadata_path = os.path.join(cellar_path, "INSTALL_RECEIPT_VERSIONED.json") with open(v_metadata_path, "w") as f: json.dump(metadata, f) @@ -343,10 +343,10 @@ def to_action(desc): for dep in deps: package = dep['name'] version = dep['version'] - dep_cellar_path = recipe_cellar_path( cellar_root, package, version ) - handle_keg( dep_cellar_path ) + dep_cellar_path = recipe_cellar_path(cellar_root, package, version) + handle_keg(dep_cellar_path) - handle_keg( cellar_path ) + handle_keg(cellar_path) if not custom_only: if path_appends: actions.append(EnvAction(cellar_path, {"action": "prepend", "variable": "PATH", "value": ":".join(path_appends)})) @@ -547,7 +547,7 @@ def which(file): # http://stackoverflow.com/questions/5226958/which-equivalent-function-in-python for path in os.environ["PATH"].split(":"): if os.path.exists(path + "/" + file): - return path + "/" + file + return path + "/" + file return None diff --git a/lib/galaxy/tools/deps/conda_util.py b/lib/galaxy/tools/deps/conda_util.py index 00efe51b8ca2..fd88491702db 100644 --- a/lib/galaxy/tools/deps/conda_util.py +++ b/lib/galaxy/tools/deps/conda_util.py @@ -11,7 +11,6 @@ from sys import platform as _platform import six -import yaml from ..deps import commands from ..deps import installable @@ -95,7 +94,6 @@ def __init__(self, conda_prefix=None, conda_exec=None, else: ensure_channels = None self.ensure_channels = ensure_channels - self.ensured_channels = False self._conda_version = None self._miniconda_version = None self._conda_build_available = None @@ -144,28 +142,20 @@ def _guess_conda_properties(self): def _conda_meta_path(self): return os.path.join(self.conda_prefix, "conda-meta") - def ensure_channels_configured(self): - if not self.ensured_channels: - self.ensured_channels = True - - changed = False - conda_conf = self.load_condarc() - if "channels" not in conda_conf: - conda_conf["channels"] = [] - channels = conda_conf["channels"] + @property + def _override_channels_args(self): + override_channels_args = [] + if self.ensure_channels: + override_channels_args.append("--override-channels") for channel in self.ensure_channels: - if channel not in channels: - changed = True - channels.append(channel) - - if changed: - self.save_condarc(conda_conf) + override_channels_args.extend(["--channel", channel]) + return override_channels_args def ensure_conda_build_installed_if_needed(self): if self.use_local and not self.conda_build_available: conda_targets = [CondaTarget("conda-build", version=CONDA_BUILD_VERSION)] # Cannot use --use-local during installation fo conda-build. - return install_conda_targets(conda_targets, env_name=None, conda_context=self, allow_local=False) + return install_conda_targets(conda_targets, conda_context=self, env_name=None, allow_local=False) else: return 0 @@ -211,34 +201,6 @@ def can_install_conda(self): self.conda_prefix, self.conda_exec) return False - def load_condarc(self): - condarc = self.condarc - if os.path.exists(condarc): - with open(condarc, "r") as f: - return yaml.safe_load(f) - else: - return {"channels": ["defaults"]} - - def save_condarc(self, conf): - condarc = self.condarc - try: - with open(condarc, "w") as f: - return yaml.safe_dump(conf, f) - except IOError: - template = ("Failed to update write to path [%s] while attempting to update conda configuration, " - "please update the configuration to override the condarc location or " - "grant this application write to the parent directory.") - message = template % condarc - raise Exception(message) - - @property - def condarc(self): - if self.condarc_override: - return self.condarc_override - else: - home = os.path.expanduser("~") - return os.path.join(home, ".condarc") - def command(self, operation, args): if isinstance(args, list): args = " ".join(args) @@ -254,14 +216,14 @@ def exec_command(self, operation, args): if condarc_override: env["CONDARC"] = condarc_override log.debug("Executing command: %s", command) - env['HOME'] = tempfile.mkdtemp(prefix='conda_exec_home_') # We don't want to pollute ~/.conda, which may not even be writable + conda_exec_home = env['HOME'] = tempfile.mkdtemp(prefix='conda_exec_home_') # We don't want to pollute ~/.conda, which may not even be writable try: return self.shell_exec(command, env=env) except commands.CommandLineException as e: log.warning(e) return e.returncode finally: - shutil.rmtree(env['HOME'], ignore_errors=True) + shutil.rmtree(conda_exec_home, ignore_errors=True) def exec_create(self, args, allow_local=True): create_base_args = [ @@ -269,6 +231,7 @@ def exec_create(self, args, allow_local=True): ] if allow_local and self.use_local: create_base_args.extend(["--use-local"]) + create_base_args.extend(self._override_channels_args) create_base_args.extend(args) return self.exec_command("create", create_base_args) @@ -288,6 +251,7 @@ def exec_install(self, args, allow_local=True): ] if allow_local and self.use_local: install_base_args.extend(["--use-local"]) + install_base_args.extend(self._override_channels_args) install_base_args.extend(args) return self.exec_command("install", install_base_args) @@ -428,8 +392,7 @@ def hash_conda_packages(conda_packages, conda_target=None): # shell makes sense for planemo, in Galaxy this should just execute # these commands as Python -def install_conda(conda_context=None, force_conda_build=False): - conda_context = _ensure_conda_context(conda_context) +def install_conda(conda_context, force_conda_build=False): f, script_path = tempfile.mkstemp(suffix=".sh", prefix="conda_install") os.close(f) download_cmd = " ".join(commands.download_command(conda_link(), to=script_path, quote_url=True)) @@ -449,9 +412,7 @@ def install_conda(conda_context=None, force_conda_build=False): os.remove(script_path) -def install_conda_targets(conda_targets, env_name=None, conda_context=None, allow_local=True): - conda_context = _ensure_conda_context(conda_context) - conda_context.ensure_channels_configured() +def install_conda_targets(conda_targets, conda_context, env_name=None, allow_local=True): if env_name is not None: create_args = [ "--name", env_name, # environment for package @@ -463,11 +424,9 @@ def install_conda_targets(conda_targets, env_name=None, conda_context=None, allo return conda_context.exec_install([t.package_specifier for t in conda_targets], allow_local=allow_local) -def install_conda_target(conda_target, conda_context=None, skip_environment=False): +def install_conda_target(conda_target, conda_context, skip_environment=False): """ Install specified target into a its own environment. """ - conda_context = _ensure_conda_context(conda_context) - conda_context.ensure_channels_configured() if not skip_environment: create_args = [ "--name", conda_target.install_environment, # environment for package @@ -478,8 +437,7 @@ def install_conda_target(conda_target, conda_context=None, skip_environment=Fals return conda_context.exec_install([conda_target.package_specifier]) -def cleanup_failed_install_of_environment(env, conda_context=None): - conda_context = _ensure_conda_context(conda_context) +def cleanup_failed_install_of_environment(env, conda_context): if conda_context.has_env(env): conda_context.exec_remove([env]) @@ -488,15 +446,11 @@ def cleanup_failed_install(conda_target, conda_context=None): cleanup_failed_install_of_environment(conda_target.install_environment, conda_context=conda_context) -def best_search_result(conda_target, conda_context=None, channels_override=None, offline=False): +def best_search_result(conda_target, conda_context, channels_override=None, offline=False): """Find best "conda search" result for specified target. Return ``None`` if no results match. """ - conda_context = _ensure_conda_context(conda_context) - if not channels_override: - conda_context.ensure_channels_configured() - search_cmd = [conda_context.conda_exec, "search", "--full-name", "--json"] if offline: search_cmd.append("--offline") @@ -504,6 +458,8 @@ def best_search_result(conda_target, conda_context=None, channels_override=None, search_cmd.append("--override-channels") for channel in channels_override: search_cmd.extend(["--channel", channel]) + else: + search_cmd.extend(conda_context._override_channels_args) search_cmd.append(conda_target.package) res = commands.execute(search_cmd) hits = json.loads(res).get(conda_target.package, []) @@ -529,22 +485,7 @@ def is_search_hit_exact(conda_target, search_hit): return not target_version or search_hit['version'] == target_version -def is_target_available(conda_target, conda_context=None, channels_override=None): - """Check if a specified target is available for installation. - - If the package name exists return ``True`` (the ``bool``). If in addition - the version matches exactly return "exact" (a string). Otherwise return - ``False``. - """ - (best_hit, exact) = best_search_result(conda_target, conda_context, channels_override) - if best_hit: - return 'exact' if exact else True - else: - return False - - -def is_conda_target_installed(conda_target, conda_context=None): - conda_context = _ensure_conda_context(conda_context) +def is_conda_target_installed(conda_target, conda_context): # fail by default if conda_context.has_env(conda_target.install_environment): return True @@ -552,8 +493,7 @@ def is_conda_target_installed(conda_target, conda_context=None): return False -def filter_installed_targets(conda_targets, conda_context=None): - conda_context = _ensure_conda_context(conda_context) +def filter_installed_targets(conda_targets, conda_context): installed = functools.partial(is_conda_target_installed, conda_context=conda_context) return list(filter(installed, conda_targets)) @@ -561,9 +501,9 @@ def filter_installed_targets(conda_targets, conda_context=None): def build_isolated_environment( conda_packages, + conda_context, path=None, copy=False, - conda_context=None, quiet=False, ): """ Build a new environment (or reuse an existing one from hashes) @@ -573,7 +513,6 @@ def build_isolated_environment( conda_packages = [conda_packages] # Lots we could do in here, hashing, checking revisions, etc... - conda_context = _ensure_conda_context(conda_context) try: hash = hash_conda_packages(conda_packages) tempdir = tempfile.mkdtemp(prefix="jobdeps", suffix=hash) @@ -624,7 +563,7 @@ def build_isolated_environment( shutil.rmtree(tempdir) -def requirement_to_conda_targets(requirement, conda_context=None): +def requirement_to_conda_targets(requirement): conda_target = None if requirement.type == "package": conda_target = CondaTarget(requirement.name, @@ -632,19 +571,11 @@ def requirement_to_conda_targets(requirement, conda_context=None): return conda_target -def requirements_to_conda_targets(requirements, conda_context=None): - r_to_ct = functools.partial(requirement_to_conda_targets, - conda_context=conda_context) - conda_targets = (r_to_ct(_) for _ in requirements) +def requirements_to_conda_targets(requirements): + conda_targets = (requirement_to_conda_targets(_) for _ in requirements) return [c for c in conda_targets if c is not None] -def _ensure_conda_context(conda_context): - if conda_context is None: - conda_context = CondaContext() - return conda_context - - __all__ = ( 'CondaContext', 'CondaTarget', diff --git a/lib/galaxy/tools/deps/containers.py b/lib/galaxy/tools/deps/containers.py index 1ac32eb024a3..c7bb8f36b36f 100644 --- a/lib/galaxy/tools/deps/containers.py +++ b/lib/galaxy/tools/deps/containers.py @@ -201,15 +201,15 @@ def __init__(self, app_info): self.app_info = app_info self.container_resolvers = self.__build_container_resolvers(app_info) - def __build_container_resolvers( self, app_info ): + def __build_container_resolvers(self, app_info): conf_file = getattr(app_info, 'containers_resolvers_config_file', None) if not conf_file: return self.__default_containers_resolvers() - if not os.path.exists( conf_file ): - log.debug( "Unable to find config file '%s'", conf_file) + if not os.path.exists(conf_file): + log.debug("Unable to find config file '%s'", conf_file) return self.__default_containers_resolvers() - plugin_source = plugin_config.plugin_source_from_path( conf_file ) - return self.__parse_resolver_conf_xml( plugin_source ) + plugin_source = plugin_config.plugin_source_from_path(conf_file) + return self.__parse_resolver_conf_xml(plugin_source) def __parse_resolver_conf_xml(self, plugin_source): extra_kwds = {} @@ -229,9 +229,9 @@ def __default_containers_resolvers(self): ]) return default_resolvers - def __resolvers_dict( self ): + def __resolvers_dict(self): import galaxy.tools.deps.container_resolvers - return plugin_config.plugins_dict( galaxy.tools.deps.container_resolvers, 'resolver_type' ) + return plugin_config.plugins_dict(galaxy.tools.deps.container_resolvers, 'resolver_type') def find_best_container_description(self, enabled_container_types, tool_info): """Yield best container description of supplied types matching tool info.""" @@ -298,7 +298,7 @@ def __init__(self, working_directory, tool_directory, job_directory, job_directo @six.add_metaclass(ABCMeta) -class Container( object ): +class Container(object): def __init__(self, container_id, app_info, tool_info, destination_info, job_info, container_description): self.container_id = container_id diff --git a/lib/galaxy/tools/deps/mulled/mulled_build.py b/lib/galaxy/tools/deps/mulled/mulled_build.py index 9fb8724dedac..5d362f12cd32 100644 --- a/lib/galaxy/tools/deps/mulled/mulled_build.py +++ b/lib/galaxy/tools/deps/mulled/mulled_build.py @@ -120,8 +120,8 @@ def get_affected_packages(args): pkg_list = check_output(cmd, shell=True) ret = list() for pkg in pkg_list.strip().split('\n'): - if pkg and os.path.exists(os.path.join( recipes_dir, pkg )): - ret.append( (get_pkg_name(args, pkg), get_tests(args, pkg)) ) + if pkg and os.path.exists(os.path.join(recipes_dir, pkg)): + ret.append((get_pkg_name(args, pkg), get_tests(args, pkg))) return ret @@ -216,7 +216,7 @@ def mull_targets( involucro_args.extend(["-set", "SINGULARITY='1'"]) involucro_args.extend(["-set", "SINGULARITY_IMAGE_NAME='%s'" % singularity_image_name]) involucro_args.extend(["-set", "SINGULARITY_IMAGE_DIR='%s'" % singularity_image_dir]) - involucro_args.extend(["-set", "USER_ID='%s:%s'" % (os.getuid(), os.getgid() )]) + involucro_args.extend(["-set", "USER_ID='%s:%s'" % (os.getuid(), os.getgid())]) if conda_version is not None: verbose = "--verbose" if verbose else "--quiet" involucro_args.extend(["-set", "PREINSTALL='conda install %s --yes conda=%s'" % (verbose, conda_version)]) diff --git a/lib/galaxy/tools/deps/mulled/mulled_search.py b/lib/galaxy/tools/deps/mulled/mulled_search.py index 4e0662d12b5e..58dc6df7dd86 100755 --- a/lib/galaxy/tools/deps/mulled/mulled_search.py +++ b/lib/galaxy/tools/deps/mulled/mulled_search.py @@ -26,6 +26,7 @@ class QuaySearch(): """ Tool to search within a quay organization for a given software name. """ + def __init__(self, organization): self.index = None self.organization = organization diff --git a/lib/galaxy/tools/deps/requirements.py b/lib/galaxy/tools/deps/requirements.py index 4d1e7b68ddd6..c784181ac0dc 100644 --- a/lib/galaxy/tools/deps/requirements.py +++ b/lib/galaxy/tools/deps/requirements.py @@ -14,32 +14,33 @@ @six.python_2_unicode_compatible -class ToolRequirement( object ): +class ToolRequirement(object): """ Represents an external requirement that must be available for the tool to run (for example, a program, package, or library). Requirements can optionally assert a specific version. """ - def __init__( self, name=None, type=None, version=None, specs=[] ): + + def __init__(self, name=None, type=None, version=None, specs=[]): self.name = name self.type = type self.version = version self.specs = specs - def to_dict( self ): + def to_dict(self): specs = [s.to_dict() for s in self.specs] return dict(name=self.name, type=self.type, version=self.version, specs=specs) - def copy( self ): - return copy.deepcopy( self ) + def copy(self): + return copy.deepcopy(self) @staticmethod - def from_dict( dict ): - version = dict.get( "version", None ) + def from_dict(dict): + version = dict.get("version", None) name = dict.get("name", None) type = dict.get("type", None) specs = [RequirementSpecification.from_dict(s) for s in dict.get("specs", [])] - return ToolRequirement( name=name, type=type, version=version, specs=specs ) + return ToolRequirement(name=name, type=type, version=version, specs=specs) def __eq__(self, other): return self.name == other.name and self.type == other.type and self.version == other.version and self.specs == other.specs @@ -94,6 +95,7 @@ class ToolRequirements(object): """ Represents all requirements (packages, env vars) needed to run a tool. """ + def __init__(self, tool_requirements=None): if tool_requirements: if not isinstance(tool_requirements, list): @@ -152,7 +154,7 @@ class ToolRequirementsException(Exception): @six.python_2_unicode_compatible -class ContainerDescription( object ): +class ContainerDescription(object): def __init__( self, @@ -166,7 +168,7 @@ def __init__( self.resolve_dependencies = resolve_dependencies self.shell = shell - def to_dict( self ): + def to_dict(self): return dict( identifier=self.identifier, type=self.type, @@ -175,7 +177,7 @@ def to_dict( self ): ) @staticmethod - def from_dict( dict ): + def from_dict(dict): identifier = dict["identifier"] type = dict.get("type", DEFAULT_CONTAINER_TYPE) resolve_dependencies = dict.get("resolve_dependencies", DEFAULT_CONTAINER_RESOLVE_DEPENDENCIES) @@ -191,13 +193,13 @@ def __str__(self): return "ContainerDescription[identifier=%s,type=%s]" % (self.identifier, self.type) -def parse_requirements_from_dict( root_dict ): +def parse_requirements_from_dict(root_dict): requirements = root_dict.get("requirements", []) containers = root_dict.get("containers", []) return ToolRequirements.from_list(requirements), map(ContainerDescription.from_dict, containers) -def parse_requirements_from_xml( xml_root ): +def parse_requirements_from_xml(xml_root): """ >>> from xml.etree import ElementTree @@ -220,23 +222,23 @@ def parse_requirements_from_xml( xml_root ): >>> reqs[0].type 'binary' """ - requirements_elem = xml_root.find( "requirements" ) + requirements_elem = xml_root.find("requirements") requirement_elems = [] if requirements_elem is not None: - requirement_elems = requirements_elem.findall( 'requirement' ) + requirement_elems = requirements_elem.findall('requirement') requirements = ToolRequirements() for requirement_elem in requirement_elems: - name = xml_text( requirement_elem ) - type = requirement_elem.get( "type", DEFAULT_REQUIREMENT_TYPE ) - version = requirement_elem.get( "version", DEFAULT_REQUIREMENT_VERSION ) - requirement = ToolRequirement( name=name, type=type, version=version ) - requirements.append( requirement ) + name = xml_text(requirement_elem) + type = requirement_elem.get("type", DEFAULT_REQUIREMENT_TYPE) + version = requirement_elem.get("version", DEFAULT_REQUIREMENT_VERSION) + requirement = ToolRequirement(name=name, type=type, version=version) + requirements.append(requirement) container_elems = [] if requirements_elem is not None: - container_elems = requirements_elem.findall( 'container' ) + container_elems = requirements_elem.findall('container') containers = map(container_from_element, container_elems) diff --git a/lib/galaxy/tools/deps/resolvers/__init__.py b/lib/galaxy/tools/deps/resolvers/__init__.py index bb1761740b09..e8188e0964a9 100644 --- a/lib/galaxy/tools/deps/resolvers/__init__.py +++ b/lib/galaxy/tools/deps/resolvers/__init__.py @@ -31,7 +31,7 @@ class DependencyResolver(Dictifiable, object): config_options = {} @abstractmethod - def resolve( self, requirement, **kwds ): + def resolve(self, requirement, **kwds): """Given inputs describing dependency in the abstract yield a Dependency object. The Dependency object describes various attributes (script, bin, @@ -239,13 +239,13 @@ class Dependency(Dictifiable, object): cacheable = False @abstractmethod - def shell_commands( self, requirement ): + def shell_commands(self, requirement): """ Return shell commands to enable this dependency. """ @abstractproperty - def exact( self ): + def exact(self): """ Return true if version information wasn't discarded to resolve the dependency. """ @@ -258,7 +258,7 @@ def resolver_msg(self): return "Using dependency %s version %s of type %s" % (self.name, self.version, self.dependency_type) -class NullDependency( Dependency ): +class NullDependency(Dependency): dependency_type = None exact = True @@ -273,7 +273,7 @@ def resolver_msg(self): """ return "Dependency %s not found." % self.name - def shell_commands( self, requirement ): + def shell_commands(self, requirement): return None diff --git a/lib/galaxy/tools/deps/resolvers/conda.py b/lib/galaxy/tools/deps/resolvers/conda.py index 908fb1cef083..3d61f057afa4 100644 --- a/lib/galaxy/tools/deps/resolvers/conda.py +++ b/lib/galaxy/tools/deps/resolvers/conda.py @@ -39,7 +39,11 @@ DEFAULT_BASE_PATH_DIRECTORY = "_conda" DEFAULT_CONDARC_OVERRIDE = "_condarc" -DEFAULT_ENSURE_CHANNELS = "iuc,bioconda,r,defaults,conda-forge" +# Conda channel order from highest to lowest, following the one used in +# https://github.com/bioconda/bioconda-recipes/blob/master/config.yml , but +# adding `iuc` as first channel (for Galaxy-specific packages) and `r` as last +# (for old R packages) +DEFAULT_ENSURE_CHANNELS = "iuc,bioconda,conda-forge,defaults,r" CONDA_SOURCE_CMD = """[ "$CONDA_DEFAULT_ENV" = "%s" ] || MAX_TRIES=3 COUNT=0 @@ -165,7 +169,7 @@ def uninstall_environments(self, environments): def install_all(self, conda_targets): env = self.merged_environment_name(conda_targets) - return_code = install_conda_targets(conda_targets, env, conda_context=self.conda_context) + return_code = install_conda_targets(conda_targets, conda_context=self.conda_context, env_name=env) if return_code != 0: is_installed = False else: @@ -448,9 +452,9 @@ def set_cache_path(self, cache_path): def build_environment(self): env_path, exit_code = build_isolated_environment( CondaTarget(self.name, self.version), + conda_context=self.conda_context, path=self.environment_path, copy=self.conda_context.copy_dependencies, - conda_context=self.conda_context, ) if exit_code: if len(os.path.abspath(self.environment_path)) > 79: @@ -479,8 +483,8 @@ def shell_commands(self, requirement): ) -def _string_as_bool( value ): - return str( value ).lower() == "true" +def _string_as_bool(value): + return str(value).lower() == "true" __all__ = ('CondaDependencyResolver', 'DEFAULT_ENSURE_CHANNELS') diff --git a/lib/galaxy/tools/deps/resolvers/galaxy_packages.py b/lib/galaxy/tools/deps/resolvers/galaxy_packages.py index 8e1a11450300..632dfc85cea5 100644 --- a/lib/galaxy/tools/deps/resolvers/galaxy_packages.py +++ b/lib/galaxy/tools/deps/resolvers/galaxy_packages.py @@ -20,14 +20,14 @@ NullDependency, ) -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) class GalaxyPackageDependency(Dependency): dict_collection_visible_keys = Dependency.dict_collection_visible_keys + ['script', 'path', 'version', 'name'] dependency_type = 'galaxy_package' - def __init__( self, script, path, version, name, exact=True ): + def __init__(self, script, path, version, name, exact=True): self.script = script self.path = path self.version = version @@ -38,15 +38,15 @@ def __init__( self, script, path, version, name, exact=True ): def exact(self): return self._exact - def shell_commands( self, requirement ): + def shell_commands(self, requirement): base_path = self.path if self.script is None and base_path is None: - log.warning( "Failed to resolve dependency on '%s', ignoring", requirement.name ) + log.warning("Failed to resolve dependency on '%s', ignoring", requirement.name) commands = None elif requirement.type == 'package' and self.script is None: - commands = 'PACKAGE_BASE=%s; export PACKAGE_BASE; PATH="%s/bin:$PATH"; export PATH' % ( base_path, base_path ) + commands = 'PACKAGE_BASE=%s; export PACKAGE_BASE; PATH="%s/bin:$PATH"; export PATH' % (base_path, base_path) else: - commands = 'PACKAGE_BASE=%s; export PACKAGE_BASE; . %s' % ( base_path, self.script ) + commands = 'PACKAGE_BASE=%s; export PACKAGE_BASE; . %s' % (base_path, self.script) return commands @@ -65,7 +65,7 @@ def __init__(self, dependency_manager, **kwds): # resolver that will just grab 'default' version of exact version # unavailable. self.versionless = str(kwds.get('versionless', "false")).lower() == "true" - self._init_base_path( dependency_manager, **kwds ) + self._init_base_path(dependency_manager, **kwds) def resolve(self, requirement, **kwds): """ @@ -76,30 +76,30 @@ def resolve(self, requirement, **kwds): if version is None or self.versionless: exact = not self.versionless or version is None - return self._find_dep_default( name, type=type, exact=exact, **kwds ) + return self._find_dep_default(name, type=type, exact=exact, **kwds) else: - return self._find_dep_versioned( name, version, type=type, **kwds ) + return self._find_dep_versioned(name, version, type=type, **kwds) - def _find_dep_versioned( self, name, version, type='package', **kwds ): + def _find_dep_versioned(self, name, version, type='package', **kwds): base_path = self.base_path - path = join( base_path, name, version ) + path = join(base_path, name, version) return self._galaxy_package_dep(path, version, name, True) - def _find_dep_default( self, name, type='package', exact=True, **kwds ): + def _find_dep_default(self, name, type='package', exact=True, **kwds): base_path = self.base_path - path = join( base_path, name, 'default' ) - if islink( path ): - real_path = realpath( path ) - real_version = basename( real_path ) + path = join(base_path, name, 'default') + if islink(path): + real_path = realpath(path) + real_version = basename(real_path) return self._galaxy_package_dep(real_path, real_version, name, exact) else: return NullDependency(version=None, name=name) - def _galaxy_package_dep( self, path, version, name, exact ): - script = join( path, 'env.sh' ) - if exists( script ): + def _galaxy_package_dep(self, path, version, name, exact): + script = join(path, 'env.sh') + if exists(script): return self.dependency_type(script, path, version, name, exact) - elif exists( join( path, 'bin' ) ): + elif exists(join(path, 'bin')): return self.dependency_type(None, path, version, name, exact) return NullDependency(version=version, name=name) diff --git a/lib/galaxy/tools/deps/resolvers/homebrew.py b/lib/galaxy/tools/deps/resolvers/homebrew.py index 2a9781d39046..d9c37db12b14 100644 --- a/lib/galaxy/tools/deps/resolvers/homebrew.py +++ b/lib/galaxy/tools/deps/resolvers/homebrew.py @@ -51,8 +51,8 @@ def resolve(self, requirement, **kwds): return self._find_dep_versioned(name, version) -def _string_as_bool( value ): - return str( value ).lower() == "true" +def _string_as_bool(value): + return str(value).lower() == "true" __all__ = ('HomebrewDependencyResolver', ) diff --git a/lib/galaxy/tools/deps/resolvers/modules.py b/lib/galaxy/tools/deps/resolvers/modules.py index 0cbbc5764f8d..2e4412b34fe0 100644 --- a/lib/galaxy/tools/deps/resolvers/modules.py +++ b/lib/galaxy/tools/deps/resolvers/modules.py @@ -20,7 +20,7 @@ NullDependency, ) -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) DEFAULT_MODULECMD_PATH = "modulecmd" # Just check path DEFAULT_MODULE_PATH = '/usr/share/modules/modulefiles' @@ -80,6 +80,7 @@ class DirectoryModuleChecker(object): Searches the paths listed in modulepath to for a file or directory matching the module name. If the version=True, searches for files named module/version.""" + def __init__(self, module_dependency_resolver, modulepath, prefetch): self.module_dependency_resolver = module_dependency_resolver self.directories = modulepath.split(pathsep) @@ -90,12 +91,12 @@ def has_module(self, module, version): has_module = False for directory in self.directories: module_directory = join(directory, module) - has_module_directory = isdir( module_directory ) + has_module_directory = isdir(module_directory) if not version: has_module = has_module_directory or exists(module_directory) # could be a bare modulefile else: - modulefile = join( module_directory, version ) - has_modulefile = exists( modulefile ) + modulefile = join(module_directory, version) + has_modulefile = exists(modulefile) has_module = has_module_directory and has_modulefile if has_module: break @@ -109,6 +110,7 @@ class AvailModuleChecker(object): module names into module and version on '/' and discarding a postfix matching default_indicator (by default '(default)'. Matching is done using the module and (if version=True) the module version.""" + def __init__(self, module_dependency_resolver, modulepath, prefetch, default_indicator=DEFAULT_INDICATOR): self.module_dependency_resolver = module_dependency_resolver self.modulepath = modulepath @@ -185,8 +187,8 @@ def shell_commands(self, requirement): return command -def _string_as_bool( value ): - return str( value ).lower() == "true" +def _string_as_bool(value): + return str(value).lower() == "true" __all__ = ('ModuleDependencyResolver', ) diff --git a/lib/galaxy/tools/deps/resolvers/resolver_mixins.py b/lib/galaxy/tools/deps/resolvers/resolver_mixins.py index 61663e0c08b0..59adaba65034 100644 --- a/lib/galaxy/tools/deps/resolvers/resolver_mixins.py +++ b/lib/galaxy/tools/deps/resolvers/resolver_mixins.py @@ -47,12 +47,12 @@ def _installed_versions(self, recipe): class UsesToolDependencyDirMixin: def _init_base_path(self, dependency_manager, **kwds): - self.base_path = os.path.abspath( kwds.get('base_path', dependency_manager.default_base_path) ) + self.base_path = os.path.abspath(kwds.get('base_path', dependency_manager.default_base_path)) class UsesInstalledRepositoriesMixin: - def _get_installed_dependency( self, name, type, version=None, **kwds ): + def _get_installed_dependency(self, name, type, version=None, **kwds): installed_tool_dependencies = kwds.get("installed_tool_dependencies") or [] for installed_tool_dependency in installed_tool_dependencies: if installed_tool_dependency.name == name and installed_tool_dependency.type == type: diff --git a/lib/galaxy/tools/deps/resolvers/tool_shed_packages.py b/lib/galaxy/tools/deps/resolvers/tool_shed_packages.py index 8b6ddac682f8..c2c5153d126c 100644 --- a/lib/galaxy/tools/deps/resolvers/tool_shed_packages.py +++ b/lib/galaxy/tools/deps/resolvers/tool_shed_packages.py @@ -16,19 +16,19 @@ class ToolShedPackageDependencyResolver(BaseGalaxyPackageDependencyResolver, Use def __init__(self, dependency_manager, **kwds): super(ToolShedPackageDependencyResolver, self).__init__(dependency_manager, **kwds) - def _find_dep_versioned( self, name, version, type='package', **kwds ): - installed_tool_dependency = self._get_installed_dependency( name, type, version=version, **kwds ) + def _find_dep_versioned(self, name, version, type='package', **kwds): + installed_tool_dependency = self._get_installed_dependency(name, type, version=version, **kwds) if installed_tool_dependency: - path = self._get_package_installed_dependency_path( installed_tool_dependency, name, version ) + path = self._get_package_installed_dependency_path(installed_tool_dependency, name, version) return self._galaxy_package_dep(path, version, name, True) else: return NullDependency(version=version, name=name) - def _find_dep_default( self, name, type='package', **kwds ): + def _find_dep_default(self, name, type='package', **kwds): if type == 'set_environment' and kwds.get('installed_tool_dependencies', None): - installed_tool_dependency = self._get_installed_dependency( name, type, version=None, **kwds ) + installed_tool_dependency = self._get_installed_dependency(name, type, version=None, **kwds) if installed_tool_dependency: - dependency = self._get_set_environment_installed_dependency_script_path( installed_tool_dependency, name ) + dependency = self._get_set_environment_installed_dependency_script_path(installed_tool_dependency, name) is_galaxy_dep = isinstance(dependency, ToolShedDependency) has_script_dep = is_galaxy_dep and dependency.script and dependency.path if has_script_dep: @@ -36,7 +36,7 @@ def _find_dep_default( self, name, type='package', **kwds ): return ToolShedDependency(dependency.script, dependency.path, None, name, True) return NullDependency(version=None, name=name) - def _get_package_installed_dependency_path( self, installed_tool_dependency, name, version ): + def _get_package_installed_dependency_path(self, installed_tool_dependency, name, version): tool_shed_repository = installed_tool_dependency.tool_shed_repository base_path = self.base_path return join( @@ -48,17 +48,17 @@ def _get_package_installed_dependency_path( self, installed_tool_dependency, nam tool_shed_repository.installed_changeset_revision ) - def _get_set_environment_installed_dependency_script_path( self, installed_tool_dependency, name ): + def _get_set_environment_installed_dependency_script_path(self, installed_tool_dependency, name): tool_shed_repository = installed_tool_dependency.tool_shed_repository base_path = self.base_path - path = abspath( join( base_path, - 'environment_settings', - name, - tool_shed_repository.owner, - tool_shed_repository.name, - tool_shed_repository.installed_changeset_revision ) ) - if exists( path ): - script = join( path, 'env.sh' ) + path = abspath(join(base_path, + 'environment_settings', + name, + tool_shed_repository.owner, + tool_shed_repository.name, + tool_shed_repository.installed_changeset_revision)) + if exists(path): + script = join(path, 'env.sh') return ToolShedDependency(script, path, None, name, True) return NullDependency(version=None, name=name) diff --git a/lib/galaxy/tools/deps/resolvers/unlinked_tool_shed_packages.py b/lib/galaxy/tools/deps/resolvers/unlinked_tool_shed_packages.py index c02bcd4a3145..b4d197188ad7 100644 --- a/lib/galaxy/tools/deps/resolvers/unlinked_tool_shed_packages.py +++ b/lib/galaxy/tools/deps/resolvers/unlinked_tool_shed_packages.py @@ -25,7 +25,7 @@ from .galaxy_packages import BaseGalaxyPackageDependencyResolver from ..resolvers import Dependency, NullDependency -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) MANUAL = "manual" PREFERRED_OWNERS = MANUAL + ",iuc,devteam" @@ -42,7 +42,7 @@ def __init__(self, dependency_manager, **kwds): # Option to ignore owner and just use last modified time self.select_by_owner = str(kwds.get('select_by_owner', "true")).lower() != "false" - def _find_dep_versioned( self, name, version, type='package', **kwds ): + def _find_dep_versioned(self, name, version, type='package', **kwds): try: possibles = self._find_possible_dependencies(name, version, type) if len(possibles) == 0: @@ -64,7 +64,7 @@ def _find_dep_versioned( self, name, version, type='package', **kwds ): def _find_possible_dependencies(self, name, version, type): possibles = [] if exists(self.base_path): - path = join( self.base_path, name, version ) + path = join(self.base_path, name, version) if exists(path): # First try the way without owner/name/revision package = self._galaxy_package_dep(path, version, name, True) @@ -145,11 +145,11 @@ def __init__(self, dependency, path, owner=MANUAL): self.path = path self.owner = owner - def shell_commands( self, requirement ): + def shell_commands(self, requirement): """ Return shell commands to enable this dependency. """ - return self.dependency.shell_commands( requirement ) + return self.dependency.shell_commands(requirement) __all__ = ('UnlinkedToolShedPackageDependencyResolver', ) diff --git a/lib/galaxy/tools/deps/views.py b/lib/galaxy/tools/deps/views.py index 683f77c42f52..716afb596858 100644 --- a/lib/galaxy/tools/deps/views.py +++ b/lib/galaxy/tools/deps/views.py @@ -181,7 +181,7 @@ def installable_resolvers(self): """ List index for all active resolvers that have the 'install_dependency' attribute. """ - return [index for index, resolver in enumerate(self._dependency_resolvers) if hasattr(resolver, "install_dependency") and not resolver.disabled ] + return [index for index, resolver in enumerate(self._dependency_resolvers) if hasattr(resolver, "install_dependency") and not resolver.disabled] @property def uninstallable_resolvers(self): diff --git a/lib/galaxy/tools/error_reports/__init__.py b/lib/galaxy/tools/error_reports/__init__.py new file mode 100644 index 000000000000..8fc4e4473986 --- /dev/null +++ b/lib/galaxy/tools/error_reports/__init__.py @@ -0,0 +1,74 @@ +"""This module defines the error reporting framework for Galaxy jobs. +""" +import collections +import logging +import os + +from galaxy.util import plugin_config + +log = logging.getLogger(__name__) + + +class ErrorReports(object): + """Load and store a collection of :class:`ErrorPlugin` objects.""" + + def __init__(self, conf_file=None, **kwargs): + """Load :class:`ErrorPlugin` objects from specified configuration file.""" + self.plugin_classes = self.__plugins_dict() + self.default_error_plugin = ErrorPlugin.from_file(self.plugin_classes, conf_file, **kwargs) + self.error_plugin = collections.defaultdict(lambda: self.default_error_plugin) + + def __plugins_dict(self): + import galaxy.tools.error_reports.plugins + return plugin_config.plugins_dict(galaxy.tools.error_reports.plugins, 'plugin_type') + + +class NullErrorPlugin(object): + + def submit_report(self, dataset, job, tool, **kwargs): + return "Submitted Bug Report" + + +NULL_ERROR_PLUGIN = NullErrorPlugin() + + +class ErrorPlugin(object): + + def __init__(self, plugin_classes, plugins_source, **kwargs): + self.extra_kwargs = kwargs + self.app = kwargs['app'] + self.plugin_classes = plugin_classes + self.plugins = self.__plugins_from_source(plugins_source) + + def _can_access_dataset(self, dataset, user): + if user: + roles = user.all_roles() + else: + roles = [] + return self.app.security_agent.can_access_dataset(roles, dataset.dataset) + + def submit_report(self, dataset, job, tool, user=None, user_submission=False, **kwargs): + if user_submission: + assert self._can_access_dataset(dataset, user), Exception("You are not allowed to access this dataset.") + + responses = [] + for plugin in self.plugins: + if user_submission == plugin.user_submission: + try: + response = plugin.submit_report(dataset, job, tool, **kwargs) + log.debug("Bug report plugin %s generated response %s", plugin, response) + if plugin.verbose and response: + responses.append(response) + except Exception: + log.exception("Failed to generate submit_report commands for plugin %s", plugin) + return responses + + def __plugins_from_source(self, plugins_source): + return plugin_config.load_plugins(self.plugin_classes, plugins_source, self.extra_kwargs) + + @staticmethod + def from_file(plugin_classes, conf_file, **kwargs): + if not conf_file or not os.path.exists(conf_file): + return NULL_ERROR_PLUGIN + plugins_source = plugin_config.plugin_source_from_path(conf_file) + return ErrorPlugin(plugin_classes, plugins_source, **kwargs) diff --git a/lib/galaxy/tools/error_reports/plugins/__init__.py b/lib/galaxy/tools/error_reports/plugins/__init__.py new file mode 100644 index 000000000000..376f05ae18f0 --- /dev/null +++ b/lib/galaxy/tools/error_reports/plugins/__init__.py @@ -0,0 +1,25 @@ +"""This module describes the abstract interface for :class:`InstrumentPlugin`. + +These are responsible for collecting and formatting a coherent set of metrics. +""" +from abc import ( + ABCMeta, + abstractmethod +) + +import six + + +@six.add_metaclass(ABCMeta) +class ErrorPlugin(object): + """Describes how to send bug reports to various locations.""" + + @property + @abstractmethod + def plugin_type(self): + """Short string providing labelling this plugin """ + + def submit_report(self, dataset, job, tool, user_submission=False, **kwargs): + """Submit the bug report and render a string to be displayed to the user. + """ + return None diff --git a/lib/galaxy/tools/error_reports/plugins/biostars.py b/lib/galaxy/tools/error_reports/plugins/biostars.py new file mode 100644 index 000000000000..5bc071a5a2da --- /dev/null +++ b/lib/galaxy/tools/error_reports/plugins/biostars.py @@ -0,0 +1,42 @@ +"""The module describes the ``biostars`` error plugin.""" +from __future__ import absolute_import + +import logging + +from galaxy.util import biostar +from galaxy.util import string_as_bool +from galaxy.web.base.controller import url_for + +from ..plugins import ErrorPlugin + +log = logging.getLogger(__name__) + + +class BiostarsPlugin(ErrorPlugin): + """Send error report as an issue on Biostars + """ + plugin_type = "biostars" + + def __init__(self, **kwargs): + self.app = kwargs['app'] + self.verbose = string_as_bool(kwargs.get('verbose', True)) + self.user_submission = string_as_bool(kwargs.get('user_submission', True)) + + def submit_report(self, dataset, job, tool, **kwargs): + """Doesn't do anything, just shows a link to submit on biostars. + """ + try: + assert biostar.biostar_enabled(self.app), ValueError("Biostar is not configured for this galaxy instance") + assert self.app.config.biostar_enable_bug_reports, ValueError("Biostar is not configured to allow bug reporting for this galaxy instance") + print(kwargs) + + url = url_for(controller='biostar', + action='biostar_tool_bug_report', + hda=self.app.security.encode_id(dataset.id), + email=kwargs['email'], message=kwargs['message']) + return ('Click here to submit to BioStars' % url, 'success') + except Exception as e: + return ("An error occurred submitting the report to biostars: %s" % str(e), "danger") + + +__all__ = ('BiostarsPlugin', ) diff --git a/lib/galaxy/tools/error_reports/plugins/email.py b/lib/galaxy/tools/error_reports/plugins/email.py new file mode 100644 index 000000000000..bbf61db56299 --- /dev/null +++ b/lib/galaxy/tools/error_reports/plugins/email.py @@ -0,0 +1,35 @@ +"""The module describes the ``email`` error plugin.""" +from __future__ import absolute_import + +import logging + +from galaxy.tools.errors import EmailErrorReporter +from galaxy.util import string_as_bool + +from ..plugins import ErrorPlugin + +log = logging.getLogger(__name__) + + +class EmailPlugin(ErrorPlugin): + """Send error report as an email + """ + plugin_type = "email" + + def __init__(self, **kwargs): + self.app = kwargs['app'] + self.verbose = string_as_bool(kwargs.get('verbose', True)) + self.user_submission = string_as_bool(kwargs.get('user_submission', True)) + + def submit_report(self, dataset, job, tool, **kwargs): + """Send report as an email + """ + try: + error_reporter = EmailErrorReporter(dataset.id, self.app) + error_reporter.send_report(user=job.get_user(), email=kwargs.get('email', None), message=kwargs.get('message', None)) + return ("Your error report has been sent", "success") + except Exception as e: + return ("An error occurred sending the report by email: %s" % str(e), "danger") + + +__all__ = ('EmailPlugin', ) diff --git a/lib/galaxy/tools/error_reports/plugins/json.py b/lib/galaxy/tools/error_reports/plugins/json.py new file mode 100644 index 000000000000..34661321589b --- /dev/null +++ b/lib/galaxy/tools/error_reports/plugins/json.py @@ -0,0 +1,57 @@ +"""The module describes the ``json`` error plugin.""" +from __future__ import absolute_import + +import json +import logging +import os +import tempfile + +from galaxy.util import string_as_bool + +from ..plugins import ErrorPlugin + +log = logging.getLogger(__name__) + + +class JsonPlugin(ErrorPlugin): + """Write error report to a JSON file. + """ + plugin_type = "json" + + def __init__(self, **kwargs): + self.app = kwargs['app'] + self.verbose = string_as_bool(kwargs.get('verbose', False)) + self.user_submission = string_as_bool(kwargs.get('user_submission', False)) + self.report_directory = kwargs.get("directory", tempfile.gettempdir()) + if not os.path.exists(self.report_directory): + os.makedirs(self.report_directory) + + def submit_report(self, dataset, job, tool, **kwargs): + """Write the report to a json file. + """ + path = os.path.join(self.report_directory, str(dataset.id)) + with open(path, 'w') as handle: + data = { + 'info' : job.info, + 'id' : job.id, + 'command_line' : job.command_line, + 'stderr' : job.stderr, + 'traceback': job.traceback, + 'exit_code': job.exit_code, + 'stdout': job.stdout, + 'handler': job.handler, + 'user': job.get_user().to_dict(), + 'tool_version': job.tool_version, + 'tool_xml': str(tool.config_file) if tool else None + } + if 'email' in kwargs: + data['email'] = kwargs['email'] + + if 'message' in kwargs: + data['message'] = kwargs['message'] + + json.dump(data, handle, indent=2) + return ('Wrote error report to %s' % path, 'success') + + +__all__ = ('JsonPlugin', ) diff --git a/lib/galaxy/tools/error_reports/plugins/sentry.py b/lib/galaxy/tools/error_reports/plugins/sentry.py new file mode 100644 index 000000000000..dc2425270a73 --- /dev/null +++ b/lib/galaxy/tools/error_reports/plugins/sentry.py @@ -0,0 +1,98 @@ +"""The module describes the ``sentry`` error plugin plugin.""" +import logging + +from galaxy import web +from galaxy.util import string_as_bool, unicodify + +from ..plugins import ErrorPlugin + +log = logging.getLogger(__name__) + +ERROR_TEMPLATE = u"""Galaxy Job Error: {tool_id} v{tool_version} + +Command Line: +{command_line} + +Stderr: +{stderr} + +Stdout: +{stdout} + +The user provided the following information: +{message}""" + + +class SentryPlugin(ErrorPlugin): + """Send error report to Sentry. + """ + plugin_type = "sentry" + + def __init__(self, **kwargs): + self.app = kwargs['app'] + self.verbose = string_as_bool(kwargs.get('verbose', False)) + self.user_submission = string_as_bool(kwargs.get('user_submission', False)) + + def submit_report(self, dataset, job, tool, **kwargs): + """Submit the error report to sentry + """ + if self.app.sentry_client: + user = job.get_user() + extra = { + 'info': job.info, + 'id': job.id, + 'command_line': unicodify(job.command_line), + 'stderr': unicodify(job.stderr), + 'traceback': unicodify(job.traceback), + 'exit_code': job.exit_code, + 'stdout': unicodify(job.stdout), + 'handler': unicodify(job.handler), + 'tool_id': unicodify(job.tool_id), + 'tool_version': unicodify(job.tool_version), + 'tool_xml': unicodify(tool.config_file) if tool else None + } + if 'email' in kwargs: + extra['email'] = unicodify(kwargs['email']) + + # User submitted message + if 'message' in kwargs: + extra['message'] = unicodify(kwargs['message']) + + # Construct the error message to send to sentry. The first line + # will be the issue title, everything after that becomes the + # "message" + error_message = ERROR_TEMPLATE.format(**extra) + + # Update context with user information in a sentry-specific manner + self.app.sentry_client.context.merge({ + # User information here also places email links + allows seeing + # a list of affected users in the tags/filtering. + 'user': { + 'name': user.username, + 'email': user.email, + }, + # This allows us to link to the dataset info page in case + # anything is missing from this report. + 'request': { + 'url': web.url_for( + controller="dataset", action="show_params", + dataset_id=self.app.security.encode_id(dataset.id), + qualified=True + ) + } + }) + + # Send the message, using message because + response = self.app.sentry_client.capture( + 'raven.events.Message', + tags={ + 'tool_id': job.tool_id, + 'tool_version': job.tool_version, + }, + extra=extra, + message=unicodify(error_message), + ) + return ('Submitted bug report to Sentry. Your guru meditation number is %s' % response, 'success') + + +__all__ = ('SentryPlugin', ) diff --git a/lib/galaxy/tools/errors.py b/lib/galaxy/tools/errors.py index 5223a07c77fb..eab04dc805cb 100644 --- a/lib/galaxy/tools/errors.py +++ b/lib/galaxy/tools/errors.py @@ -67,7 +67,7 @@

    Error Localization

    - + @@ -84,7 +84,7 @@

    Detailed Job Information

    -Job environment and execution information is available at the job Info Page. +Job environment and execution information is available at the job info page.
    Dataset${dataset_id} (${dataset_id_encoded})
    Dataset${dataset_id} (${dataset_id_encoded})
    History${history_id} (${history_id_encoded})
    Failed Job${hid}: ${history_item_name} (${hda_id_encoded})
    @@ -128,18 +128,18 @@ """ -class ErrorReporter( object ): - def __init__( self, hda, app ): +class ErrorReporter(object): + def __init__(self, hda, app): # Get the dataset sa_session = app.model.context - if not isinstance( hda, model.HistoryDatasetAssociation ): + if not isinstance(hda, model.HistoryDatasetAssociation): hda_id = hda try: - hda = sa_session.query( model.HistoryDatasetAssociation ).get( hda_id ) - assert hda is not None, ValueError( "No HDA yet" ) + hda = sa_session.query(model.HistoryDatasetAssociation).get(hda_id) + assert hda is not None, ValueError("No HDA yet") except Exception: - hda = sa_session.query( model.HistoryDatasetAssociation ).get( app.security.decode_id( hda_id ) ) - assert isinstance( hda, model.HistoryDatasetAssociation ), ValueError( "Bad value provided for HDA (%s)." % ( hda ) ) + hda = sa_session.query(model.HistoryDatasetAssociation).get(app.security.decode_id(hda_id)) + assert isinstance(hda, model.HistoryDatasetAssociation), ValueError("Bad value provided for HDA (%s)." % (hda)) self.hda = hda # Get the associated job self.job = hda.creating_job @@ -147,21 +147,21 @@ def __init__( self, hda, app ): self.tool_id = self.job.tool_id self.report = None - def _can_access_dataset( self, user ): + def _can_access_dataset(self, user): if user: roles = user.all_roles() else: roles = [] - return self.app.security_agent.can_access_dataset( roles, self.hda.dataset ) + return self.app.security_agent.can_access_dataset(roles, self.hda.dataset) - def create_report( self, user, email='', message='', **kwd ): + def create_report(self, user, email='', message='', **kwd): hda = self.hda job = self.job - host = web.url_for( '/', qualified=True ) - history_id_encoded = self.app.security.encode_id( hda.history_id ) - history_view_link = web.url_for( controller="history", action="view", id=history_id_encoded, qualified=True ) - hda_id_encoded = self.app.security.encode_id( hda.id ) - hda_show_params_link = web.url_for( controller="dataset", action="show_params", dataset_id=hda_id_encoded, qualified=True ) + host = web.url_for('/', qualified=True) + history_id_encoded = self.app.security.encode_id(hda.history_id) + history_view_link = web.url_for(controller="history", action="view", id=history_id_encoded, qualified=True) + hda_id_encoded = self.app.security.encode_id(hda.id) + hda_show_params_link = web.url_for(controller="dataset", action="show_params", dataset_id=hda_id_encoded, qualified=True) # Build the email message if user and user.email != email: email_str = "'%s' (providing preferred contact email '%s')" % (user.email, email) @@ -170,7 +170,7 @@ def create_report( self, user, email='', message='', **kwd ): report_variables = dict( host=host, - dataset_id_encoded=self.app.security.encode_id( hda.dataset_id ), + dataset_id_encoded=self.app.security.encode_id(hda.dataset_id), dataset_id=hda.dataset_id, history_id_encoded=history_id_encoded, history_id=hda.history_id, @@ -179,60 +179,60 @@ def create_report( self, user, email='', message='', **kwd ): history_item_name=hda.get_display_name(), history_view_link=history_view_link, hda_show_params_link=hda_show_params_link, - job_id_encoded=self.app.security.encode_id( job.id ), + job_id_encoded=self.app.security.encode_id(job.id), job_id=job.id, tool_version=job.tool_version, job_tool_id=job.tool_id, job_tool_version=hda.tool_version, job_runner_external_id=job.job_runner_external_id, job_command_line=job.command_line, - job_stderr=util.unicodify( job.stderr ), - job_stdout=util.unicodify( job.stdout ), - job_info=util.unicodify( job.info ), - job_traceback=util.unicodify( job.traceback ), + job_stderr=util.unicodify(job.stderr), + job_stdout=util.unicodify(job.stdout), + job_info=util.unicodify(job.info), + job_traceback=util.unicodify(job.traceback), email_str=email_str, - message=util.unicodify( message ) + message=util.unicodify(message) ) - self.report = string.Template( error_report_template ).safe_substitute( report_variables ) + self.report = string.Template(error_report_template).safe_substitute(report_variables) # Escape all of the content for use in the HTML report for parameter in report_variables.keys(): if report_variables[parameter] is not None: report_variables[parameter] = cgi.escape(unicodify(report_variables[parameter])) - self.html_report = string.Template( error_report_template_html ).safe_substitute( report_variables ) + self.html_report = string.Template(error_report_template_html).safe_substitute(report_variables) - def _send_report( self, user, email=None, message=None, **kwd ): + def _send_report(self, user, email=None, message=None, **kwd): return self.report - def send_report( self, user, email=None, message=None, **kwd ): + def send_report(self, user, email=None, message=None, **kwd): if self.report is None: - self.create_report( user, email=email, message=message, **kwd ) - return self._send_report( user, email=email, message=message, **kwd ) + self.create_report(user, email=email, message=message, **kwd) + return self._send_report(user, email=email, message=message, **kwd) -class EmailErrorReporter( ErrorReporter ): - def _send_report( self, user, email=None, message=None, **kwd ): +class EmailErrorReporter(ErrorReporter): + def _send_report(self, user, email=None, message=None, **kwd): smtp_server = self.app.config.smtp_server - assert smtp_server, ValueError( "Mail is not configured for this galaxy instance" ) + assert smtp_server, ValueError("Mail is not configured for this galaxy instance") to_address = self.app.config.error_email_to - assert to_address, ValueError( "Error reporting has been disabled for this galaxy instance" ) + assert to_address, ValueError("Error reporting has been disabled for this galaxy instance") frm = to_address # Check email a bit email = email or '' email = email.strip() parts = email.split() - if len( parts ) == 1 and len( email ) > 0 and self._can_access_dataset( user ): + if len(parts) == 1 and len(email) > 0 and self._can_access_dataset(user): to = to_address + ", " + email else: to = to_address subject = "Galaxy tool error report from %s" % email try: - subject = "%s (%s)" % ( subject, self.app.toolbox.get_tool( self.job.tool_id, self.job.tool_version ).old_id ) + subject = "%s (%s)" % (subject, self.app.toolbox.get_tool(self.job.tool_id, self.job.tool_version).old_id) except Exception: pass # Send it - return util.send_mail( frm, to, subject, self.report, self.app.config, html=self.html_report ) + return util.send_mail(frm, to, subject, self.report, self.app.config, html=self.html_report) diff --git a/lib/galaxy/tools/evaluation.py b/lib/galaxy/tools/evaluation.py index 8ada405ec6cd..492f491c0d0b 100644 --- a/lib/galaxy/tools/evaluation.py +++ b/lib/galaxy/tools/evaluation.py @@ -37,21 +37,21 @@ from galaxy.util.template import fill_template from galaxy.work.context import WorkRequestContext -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class ToolEvaluator( object ): +class ToolEvaluator(object): """ An abstraction linking together a tool and a job runtime to evaluate tool inputs in an isolated, testable manner. """ - def __init__( self, app, tool, job, local_working_directory ): + def __init__(self, app, tool, job, local_working_directory): self.app = app self.job = job self.tool = tool self.local_working_directory = local_working_directory - def set_compute_environment( self, compute_environment, get_special=None ): + def set_compute_environment(self, compute_environment, get_special=None): """ Setup the compute environment and established the outline of the param_dict for evaluating command and config cheetah templates. @@ -60,25 +60,25 @@ def set_compute_environment( self, compute_environment, get_special=None ): self.unstructured_path_rewriter = compute_environment.unstructured_path_rewriter() job = self.job - incoming = dict( [ ( p.name, p.value ) for p in job.parameters ] ) - incoming = self.tool.params_from_strings( incoming, self.app ) + incoming = dict([(p.name, p.value) for p in job.parameters]) + incoming = self.tool.params_from_strings(incoming, self.app) # Full parameter validation - request_context = WorkRequestContext( app=self.app, user=job.history and job.history.user, history=job.history ) + request_context = WorkRequestContext(app=self.app, user=job.history and job.history.user, history=job.history) - def validate_inputs( input, value, context, **kwargs ): - value = input.from_json( value, request_context, context ) - input.validate( value, request_context ) - visit_input_values( self.tool.inputs, incoming, validate_inputs ) + def validate_inputs(input, value, context, **kwargs): + value = input.from_json(value, request_context, context) + input.validate(value, request_context) + visit_input_values(self.tool.inputs, incoming, validate_inputs) # Restore input / output data lists - inp_data = dict( [ ( da.name, da.dataset ) for da in job.input_datasets ] ) - out_data = dict( [ ( da.name, da.dataset ) for da in job.output_datasets ] ) - inp_data.update( [ ( da.name, da.dataset ) for da in job.input_library_datasets ] ) - out_data.update( [ ( da.name, da.dataset ) for da in job.output_library_datasets ] ) + inp_data = dict([(da.name, da.dataset) for da in job.input_datasets]) + out_data = dict([(da.name, da.dataset) for da in job.output_datasets]) + inp_data.update([(da.name, da.dataset) for da in job.input_library_datasets]) + out_data.update([(da.name, da.dataset) for da in job.output_library_datasets]) - out_collections = dict( [ ( obj.name, obj.dataset_collection_instance ) for obj in job.output_dataset_collection_instances ] ) - out_collections.update( [ ( obj.name, obj.dataset_collection ) for obj in job.output_dataset_collections ] ) + out_collections = dict([(obj.name, obj.dataset_collection_instance) for obj in job.output_dataset_collection_instances]) + out_collections.update([(obj.name, obj.dataset_collection) for obj in job.output_dataset_collections]) if get_special: @@ -86,8 +86,8 @@ def validate_inputs( input, value, context, **kwargs ): # uses a Dataset rather than an HDA or LDA, it's necessary to set up a # fake dataset association that provides the needed attributes for # preparing a job. - class FakeDatasetAssociation ( object ): - def __init__( self, dataset=None ): + class FakeDatasetAssociation (object): + def __init__(self, dataset=None): self.dataset = dataset self.file_name = dataset.file_name self.metadata = dict() @@ -95,10 +95,10 @@ def __init__( self, dataset=None ): special = get_special() if special: - out_data[ "output_file" ] = FakeDatasetAssociation( dataset=special.dataset ) + out_data["output_file"] = FakeDatasetAssociation(dataset=special.dataset) # These can be passed on the command line if wanted as $__user_*__ - incoming.update( model.User.user_template_environment( job.history and job.history.user ) ) + incoming.update(model.User.user_template_environment(job.history and job.history.user)) # Build params, done before hook so hook can use param_dict = self.build_param_dict( @@ -113,14 +113,14 @@ def __init__( self, dataset=None ): # Certain tools require tasks to be completed prior to job execution # ( this used to be performed in the "exec_before_job" hook, but hooks are deprecated ). - self.tool.exec_before_job( self.app, inp_data, out_data, param_dict ) + self.tool.exec_before_job(self.app, inp_data, out_data, param_dict) # Run the before queue ("exec_before_job") hook - self.tool.call_hook( 'exec_before_job', self.app, inp_data=inp_data, - out_data=out_data, tool=self.tool, param_dict=incoming) + self.tool.call_hook('exec_before_job', self.app, inp_data=inp_data, + out_data=out_data, tool=self.tool, param_dict=incoming) self.param_dict = param_dict - def build_param_dict( self, incoming, input_datasets, output_datasets, output_collections, output_paths, job_working_directory, input_paths=[] ): + def build_param_dict(self, incoming, input_datasets, output_datasets, output_collections, output_paths, job_working_directory, input_paths=[]): """ Build the dictionary of parameters for substituting into the command line. Each value is wrapped in a `InputValueWrapper`, which allows @@ -137,16 +137,16 @@ def input(): param_dict.update(self.tool.template_macro_params) # All parameters go into the param_dict - param_dict.update( incoming ) + param_dict.update(incoming) - input_dataset_paths = dataset_path_rewrites( input_paths ) + input_dataset_paths = dataset_path_rewrites(input_paths) self.__populate_wrappers(param_dict, input_datasets, input_dataset_paths, job_working_directory) self.__populate_input_dataset_wrappers(param_dict, input_datasets, input_dataset_paths) self.__populate_output_dataset_wrappers(param_dict, output_datasets, output_paths, job_working_directory) self.__populate_output_collection_wrappers(param_dict, output_collections, output_paths, job_working_directory) self.__populate_unstructured_path_rewrites(param_dict) # Call param dict sanitizer, before non-job params are added, as we don't want to sanitize filenames. - self.__sanitize_param_dict( param_dict ) + self.__sanitize_param_dict(param_dict) # Parameters added after this line are not sanitized self.__populate_non_job_params(param_dict) @@ -155,42 +155,42 @@ def input(): def __walk_inputs(self, inputs, input_values, func): - def do_walk( inputs, input_values ): + def do_walk(inputs, input_values): """ Wraps parameters as neccesary. """ for input in inputs.values(): - if isinstance( input, Repeat ): - for d in input_values[ input.name ]: - do_walk( input.inputs, d ) - elif isinstance( input, Conditional ): - values = input_values[ input.name ] + if isinstance(input, Repeat): + for d in input_values[input.name]: + do_walk(input.inputs, d) + elif isinstance(input, Conditional): + values = input_values[input.name] current = values["__current_case__"] - func( values, input.test_param ) - do_walk( input.cases[current].inputs, values ) - elif isinstance( input, Section ): - values = input_values[ input.name ] - do_walk( input.inputs, values ) + func(values, input.test_param) + do_walk(input.cases[current].inputs, values) + elif isinstance(input, Section): + values = input_values[input.name] + do_walk(input.inputs, values) else: - func( input_values, input ) + func(input_values, input) - do_walk( inputs, input_values ) + do_walk(inputs, input_values) def __populate_wrappers(self, param_dict, input_datasets, input_dataset_paths, job_working_directory): - def wrap_input( input_values, input ): - value = input_values[ input.name ] - if isinstance( input, DataToolParameter ) and input.multiple: - dataset_instances = DatasetListWrapper.to_dataset_instances( value ) - input_values[ input.name ] = \ - DatasetListWrapper( job_working_directory, - dataset_instances, - dataset_paths=input_dataset_paths, - datatypes_registry=self.app.datatypes_registry, - tool=self.tool, - name=input.name ) - - elif isinstance( input, DataToolParameter ): + def wrap_input(input_values, input): + value = input_values[input.name] + if isinstance(input, DataToolParameter) and input.multiple: + dataset_instances = DatasetListWrapper.to_dataset_instances(value) + input_values[input.name] = \ + DatasetListWrapper(job_working_directory, + dataset_instances, + dataset_paths=input_dataset_paths, + datatypes_registry=self.app.datatypes_registry, + tool=self.tool, + name=input.name) + + elif isinstance(input, DataToolParameter): # FIXME: We're populating param_dict with conversions when # wrapping values, this should happen as a separate # step before wrapping (or call this wrapping step @@ -200,7 +200,7 @@ def wrap_input( input_values, input ): for conversion_name, conversion_extensions, conversion_datatypes in input.conversions: # If we are at building cmdline step, then converters # have already executed - conv_ext, converted_dataset = input_values[ input.name ].find_conversion_destination( conversion_datatypes ) + conv_ext, converted_dataset = input_values[input.name].find_conversion_destination(conversion_datatypes) # When dealing with optional inputs, we'll provide a # valid extension to be used for None converted dataset if not conv_ext: @@ -208,21 +208,21 @@ def wrap_input( input_values, input ): # input_values[ input.name ] is None when optional # dataset, 'conversion' of optional dataset should # create wrapper around NoneDataset for converter output - if input_values[ input.name ] and not converted_dataset: + if input_values[input.name] and not converted_dataset: # Input that converter is based from has a value, # but converted dataset does not exist - raise Exception( 'A path for explicit datatype conversion has not been found: %s --/--> %s' - % ( input_values[ input.name ].extension, conversion_extensions ) ) + raise Exception('A path for explicit datatype conversion has not been found: %s --/--> %s' + % (input_values[input.name].extension, conversion_extensions)) else: # Trick wrapper into using target conv ext (when # None) without actually being a tool parameter - input_values[ conversion_name ] = \ - DatasetFilenameWrapper( converted_dataset, - datatypes_registry=self.app.datatypes_registry, - tool=Bunch( conversion_name=Bunch( extensions=conv_ext ) ), - name=conversion_name ) + input_values[conversion_name] = \ + DatasetFilenameWrapper(converted_dataset, + datatypes_registry=self.app.datatypes_registry, + tool=Bunch(conversion_name=Bunch(extensions=conv_ext)), + name=conversion_name) # Wrap actual input dataset - dataset = input_values[ input.name ] + dataset = input_values[input.name] wrapper_kwds = dict( datatypes_registry=self.app.datatypes_registry, tool=self, @@ -232,15 +232,15 @@ def wrap_input( input_values, input ): # A None dataset does not have a filename real_path = dataset.file_name if real_path in input_dataset_paths: - wrapper_kwds[ "dataset_path" ] = input_dataset_paths[ real_path ] + wrapper_kwds["dataset_path"] = input_dataset_paths[real_path] identifier_key = identifier_key_dict.get(dataset, None) if identifier_key: element_identifier = param_dict.get(identifier_key, None) if element_identifier: - wrapper_kwds[ "identifier" ] = element_identifier - input_values[ input.name ] = \ - DatasetFilenameWrapper( dataset, **wrapper_kwds ) - elif isinstance( input, DataCollectionToolParameter ): + wrapper_kwds["identifier"] = element_identifier + input_values[input.name] = \ + DatasetFilenameWrapper(dataset, **wrapper_kwds) + elif isinstance(input, DataCollectionToolParameter): dataset_collection = value wrapper_kwds = dict( datatypes_registry=self.app.datatypes_registry, @@ -253,20 +253,20 @@ def wrap_input( input_values, input ): dataset_collection, **wrapper_kwds ) - input_values[ input.name ] = wrapper - elif isinstance( input, SelectToolParameter ): - input_values[ input.name ] = SelectToolParameterWrapper( - input, value, other_values=param_dict, path_rewriter=self.unstructured_path_rewriter ) + input_values[input.name] = wrapper + elif isinstance(input, SelectToolParameter): + input_values[input.name] = SelectToolParameterWrapper( + input, value, other_values=param_dict, path_rewriter=self.unstructured_path_rewriter) else: - input_values[ input.name ] = InputValueWrapper( - input, value, param_dict ) + input_values[input.name] = InputValueWrapper( + input, value, param_dict) # HACK: only wrap if check_values is not false, this deals with external # tools where the inputs don't even get passed through. These # tools (e.g. UCSC) should really be handled in a special way. if self.tool.check_values: identifier_key_dict = dict((v, "%s|__identifier__" % k) for k, v in input_datasets.items()) # allows lookup of identifier through HDA. - self.__walk_inputs( self.tool.inputs, param_dict, wrap_input ) + self.__walk_inputs(self.tool.inputs, param_dict, wrap_input) def __populate_input_dataset_wrappers(self, param_dict, input_datasets, input_dataset_paths): # TODO: Update this method for dataset collections? Need to test. -John. @@ -299,15 +299,15 @@ def __populate_input_dataset_wrappers(self, param_dict, input_datasets, input_da if data: real_path = data.file_name if real_path in input_dataset_paths: - dataset_path = input_dataset_paths[ real_path ] - wrapper_kwds[ 'dataset_path' ] = dataset_path - param_dict[name] = DatasetFilenameWrapper( data, **wrapper_kwds ) + dataset_path = input_dataset_paths[real_path] + wrapper_kwds['dataset_path'] = dataset_path + param_dict[name] = DatasetFilenameWrapper(data, **wrapper_kwds) if data: for child in data.children: - param_dict[ "_CHILD___%s___%s" % ( name, child.designation ) ] = DatasetFilenameWrapper( child ) + param_dict["_CHILD___%s___%s" % (name, child.designation)] = DatasetFilenameWrapper(child) def __populate_output_collection_wrappers(self, param_dict, output_collections, output_paths, job_working_directory): - output_dataset_paths = dataset_path_rewrites( output_paths ) + output_dataset_paths = dataset_path_rewrites(output_paths) tool = self.tool for name, out_collection in output_collections.items(): if name not in tool.output_collections: @@ -327,42 +327,42 @@ def __populate_output_collection_wrappers(self, param_dict, output_collections, out_collection, **wrapper_kwds ) - param_dict[ name ] = wrapper + param_dict[name] = wrapper # TODO: Handle nested collections... - output_def = tool.output_collections[ name ] + output_def = tool.output_collections[name] for element_identifier, output_def in output_def.outputs.items(): if not output_def.implicit: - dataset_wrapper = wrapper[ element_identifier ] - param_dict[ output_def.name ] = dataset_wrapper - log.info("Updating param_dict for %s with %s" % (output_def.name, dataset_wrapper) ) + dataset_wrapper = wrapper[element_identifier] + param_dict[output_def.name] = dataset_wrapper + log.info("Updating param_dict for %s with %s" % (output_def.name, dataset_wrapper)) def __populate_output_dataset_wrappers(self, param_dict, output_datasets, output_paths, job_working_directory): - output_dataset_paths = dataset_path_rewrites( output_paths ) + output_dataset_paths = dataset_path_rewrites(output_paths) for name, hda in output_datasets.items(): # Write outputs to the working directory (for security purposes) # if desired. real_path = hda.file_name if real_path in output_dataset_paths: - dataset_path = output_dataset_paths[ real_path ] - param_dict[name] = DatasetFilenameWrapper( hda, dataset_path=dataset_path ) + dataset_path = output_dataset_paths[real_path] + param_dict[name] = DatasetFilenameWrapper(hda, dataset_path=dataset_path) try: - open( dataset_path.false_path, 'w' ).close() + open(dataset_path.false_path, 'w').close() except EnvironmentError: pass # May well not exist - e.g. Pulsar. else: - param_dict[name] = DatasetFilenameWrapper( hda ) + param_dict[name] = DatasetFilenameWrapper(hda) # Provide access to a path to store additional files # TODO: path munging for cluster/dataset server relocatability - param_dict[name].files_path = os.path.abspath(os.path.join( job_working_directory, "dataset_%s_files" % (hda.dataset.id) )) + param_dict[name].files_path = os.path.abspath(os.path.join(job_working_directory, "dataset_%s_files" % (hda.dataset.id))) for child in hda.children: - param_dict[ "_CHILD___%s___%s" % ( name, child.designation ) ] = DatasetFilenameWrapper( child ) + param_dict["_CHILD___%s___%s" % (name, child.designation)] = DatasetFilenameWrapper(child) for out_name, output in self.tool.outputs.items(): if out_name not in param_dict and output.filters: # Assume the reason we lack this output is because a filter # failed to pass; for tool writing convienence, provide a # NoneDataset - ext = getattr( output, "format", None ) # populate only for output datasets (not collections) - param_dict[ out_name ] = NoneDataset( datatypes_registry=self.app.datatypes_registry, ext=ext ) + ext = getattr(output, "format", None) # populate only for output datasets (not collections) + param_dict[out_name] = NoneDataset(datatypes_registry=self.app.datatypes_registry, ext=ext) def __populate_non_job_params(self, param_dict): # -- Add useful attributes/functions for use in creating command line. @@ -374,13 +374,13 @@ def get_data_table_entry(table_name, query_attr, query_val, return_attr): """ if table_name in self.app.tool_data_tables: - return self.app.tool_data_tables[ table_name ].get_entry( query_attr, query_val, return_attr ) + return self.app.tool_data_tables[table_name].get_entry(query_attr, query_val, return_attr) param_dict['__tool_directory__'] = self.compute_environment.tool_directory() param_dict['__get_data_table_entry__'] = get_data_table_entry # We add access to app here, this allows access to app.config, etc - param_dict['__app__'] = RawObjectWrapper( self.app ) + param_dict['__app__'] = RawObjectWrapper(self.app) # More convienent access to app.config.new_file_path; we don't need to # wrap a string, but this method of generating additional datasets # should be considered DEPRECATED @@ -390,24 +390,24 @@ def get_data_table_entry(table_name, query_attr, query_val, return_attr): param_dict['__tool_data_path__'] = param_dict['GALAXY_DATA_INDEX_DIR'] = self.app.config.tool_data_path # For the upload tool, we need to know the root directory and the # datatypes conf path, so we can load the datatypes registry - param_dict['__root_dir__'] = param_dict['GALAXY_ROOT_DIR'] = os.path.abspath( self.app.config.root ) + param_dict['__root_dir__'] = param_dict['GALAXY_ROOT_DIR'] = os.path.abspath(self.app.config.root) param_dict['__datatypes_config__'] = param_dict['GALAXY_DATATYPES_CONF_FILE'] = self.app.datatypes_registry.integrated_datatypes_configs param_dict['__admin_users__'] = self.app.config.admin_users - param_dict['__user__'] = RawObjectWrapper( param_dict.get( '__user__', None ) ) + param_dict['__user__'] = RawObjectWrapper(param_dict.get('__user__', None)) def __populate_unstructured_path_rewrites(self, param_dict): - def rewrite_unstructured_paths( input_values, input ): - if isinstance( input, SelectToolParameter ): - input_values[ input.name ] = SelectToolParameterWrapper( - input, input_values[ input.name ], other_values=param_dict, path_rewriter=self.unstructured_path_rewriter ) + def rewrite_unstructured_paths(input_values, input): + if isinstance(input, SelectToolParameter): + input_values[input.name] = SelectToolParameterWrapper( + input, input_values[input.name], other_values=param_dict, path_rewriter=self.unstructured_path_rewriter) if not self.tool.check_values and self.unstructured_path_rewriter: # The tools weren't "wrapped" yet, but need to be in order to get # the paths rewritten. - self.__walk_inputs( self.tool.inputs, param_dict, rewrite_unstructured_paths ) + self.__walk_inputs(self.tool.inputs, param_dict, rewrite_unstructured_paths) - def __sanitize_param_dict( self, param_dict ): + def __sanitize_param_dict(self, param_dict): """ Sanitize all values that will be substituted on the command line, with the exception of ToolParameterValueWrappers, which already have their own specific sanitization rules and also exclude special-cased named values. @@ -416,16 +416,16 @@ def __sanitize_param_dict( self, param_dict ): Note: this method follows the style of the similar populate calls, in that param_dict is modified in-place. """ # chromInfo is a filename, do not sanitize it. - skip = [ 'chromInfo' ] + list(self.tool.template_macro_params.keys()) + skip = ['chromInfo'] + list(self.tool.template_macro_params.keys()) if not self.tool or not self.tool.options or self.tool.options.sanitize: for key, value in list(param_dict.items()): if key not in skip: # Remove key so that new wrapped object will occupy key slot del param_dict[key] # And replace with new wrapped key - param_dict[ wrap_with_safe_string( key, no_wrap_classes=ToolParameterValueWrapper ) ] = wrap_with_safe_string( value, no_wrap_classes=ToolParameterValueWrapper ) + param_dict[wrap_with_safe_string(key, no_wrap_classes=ToolParameterValueWrapper)] = wrap_with_safe_string(value, no_wrap_classes=ToolParameterValueWrapper) - def build( self ): + def build(self): """ Build runtime description of job to execute, evaluate command and config templates corresponding to this tool with these inputs on this @@ -435,19 +435,19 @@ def build( self ): self.command_line = None try: - self.__build_config_files( ) + self.__build_config_files() except Exception as e: # capture and log parsing errors global_tool_errors.add_error(self.tool.config_file, "Building Config Files", e) raise e try: - self.__build_param_file( ) + self.__build_param_file() except Exception as e: # capture and log parsing errors global_tool_errors.add_error(self.tool.config_file, "Building Param File", e) raise e try: - self.__build_command_line( ) + self.__build_command_line() except Exception as e: # capture and log parsing errors global_tool_errors.add_error(self.tool.config_file, "Building Command Line", e) @@ -460,7 +460,7 @@ def build( self ): return self.command_line, self.extra_filenames, self.environment_variables - def __build_command_line( self ): + def __build_command_line(self): """ Build command line to invoke this tool given a populated param_dict """ @@ -472,14 +472,14 @@ def __build_command_line( self ): return try: # Substituting parameters into the command - command_line = fill_template( command, context=param_dict ) + command_line = fill_template(command, context=param_dict) cleaned_command_line = [] # Remove leading and trailing whitespace from each line for readability. - for line in command_line.split( '\n' ): - cleaned_command_line.append( line.strip() ) - command_line = '\n'.join( cleaned_command_line ) + for line in command_line.split('\n'): + cleaned_command_line.append(line.strip()) + command_line = '\n'.join(cleaned_command_line) # Remove newlines from command line, and any leading/trailing white space - command_line = command_line.replace( "\n", " " ).replace( "\r", " " ).strip() + command_line = command_line.replace("\n", " ").replace("\r", " ").strip() except Exception: # Modify exception message to be more clear # e.args = ( 'Error substituting into command line. Params: %r, Command: %s' % ( param_dict, self.command ), ) @@ -487,13 +487,13 @@ def __build_command_line( self ): if interpreter: # TODO: path munging for cluster/dataset server relocatability executable = command_line.split()[0] - tool_dir = os.path.abspath( self.tool.tool_dir ) - abs_executable = os.path.join( tool_dir, executable ) + tool_dir = os.path.abspath(self.tool.tool_dir) + abs_executable = os.path.join(tool_dir, executable) command_line = command_line.replace(executable, abs_executable, 1) command_line = interpreter + " " + command_line self.command_line = command_line - def __build_config_files( self ): + def __build_config_files(self): """ Build temporary file for file based parameter transfer if needed """ @@ -504,26 +504,26 @@ def __build_config_files( self ): # If a particular filename was forced by the config use it directory = self.local_working_directory if filename is not None: - config_filename = os.path.join( directory, filename ) + config_filename = os.path.join(directory, filename) else: - fd, config_filename = tempfile.mkstemp( dir=directory ) - os.close( fd ) - self.__write_workdir_file( config_filename, config_text, param_dict, is_template=is_template ) - self.__register_extra_file( name, config_filename ) - config_filenames.append( config_filename ) + fd, config_filename = tempfile.mkstemp(dir=directory) + os.close(fd) + self.__write_workdir_file(config_filename, config_text, param_dict, is_template=is_template) + self.__register_extra_file(name, config_filename) + config_filenames.append(config_filename) return config_filenames - def __build_environment_variables( self ): + def __build_environment_variables(self): param_dict = self.param_dict environment_variables = [] for environment_variable_def in self.tool.environment_variables: directory = self.local_working_directory environment_variable = environment_variable_def.copy() environment_variable_template = environment_variable_def["template"] - fd, config_filename = tempfile.mkstemp( dir=directory ) - os.close( fd ) - self.__write_workdir_file( config_filename, environment_variable_template, param_dict ) - config_file_basename = os.path.basename( config_filename ) + fd, config_filename = tempfile.mkstemp(dir=directory) + os.close(fd) + self.__write_workdir_file(config_filename, environment_variable_template, param_dict) + config_file_basename = os.path.basename(config_filename) environment_variable["value"] = "`cat %s`" % config_file_basename environment_variable["raw"] = True environment_variables.append(environment_variable) @@ -531,7 +531,7 @@ def __build_environment_variables( self ): self.environment_variables = environment_variables return environment_variables - def __build_param_file( self ): + def __build_param_file(self): """ Build temporary file for file based parameter transfer if needed """ @@ -539,23 +539,23 @@ def __build_param_file( self ): directory = self.local_working_directory command = self.tool.command if self.tool.profile < 16.04 and command and "$param_file" in command: - fd, param_filename = tempfile.mkstemp( dir=directory ) - os.close( fd ) - f = open( param_filename, "w" ) + fd, param_filename = tempfile.mkstemp(dir=directory) + os.close(fd) + f = open(param_filename, "w") for key, value in param_dict.items(): # parameters can be strings or lists of strings, coerce to list if not isinstance(value, list): - value = [ value ] + value = [value] for elem in value: - f.write( '%s=%s\n' % (key, elem) ) + f.write('%s=%s\n' % (key, elem)) f.close() - self.__register_extra_file( 'param_file', param_filename ) + self.__register_extra_file('param_file', param_filename) return param_filename else: return None - def __build_config_file_text( self, content ): - if isinstance( content, string_types ): + def __build_config_file_text(self, content): + if isinstance(content, string_types): return content, True content_format = content["format"] @@ -566,29 +566,29 @@ def __build_config_file_text( self, content ): return json.dumps(wrapped_json.json_wrap(self.tool.inputs, self.param_dict)), False - def __write_workdir_file( self, config_filename, content, context, is_template=True ): + def __write_workdir_file(self, config_filename, content, context, is_template=True): if is_template: - value = fill_template( content, context=context ) + value = fill_template(content, context=context) else: value = content - with open( config_filename, "w" ) as f: - f.write( value ) + with open(config_filename, "w") as f: + f.write(value) # For running jobs as the actual user, ensure the config file is globally readable - os.chmod( config_filename, 0o644 ) + os.chmod(config_filename, 0o644) - def __register_extra_file( self, name, local_config_path ): + def __register_extra_file(self, name, local_config_path): """ Takes in the local path to a config file and registers the (potentially remote) ultimate path of the config file with the parameter dict. """ - self.extra_filenames.append( local_config_path ) - config_basename = os.path.basename( local_config_path ) + self.extra_filenames.append(local_config_path) + config_basename = os.path.basename(local_config_path) compute_config_path = self.__join_for_compute(self.compute_environment.config_directory(), config_basename) - self.param_dict[ name ] = compute_config_path + self.param_dict[name] = compute_config_path - def __join_for_compute( self, *args ): + def __join_for_compute(self, *args): """ os.path.join but with compute_environment.sep for cross-platform compat. """ - return self.compute_environment.sep().join( args ) + return self.compute_environment.sep().join(args) diff --git a/lib/galaxy/tools/execute.py b/lib/galaxy/tools/execute.py index c926e5d09e13..c24a57af1c02 100644 --- a/lib/galaxy/tools/execute.py +++ b/lib/galaxy/tools/execute.py @@ -13,43 +13,43 @@ from galaxy.tools.parser import ToolOutputCollectionPart from galaxy.util import ExecutionTimer -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) EXECUTION_SUCCESS_MESSAGE = "Tool [%s] created job [%s] %s" -def execute( trans, tool, param_combinations, history, rerun_remap_job_id=None, collection_info=None, workflow_invocation_uuid=None ): +def execute(trans, tool, param_combinations, history, rerun_remap_job_id=None, collection_info=None, workflow_invocation_uuid=None): """ Execute a tool and return object containing summary (output data, number of failures, etc...). """ all_jobs_timer = ExecutionTimer() - execution_tracker = ToolExecutionTracker( tool, param_combinations, collection_info ) + execution_tracker = ToolExecutionTracker(tool, param_combinations, collection_info) app = trans.app execution_cache = ToolExecutionCache(trans) def execute_single_job(params): job_timer = ExecutionTimer() if workflow_invocation_uuid: - params[ '__workflow_invocation_uuid__' ] = workflow_invocation_uuid + params['__workflow_invocation_uuid__'] = workflow_invocation_uuid elif '__workflow_invocation_uuid__' in params: # Only workflow invocation code gets to set this, ignore user supplied # values or rerun parameters. - del params[ '__workflow_invocation_uuid__' ] - job, result = tool.handle_single_execution( trans, rerun_remap_job_id, params, history, collection_info, execution_cache ) + del params['__workflow_invocation_uuid__'] + job, result = tool.handle_single_execution(trans, rerun_remap_job_id, params, history, collection_info, execution_cache) if job: message = EXECUTION_SUCCESS_MESSAGE % (tool.id, job.id, job_timer) log.debug(message) - execution_tracker.record_success( job, result ) + execution_tracker.record_success(job, result) else: - execution_tracker.record_error( result ) + execution_tracker.record_error(result) config = app.config - burst_at = getattr( config, 'tool_submission_burst_at', 10 ) - burst_threads = getattr( config, 'tool_submission_burst_threads', 1 ) + burst_at = getattr(config, 'tool_submission_burst_at', 10) + burst_threads = getattr(config, 'tool_submission_burst_threads', 1) tool_action = tool.tool_action - if hasattr( tool_action, "check_inputs_ready" ): + if hasattr(tool_action, "check_inputs_ready"): for params in execution_tracker.param_combinations: # This will throw an exception if the tool is not ready. tool_action.check_inputs_ready( @@ -84,21 +84,21 @@ def worker(): log.debug("Executed %d job(s) for tool %s request: %s" % (job_count, tool.id, all_jobs_timer)) if collection_info: - history = history or tool.get_default_history_by_trans( trans ) + history = history or tool.get_default_history_by_trans(trans) if len(param_combinations) == 0: template = "Attempting to map over an empty collection, this is not yet implemented. collection_info is [%s]" message = template % collection_info - log.warn(message) + log.warning(message) raise Exception(message) params = param_combinations[0] - execution_tracker.create_output_collections( trans, history, params ) + execution_tracker.create_output_collections(trans, history, params) return execution_tracker -class ToolExecutionTracker( object ): +class ToolExecutionTracker(object): - def __init__( self, tool, param_combinations, collection_info ): + def __init__(self, tool, param_combinations, collection_info): self.tool = tool self.param_combinations = param_combinations self.collection_info = collection_info @@ -110,27 +110,27 @@ def __init__( self, tool, param_combinations, collection_info ): self.outputs_by_output_name = collections.defaultdict(list) self.implicit_collections = {} - def record_success( self, job, outputs ): - self.successful_jobs.append( job ) - self.output_datasets.extend( outputs ) + def record_success(self, job, outputs): + self.successful_jobs.append(job) + self.output_datasets.extend(outputs) for output_name, output_dataset in outputs: - if ToolOutputCollectionPart.is_named_collection_part_name( output_name ): + if ToolOutputCollectionPart.is_named_collection_part_name(output_name): # Skip known collection outputs, these will be covered by # output collections. continue - self.outputs_by_output_name[ output_name ].append( output_dataset ) + self.outputs_by_output_name[output_name].append(output_dataset) for job_output in job.output_dataset_collections: - self.outputs_by_output_name[ job_output.name ].append( job_output.dataset_collection ) + self.outputs_by_output_name[job_output.name].append(job_output.dataset_collection) for job_output in job.output_dataset_collection_instances: - self.output_collections.append( ( job_output.name, job_output.dataset_collection_instance ) ) + self.output_collections.append((job_output.name, job_output.dataset_collection_instance)) - def record_error( self, error ): + def record_error(self, error): self.failed_jobs += 1 message = "There was a failure executing a job for tool [%s] - %s" log.warning(message, self.tool.id, error) - self.execution_errors.append( error ) + self.execution_errors.append(error) - def create_output_collections( self, trans, history, params ): + def create_output_collections(self, trans, history, params): # TODO: Move this function - it doesn't belong here but it does need # the information in this class and potential extensions. if self.failed_jobs > 0: @@ -142,22 +142,36 @@ def create_output_collections( self, trans, history, params ): # collection replaced with a specific dataset. Need to replace this # with the collection and wrap everything up so can evaluate output # label. - params.update( self.collection_info.collections ) # Replace datasets with source collections for labelling outputs. + params.update(self.collection_info.collections) # Replace datasets with source collections for labelling outputs. collection_names = ["collection %d" % c.hid for c in self.collection_info.collections.values()] - on_text = on_text_for_names( collection_names ) + on_text = on_text_for_names(collection_names) collections = {} implicit_inputs = list(self.collection_info.collections.items()) for output_name, outputs in self.outputs_by_output_name.items(): - if not len( structure ) == len( outputs ): + if not len(structure) == len(outputs): # Output does not have the same structure, if all jobs were # successfully submitted this shouldn't have happened. - log.warning( "Problem matching up datasets while attempting to create implicit dataset collections") + log.warning("Problem matching up datasets while attempting to create implicit dataset collections") continue - output = self.tool.outputs[ output_name ] - element_identifiers = structure.element_identifiers_for_outputs( trans, outputs ) + output = self.tool.outputs[output_name] + + element_identifiers = None + if hasattr(output, "default_identifier_source"): + # Switch the structure for outputs if the output specified a default_identifier_source + collection_type_descriptions = trans.app.dataset_collections_service.collection_type_descriptions + + source_collection = self.collection_info.collections.get(output.default_identifier_source) + if source_collection: + collection_type_description = collection_type_descriptions.for_collection_type(source_collection.collection.collection_type) + _structure = structure.for_dataset_collection(source_collection.collection, collection_type_description=collection_type_description) + if structure.can_match(_structure): + element_identifiers = _structure.element_identifiers_for_outputs(trans, outputs) + + if not element_identifiers: + element_identifiers = structure.element_identifiers_for_outputs(trans, outputs) implicit_collection_info = dict( implicit_inputs=implicit_inputs, @@ -177,10 +191,10 @@ def create_output_collections( self, trans, history, params ): job_params=None, ) except Exception: - output_collection_name = "%s across %s" % ( self.tool.name, on_text ) + output_collection_name = "%s across %s" % (self.tool.name, on_text) - child_element_identifiers = element_identifiers[ "element_identifiers" ] - collection_type = element_identifiers[ "collection_type" ] + child_element_identifiers = element_identifiers["element_identifiers"] + collection_type = element_identifiers["collection_type"] collection = trans.app.dataset_collections_service.create( trans=trans, parent=history, @@ -194,9 +208,9 @@ def create_output_collections( self, trans, history, params ): # collections - or we may be already recording data in some # other way. if job not in trans.sa_session: - job = trans.sa_session.query( trans.app.model.Job ).get( job.id ) - job.add_output_dataset_collection( output_name, collection ) - collections[ output_name ] = collection + job = trans.sa_session.query(trans.app.model.Job).get(job.id) + job.add_output_dataset_collection(output_name, collection) + collections[output_name] = collection # Needed to flush the association created just above with # job.add_output_dataset_collection. @@ -204,4 +218,4 @@ def create_output_collections( self, trans, history, params ): self.implicit_collections = collections -__all__ = ( 'execute', ) +__all__ = ('execute', ) diff --git a/lib/galaxy/tools/fetcher.py b/lib/galaxy/tools/fetcher.py index 3466c1a52121..686de383fb99 100644 --- a/lib/galaxy/tools/fetcher.py +++ b/lib/galaxy/tools/fetcher.py @@ -6,7 +6,7 @@ class ToolLocationFetcher(object): def __init__(self): self.resolver_classes = self.__resolvers_dict() - def __resolvers_dict( self ): + def __resolvers_dict(self): import galaxy.tools.locations return plugin_config.plugins_dict(galaxy.tools.locations, 'scheme') diff --git a/lib/galaxy/tools/imp_exp/__init__.py b/lib/galaxy/tools/imp_exp/__init__.py index 0ffc53e06543..8b810c434458 100644 --- a/lib/galaxy/tools/imp_exp/__init__.py +++ b/lib/galaxy/tools/imp_exp/__init__.py @@ -17,44 +17,45 @@ log = logging.getLogger(__name__) -class JobImportHistoryArchiveWrapper( object, UsesAnnotations ): +class JobImportHistoryArchiveWrapper(object, UsesAnnotations): """ Class provides support for performing jobs that import a history from an archive. """ - def __init__( self, app, job_id ): + + def __init__(self, app, job_id): self.app = app self.job_id = job_id self.sa_session = self.app.model.context - def cleanup_after_job( self ): + def cleanup_after_job(self): """ Set history, datasets, and jobs' attributes and clean up archive directory. """ # # Helper methods. # - def file_in_dir( file_path, a_dir ): + def file_in_dir(file_path, a_dir): """ Returns true if file is in directory. """ - abs_file_path = os.path.abspath( file_path ) - return os.path.split( abs_file_path )[0] == a_dir + abs_file_path = os.path.abspath(file_path) + return os.path.split(abs_file_path)[0] == a_dir - def read_file_contents( file_path ): + def read_file_contents(file_path): """ Read contents of a file. """ - fp = open( file_path, 'rb' ) + fp = open(file_path, 'rb') buffsize = 1048576 file_contents = '' try: while True: - file_contents += fp.read( buffsize ) - if not file_contents or len( file_contents ) % buffsize != 0: + file_contents += fp.read(buffsize) + if not file_contents or len(file_contents) % buffsize != 0: break except OverflowError: pass fp.close() return file_contents - def get_tag_str( tag, value ): + def get_tag_str(tag, value): """ Builds a tag string for a tag, value pair. """ if not value: return tag @@ -65,39 +66,39 @@ def get_tag_str( tag, value ): # Import history. # - jiha = self.sa_session.query( model.JobImportHistoryArchive ).filter_by( job_id=self.job_id ).first() + jiha = self.sa_session.query(model.JobImportHistoryArchive).filter_by(job_id=self.job_id).first() if jiha: try: archive_dir = jiha.archive_dir user = jiha.job.user # Bioblend previous to 17.01 exported histories with an extra subdir. - if not os.path.exists( os.path.join( archive_dir, 'history_attrs.txt' ) ): - for d in os.listdir( archive_dir ): - if os.path.isdir( os.path.join( archive_dir, d ) ): - archive_dir = os.path.join( archive_dir, d ) + if not os.path.exists(os.path.join(archive_dir, 'history_attrs.txt')): + for d in os.listdir(archive_dir): + if os.path.isdir(os.path.join(archive_dir, d)): + archive_dir = os.path.join(archive_dir, d) break # # Create history. # - history_attr_file_name = os.path.join( archive_dir, 'history_attrs.txt') - history_attr_str = read_file_contents( history_attr_file_name ) - history_attrs = loads( history_attr_str ) + history_attr_file_name = os.path.join(archive_dir, 'history_attrs.txt') + history_attr_str = read_file_contents(history_attr_file_name) + history_attrs = loads(history_attr_str) # Create history. - new_history = model.History( name='imported from archive: %s' % history_attrs['name'].encode( 'utf-8' ), - user=user ) + new_history = model.History(name='imported from archive: %s' % history_attrs['name'].encode('utf-8'), + user=user) new_history.importing = True new_history.hid_counter = history_attrs['hid_counter'] new_history.genome_build = history_attrs['genome_build'] - self.sa_session.add( new_history ) + self.sa_session.add(new_history) jiha.history = new_history self.sa_session.flush() # Add annotation, tags. if user: - self.add_item_annotation( self.sa_session, user, new_history, history_attrs[ 'annotation' ] ) + self.add_item_annotation(self.sa_session, user, new_history, history_attrs['annotation']) """ TODO: figure out to how add tags to item. for tag, value in history_attrs[ 'tags' ].items(): @@ -107,13 +108,13 @@ def get_tag_str( tag, value ): # # Create datasets. # - datasets_attrs_file_name = os.path.join( archive_dir, 'datasets_attrs.txt') - datasets_attr_str = read_file_contents( datasets_attrs_file_name ) - datasets_attrs = loads( datasets_attr_str ) + datasets_attrs_file_name = os.path.join(archive_dir, 'datasets_attrs.txt') + datasets_attr_str = read_file_contents(datasets_attrs_file_name) + datasets_attrs = loads(datasets_attr_str) - if os.path.exists( datasets_attrs_file_name + ".provenance" ): - provenance_attr_str = read_file_contents( datasets_attrs_file_name + ".provenance" ) - provenance_attrs = loads( provenance_attr_str ) + if os.path.exists(datasets_attrs_file_name + ".provenance"): + provenance_attr_str = read_file_contents(datasets_attrs_file_name + ".provenance") + provenance_attrs = loads(provenance_attr_str) datasets_attrs += provenance_attrs # Get counts of how often each dataset file is used; a file can @@ -121,28 +122,28 @@ def get_tag_str( tag, value ): datasets_usage_counts = {} for dataset_attrs in datasets_attrs: temp_dataset_file_name = \ - os.path.abspath( os.path.join( archive_dir, dataset_attrs['file_name'] ) ) - if ( temp_dataset_file_name not in datasets_usage_counts ): - datasets_usage_counts[ temp_dataset_file_name ] = 0 - datasets_usage_counts[ temp_dataset_file_name ] += 1 + os.path.abspath(os.path.join(archive_dir, dataset_attrs['file_name'])) + if (temp_dataset_file_name not in datasets_usage_counts): + datasets_usage_counts[temp_dataset_file_name] = 0 + datasets_usage_counts[temp_dataset_file_name] += 1 # Create datasets. for dataset_attrs in datasets_attrs: metadata = dataset_attrs['metadata'] # Create dataset and HDA. - hda = model.HistoryDatasetAssociation( name=dataset_attrs['name'].encode( 'utf-8' ), - extension=dataset_attrs['extension'], - info=dataset_attrs['info'].encode( 'utf-8' ), - blurb=dataset_attrs['blurb'], - peek=dataset_attrs['peek'], - designation=dataset_attrs['designation'], - visible=dataset_attrs['visible'], - dbkey=metadata['dbkey'], - metadata=metadata, - history=new_history, - create_dataset=True, - sa_session=self.sa_session ) + hda = model.HistoryDatasetAssociation(name=dataset_attrs['name'].encode('utf-8'), + extension=dataset_attrs['extension'], + info=dataset_attrs['info'].encode('utf-8'), + blurb=dataset_attrs['blurb'], + peek=dataset_attrs['peek'], + designation=dataset_attrs['designation'], + visible=dataset_attrs['visible'], + dbkey=metadata['dbkey'], + metadata=metadata, + history=new_history, + create_dataset=True, + sa_session=self.sa_session) if 'uuid' in dataset_attrs: hda.dataset.uuid = dataset_attrs["uuid"] if dataset_attrs.get('exported', True) is False: @@ -151,9 +152,9 @@ def get_tag_str( tag, value ): hda.purged = True else: hda.state = hda.states.OK - self.sa_session.add( hda ) + self.sa_session.add(hda) self.sa_session.flush() - new_history.add_dataset( hda, genome_build=None ) + new_history.add_dataset(hda, genome_build=None) hda.hid = dataset_attrs['hid'] # Overwrite default hid set when HDA added to history. # TODO: Is there a way to recover permissions? Is this needed? # permissions = trans.app.security_agent.history_get_default_permissions( new_history ) @@ -162,17 +163,17 @@ def get_tag_str( tag, value ): if dataset_attrs.get('exported', True) is True: # Do security check and move/copy dataset data. temp_dataset_file_name = \ - os.path.realpath( os.path.abspath( os.path.join( archive_dir, dataset_attrs['file_name'] ) ) ) - if not file_in_dir( temp_dataset_file_name, os.path.join( archive_dir, "datasets" ) ): - raise MalformedContents( "Invalid dataset path: %s" % temp_dataset_file_name ) - if datasets_usage_counts[ temp_dataset_file_name ] == 1: - self.app.object_store.update_from_file( hda.dataset, file_name=temp_dataset_file_name, create=True ) + os.path.realpath(os.path.abspath(os.path.join(archive_dir, dataset_attrs['file_name']))) + if not file_in_dir(temp_dataset_file_name, os.path.join(archive_dir, "datasets")): + raise MalformedContents("Invalid dataset path: %s" % temp_dataset_file_name) + if datasets_usage_counts[temp_dataset_file_name] == 1: + self.app.object_store.update_from_file(hda.dataset, file_name=temp_dataset_file_name, create=True) # Import additional files if present. Histories exported previously might not have this attribute set. - dataset_extra_files_path = dataset_attrs.get( 'extra_files_path', None ) + dataset_extra_files_path = dataset_attrs.get('extra_files_path', None) if dataset_extra_files_path: try: - file_list = os.listdir( os.path.join( archive_dir, dataset_extra_files_path ) ) + file_list = os.listdir(os.path.join(archive_dir, dataset_extra_files_path)) except OSError: file_list = [] @@ -180,16 +181,16 @@ def get_tag_str( tag, value ): for extra_file in file_list: self.app.object_store.update_from_file( hda.dataset, extra_dir='dataset_%s_files' % hda.dataset.id, - alt_name=extra_file, file_name=os.path.join( archive_dir, dataset_extra_files_path, extra_file ), - create=True ) + alt_name=extra_file, file_name=os.path.join(archive_dir, dataset_extra_files_path, extra_file), + create=True) else: - datasets_usage_counts[ temp_dataset_file_name ] -= 1 - shutil.copyfile( temp_dataset_file_name, hda.file_name ) + datasets_usage_counts[temp_dataset_file_name] -= 1 + shutil.copyfile(temp_dataset_file_name, hda.file_name) hda.dataset.set_total_size() # update the filesize record in the database # Set tags, annotations. if user: - self.add_item_annotation( self.sa_session, user, hda, dataset_attrs[ 'annotation' ] ) + self.add_item_annotation(self.sa_session, user, hda, dataset_attrs['annotation']) # TODO: Set tags. """ for tag, value in dataset_attrs[ 'tags' ].items(): @@ -201,7 +202,7 @@ def get_tag_str( tag, value ): if hda.extension == 'bam': self.app.datatypes_registry.set_external_metadata_tool.tool_action.execute_via_app( self.app.datatypes_registry.set_external_metadata_tool, self.app, jiha.job.session_id, - new_history.id, jiha.job.user, incoming={ 'input1': hda }, overwrite=False + new_history.id, jiha.job.user, incoming={'input1': hda}, overwrite=False ) # @@ -209,18 +210,18 @@ def get_tag_str( tag, value ): # # Read jobs attributes. - jobs_attr_file_name = os.path.join( archive_dir, 'jobs_attrs.txt') - jobs_attr_str = read_file_contents( jobs_attr_file_name ) + jobs_attr_file_name = os.path.join(archive_dir, 'jobs_attrs.txt') + jobs_attr_str = read_file_contents(jobs_attr_file_name) # Decode jobs attributes. - def as_hda( obj_dct ): + def as_hda(obj_dct): """ Hook to 'decode' an HDA; method uses history and HID to get the HDA represented by the encoded object. This only works because HDAs are created above. """ - if obj_dct.get( '__HistoryDatasetAssociation__', False ): - return self.sa_session.query( model.HistoryDatasetAssociation - ).filter_by( history=new_history, hid=obj_dct['hid'] ).first() + if obj_dct.get('__HistoryDatasetAssociation__', False): + return self.sa_session.query(model.HistoryDatasetAssociation) \ + .filter_by(history=new_history, hid=obj_dct['hid']).first() return obj_dct - jobs_attrs = loads( jobs_attr_str, object_hook=as_hda ) + jobs_attrs = loads(jobs_attr_str, object_hook=as_hda) # Create each job. for job_attrs in jobs_attrs: @@ -230,9 +231,9 @@ def as_hda( obj_dct ): # imported_job.session = trans.get_galaxy_session().id imported_job.history = new_history imported_job.imported = True - imported_job.tool_id = job_attrs[ 'tool_id' ] - imported_job.tool_version = job_attrs[ 'tool_version' ] - imported_job.set_state( job_attrs[ 'state' ] ) + imported_job.tool_id = job_attrs['tool_id'] + imported_job.tool_version = job_attrs['tool_version'] + imported_job.set_state(job_attrs['state']) imported_job.info = job_attrs.get('info', None) imported_job.exit_code = job_attrs.get('exit_code', None) imported_job.traceback = job_attrs.get('traceback', None) @@ -244,16 +245,17 @@ def as_hda( obj_dct ): imported_job.update_time = datetime.datetime.strptime(job_attrs["update_time"], "%Y-%m-%dT%H:%M:%S.%f") except: pass - self.sa_session.add( imported_job ) + self.sa_session.add(imported_job) self.sa_session.flush() - class HistoryDatasetAssociationIDEncoder( json.JSONEncoder ): + class HistoryDatasetAssociationIDEncoder(json.JSONEncoder): """ Custom JSONEncoder for a HistoryDatasetAssociation that encodes an HDA as its ID. """ - def default( self, obj ): + + def default(self, obj): """ Encode an HDA, default encoding for everything else. """ - if isinstance( obj, model.HistoryDatasetAssociation ): + if isinstance(obj, model.HistoryDatasetAssociation): return obj.id - return json.JSONEncoder.default( self, obj ) + return json.JSONEncoder.default(self, obj) # Set parameters. May be useful to look at metadata.py for creating parameters. # TODO: there may be a better way to set parameters, e.g.: @@ -261,33 +263,33 @@ def default( self, obj ): # job.add_parameter( name, value ) # to make this work, we'd need to flesh out the HDA objects. The code below is # relatively similar. - for name, value in job_attrs[ 'params' ].items(): + for name, value in job_attrs['params'].items(): # Transform parameter values when necessary. - if isinstance( value, model.HistoryDatasetAssociation ): + if isinstance(value, model.HistoryDatasetAssociation): # HDA input: use hid to find input. - input_hda = self.sa_session.query( model.HistoryDatasetAssociation ) \ - .filter_by( history=new_history, hid=value.hid ).first() + input_hda = self.sa_session.query(model.HistoryDatasetAssociation) \ + .filter_by(history=new_history, hid=value.hid).first() value = input_hda.id # print "added parameter %s-->%s to job %i" % ( name, value, imported_job.id ) - imported_job.add_parameter( name, dumps( value, cls=HistoryDatasetAssociationIDEncoder ) ) + imported_job.add_parameter(name, dumps(value, cls=HistoryDatasetAssociationIDEncoder)) # TODO: Connect jobs to input datasets. # Connect jobs to output datasets. - for output_hid in job_attrs[ 'output_datasets' ]: + for output_hid in job_attrs['output_datasets']: # print "%s job has output dataset %i" % (imported_job.id, output_hid) - output_hda = self.sa_session.query( model.HistoryDatasetAssociation - ).filter_by(history=new_history, hid=output_hid ).first() + output_hda = self.sa_session.query(model.HistoryDatasetAssociation) \ + .filter_by(history=new_history, hid=output_hid).first() if output_hda: - imported_job.add_output_dataset( output_hda.name, output_hda ) + imported_job.add_output_dataset(output_hda.name, output_hda) # Connect jobs to input datasets. if 'input_mapping' in job_attrs: - for input_name, input_hid in job_attrs[ 'input_mapping' ].items(): - input_hda = self.sa_session.query( model.HistoryDatasetAssociation ) \ - .filter_by( history=new_history, hid=input_hid ).first() + for input_name, input_hid in job_attrs['input_mapping'].items(): + input_hda = self.sa_session.query(model.HistoryDatasetAssociation) \ + .filter_by(history=new_history, hid=input_hid).first() if input_hda: - imported_job.add_input_dataset( input_name, input_hda ) + imported_job.add_input_dataset(input_name, input_hda) self.sa_session.flush() @@ -296,38 +298,39 @@ def default( self, obj ): self.sa_session.flush() # Cleanup. - if os.path.exists( archive_dir ): - shutil.rmtree( archive_dir ) + if os.path.exists(archive_dir): + shutil.rmtree(archive_dir) except Exception as e: jiha.job.stderr += "Error cleaning up history import job: %s" % e self.sa_session.flush() raise -class JobExportHistoryArchiveWrapper( object, UsesAnnotations ): +class JobExportHistoryArchiveWrapper(object, UsesAnnotations): """ Class provides support for performing jobs that export a history to an archive. """ - def __init__( self, job_id ): + + def __init__(self, job_id): self.job_id = job_id - def get_history_datasets( self, trans, history ): + def get_history_datasets(self, trans, history): """ Returns history's datasets. """ - query = ( trans.sa_session.query( trans.model.HistoryDatasetAssociation ) - .filter( trans.model.HistoryDatasetAssociation.history == history ) - .options( eagerload( "children" ) ) - .join( "dataset" ) - .options( eagerload_all( "dataset.actions" ) ) - .order_by( trans.model.HistoryDatasetAssociation.hid ) - .filter( trans.model.HistoryDatasetAssociation.deleted == expression.false() ) - .filter( trans.model.Dataset.purged == expression.false() ) ) + query = (trans.sa_session.query(trans.model.HistoryDatasetAssociation) + .filter(trans.model.HistoryDatasetAssociation.history == history) + .options(eagerload("children")) + .join("dataset") + .options(eagerload_all("dataset.actions")) + .order_by(trans.model.HistoryDatasetAssociation.hid) + .filter(trans.model.HistoryDatasetAssociation.deleted == expression.false()) + .filter(trans.model.Dataset.purged == expression.false())) return query.all() # TODO: should use db_session rather than trans in this method. - def setup_job( self, trans, jeha, include_hidden=False, include_deleted=False ): + def setup_job(self, trans, jeha, include_hidden=False, include_deleted=False): """ Perform setup for job to export a history into an archive. Method generates attribute files for export, sets the corresponding attributes in the jeha object, and returns a command line for running the job. The command line @@ -338,48 +341,49 @@ def setup_job( self, trans, jeha, include_hidden=False, include_deleted=False ): # Helper methods/classes. # - def get_item_tag_dict( item ): + def get_item_tag_dict(item): """ Create dictionary of an item's tags. """ tags = {} for tag in item.tags: - tag_user_tname = to_unicode( tag.user_tname ) - tag_user_value = to_unicode( tag.user_value ) - tags[ tag_user_tname ] = tag_user_value + tag_user_tname = to_unicode(tag.user_tname) + tag_user_value = to_unicode(tag.user_value) + tags[tag_user_tname] = tag_user_value return tags - def prepare_metadata( metadata ): + def prepare_metadata(metadata): """ Prepare metatdata for exporting. """ for name, value in list(metadata.items()): # Metadata files are not needed for export because they can be # regenerated. - if isinstance( value, trans.app.model.MetadataFile ): - del metadata[ name ] + if isinstance(value, trans.app.model.MetadataFile): + del metadata[name] return metadata - class HistoryDatasetAssociationEncoder( json.JSONEncoder ): + class HistoryDatasetAssociationEncoder(json.JSONEncoder): """ Custom JSONEncoder for a HistoryDatasetAssociation. """ - def default( self, obj ): + + def default(self, obj): """ Encode an HDA, default encoding for everything else. """ - if isinstance( obj, trans.app.model.HistoryDatasetAssociation ): + if isinstance(obj, trans.app.model.HistoryDatasetAssociation): rval = { "__HistoryDatasetAssociation__": True, "create_time": obj.create_time.__str__(), "update_time": obj.update_time.__str__(), "hid": obj.hid, - "name": to_unicode( obj.name ), - "info": to_unicode( obj.info ), + "name": to_unicode(obj.name), + "info": to_unicode(obj.info), "blurb": obj.blurb, "peek": obj.peek, "extension": obj.extension, - "metadata": prepare_metadata( dict( obj.metadata.items() ) ), + "metadata": prepare_metadata(dict(obj.metadata.items())), "parent_id": obj.parent_id, "designation": obj.designation, "deleted": obj.deleted, "visible": obj.visible, "file_name": obj.file_name, - "uuid": ( lambda uuid: str( uuid ) if uuid else None )( obj.dataset.uuid ), - "annotation": to_unicode( getattr( obj, 'annotation', '' ) ), - "tags": get_item_tag_dict( obj ), + "uuid": (lambda uuid: str(uuid) if uuid else None)(obj.dataset.uuid), + "annotation": to_unicode(getattr(obj, 'annotation', '')), + "tags": get_item_tag_dict(obj), "extra_files_path": obj.extra_files_path } if not obj.visible and not include_hidden: @@ -389,7 +393,7 @@ def default( self, obj ): else: rval['exported'] = True return rval - return json.JSONEncoder.default( self, obj ) + return json.JSONEncoder.default(self, obj) # # Create attributes/metadata files for export. @@ -401,40 +405,40 @@ def default( self, obj ): history_attrs = { "create_time": history.create_time.__str__(), "update_time": history.update_time.__str__(), - "name": to_unicode( history.name ), + "name": to_unicode(history.name), "hid_counter": history.hid_counter, "genome_build": history.genome_build, - "annotation": to_unicode( self.get_item_annotation_str( trans.sa_session, history.user, history ) ), - "tags": get_item_tag_dict( history ), + "annotation": to_unicode(self.get_item_annotation_str(trans.sa_session, history.user, history)), + "tags": get_item_tag_dict(history), "includes_hidden_datasets": include_hidden, "includes_deleted_datasets": include_deleted } - history_attrs_filename = tempfile.NamedTemporaryFile( dir=temp_output_dir ).name - history_attrs_out = open( history_attrs_filename, 'w' ) - history_attrs_out.write( dumps( history_attrs ) ) + history_attrs_filename = tempfile.NamedTemporaryFile(dir=temp_output_dir).name + history_attrs_out = open(history_attrs_filename, 'w') + history_attrs_out.write(dumps(history_attrs)) history_attrs_out.close() jeha.history_attrs_filename = history_attrs_filename # Write datasets' attributes to file. - datasets = self.get_history_datasets( trans, history ) + datasets = self.get_history_datasets(trans, history) included_datasets = [] datasets_attrs = [] provenance_attrs = [] for dataset in datasets: - dataset.annotation = self.get_item_annotation_str( trans.sa_session, history.user, dataset ) + dataset.annotation = self.get_item_annotation_str(trans.sa_session, history.user, dataset) if (not dataset.visible and not include_hidden) or (dataset.deleted and not include_deleted): - provenance_attrs.append( dataset ) + provenance_attrs.append(dataset) else: - datasets_attrs.append( dataset ) - included_datasets.append( dataset ) - datasets_attrs_filename = tempfile.NamedTemporaryFile( dir=temp_output_dir ).name - datasets_attrs_out = open( datasets_attrs_filename, 'w' ) - datasets_attrs_out.write( dumps( datasets_attrs, cls=HistoryDatasetAssociationEncoder ) ) + datasets_attrs.append(dataset) + included_datasets.append(dataset) + datasets_attrs_filename = tempfile.NamedTemporaryFile(dir=temp_output_dir).name + datasets_attrs_out = open(datasets_attrs_filename, 'w') + datasets_attrs_out.write(dumps(datasets_attrs, cls=HistoryDatasetAssociationEncoder)) datasets_attrs_out.close() jeha.datasets_attrs_filename = datasets_attrs_filename - provenance_attrs_out = open( datasets_attrs_filename + ".provenance", 'w' ) - provenance_attrs_out.write( dumps( provenance_attrs, cls=HistoryDatasetAssociationEncoder ) ) + provenance_attrs_out = open(datasets_attrs_filename + ".provenance", 'w') + provenance_attrs_out.write(dumps(provenance_attrs, cls=HistoryDatasetAssociationEncoder)) provenance_attrs_out.close() # @@ -462,35 +466,35 @@ def default( self, obj ): # No viable job. continue - jobs_dict[ job.id ] = job + jobs_dict[job.id] = job # Get jobs' attributes. jobs_attrs = [] for id, job in jobs_dict.items(): job_attrs = {} - job_attrs[ 'tool_id' ] = job.tool_id - job_attrs[ 'tool_version' ] = job.tool_version - job_attrs[ 'state' ] = job.state - job_attrs[ 'info' ] = job.info - job_attrs[ 'traceback' ] = job.traceback - job_attrs[ 'command_line' ] = job.command_line - job_attrs[ 'stderr' ] = job.stderr - job_attrs[ 'stdout' ] = job.stdout - job_attrs[ 'exit_code' ] = job.exit_code - job_attrs[ 'create_time' ] = job.create_time.isoformat() - job_attrs[ 'update_time' ] = job.update_time.isoformat() + job_attrs['tool_id'] = job.tool_id + job_attrs['tool_version'] = job.tool_version + job_attrs['state'] = job.state + job_attrs['info'] = job.info + job_attrs['traceback'] = job.traceback + job_attrs['command_line'] = job.command_line + job_attrs['stderr'] = job.stderr + job_attrs['stdout'] = job.stdout + job_attrs['exit_code'] = job.exit_code + job_attrs['create_time'] = job.create_time.isoformat() + job_attrs['update_time'] = job.update_time.isoformat() # Get the job's parameters try: - params_objects = job.get_param_values( trans.app ) + params_objects = job.get_param_values(trans.app) except: # Could not get job params. continue params_dict = {} for name, value in params_objects.items(): - params_dict[ name ] = value - job_attrs[ 'params' ] = params_dict + params_dict[name] = value + job_attrs['params'] = params_dict # -- Get input, output datasets. -- @@ -499,18 +503,18 @@ def default( self, obj ): for assoc in job.input_datasets: # Optional data inputs will not have a dataset. if assoc.dataset: - input_datasets.append( assoc.dataset.hid ) + input_datasets.append(assoc.dataset.hid) input_mapping[assoc.name] = assoc.dataset.hid - job_attrs[ 'input_datasets' ] = input_datasets - job_attrs[ 'input_mapping'] = input_mapping - output_datasets = [ assoc.dataset.hid for assoc in job.output_datasets ] - job_attrs[ 'output_datasets' ] = output_datasets + job_attrs['input_datasets'] = input_datasets + job_attrs['input_mapping'] = input_mapping + output_datasets = [assoc.dataset.hid for assoc in job.output_datasets] + job_attrs['output_datasets'] = output_datasets - jobs_attrs.append( job_attrs ) + jobs_attrs.append(job_attrs) - jobs_attrs_filename = tempfile.NamedTemporaryFile( dir=temp_output_dir ).name - jobs_attrs_out = open( jobs_attrs_filename, 'w' ) - jobs_attrs_out.write( dumps( jobs_attrs, cls=HistoryDatasetAssociationEncoder ) ) + jobs_attrs_filename = tempfile.NamedTemporaryFile(dir=temp_output_dir).name + jobs_attrs_out = open(jobs_attrs_filename, 'w') + jobs_attrs_out.write(dumps(jobs_attrs, cls=HistoryDatasetAssociationEncoder)) jobs_attrs_out.close() jeha.jobs_attrs_filename = jobs_attrs_filename @@ -520,22 +524,22 @@ def default( self, obj ): options = "" if jeha.compressed: options = "-G" - return "%s %s %s %s" % ( options, history_attrs_filename, - datasets_attrs_filename, - jobs_attrs_filename ) + return "%s %s %s %s" % (options, history_attrs_filename, + datasets_attrs_filename, + jobs_attrs_filename) - def cleanup_after_job( self, db_session ): + def cleanup_after_job(self, db_session): """ Remove temporary directory and attribute files generated during setup for this job. """ # Get jeha for job. - jeha = db_session.query( model.JobExportHistoryArchive ).filter_by( job_id=self.job_id ).first() + jeha = db_session.query(model.JobExportHistoryArchive).filter_by(job_id=self.job_id).first() if jeha: - for filename in [ jeha.history_attrs_filename, jeha.datasets_attrs_filename, jeha.jobs_attrs_filename ]: + for filename in [jeha.history_attrs_filename, jeha.datasets_attrs_filename, jeha.jobs_attrs_filename]: try: - os.remove( filename ) + os.remove(filename) except Exception as e: - log.debug( 'Failed to cleanup attributes file (%s): %s' % ( filename, e ) ) - temp_dir = os.path.split( jeha.history_attrs_filename )[0] + log.debug('Failed to cleanup attributes file (%s): %s' % (filename, e)) + temp_dir = os.path.split(jeha.history_attrs_filename)[0] try: - shutil.rmtree( temp_dir ) + shutil.rmtree(temp_dir) except Exception as e: - log.debug( 'Error deleting directory containing attribute files (%s): %s' % ( temp_dir, e ) ) + log.debug('Error deleting directory containing attribute files (%s): %s' % (temp_dir, e)) diff --git a/lib/galaxy/tools/imp_exp/export_history.py b/lib/galaxy/tools/imp_exp/export_history.py index 3f1ae0e19583..5c41387cb0cb 100644 --- a/lib/galaxy/tools/imp_exp/export_history.py +++ b/lib/galaxy/tools/imp_exp/export_history.py @@ -16,96 +16,96 @@ from galaxy.util import FILENAME_VALID_CHARS -def get_dataset_filename( name, ext, hid ): +def get_dataset_filename(name, ext, hid): """ Builds a filename for a dataset using its name an extension. """ - base = ''.join( c in FILENAME_VALID_CHARS and c or '_' for c in name ) + base = ''.join(c in FILENAME_VALID_CHARS and c or '_' for c in name) return base + "_%s.%s" % (hid, ext) -def create_archive( history_attrs_file, datasets_attrs_file, jobs_attrs_file, out_file, gzip=False ): +def create_archive(history_attrs_file, datasets_attrs_file, jobs_attrs_file, out_file, gzip=False): """ Create archive from the given attribute/metadata files and save it to out_file. """ tarfile_mode = "w" if gzip: tarfile_mode += ":gz" try: - history_archive = tarfile.open( out_file, tarfile_mode ) + history_archive = tarfile.open(out_file, tarfile_mode) # Read datasets attributes from file. - datasets_attr_in = open( datasets_attrs_file, 'rb' ) + datasets_attr_in = open(datasets_attrs_file, 'rb') datasets_attr_str = '' buffsize = 1048576 try: while True: - datasets_attr_str += datasets_attr_in.read( buffsize ) - if not datasets_attr_str or len( datasets_attr_str ) % buffsize != 0: + datasets_attr_str += datasets_attr_in.read(buffsize) + if not datasets_attr_str or len(datasets_attr_str) % buffsize != 0: break except OverflowError: pass datasets_attr_in.close() - datasets_attrs = loads( datasets_attr_str ) + datasets_attrs = loads(datasets_attr_str) # Add datasets to archive and update dataset attributes. # TODO: security check to ensure that files added are in Galaxy dataset directory? for dataset_attrs in datasets_attrs: if dataset_attrs['exported']: - dataset_file_name = dataset_attrs[ 'file_name' ] # Full file name. - dataset_hid = dataset_attrs[ 'hid'] - dataset_archive_name = os.path.join( 'datasets', - get_dataset_filename( dataset_attrs[ 'name' ], dataset_attrs[ 'extension' ], dataset_hid ) ) - history_archive.add( dataset_file_name, arcname=dataset_archive_name ) + dataset_file_name = dataset_attrs['file_name'] # Full file name. + dataset_hid = dataset_attrs['hid'] + dataset_archive_name = os.path.join('datasets', + get_dataset_filename(dataset_attrs['name'], dataset_attrs['extension'], dataset_hid)) + history_archive.add(dataset_file_name, arcname=dataset_archive_name) # Include additional files for example, files/images included in HTML output. - extra_files_path = dataset_attrs[ 'extra_files_path' ] + extra_files_path = dataset_attrs['extra_files_path'] if extra_files_path: try: - file_list = os.listdir( extra_files_path ) + file_list = os.listdir(extra_files_path) except OSError: file_list = [] - if len( file_list ): + if len(file_list): dataset_extra_files_path = 'datasets/extra_files_path_%s' % dataset_hid for fname in file_list: - history_archive.add( os.path.join( extra_files_path, fname ), - arcname=( os.path.join( dataset_extra_files_path, fname ) ) ) - dataset_attrs[ 'extra_files_path' ] = dataset_extra_files_path + history_archive.add(os.path.join(extra_files_path, fname), + arcname=(os.path.join(dataset_extra_files_path, fname))) + dataset_attrs['extra_files_path'] = dataset_extra_files_path else: - dataset_attrs[ 'extra_files_path' ] = '' + dataset_attrs['extra_files_path'] = '' # Update dataset filename to be archive name. - dataset_attrs[ 'file_name' ] = dataset_archive_name + dataset_attrs['file_name'] = dataset_archive_name # Rewrite dataset attributes file. - datasets_attrs_out = open( datasets_attrs_file, 'w' ) - datasets_attrs_out.write( dumps( datasets_attrs ) ) + datasets_attrs_out = open(datasets_attrs_file, 'w') + datasets_attrs_out.write(dumps(datasets_attrs)) datasets_attrs_out.close() # Finish archive. - history_archive.add( history_attrs_file, arcname="history_attrs.txt" ) - history_archive.add( datasets_attrs_file, arcname="datasets_attrs.txt" ) - if os.path.exists( datasets_attrs_file + ".provenance" ): - history_archive.add( datasets_attrs_file + ".provenance", arcname="datasets_attrs.txt.provenance" ) - history_archive.add( jobs_attrs_file, arcname="jobs_attrs.txt" ) + history_archive.add(history_attrs_file, arcname="history_attrs.txt") + history_archive.add(datasets_attrs_file, arcname="datasets_attrs.txt") + if os.path.exists(datasets_attrs_file + ".provenance"): + history_archive.add(datasets_attrs_file + ".provenance", arcname="datasets_attrs.txt.provenance") + history_archive.add(jobs_attrs_file, arcname="jobs_attrs.txt") history_archive.close() # Status. return 'Created history archive.' except Exception as e: - return 'Error creating history archive: %s' % str( e ), sys.stderr + return 'Error creating history archive: %s' % str(e), sys.stderr def main(): # Parse command line. parser = optparse.OptionParser() - parser.add_option( '-G', '--gzip', dest='gzip', action="store_true", help='Compress archive using gzip.' ) + parser.add_option('-G', '--gzip', dest='gzip', action="store_true", help='Compress archive using gzip.') (options, args) = parser.parse_args() - gzip = bool( options.gzip ) + gzip = bool(options.gzip) history_attrs, dataset_attrs, job_attrs, out_file = args # Create archive. - status = create_archive( history_attrs, dataset_attrs, job_attrs, out_file, gzip ) + status = create_archive(history_attrs, dataset_attrs, job_attrs, out_file, gzip) print(status) diff --git a/lib/galaxy/tools/imp_exp/unpack_tar_gz_archive.py b/lib/galaxy/tools/imp_exp/unpack_tar_gz_archive.py index ee15bcf8cfda..c00a636d5a78 100644 --- a/lib/galaxy/tools/imp_exp/unpack_tar_gz_archive.py +++ b/lib/galaxy/tools/imp_exp/unpack_tar_gz_archive.py @@ -19,23 +19,23 @@ # Set max size of archive/file that will be handled to be 100 GB. This is # arbitrary and should be adjusted as needed. -MAX_SIZE = 100 * math.pow( 2, 30 ) +MAX_SIZE = 100 * math.pow(2, 30) -def url_to_file( url, dest_file ): +def url_to_file(url, dest_file): """ Transfer a file from a remote URL to a temporary file. """ try: - url_reader = urlopen( url ) + url_reader = urlopen(url) CHUNK = 10 * 1024 # 10k total = 0 - fp = open( dest_file, 'wb') + fp = open(dest_file, 'wb') while True: - chunk = url_reader.read( CHUNK ) + chunk = url_reader.read(CHUNK) if not chunk: break - fp.write( chunk ) + fp.write(chunk) total += CHUNK if total > MAX_SIZE: break @@ -46,57 +46,57 @@ def url_to_file( url, dest_file ): return None -def check_archive( archive_file, dest_dir ): +def check_archive(archive_file, dest_dir): """ Ensure that a tar archive has no absolute paths or relative paths outside the archive. """ - with tarfile.open( archive_file, mode='r:gz' ) as archive_fp: + with tarfile.open(archive_file, mode='r:gz') as archive_fp: for arc_path in archive_fp.getnames(): assert os.path.normpath( os.path.join( dest_dir, arc_path - ) ).startswith( dest_dir.rstrip(os.sep) + os.sep ), \ + )).startswith(dest_dir.rstrip(os.sep) + os.sep), \ "Archive member would extract outside target directory: %s" % arc_path return True -def unpack_archive( archive_file, dest_dir ): +def unpack_archive(archive_file, dest_dir): """ Unpack a tar and/or gzipped archive into a destination directory. """ - archive_fp = tarfile.open( archive_file, mode='r:gz' ) - archive_fp.extractall( path=dest_dir ) + archive_fp = tarfile.open(archive_file, mode='r:gz') + archive_fp.extractall(path=dest_dir) archive_fp.close() def main(options, args): - is_url = bool( options.is_url ) - is_file = bool( options.is_file ) + is_url = bool(options.is_url) + is_file = bool(options.is_file) archive_source, dest_dir = args if options.is_b64encoded: - archive_source = b64decode( archive_source ) - dest_dir = b64decode( dest_dir ) + archive_source = b64decode(archive_source) + dest_dir = b64decode(dest_dir) # Get archive from URL. if is_url: - archive_file = url_to_file( archive_source, tempfile.NamedTemporaryFile( dir=dest_dir ).name ) + archive_file = url_to_file(archive_source, tempfile.NamedTemporaryFile(dir=dest_dir).name) elif is_file: archive_file = archive_source # Unpack archive. - check_archive( archive_file, dest_dir ) - unpack_archive( archive_file, dest_dir ) + check_archive(archive_file, dest_dir) + unpack_archive(archive_file, dest_dir) if __name__ == "__main__": # Parse command line. parser = optparse.OptionParser() - parser.add_option( '-U', '--url', dest='is_url', action="store_true", help='Source is a URL.' ) - parser.add_option( '-F', '--file', dest='is_file', action="store_true", help='Source is a URL.' ) - parser.add_option( '-e', '--encoded', dest='is_b64encoded', action="store_true", default=False, help='Source and destination dir values are base64 encoded.' ) + parser.add_option('-U', '--url', dest='is_url', action="store_true", help='Source is a URL.') + parser.add_option('-F', '--file', dest='is_file', action="store_true", help='Source is a URL.') + parser.add_option('-e', '--encoded', dest='is_b64encoded', action="store_true", default=False, help='Source and destination dir values are base64 encoded.') (options, args) = parser.parse_args() try: main(options, args) diff --git a/lib/galaxy/tools/parameters/__init__.py b/lib/galaxy/tools/parameters/__init__.py index e48c37eb6e64..5c5fe29fa32c 100644 --- a/lib/galaxy/tools/parameters/__init__.py +++ b/lib/galaxy/tools/parameters/__init__.py @@ -14,10 +14,10 @@ REPLACE_ON_TRUTHY = object() # Some tools use the code tag and access the code base, expecting certain tool parameters to be available here. -__all__ = ( 'DataCollectionToolParameter', 'DataToolParameter', 'SelectToolParameter' ) +__all__ = ('DataCollectionToolParameter', 'DataToolParameter', 'SelectToolParameter') -def visit_input_values( inputs, input_values, callback, name_prefix='', label_prefix='', parent_prefix='', context=None, no_replacement_value=REPLACE_ON_TRUTHY ): +def visit_input_values(inputs, input_values, callback, name_prefix='', label_prefix='', parent_prefix='', context=None, no_replacement_value=REPLACE_ON_TRUTHY): """ Given a tools parameter definition (`inputs`) and a specific set of parameter `values`, call `callback` for each non-grouping parameter, @@ -61,58 +61,58 @@ def visit_input_values( inputs, input_values, callback, name_prefix='', label_pr >>> params_from_strings( inputs, params_to_strings( inputs, nested, None ), None )[ 'b' ][ 0 ][ 'd' ][ 0 ][ 'f' ][ 'g' ] is True True """ - def callback_helper( input, input_values, name_prefix, label_prefix, parent_prefix, context=None, error=None ): + def callback_helper(input, input_values, name_prefix, label_prefix, parent_prefix, context=None, error=None): args = { 'input' : input, 'parent' : input_values, - 'value' : input_values.get( input.name ), - 'prefixed_name' : '%s%s' % ( name_prefix, input.name ), - 'prefixed_label' : '%s%s' % ( label_prefix, input.label or input.name ), + 'value' : input_values.get(input.name), + 'prefixed_name' : '%s%s' % (name_prefix, input.name), + 'prefixed_label' : '%s%s' % (label_prefix, input.label or input.name), 'prefix' : parent_prefix, 'context' : context, 'error' : error } if input.name not in input_values: - args[ 'error' ] = 'No value found for \'%s\'.' % args.get( 'prefixed_label' ) - new_value = callback( **args ) + args['error'] = 'No value found for \'%s\'.' % args.get('prefixed_label') + new_value = callback(**args) if no_replacement_value is REPLACE_ON_TRUTHY: - replace = bool( new_value ) + replace = bool(new_value) else: replace = new_value != no_replacement_value if replace: - input_values[ input.name ] = new_value + input_values[input.name] = new_value - context = ExpressionContext( input_values, context ) - payload = { 'context': context, 'no_replacement_value': no_replacement_value } + context = ExpressionContext(input_values, context) + payload = {'context': context, 'no_replacement_value': no_replacement_value} for input in inputs.values(): - if isinstance( input, Repeat ) or isinstance( input, UploadDataset ): - values = input_values[ input.name ] = input_values.get( input.name, [] ) - for i, d in enumerate( values ): - d[ '__index__' ] = i - new_name_prefix = name_prefix + '%s_%d|' % ( input.name, i ) - new_label_prefix = label_prefix + '%s %d > ' % ( input.title, i + 1 ) - visit_input_values( input.inputs, d, callback, new_name_prefix, new_label_prefix, parent_prefix=new_name_prefix, **payload ) - elif isinstance( input, Conditional ): - values = input_values[ input.name ] = input_values.get( input.name, {} ) + if isinstance(input, Repeat) or isinstance(input, UploadDataset): + values = input_values[input.name] = input_values.get(input.name, []) + for i, d in enumerate(values): + d['__index__'] = i + new_name_prefix = name_prefix + '%s_%d|' % (input.name, i) + new_label_prefix = label_prefix + '%s %d > ' % (input.title, i + 1) + visit_input_values(input.inputs, d, callback, new_name_prefix, new_label_prefix, parent_prefix=new_name_prefix, **payload) + elif isinstance(input, Conditional): + values = input_values[input.name] = input_values.get(input.name, {}) new_name_prefix = name_prefix + input.name + '|' case_error = None try: - input.get_current_case( values[ input.test_param.name ] ) + input.get_current_case(values[input.test_param.name]) except: case_error = 'The selected case is unavailable/invalid.' pass - callback_helper( input.test_param, values, new_name_prefix, label_prefix, parent_prefix=name_prefix, context=context, error=case_error ) - values[ '__current_case__' ] = input.get_current_case( values[ input.test_param.name ] ) - visit_input_values( input.cases[ values[ '__current_case__' ] ].inputs, values, callback, new_name_prefix, label_prefix, parent_prefix=name_prefix, **payload ) - elif isinstance( input, Section ): - values = input_values[ input.name ] = input_values.get( input.name, {} ) + callback_helper(input.test_param, values, new_name_prefix, label_prefix, parent_prefix=name_prefix, context=context, error=case_error) + values['__current_case__'] = input.get_current_case(values[input.test_param.name]) + visit_input_values(input.cases[values['__current_case__']].inputs, values, callback, new_name_prefix, label_prefix, parent_prefix=name_prefix, **payload) + elif isinstance(input, Section): + values = input_values[input.name] = input_values.get(input.name, {}) new_name_prefix = name_prefix + input.name + '|' - visit_input_values( input.inputs, values, callback, new_name_prefix, label_prefix, parent_prefix=name_prefix, **payload ) + visit_input_values(input.inputs, values, callback, new_name_prefix, label_prefix, parent_prefix=name_prefix, **payload) else: - callback_helper( input, input_values, name_prefix, label_prefix, parent_prefix=parent_prefix, context=context ) + callback_helper(input, input_values, name_prefix, label_prefix, parent_prefix=parent_prefix, context=context) -def check_param( trans, param, incoming_value, param_values ): +def check_param(trans, param, incoming_value, param_values): """ Check the value of a single parameter `param`. The value in `incoming_value` is converted from its HTML encoding and validated. @@ -124,19 +124,19 @@ def check_param( trans, param, incoming_value, param_values ): error = None try: if trans.workflow_building_mode: - if isinstance( value, RuntimeValue ): - return [ { '__class__' : 'RuntimeValue' }, None ] - if isinstance( value, dict ): - if value.get( '__class__' ) == 'RuntimeValue': - return [ value, None ] - value = param.from_json( value, trans, param_values ) - param.validate( value, trans ) + if isinstance(value, RuntimeValue): + return [{'__class__' : 'RuntimeValue'}, None] + if isinstance(value, dict): + if value.get('__class__') == 'RuntimeValue': + return [value, None] + value = param.from_json(value, trans, param_values) + param.validate(value, trans) except ValueError as e: - error = str( e ) + error = str(e) return value, error -def params_to_strings( params, param_values, app, nested=False ): +def params_to_strings(params, param_values, app, nested=False): """ Convert a dictionary of parameter values to a dictionary of strings suitable for persisting. The `value_to_basic` method of each parameter @@ -147,12 +147,12 @@ def params_to_strings( params, param_values, app, nested=False ): rval = dict() for key, value in param_values.items(): if key in params: - value = params[ key ].value_to_basic( value, app ) - rval[ key ] = value if nested else str( dumps( value ) ) + value = params[key].value_to_basic(value, app) + rval[key] = value if nested else str(dumps(value)) return rval -def params_from_strings( params, param_values, app, ignore_errors=False ): +def params_from_strings(params, param_values, app, ignore_errors=False): """ Convert a dictionary of strings as produced by `params_to_strings` back into parameter values (decode the json representation and then @@ -162,14 +162,14 @@ def params_from_strings( params, param_values, app, ignore_errors=False ): rval = dict() param_values = param_values or {} for key, value in param_values.items(): - value = json_fix( safe_loads( value ) ) + value = json_fix(safe_loads(value)) if key in params: - value = params[ key ].value_from_basic( value, app, ignore_errors ) - rval[ key ] = value + value = params[key].value_from_basic(value, app, ignore_errors) + rval[key] = value return rval -def params_to_incoming( incoming, inputs, input_values, app, name_prefix="" ): +def params_to_incoming(incoming, inputs, input_values, app, name_prefix=""): """ Given a tool's parameter definition (`inputs`) and a specific set of parameter `input_values` objects, populate `incoming` with the html values. @@ -177,46 +177,46 @@ def params_to_incoming( incoming, inputs, input_values, app, name_prefix="" ): Useful for e.g. the rerun function. """ for input in inputs.values(): - if isinstance( input, Repeat ) or isinstance( input, UploadDataset ): - for d in input_values[ input.name ]: - index = d[ '__index__' ] - new_name_prefix = name_prefix + '%s_%d|' % ( input.name, index ) - params_to_incoming( incoming, input.inputs, d, app, new_name_prefix ) - elif isinstance( input, Conditional ): - values = input_values[ input.name ] - current = values[ '__current_case__' ] + if isinstance(input, Repeat) or isinstance(input, UploadDataset): + for d in input_values[input.name]: + index = d['__index__'] + new_name_prefix = name_prefix + '%s_%d|' % (input.name, index) + params_to_incoming(incoming, input.inputs, d, app, new_name_prefix) + elif isinstance(input, Conditional): + values = input_values[input.name] + current = values['__current_case__'] new_name_prefix = name_prefix + input.name + '|' - incoming[ new_name_prefix + input.test_param.name ] = values[ input.test_param.name ] - params_to_incoming( incoming, input.cases[current].inputs, values, app, new_name_prefix ) - elif isinstance( input, Section ): - values = input_values[ input.name ] + incoming[new_name_prefix + input.test_param.name] = values[input.test_param.name] + params_to_incoming(incoming, input.cases[current].inputs, values, app, new_name_prefix) + elif isinstance(input, Section): + values = input_values[input.name] new_name_prefix = name_prefix + input.name + '|' - params_to_incoming( incoming, input.inputs, values, app, new_name_prefix ) + params_to_incoming(incoming, input.inputs, values, app, new_name_prefix) else: - value = input_values.get( input.name ) - incoming[ name_prefix + input.name ] = value + value = input_values.get(input.name) + incoming[name_prefix + input.name] = value -def update_param( prefixed_name, input_values, new_value ): +def update_param(prefixed_name, input_values, new_value): """ Given a prefixed parameter name, e.g. 'parameter_0|parameter_1', update the corresponding input value in a nested input values dictionary. """ for key in input_values: - match = re.match( '^' + key + '_(\d+)\|(.+)', prefixed_name ) - if match and not key.endswith( "|__identifier__" ): - index = int( match.group( 1 ) ) - if isinstance( input_values[ key ], list ) and len( input_values[ key ] ) > index: - update_param( match.group( 2 ), input_values[ key ][ index ], new_value ) + match = re.match('^' + key + '_(\d+)\|(.+)', prefixed_name) + if match and not key.endswith("|__identifier__"): + index = int(match.group(1)) + if isinstance(input_values[key], list) and len(input_values[key]) > index: + update_param(match.group(2), input_values[key][index], new_value) else: - match = re.match( '^' + key + '\|(.+)', prefixed_name ) - if isinstance( input_values[ key ], dict ) and match: - update_param( match.group( 1 ), input_values[ key ], new_value ) + match = re.match('^' + key + '\|(.+)', prefixed_name) + if isinstance(input_values[key], dict) and match: + update_param(match.group(1), input_values[key], new_value) elif prefixed_name == key: - input_values[ key ] = new_value + input_values[key] = new_value -def populate_state( request_context, inputs, incoming, state, errors={}, prefix='', context=None, check=True ): +def populate_state(request_context, inputs, incoming, state, errors={}, prefix='', context=None, check=True): """ Populates nested state dict from incoming parameter values. >>> from xml.etree.ElementTree import XML @@ -258,77 +258,77 @@ def populate_state( request_context, inputs, incoming, state, errors={}, prefix= >>> print state[ 'b' ][ 0 ][ 'd' ][ 0 ][ 'f' ][ 'h' ] 4 """ - context = ExpressionContext( state, context ) + context = ExpressionContext(state, context) for input in inputs.values(): - state[ input.name ] = input.get_initial_value( request_context, context ) + state[input.name] = input.get_initial_value(request_context, context) key = prefix + input.name - group_state = state[ input.name ] - group_prefix = '%s|' % ( key ) + group_state = state[input.name] + group_prefix = '%s|' % (key) if input.type == 'repeat': rep_index = 0 del group_state[:] while True: - rep_prefix = '%s_%d' % ( key, rep_index ) - if not any( incoming_key.startswith( rep_prefix ) for incoming_key in incoming.keys() ) and rep_index >= input.min: + rep_prefix = '%s_%d' % (key, rep_index) + if not any(incoming_key.startswith(rep_prefix) for incoming_key in incoming.keys()) and rep_index >= input.min: break if rep_index < input.max: - new_state = { '__index__' : rep_index } - group_state.append( new_state ) - populate_state( request_context, input.inputs, incoming, new_state, errors, prefix=rep_prefix + '|', context=context, check=check ) + new_state = {'__index__' : rep_index} + group_state.append(new_state) + populate_state(request_context, input.inputs, incoming, new_state, errors, prefix=rep_prefix + '|', context=context, check=check) rep_index += 1 elif input.type == 'conditional': if input.value_ref and not input.value_ref_in_group: test_param_key = prefix + input.test_param.name else: test_param_key = group_prefix + input.test_param.name - test_param_value = incoming.get( test_param_key, group_state.get( input.test_param.name ) ) - value, error = check_param( request_context, input.test_param, test_param_value, context ) if check else [ test_param_value, None ] + test_param_value = incoming.get(test_param_key, group_state.get(input.test_param.name)) + value, error = check_param(request_context, input.test_param, test_param_value, context) if check else [test_param_value, None] if error: - errors[ test_param_key ] = error + errors[test_param_key] = error else: try: - current_case = input.get_current_case( value ) - group_state = state[ input.name ] = {} - populate_state( request_context, input.cases[ current_case ].inputs, incoming, group_state, errors, prefix=group_prefix, context=context, check=check ) - group_state[ '__current_case__' ] = current_case + current_case = input.get_current_case(value) + group_state = state[input.name] = {} + populate_state(request_context, input.cases[current_case].inputs, incoming, group_state, errors, prefix=group_prefix, context=context, check=check) + group_state['__current_case__'] = current_case except Exception: - errors[ test_param_key ] = 'The selected case is unavailable/invalid.' + errors[test_param_key] = 'The selected case is unavailable/invalid.' pass - group_state[ input.test_param.name ] = value + group_state[input.test_param.name] = value elif input.type == 'section': - populate_state( request_context, input.inputs, incoming, group_state, errors, prefix=group_prefix, context=context, check=check ) + populate_state(request_context, input.inputs, incoming, group_state, errors, prefix=group_prefix, context=context, check=check) elif input.type == 'upload_dataset': - d_type = input.get_datatype( request_context, context=context ) + d_type = input.get_datatype(request_context, context=context) writable_files = d_type.writable_files - while len( group_state ) > len( writable_files ): - del group_state[ -1 ] - while len( writable_files ) > len( group_state ): - new_state = { '__index__' : len( group_state ) } + while len(group_state) > len(writable_files): + del group_state[-1] + while len(writable_files) > len(group_state): + new_state = {'__index__' : len(group_state)} for upload_item in input.inputs.values(): - new_state[ upload_item.name ] = upload_item.get_initial_value( request_context, context ) - group_state.append( new_state ) - for i, rep_state in enumerate( group_state ): - rep_index = rep_state[ '__index__' ] - rep_prefix = '%s_%d|' % ( key, rep_index ) - populate_state( request_context, input.inputs, incoming, rep_state, errors, prefix=rep_prefix, context=context, check=check ) + new_state[upload_item.name] = upload_item.get_initial_value(request_context, context) + group_state.append(new_state) + for i, rep_state in enumerate(group_state): + rep_index = rep_state['__index__'] + rep_prefix = '%s_%d|' % (key, rep_index) + populate_state(request_context, input.inputs, incoming, rep_state, errors, prefix=rep_prefix, context=context, check=check) else: - param_value = _get_incoming_value( incoming, key, state.get( input.name ) ) - value, error = check_param( request_context, input, param_value, context ) if check else [ param_value, None ] + param_value = _get_incoming_value(incoming, key, state.get(input.name)) + value, error = check_param(request_context, input, param_value, context) if check else [param_value, None] if error: - errors[ key ] = error - state[ input.name ] = value + errors[key] = error + state[input.name] = value -def _get_incoming_value( incoming, key, default ): +def _get_incoming_value(incoming, key, default): """ Fetch value from incoming dict directly or check special nginx upload created variants of this key. """ if '__' + key + '__is_composite' in incoming: - composite_keys = incoming[ '__' + key + '__keys' ].split() + composite_keys = incoming['__' + key + '__keys'].split() value = dict() for composite_key in composite_keys: - value[ composite_key ] = incoming[ key + '_' + composite_key ] + value[composite_key] = incoming[key + '_' + composite_key] return value else: - return incoming.get( key, default ) + return incoming.get(key, default) diff --git a/lib/galaxy/tools/parameters/basic.py b/lib/galaxy/tools/parameters/basic.py index f8c6018f7524..8235b3e8fa87 100644 --- a/lib/galaxy/tools/parameters/basic.py +++ b/lib/galaxy/tools/parameters/basic.py @@ -34,87 +34,87 @@ ) from ..parser import get_input_source as ensure_input_source -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -workflow_building_modes = Bunch( DISABLED=False, ENABLED=True, USE_HISTORY=1 ) +workflow_building_modes = Bunch(DISABLED=False, ENABLED=True, USE_HISTORY=1) -WORKFLOW_PARAMETER_REGULAR_EXPRESSION = re.compile( '''\$\{.+?\}''' ) +WORKFLOW_PARAMETER_REGULAR_EXPRESSION = re.compile('''\$\{.+?\}''') -def contains_workflow_parameter( value, search=False ): - if not isinstance( value, string_types ): +def contains_workflow_parameter(value, search=False): + if not isinstance(value, string_types): return False - if search and WORKFLOW_PARAMETER_REGULAR_EXPRESSION.search( value ): + if search and WORKFLOW_PARAMETER_REGULAR_EXPRESSION.search(value): return True - if not search and WORKFLOW_PARAMETER_REGULAR_EXPRESSION.match( value ): + if not search and WORKFLOW_PARAMETER_REGULAR_EXPRESSION.match(value): return True return False -def is_runtime_value( value ): - return isinstance( value, RuntimeValue ) or ( isinstance( value, dict ) and value.get( '__class__' ) == 'RuntimeValue' ) +def is_runtime_value(value): + return isinstance(value, RuntimeValue) or (isinstance(value, dict) and value.get('__class__') == 'RuntimeValue') -def parse_dynamic_options( param, input_source ): +def parse_dynamic_options(param, input_source): options_elem = input_source.parse_dynamic_options_elem() if options_elem is not None: - return dynamic_options.DynamicOptions( options_elem, param ) + return dynamic_options.DynamicOptions(options_elem, param) return None -class ToolParameter( object, Dictifiable ): +class ToolParameter(object, Dictifiable): """ Describes a parameter accepted by a tool. This is just a simple stub at the moment but in the future should encapsulate more complex parameters (lists of valid choices, validation logic, ...) """ - dict_collection_visible_keys = ( 'name', 'argument', 'type', 'label', 'help', 'refresh_on_change' ) + dict_collection_visible_keys = ('name', 'argument', 'type', 'label', 'help', 'refresh_on_change') - def __init__( self, tool, input_source, context=None ): + def __init__(self, tool, input_source, context=None): input_source = ensure_input_source(input_source) self.tool = tool self.refresh_on_change_values = [] - self.argument = input_source.get( "argument" ) - self.name = self.__class__.parse_name( input_source ) - self.type = input_source.get( "type" ) - self.hidden = input_source.get( "hidden", False ) - self.refresh_on_change = input_source.get_bool( "refresh_on_change", False ) + self.argument = input_source.get("argument") + self.name = self.__class__.parse_name(input_source) + self.type = input_source.get("type") + self.hidden = input_source.get("hidden", False) + self.refresh_on_change = input_source.get_bool("refresh_on_change", False) self.optional = input_source.parse_optional() self.is_dynamic = False self.label = input_source.parse_label() self.help = input_source.parse_help() sanitizer_elem = input_source.parse_sanitizer_elem() if sanitizer_elem is not None: - self.sanitizer = ToolParameterSanitizer.from_element( sanitizer_elem ) + self.sanitizer = ToolParameterSanitizer.from_element(sanitizer_elem) else: self.sanitizer = None self.validators = [] for elem in input_source.parse_validator_elems(): - self.validators.append( validation.Validator.from_element( self, elem ) ) + self.validators.append(validation.Validator.from_element(self, elem)) @property - def visible( self ): + def visible(self): """Return true if the parameter should be rendered on the form""" return True - def get_label( self ): + def get_label(self): """Return user friendly name for the parameter""" return self.label if self.label else self.name - def from_json( self, value, trans=None, other_values={} ): + def from_json(self, value, trans=None, other_values={}): """ Convert a value from an HTML POST into the parameters preferred value format. """ return value - def get_initial_value( self, trans, other_values ): + def get_initial_value(self, trans, other_values): """ Return the starting value of the parameter """ return None - def get_required_enctype( self ): + def get_required_enctype(self): """ If this parameter needs the form to have a specific encoding return it, otherwise return None (indicating compatibility with @@ -122,46 +122,46 @@ def get_required_enctype( self ): """ return None - def get_dependencies( self ): + def get_dependencies(self): """ Return the names of any other parameters this parameter depends on """ return [] - def to_json( self, value, app, use_security ): + def to_json(self, value, app, use_security): """Convert a value to a string representation suitable for persisting""" - return unicodify( value ) + return unicodify(value) - def to_python( self, value, app ): + def to_python(self, value, app): """Convert a value created with to_json back to an object representation""" return value - def value_to_basic( self, value, app, use_security=False ): - if is_runtime_value( value ): - return { '__class__': 'RuntimeValue' } - return self.to_json( value, app, use_security ) + def value_to_basic(self, value, app, use_security=False): + if is_runtime_value(value): + return {'__class__': 'RuntimeValue'} + return self.to_json(value, app, use_security) - def value_from_basic( self, value, app, ignore_errors=False ): + def value_from_basic(self, value, app, ignore_errors=False): # Handle Runtime and Unvalidated values - if is_runtime_value( value ): + if is_runtime_value(value): return RuntimeValue() - elif isinstance( value, dict ) and value.get( '__class__' ) == 'UnvalidatedValue': - return value[ 'value' ] + elif isinstance(value, dict) and value.get('__class__') == 'UnvalidatedValue': + return value['value'] # Delegate to the 'to_python' method if ignore_errors: try: - return self.to_python( value, app ) + return self.to_python(value, app) except: return value else: - return self.to_python( value, app ) + return self.to_python(value, app) - def value_to_display_text( self, value ): - if is_runtime_value( value ): + def value_to_display_text(self, value): + if is_runtime_value(value): return "Not available." - return self.to_text( value ) + return self.to_text(value) - def to_text( self, value ): + def to_text(self, value): """ Convert a value to a text representation suitable for displaying to the user @@ -180,66 +180,66 @@ def to_text( self, value ): 0 """ if value is not None: - str_value = unicodify( value ) + str_value = unicodify(value) if not str_value: return "Empty." return str_value return "Not available." - def to_param_dict_string( self, value, other_values={} ): + def to_param_dict_string(self, value, other_values={}): """Called via __str__ when used in the Cheetah template""" if value is None: value = "" - elif not isinstance( value, string_types ): - value = str( value ) + elif not isinstance(value, string_types): + value = str(value) if self.tool is None or self.tool.options.sanitize: if self.sanitizer: - value = self.sanitizer.sanitize_param( value ) + value = self.sanitizer.sanitize_param(value) else: - value = sanitize_param( value ) + value = sanitize_param(value) return value - def validate( self, value, trans=None ): - if value in [ "", None ] and self.optional: + def validate(self, value, trans=None): + if value in ["", None] and self.optional: return for validator in self.validators: - validator.validate( value, trans ) + validator.validate(value, trans) - def to_dict( self, trans, other_values={} ): + def to_dict(self, trans, other_values={}): """ to_dict tool parameter. This can be overridden by subclasses. """ - tool_dict = super( ToolParameter, self ).to_dict() - tool_dict[ 'model_class' ] = self.__class__.__name__ - tool_dict[ 'optional' ] = self.optional - tool_dict[ 'hidden' ] = self.hidden - tool_dict[ 'is_dynamic' ] = self.is_dynamic - tool_dict[ 'value' ] = self.value_to_basic( self.get_initial_value( trans, other_values ), trans.app, use_security=True ) + tool_dict = super(ToolParameter, self).to_dict() + tool_dict['model_class'] = self.__class__.__name__ + tool_dict['optional'] = self.optional + tool_dict['hidden'] = self.hidden + tool_dict['is_dynamic'] = self.is_dynamic + tool_dict['value'] = self.value_to_basic(self.get_initial_value(trans, other_values), trans.app, use_security=True) return tool_dict @classmethod - def build( cls, tool, param ): + def build(cls, tool, param): """Factory method to create parameter of correct type""" - param_name = cls.parse_name( param ) - param_type = param.get( 'type' ) + param_name = cls.parse_name(param) + param_type = param.get('type') if not param_type: - raise ValueError( "Tool parameter '%s' requires a 'type'" % ( param_name ) ) + raise ValueError("Tool parameter '%s' requires a 'type'" % (param_name)) elif param_type not in parameter_types: - raise ValueError( "Tool parameter '%s' uses an unknown type '%s'" % ( param_name, param_type ) ) + raise ValueError("Tool parameter '%s' uses an unknown type '%s'" % (param_name, param_type)) else: - return parameter_types[ param_type ]( tool, param ) + return parameter_types[param_type](tool, param) @staticmethod - def parse_name( input_source ): - name = input_source.get( 'name' ) + def parse_name(input_source): + name = input_source.get('name') if name is None: - argument = input_source.get( 'argument' ) + argument = input_source.get('argument') if argument: - name = argument.lstrip( '-' ) + name = argument.lstrip('-') else: - raise ValueError( "Tool parameter must specify a name." ) + raise ValueError("Tool parameter must specify a name.") return name -class TextToolParameter( ToolParameter ): +class TextToolParameter(ToolParameter): """ Parameter that can take on any text value. @@ -251,39 +251,40 @@ class TextToolParameter( ToolParameter ): >>> sorted( p.to_dict( trans ).items() ) [('area', False), ('argument', None), ('datalist', []), ('help', ''), ('hidden', False), ('is_dynamic', False), ('label', ''), ('model_class', 'TextToolParameter'), ('name', '_name'), ('optional', False), ('refresh_on_change', False), ('type', 'text'), ('value', 'default')] """ - def __init__( self, tool, input_source ): + + def __init__(self, tool, input_source): input_source = ensure_input_source(input_source) - ToolParameter.__init__( self, tool, input_source ) + ToolParameter.__init__(self, tool, input_source) self.datalist = [] - for ( title, value, selected ) in input_source.parse_static_options(): - self.datalist.append( { 'label' : title, 'value': value } ) - self.value = input_source.get( 'value' ) - self.area = input_source.get_bool( 'area', False ) + for (title, value, selected) in input_source.parse_static_options(): + self.datalist.append({'label' : title, 'value': value}) + self.value = input_source.get('value') + self.area = input_source.get_bool('area', False) - def to_json( self, value, app, use_security ): + def to_json(self, value, app, use_security): """Convert a value to a string representation suitable for persisting""" if value is None: rval = '' else: - rval = util.smart_str( value ) + rval = util.smart_str(value) return rval - def validate( self, value, trans=None ): + def validate(self, value, trans=None): search = self.type == "text" - if not ( trans and trans.workflow_building_mode is workflow_building_modes.ENABLED and contains_workflow_parameter(value, search=search) ): - return super( TextToolParameter, self ).validate( value, trans ) + if not (trans and trans.workflow_building_mode is workflow_building_modes.ENABLED and contains_workflow_parameter(value, search=search)): + return super(TextToolParameter, self).validate(value, trans) - def get_initial_value( self, trans, other_values ): + def get_initial_value(self, trans, other_values): return self.value - def to_dict( self, trans, other_values={} ): + def to_dict(self, trans, other_values={}): d = super(TextToolParameter, self).to_dict(trans) d['area'] = self.area d['datalist'] = self.datalist return d -class IntegerToolParameter( TextToolParameter ): +class IntegerToolParameter(TextToolParameter): """ Parameter that takes an integer value. @@ -302,49 +303,49 @@ class IntegerToolParameter( TextToolParameter ): ValueError: An integer or workflow parameter e.g. ${name} is required """ - dict_collection_visible_keys = ToolParameter.dict_collection_visible_keys + ( 'min', 'max' ) + dict_collection_visible_keys = ToolParameter.dict_collection_visible_keys + ('min', 'max') - def __init__( self, tool, input_source ): + def __init__(self, tool, input_source): input_source = ensure_input_source(input_source) - TextToolParameter.__init__( self, tool, input_source ) + TextToolParameter.__init__(self, tool, input_source) if self.value: try: - int( self.value ) + int(self.value) except: - raise ValueError( "An integer is required" ) + raise ValueError("An integer is required") elif self.value is None and not self.optional: - raise ValueError( "The settings for the field named '%s' require a 'value' setting and optionally a default value which must be an integer" % self.name ) - self.min = input_source.get( 'min' ) - self.max = input_source.get( 'max' ) + raise ValueError("The settings for the field named '%s' require a 'value' setting and optionally a default value which must be an integer" % self.name) + self.min = input_source.get('min') + self.max = input_source.get('max') if self.min: try: - self.min = int( self.min ) + self.min = int(self.min) except: - raise ValueError( "An integer is required" ) + raise ValueError("An integer is required") if self.max: try: - self.max = int( self.max ) + self.max = int(self.max) except: - raise ValueError( "An integer is required" ) + raise ValueError("An integer is required") if self.min is not None or self.max is not None: - self.validators.append( validation.InRangeValidator( None, self.min, self.max ) ) + self.validators.append(validation.InRangeValidator(None, self.min, self.max)) - def from_json( self, value, trans, other_values={} ): + def from_json(self, value, trans, other_values={}): try: - return int( value ) + return int(value) except: - if contains_workflow_parameter( value ) and trans.workflow_building_mode is workflow_building_modes.ENABLED: + if contains_workflow_parameter(value) and trans.workflow_building_mode is workflow_building_modes.ENABLED: return value if not value and self.optional: return "" if trans.workflow_building_mode is workflow_building_modes.ENABLED: - raise ValueError( "An integer or workflow parameter e.g. ${name} is required" ) + raise ValueError("An integer or workflow parameter e.g. ${name} is required") else: - raise ValueError( "An integer is required" ) + raise ValueError("An integer is required") - def to_python( self, value, app ): + def to_python(self, value, app): try: - return int( value ) + return int(value) except Exception as err: if contains_workflow_parameter(value): return value @@ -352,14 +353,14 @@ def to_python( self, value, app ): return None raise err - def get_initial_value( self, trans, other_values ): + def get_initial_value(self, trans, other_values): if self.value: - return int( self.value ) + return int(self.value) else: return None -class FloatToolParameter( TextToolParameter ): +class FloatToolParameter(TextToolParameter): """ Parameter that takes a real number value. @@ -378,64 +379,64 @@ class FloatToolParameter( TextToolParameter ): ValueError: A real number or workflow parameter e.g. ${name} is required """ - dict_collection_visible_keys = ToolParameter.dict_collection_visible_keys + ( 'min', 'max' ) + dict_collection_visible_keys = ToolParameter.dict_collection_visible_keys + ('min', 'max') - def __init__( self, tool, input_source ): + def __init__(self, tool, input_source): input_source = ensure_input_source(input_source) - TextToolParameter.__init__( self, tool, input_source ) - self.min = input_source.get( 'min' ) - self.max = input_source.get( 'max' ) + TextToolParameter.__init__(self, tool, input_source) + self.min = input_source.get('min') + self.max = input_source.get('max') if self.value: try: - float( self.value ) + float(self.value) except: - raise ValueError( "A real number is required" ) + raise ValueError("A real number is required") elif self.value is None and not self.optional: - raise ValueError( "The settings for this field require a 'value' setting and optionally a default value which must be a real number" ) + raise ValueError("The settings for this field require a 'value' setting and optionally a default value which must be a real number") if self.min: try: - self.min = float( self.min ) + self.min = float(self.min) except: - raise ValueError( "A real number is required" ) + raise ValueError("A real number is required") if self.max: try: - self.max = float( self.max ) + self.max = float(self.max) except: - raise ValueError( "A real number is required" ) + raise ValueError("A real number is required") if self.min is not None or self.max is not None: - self.validators.append( validation.InRangeValidator( None, self.min, self.max ) ) + self.validators.append(validation.InRangeValidator(None, self.min, self.max)) - def from_json( self, value, trans, other_values={} ): + def from_json(self, value, trans, other_values={}): try: - return float( value ) + return float(value) except: - if contains_workflow_parameter( value ) and trans.workflow_building_mode is workflow_building_modes.ENABLED: + if contains_workflow_parameter(value) and trans.workflow_building_mode is workflow_building_modes.ENABLED: return value if not value and self.optional: return "" if trans and trans.workflow_building_mode is workflow_building_modes.ENABLED: - raise ValueError( "A real number or workflow parameter e.g. ${name} is required" ) + raise ValueError("A real number or workflow parameter e.g. ${name} is required") else: - raise ValueError( "A real number is required" ) + raise ValueError("A real number is required") - def to_python( self, value, app ): + def to_python(self, value, app): try: - return float( value ) + return float(value) except Exception as err: - if contains_workflow_parameter( value ): + if contains_workflow_parameter(value): return value if not value and self.optional: return None raise err - def get_initial_value( self, trans, other_values ): + def get_initial_value(self, trans, other_values): try: - return float( self.value ) + return float(self.value) except: return None -class BooleanToolParameter( ToolParameter ): +class BooleanToolParameter(ToolParameter): """ Parameter that takes one of two values. @@ -455,46 +456,47 @@ class BooleanToolParameter( ToolParameter ): >>> print p.to_param_dict_string( False ) _falsevalue """ - def __init__( self, tool, input_source ): + + def __init__(self, tool, input_source): input_source = ensure_input_source(input_source) - ToolParameter.__init__( self, tool, input_source ) - self.truevalue = input_source.get( 'truevalue', 'true' ) - self.falsevalue = input_source.get( 'falsevalue', 'false' ) - self.checked = input_source.get_bool( 'checked', False ) + ToolParameter.__init__(self, tool, input_source) + self.truevalue = input_source.get('truevalue', 'true') + self.falsevalue = input_source.get('falsevalue', 'false') + self.checked = input_source.get_bool('checked', False) - def from_json( self, value, trans=None, other_values={} ): - return self.to_python( value ) + def from_json(self, value, trans=None, other_values={}): + return self.to_python(value) - def to_python( self, value, app=None ): - return ( value in [ True, 'True', 'true' ] ) + def to_python(self, value, app=None): + return (value in [True, 'True', 'true']) - def to_json( self, value, app, use_security ): - if self.to_python( value, app ): + def to_json(self, value, app, use_security): + if self.to_python(value, app): return 'true' else: return 'false' - def get_initial_value( self, trans, other_values ): + def get_initial_value(self, trans, other_values): return self.checked - def to_param_dict_string( self, value, other_values={} ): - if self.to_python( value ): + def to_param_dict_string(self, value, other_values={}): + if self.to_python(value): return self.truevalue else: return self.falsevalue - def to_dict( self, trans, other_values={} ): - d = super( BooleanToolParameter, self ).to_dict( trans ) + def to_dict(self, trans, other_values={}): + d = super(BooleanToolParameter, self).to_dict(trans) d['truevalue'] = self.truevalue d['falsevalue'] = self.falsevalue return d @property - def legal_values( self ): - return [ self.truevalue, self.falsevalue ] + def legal_values(self): + return [self.truevalue, self.falsevalue] -class FileToolParameter( ToolParameter ): +class FileToolParameter(ToolParameter): """ Parameter that takes an uploaded file as a value. @@ -506,51 +508,52 @@ class FileToolParameter( ToolParameter ): >>> sorted( p.to_dict( trans ).items() ) [('argument', None), ('help', ''), ('hidden', False), ('is_dynamic', False), ('label', ''), ('model_class', 'FileToolParameter'), ('name', '_name'), ('optional', False), ('refresh_on_change', False), ('type', 'file'), ('value', None)] """ - def __init__( self, tool, input_source ): + + def __init__(self, tool, input_source): input_source = ensure_input_source(input_source) - ToolParameter.__init__( self, tool, input_source ) + ToolParameter.__init__(self, tool, input_source) - def from_json( self, value, trans=None, other_values={} ): + def from_json(self, value, trans=None, other_values={}): # Middleware or proxies may encode files in special ways (TODO: this # should be pluggable) - if type( value ) == dict: + if type(value) == dict: upload_store = trans.app.config.nginx_upload_store assert upload_store, "Request appears to have been processed by nginx_upload_module but Galaxy is not configured to recognize it." # Check that the file is in the right location - local_filename = os.path.abspath( value[ 'path' ] ) - assert local_filename.startswith( upload_store ), "Filename provided by nginx (%s) is not in correct directory (%s)." % (local_filename, upload_store) - value = dict( filename=value[ "name" ], local_filename=local_filename ) + local_filename = os.path.abspath(value['path']) + assert local_filename.startswith(upload_store), "Filename provided by nginx (%s) is not in correct directory (%s)." % (local_filename, upload_store) + value = dict(filename=value["name"], local_filename=local_filename) return value - def get_required_enctype( self ): + def get_required_enctype(self): """ File upload elements require the multipart/form-data encoding """ return "multipart/form-data" - def to_json( self, value, app, use_security ): - if value in [ None, '' ]: + def to_json(self, value, app, use_security): + if value in [None, '']: return None - elif isinstance( value, string_types ): + elif isinstance(value, string_types): return value - elif isinstance( value, dict ): + elif isinstance(value, dict): # or should we jsonify? try: return value['local_filename'] except: return None - raise Exception( "FileToolParameter cannot be persisted" ) + raise Exception("FileToolParameter cannot be persisted") - def to_python( self, value, app ): + def to_python(self, value, app): if value is None: return None - elif isinstance( value, string_types ): + elif isinstance(value, string_types): return value else: - raise Exception( "FileToolParameter cannot be persisted" ) + raise Exception("FileToolParameter cannot be persisted") -class FTPFileToolParameter( ToolParameter ): +class FTPFileToolParameter(ToolParameter): """ Parameter that takes a file uploaded via FTP as a value. @@ -562,67 +565,82 @@ class FTPFileToolParameter( ToolParameter ): >>> sorted( p.to_dict( trans ).items() ) [('argument', None), ('help', ''), ('hidden', False), ('is_dynamic', False), ('label', ''), ('model_class', 'FTPFileToolParameter'), ('multiple', True), ('name', '_name'), ('optional', True), ('refresh_on_change', False), ('type', 'ftpfile'), ('value', None)] """ - def __init__( self, tool, input_source ): + + def __init__(self, tool, input_source): input_source = ensure_input_source(input_source) - ToolParameter.__init__( self, tool, input_source ) - self.multiple = input_source.get_bool( 'multiple', True ) - self.optional = input_source.parse_optional( True ) + ToolParameter.__init__(self, tool, input_source) + self.multiple = input_source.get_bool('multiple', True) + self.optional = input_source.parse_optional(True) self.user_ftp_dir = '' - def get_initial_value( self, trans, other_values ): + def get_initial_value(self, trans, other_values): if trans is not None: if trans.user is not None: self.user_ftp_dir = "%s/" % trans.user_ftp_dir return None @property - def visible( self ): + def visible(self): if self.tool.app.config.ftp_upload_dir is None or self.tool.app.config.ftp_upload_site is None: return False return True - def to_param_dict_string( self, value, other_values={} ): + def to_param_dict_string(self, value, other_values={}): if value is '': return 'None' - lst = [ '%s%s' % (self.user_ftp_dir, dataset) for dataset in value ] + lst = ['%s%s' % (self.user_ftp_dir, dataset) for dataset in value] if self.multiple: return lst else: - return lst[ 0 ] + return lst[0] - def from_json( self, value, trans=None, other_values={} ): - return self.to_python( value, trans.app, validate=True ) + def from_json(self, value, trans=None, other_values={}): + return self.to_python(value, trans.app, validate=True) - def to_json( self, value, app, use_security ): - return self.to_python( value, app ) + def to_json(self, value, app, use_security): + return self.to_python(value, app) - def to_python( self, value, app, validate=False ): - if not isinstance( value, list ): - value = [ value ] + def to_python(self, value, app, validate=False): + if not isinstance(value, list): + value = [value] lst = [] for val in value: - if val in [ None, '' ]: + if val in [None, '']: lst = [] break - if isinstance( val, dict ): - lst.append( val[ 'name' ] ) + if isinstance(val, dict): + lst.append(val['name']) else: - lst.append( val ) - if len( lst ) == 0: + lst.append(val) + if len(lst) == 0: if not self.optional and validate: - raise ValueError( "Please select a valid FTP file." ) + raise ValueError("Please select a valid FTP file.") return None if validate and self.tool.app.config.ftp_upload_dir is None: - raise ValueError( "The FTP directory is not configured." ) + raise ValueError("The FTP directory is not configured.") return lst - def to_dict( self, trans, other_values=None ): - d = super( FTPFileToolParameter, self ).to_dict( trans ) - d[ 'multiple' ] = self.multiple + def to_dict(self, trans, other_values=None): + d = super(FTPFileToolParameter, self).to_dict(trans) + d['multiple'] = self.multiple return d -class HiddenToolParameter( ToolParameter ): +class GenomespaceFileToolParameter(ToolParameter): + """ + Parameter that takes one of two values. + """ + + def __init__(self, tool, input_source): + input_source = ensure_input_source(input_source) + ToolParameter.__init__(self, tool, input_source) + self.value = input_source.get('value') + + def get_initial_value(self, trans, other_values): + return self.value + + +class HiddenToolParameter(ToolParameter): """ Parameter that takes one of two values. @@ -634,20 +652,21 @@ class HiddenToolParameter( ToolParameter ): >>> sorted( p.to_dict( trans ).items() ) [('argument', None), ('help', ''), ('hidden', True), ('is_dynamic', False), ('label', ''), ('model_class', 'HiddenToolParameter'), ('name', '_name'), ('optional', False), ('refresh_on_change', False), ('type', 'hidden'), ('value', u'_value')] """ - def __init__( self, tool, input_source ): - input_source = ensure_input_source( input_source ) - ToolParameter.__init__( self, tool, input_source ) - self.value = input_source.get( 'value' ) + + def __init__(self, tool, input_source): + input_source = ensure_input_source(input_source) + ToolParameter.__init__(self, tool, input_source) + self.value = input_source.get('value') self.hidden = True - def get_initial_value( self, trans, other_values ): + def get_initial_value(self, trans, other_values): return self.value - def get_label( self ): + def get_label(self): return None -class ColorToolParameter( ToolParameter ): +class ColorToolParameter(ToolParameter): """ Parameter that stores a color. @@ -668,25 +687,26 @@ class ColorToolParameter( ToolParameter ): ... ValueError: Failed to convert 'None' to RGB. """ - def __init__( self, tool, input_source ): - input_source = ensure_input_source( input_source ) - ToolParameter.__init__( self, tool, input_source ) - self.value = input_source.get( 'value', '#fdeada' ) - self.rgb = input_source.get( 'rgb', False ) - def get_initial_value( self, trans, other_values ): + def __init__(self, tool, input_source): + input_source = ensure_input_source(input_source) + ToolParameter.__init__(self, tool, input_source) + self.value = input_source.get('value', '#fdeada') + self.rgb = input_source.get('rgb', False) + + def get_initial_value(self, trans, other_values): return self.value.lower() - def to_param_dict_string( self, value, other_values={} ): + def to_param_dict_string(self, value, other_values={}): if self.rgb: try: - return str( tuple( int( value.lstrip( '#' )[ i : i + 2 ], 16 ) for i in ( 0, 2, 4 ) ) ) + return str(tuple(int(value.lstrip('#')[i : i + 2], 16) for i in (0, 2, 4))) except Exception: - raise ValueError( "Failed to convert \'%s\' to RGB." % value ) - return str( value ) + raise ValueError("Failed to convert \'%s\' to RGB." % value) + return str(value) -class BaseURLToolParameter( HiddenToolParameter ): +class BaseURLToolParameter(HiddenToolParameter): """ Returns a parameter that contains its value prepended by the current server base url. Used in all redirects. @@ -699,30 +719,31 @@ class BaseURLToolParameter( HiddenToolParameter ): >>> sorted( p.to_dict( trans ).items() ) [('argument', None), ('help', ''), ('hidden', True), ('is_dynamic', False), ('label', ''), ('model_class', 'BaseURLToolParameter'), ('name', '_name'), ('optional', False), ('refresh_on_change', False), ('type', 'base_url'), ('value', u'_value')] """ - def __init__( self, tool, input_source ): - input_source = ensure_input_source( input_source ) - super( BaseURLToolParameter, self ).__init__( tool, input_source ) - self.value = input_source.get( 'value', '' ) - def get_initial_value( self, trans, other_values ): + def __init__(self, tool, input_source): + input_source = ensure_input_source(input_source) + super(BaseURLToolParameter, self).__init__(tool, input_source) + self.value = input_source.get('value', '') + + def get_initial_value(self, trans, other_values): return self._get_value() - def from_json( self, value=None, trans=None, other_values={} ): + def from_json(self, value=None, trans=None, other_values={}): return self._get_value() - def _get_value( self ): + def _get_value(self): try: - return url_for( self.value, qualified=True ) + return url_for(self.value, qualified=True) except Exception as e: - log.debug( 'Url creation failed for "%s": %s', self.name, e ) + log.debug('Url creation failed for "%s": %s', self.name, e) return self.value - def to_dict( self, trans, other_values={} ): - d = super( BaseURLToolParameter, self ).to_dict( trans ) + def to_dict(self, trans, other_values={}): + d = super(BaseURLToolParameter, self).to_dict(trans) return d -class SelectToolParameter( ToolParameter ): +class SelectToolParameter(ToolParameter): """ Parameter that takes on one (or many) or a specific set of values. @@ -755,67 +776,68 @@ class SelectToolParameter( ToolParameter ): >>> print p.to_param_dict_string( ["y", "z"] ) y,z """ - def __init__( self, tool, input_source, context=None ): - input_source = ensure_input_source( input_source ) - ToolParameter.__init__( self, tool, input_source ) - self.multiple = input_source.get_bool( 'multiple', False ) + + def __init__(self, tool, input_source, context=None): + input_source = ensure_input_source(input_source) + ToolParameter.__init__(self, tool, input_source) + self.multiple = input_source.get_bool('multiple', False) # Multiple selects are optional by default, single selection is the inverse. - self.optional = input_source.parse_optional( self.multiple ) - self.display = input_source.get( 'display', None ) - self.separator = input_source.get( 'separator', ',' ) + self.optional = input_source.parse_optional(self.multiple) + self.display = input_source.get('display', None) + self.separator = input_source.get('separator', ',') self.legal_values = set() - self.dynamic_options = input_source.get( 'dynamic_options', None ) - self.options = parse_dynamic_options( self, input_source ) + self.dynamic_options = input_source.get('dynamic_options', None) + self.options = parse_dynamic_options(self, input_source) if self.options is not None: for validator in self.options.validators: - self.validators.append( validator ) + self.validators.append(validator) if self.dynamic_options is None and self.options is None: self.static_options = input_source.parse_static_options() for (title, value, selected) in self.static_options: - self.legal_values.add( value ) - self.is_dynamic = ( ( self.dynamic_options is not None ) or ( self.options is not None ) ) + self.legal_values.add(value) + self.is_dynamic = ((self.dynamic_options is not None) or (self.options is not None)) - def _get_dynamic_options_call_other_values( self, trans, other_values ): - call_other_values = ExpressionContext( { '__trans__': trans } ) + def _get_dynamic_options_call_other_values(self, trans, other_values): + call_other_values = ExpressionContext({'__trans__': trans}) if other_values: call_other_values.parent = other_values.parent - call_other_values.update( other_values.dict ) + call_other_values.update(other_values.dict) return call_other_values - def get_options( self, trans, other_values ): + def get_options(self, trans, other_values): if self.options: - return self.options.get_options( trans, other_values ) + return self.options.get_options(trans, other_values) elif self.dynamic_options: - call_other_values = self._get_dynamic_options_call_other_values( trans, other_values ) + call_other_values = self._get_dynamic_options_call_other_values(trans, other_values) try: - return eval( self.dynamic_options, self.tool.code_namespace, call_other_values ) + return eval(self.dynamic_options, self.tool.code_namespace, call_other_values) except Exception as e: - log.debug( "Error determining dynamic options for parameter '%s' in tool '%s':", self.name, self.tool.id, exc_info=e ) + log.debug("Error determining dynamic options for parameter '%s' in tool '%s':", self.name, self.tool.id, exc_info=e) return [] else: return self.static_options - def get_legal_values( self, trans, other_values ): + def get_legal_values(self, trans, other_values): if self.options: - return set( v for _, v, _ in self.options.get_options( trans, other_values ) ) + return set(v for _, v, _ in self.options.get_options(trans, other_values)) elif self.dynamic_options: try: - call_other_values = self._get_dynamic_options_call_other_values( trans, other_values ) - return set( v for _, v, _ in eval( self.dynamic_options, self.tool.code_namespace, call_other_values ) ) + call_other_values = self._get_dynamic_options_call_other_values(trans, other_values) + return set(v for _, v, _ in eval(self.dynamic_options, self.tool.code_namespace, call_other_values)) except Exception as e: - log.debug( "Determining legal values failed for '%s': %s", self.name, e ) + log.debug("Determining legal values failed for '%s': %s", self.name, e) return set() else: return self.legal_values - def from_json( self, value, trans, other_values={} ): - legal_values = self.get_legal_values( trans, other_values ) + def from_json(self, value, trans, other_values={}): + legal_values = self.get_legal_values(trans, other_values) workflow_building_mode = trans.workflow_building_mode for context_value in other_values.values(): - if is_runtime_value( context_value ): + if is_runtime_value(context_value): workflow_building_mode = True break - if len( list( legal_values ) ) == 0 and workflow_building_mode: + if len(list(legal_values)) == 0 and workflow_building_mode: if self.multiple: # While it is generally allowed that a select value can be '', # we do not allow this to be the case in a dynamically @@ -824,92 +846,92 @@ def from_json( self, value, trans, other_values={} ): if value == '': value = None else: - if isinstance( value, string_types ): + if isinstance(value, string_types): # Split on all whitespace. This not only provides flexibility # in interpreting values but also is needed because many browsers # use \r\n to separate lines. value = value.split() return value - if ( not legal_values or value is None ) and self.optional: + if (not legal_values or value is None) and self.optional: return None if not legal_values: - raise ValueError( "Parameter %s requires a value, but has no legal values defined." % self.name ) - if isinstance( value, list ): + raise ValueError("Parameter %s requires a value, but has no legal values defined." % self.name) + if isinstance(value, list): if not self.multiple: - raise ValueError( "Multiple values provided but parameter %s is not expecting multiple values." % self.name ) + raise ValueError("Multiple values provided but parameter %s is not expecting multiple values." % self.name) rval = [] for v in value: if v not in legal_values: - raise ValueError( "An invalid option was selected for %s, %r, please verify." % ( self.name, v ) ) - rval.append( v ) + raise ValueError("An invalid option was selected for %s, %r, please verify." % (self.name, v)) + rval.append(v) return rval else: - value_is_none = ( value == "None" and "None" not in legal_values ) + value_is_none = (value == "None" and "None" not in legal_values) if value_is_none or not value: if self.multiple: if self.optional: return [] else: - raise ValueError( "No option was selected for %s but input is not optional." % self.name ) + raise ValueError("No option was selected for %s but input is not optional." % self.name) if value not in legal_values: - raise ValueError( "An invalid option was selected for %s, %r, please verify." % ( self.name, value ) ) + raise ValueError("An invalid option was selected for %s, %r, please verify." % (self.name, value)) return value - def to_param_dict_string( self, value, other_values={} ): + def to_param_dict_string(self, value, other_values={}): if value is None: return "None" - if isinstance( value, list ): + if isinstance(value, list): if not self.multiple: - raise ValueError( "Multiple values provided but parameter %s is not expecting multiple values." % self.name ) - value = list(map( str, value )) + raise ValueError("Multiple values provided but parameter %s is not expecting multiple values." % self.name) + value = list(map(str, value)) else: - value = str( value ) + value = str(value) if self.tool is None or self.tool.options.sanitize: if self.sanitizer: - value = self.sanitizer.sanitize_param( value ) + value = self.sanitizer.sanitize_param(value) else: - value = sanitize_param( value ) - if isinstance( value, list ): - value = self.separator.join( value ) + value = sanitize_param(value) + if isinstance(value, list): + value = self.separator.join(value) return value - def to_json( self, value, app, use_security ): + def to_json(self, value, app, use_security): return value - def get_initial_value( self, trans, other_values ): - options = list( self.get_options( trans, other_values ) ) + def get_initial_value(self, trans, other_values): + options = list(self.get_options(trans, other_values)) if len(options) == 0 and trans.workflow_building_mode: return None - value = [ optval for _, optval, selected in options if selected ] - if len( value ) == 0: + value = [optval for _, optval, selected in options if selected] + if len(value) == 0: if not self.optional and not self.multiple and options: # Nothing selected, but not optional and not a multiple select, with some values, # so we have to default to something (the HTML form will anyway) - value = options[ 0 ][ 1 ] + value = options[0][1] else: value = None - elif len( value ) == 1: - value = value[ 0 ] + elif len(value) == 1: + value = value[0] return value - def to_text( self, value ): - if not isinstance( value, list ): - value = [ value ] + def to_text(self, value): + if not isinstance(value, list): + value = [value] # FIXME: Currently only translating values back to labels if they # are not dynamic if self.is_dynamic: - rval = map( str, value ) + rval = map(str, value) else: - options = list( self.static_options ) + options = list(self.static_options) rval = [] for t, v, s in options: if v in value: - rval.append( t ) + rval.append(t) if rval: - return "\n".join( rval ) + return "\n".join(rval) return "Nothing selected." - def get_dependencies( self ): + def get_dependencies(self): """ Get the *names* of the other params this param depends on. """ @@ -918,18 +940,18 @@ def get_dependencies( self ): else: return [] - def to_dict( self, trans, other_values={} ): - d = super( SelectToolParameter, self ).to_dict( trans ) + def to_dict(self, trans, other_values={}): + d = super(SelectToolParameter, self).to_dict(trans) # Get options, value. - options = self.get_options( trans, other_values ) - d[ 'options' ] = options - d[ 'display' ] = self.display - d[ 'multiple' ] = self.multiple + options = self.get_options(trans, other_values) + d['options'] = options + d['display'] = self.display + d['multiple'] = self.multiple return d -class GenomeBuildParameter( SelectToolParameter ): +class GenomeBuildParameter(SelectToolParameter): """ Select list that sets the last used genome build for the current history as "selected". @@ -946,27 +968,28 @@ class GenomeBuildParameter( SelectToolParameter ): >>> [ i for i in o if i[ 1 ] == 'hg18' ] [('Human Mar. 2006 (NCBI36/hg18) (hg18)', 'hg18', False)] """ - def __init__( self, *args, **kwds ): - super( GenomeBuildParameter, self ).__init__( *args, **kwds ) + + def __init__(self, *args, **kwds): + super(GenomeBuildParameter, self).__init__(*args, **kwds) if self.tool: - self.static_options = [ ( value, key, False ) for key, value in self._get_dbkey_names()] + self.static_options = [(value, key, False) for key, value in self._get_dbkey_names()] - def get_options( self, trans, other_values ): + def get_options(self, trans, other_values): last_used_build = object() if trans.history: last_used_build = trans.history.genome_build - for dbkey, build_name in self._get_dbkey_names( trans=trans ): - yield build_name, dbkey, ( dbkey == last_used_build ) + for dbkey, build_name in self._get_dbkey_names(trans=trans): + yield build_name, dbkey, (dbkey == last_used_build) - def get_legal_values( self, trans, other_values ): - return set( dbkey for dbkey, _ in self._get_dbkey_names( trans=trans ) ) + def get_legal_values(self, trans, other_values): + return set(dbkey for dbkey, _ in self._get_dbkey_names(trans=trans)) - def to_dict( self, trans, other_values={} ): + def to_dict(self, trans, other_values={}): # skip SelectToolParameter (the immediate parent) bc we need to get options in a different way here - d = ToolParameter.to_dict( self, trans ) + d = ToolParameter.to_dict(self, trans) # Get options, value - options is a generator here, so compile to list - options = list( self.get_options( trans, {} ) ) + options = list(self.get_options(trans, {})) value = options[0][1] for option in options: if option[2]: @@ -982,14 +1005,14 @@ def to_dict( self, trans, other_values={} ): return d - def _get_dbkey_names( self, trans=None ): + def _get_dbkey_names(self, trans=None): if not self.tool: # Hack for unit tests, since we have no tool - return util.read_dbnames( None ) - return self.tool.app.genome_builds.get_genome_build_names( trans=trans ) + return util.read_dbnames(None) + return self.tool.app.genome_builds.get_genome_build_names(trans=trans) -class ColumnListParameter( SelectToolParameter ): +class ColumnListParameter(SelectToolParameter): """ Select list that consists of either the total number of columns or only those columns that contain numerical values in the associated DataToolParameter. @@ -1013,91 +1036,92 @@ class ColumnListParameter( SelectToolParameter ): >>> print clp.name numerical_column """ - def __init__( self, tool, input_source ): - input_source = ensure_input_source( input_source ) - SelectToolParameter.__init__( self, tool, input_source ) + + def __init__(self, tool, input_source): + input_source = ensure_input_source(input_source) + SelectToolParameter.__init__(self, tool, input_source) self.tool = tool - self.numerical = input_source.get_bool( "numerical", False ) - self.optional = input_source.parse_optional( False ) - self.accept_default = input_source.get_bool( "accept_default", False ) + self.numerical = input_source.get_bool("numerical", False) + self.optional = input_source.parse_optional(False) + self.accept_default = input_source.get_bool("accept_default", False) if self.accept_default: self.optional = True - self.data_ref = input_source.get( "data_ref", None ) + self.data_ref = input_source.get("data_ref", None) self.ref_input = None # Legacy style default value specification... - self.default_value = input_source.get( "default_value", None ) + self.default_value = input_source.get("default_value", None) if self.default_value is None: # Newer style... more in line with other parameters. - self.default_value = input_source.get( "value", None ) + self.default_value = input_source.get("value", None) if self.default_value is not None: - self.default_value = ColumnListParameter._strip_c( self.default_value ) + self.default_value = ColumnListParameter._strip_c(self.default_value) self.is_dynamic = True - self.usecolnames = input_source.get_bool( "use_header_names", False ) + self.usecolnames = input_source.get_bool("use_header_names", False) - def from_json( self, value, trans, other_values={} ): + def from_json(self, value, trans, other_values={}): """ Label convention prepends column number with a 'c', but tool uses the integer. This removes the 'c' when entered into a workflow. """ if self.multiple: # split on newline and , - if isinstance( value, list ) or isinstance( value, string_types ): + if isinstance(value, list) or isinstance(value, string_types): column_list = [] - if not isinstance( value, list ): - value = value.split( '\n' ) + if not isinstance(value, list): + value = value.split('\n') for column in value: - for column2 in str( column ).split( ',' ): + for column2 in str(column).split(','): column2 = column2.strip() if column2: - column_list.append( column2 ) - value = list(map( ColumnListParameter._strip_c, column_list )) + column_list.append(column2) + value = list(map(ColumnListParameter._strip_c, column_list)) else: value = [] else: if value: - value = ColumnListParameter._strip_c( value ) + value = ColumnListParameter._strip_c(value) else: value = None if not value and self.accept_default: value = self.default_value or '1' - return [ value ] if self.multiple else value - return super( ColumnListParameter, self ).from_json( value, trans, other_values ) + return [value] if self.multiple else value + return super(ColumnListParameter, self).from_json(value, trans, other_values) @staticmethod def _strip_c(column): if isinstance(column, string_types): - if column.startswith( 'c' ): + if column.startswith('c'): column = column.strip().lower()[1:] return column - def get_column_list( self, trans, other_values ): + def get_column_list(self, trans, other_values): """ Generate a select list containing the columns of the associated dataset (if found). """ # Get the value of the associated data reference (a dataset) - dataset = other_values.get( self.data_ref, None ) + dataset = other_values.get(self.data_ref, None) # Check if a dataset is selected if not dataset: return [] column_list = None - for dataset in util.listify( dataset ): + for dataset in util.listify(dataset): # Use representative dataset if a dataset collection is parsed - if isinstance( dataset, trans.app.model.HistoryDatasetCollectionAssociation ): + if isinstance(dataset, trans.app.model.HistoryDatasetCollectionAssociation): dataset = dataset.to_hda_representative() # Columns can only be identified if metadata is available - if not hasattr( dataset, 'metadata' ) or not hasattr( dataset.metadata, 'columns' ) or not dataset.metadata.columns: + if not hasattr(dataset, 'metadata') or not hasattr(dataset.metadata, 'columns') or not dataset.metadata.columns: return [] # Build up possible columns for this dataset this_column_list = [] if self.numerical: # If numerical was requested, filter columns based on metadata - for i, col in enumerate( dataset.metadata.column_types ): + for i, col in enumerate(dataset.metadata.column_types): if col == 'int' or col == 'float': - this_column_list.append( str( i + 1 ) ) + this_column_list.append(str(i + 1)) else: - for i in range( 0, dataset.metadata.columns ): - this_column_list.append( str( i + 1 ) ) + for i in range(0, dataset.metadata.columns): + this_column_list.append(str(i + 1)) # Take the intersection of these columns with the other columns. if column_list is None: column_list = this_column_list @@ -1105,54 +1129,54 @@ def get_column_list( self, trans, other_values ): column_list = [c for c in column_list if c in this_column_list] return column_list - def get_options( self, trans, other_values ): + def get_options(self, trans, other_values): """ Show column labels rather than c1..cn if use_header_names=True """ options = [] if self.usecolnames: # read first row - assume is a header with metadata useful for making good choices - dataset = other_values.get( self.data_ref, None ) + dataset = other_values.get(self.data_ref, None) try: - head = open( dataset.get_file_name(), 'r' ).readline() - cnames = head.rstrip().split( '\t' ) - column_list = [ ( '%d' % ( i + 1 ), 'c%d: %s' % ( i + 1, x ) ) for i, x in enumerate( cnames ) ] + head = open(dataset.get_file_name(), 'r').readline() + cnames = head.rstrip().split('\t') + column_list = [('%d' % (i + 1), 'c%d: %s' % (i + 1, x)) for i, x in enumerate(cnames)] if self.numerical: # If numerical was requested, filter columns based on metadata - if hasattr( dataset, 'metadata' ) and hasattr( dataset.metadata, 'column_types' ): - if len( dataset.metadata.column_types ) >= len( cnames ): - numerics = [ i for i, x in enumerate( dataset.metadata.column_types ) if x in [ 'int', 'float' ] ] - column_list = [ column_list[ i ] for i in numerics ] + if hasattr(dataset, 'metadata') and hasattr(dataset.metadata, 'column_types'): + if len(dataset.metadata.column_types) >= len(cnames): + numerics = [i for i, x in enumerate(dataset.metadata.column_types) if x in ['int', 'float']] + column_list = [column_list[i] for i in numerics] except: - column_list = self.get_column_list( trans, other_values ) + column_list = self.get_column_list(trans, other_values) else: - column_list = self.get_column_list( trans, other_values ) + column_list = self.get_column_list(trans, other_values) for col in column_list: - if isinstance( col, tuple ) and len( col ) == 2: - options.append( ( col[ 1 ], col[ 0 ], False ) ) + if isinstance(col, tuple) and len(col) == 2: + options.append((col[1], col[0], False)) else: - options.append( ( 'Column: ' + col, col, False ) ) + options.append(('Column: ' + col, col, False)) return options - def get_initial_value( self, trans, other_values ): + def get_initial_value(self, trans, other_values): if self.default_value is not None: return self.default_value - return SelectToolParameter.get_initial_value( self, trans, other_values ) + return SelectToolParameter.get_initial_value(self, trans, other_values) - def get_legal_values( self, trans, other_values ): + def get_legal_values(self, trans, other_values): if self.data_ref not in other_values: - raise ValueError( "Value for associated data reference not found (data_ref)." ) - return set( self.get_column_list( trans, other_values ) ) + raise ValueError("Value for associated data reference not found (data_ref).") + return set(self.get_column_list(trans, other_values)) - def get_dependencies( self ): - return [ self.data_ref ] + def get_dependencies(self): + return [self.data_ref] - def to_dict( self, trans, other_values={} ): - d = super( ColumnListParameter, self ).to_dict( trans, other_values=other_values) - d[ 'data_ref' ] = self.data_ref - d[ 'numerical' ] = self.numerical + def to_dict(self, trans, other_values={}): + d = super(ColumnListParameter, self).to_dict(trans, other_values=other_values) + d['data_ref'] = self.data_ref + d['numerical'] = self.numerical return d -class DrillDownSelectToolParameter( SelectToolParameter ): +class DrillDownSelectToolParameter(SelectToolParameter): """ Parameter that takes on one (or many) of a specific set of values. Creating a hierarchical select menu, which allows users to 'drill down' a tree-like set of options. @@ -1195,131 +1219,132 @@ class DrillDownSelectToolParameter( SelectToolParameter ): >>> assert d[ 'options' ][ 1 ][ 'name' ] == 'Option 5' >>> assert d[ 'options' ][ 1 ][ 'value' ] == 'option5' """ - def __init__( self, tool, input_source, context=None ): - input_source = ensure_input_source( input_source ) - def recurse_option_elems( cur_options, option_elems ): + def __init__(self, tool, input_source, context=None): + input_source = ensure_input_source(input_source) + + def recurse_option_elems(cur_options, option_elems): for option_elem in option_elems: - selected = string_as_bool( option_elem.get( 'selected', False ) ) - cur_options.append( { 'name': option_elem.get( 'name' ), 'value': option_elem.get( 'value' ), 'options': [], 'selected': selected } ) - recurse_option_elems( cur_options[-1]['options'], option_elem.findall( 'option' ) ) - ToolParameter.__init__( self, tool, input_source ) + selected = string_as_bool(option_elem.get('selected', False)) + cur_options.append({'name': option_elem.get('name'), 'value': option_elem.get('value'), 'options': [], 'selected': selected}) + recurse_option_elems(cur_options[-1]['options'], option_elem.findall('option')) + ToolParameter.__init__(self, tool, input_source) # TODO: abstract XML out of here - so non-XML InputSources can # specify DrillDown parameters. elem = input_source.elem() - self.multiple = string_as_bool( elem.get( 'multiple', False ) ) - self.display = elem.get( 'display', None ) - self.hierarchy = elem.get( 'hierarchy', 'exact' ) # exact or recurse - self.separator = elem.get( 'separator', ',' ) - from_file = elem.get( 'from_file', None ) + self.multiple = string_as_bool(elem.get('multiple', False)) + self.display = elem.get('display', None) + self.hierarchy = elem.get('hierarchy', 'exact') # exact or recurse + self.separator = elem.get('separator', ',') + from_file = elem.get('from_file', None) if from_file: - if not os.path.isabs( from_file ): - from_file = os.path.join( tool.app.config.tool_data_path, from_file ) - elem = XML( "%s" % open( from_file ).read() ) - self.dynamic_options = elem.get( 'dynamic_options', None ) + if not os.path.isabs(from_file): + from_file = os.path.join(tool.app.config.tool_data_path, from_file) + elem = XML("%s" % open(from_file).read()) + self.dynamic_options = elem.get('dynamic_options', None) if self.dynamic_options: self.is_dynamic = True self.options = [] self.filtered = {} - if elem.find( 'filter' ): + if elem.find('filter'): self.is_dynamic = True - for filter in elem.findall( 'filter' ): + for filter in elem.findall('filter'): # currently only filtering by metadata key matching input file is allowed - if filter.get( 'type' ) == 'data_meta': - if filter.get( 'data_ref' ) not in self.filtered: - self.filtered[filter.get( 'data_ref' )] = {} - if filter.get( 'meta_key' ) not in self.filtered[filter.get( 'data_ref' )]: - self.filtered[filter.get( 'data_ref' )][filter.get( 'meta_key' )] = {} - if filter.get( 'value' ) not in self.filtered[filter.get( 'data_ref' )][filter.get( 'meta_key' )]: - self.filtered[filter.get( 'data_ref' )][filter.get( 'meta_key' )][filter.get( 'value' )] = [] - recurse_option_elems( self.filtered[filter.get( 'data_ref' )][filter.get( 'meta_key' )][filter.get( 'value' )], filter.find( 'options' ).findall( 'option' ) ) + if filter.get('type') == 'data_meta': + if filter.get('data_ref') not in self.filtered: + self.filtered[filter.get('data_ref')] = {} + if filter.get('meta_key') not in self.filtered[filter.get('data_ref')]: + self.filtered[filter.get('data_ref')][filter.get('meta_key')] = {} + if filter.get('value') not in self.filtered[filter.get('data_ref')][filter.get('meta_key')]: + self.filtered[filter.get('data_ref')][filter.get('meta_key')][filter.get('value')] = [] + recurse_option_elems(self.filtered[filter.get('data_ref')][filter.get('meta_key')][filter.get('value')], filter.find('options').findall('option')) elif not self.dynamic_options: - recurse_option_elems( self.options, elem.find( 'options' ).findall( 'option' ) ) + recurse_option_elems(self.options, elem.find('options').findall('option')) - def _get_options_from_code( self, trans=None, value=None, other_values=None ): - assert self.dynamic_options, Exception( "dynamic_options was not specifed" ) - call_other_values = ExpressionContext({ '__trans__': trans, '__value__': value }) + def _get_options_from_code(self, trans=None, value=None, other_values=None): + assert self.dynamic_options, Exception("dynamic_options was not specifed") + call_other_values = ExpressionContext({'__trans__': trans, '__value__': value}) if other_values: call_other_values.parent = other_values.parent - call_other_values.update( other_values.dict ) + call_other_values.update(other_values.dict) try: - return eval( self.dynamic_options, self.tool.code_namespace, call_other_values ) + return eval(self.dynamic_options, self.tool.code_namespace, call_other_values) except Exception: return [] - def get_options( self, trans=None, value=None, other_values={} ): + def get_options(self, trans=None, value=None, other_values={}): if self.is_dynamic: if self.dynamic_options: - options = self._get_options_from_code( trans=trans, value=value, other_values=other_values ) + options = self._get_options_from_code(trans=trans, value=value, other_values=other_values) else: options = [] for filter_key, filter_value in self.filtered.items(): dataset = other_values.get(filter_key) - if dataset.__class__.__name__.endswith( "DatasetFilenameWrapper" ): # this is a bad way to check for this, but problems importing class ( due to circular imports? ) + if dataset.__class__.__name__.endswith("DatasetFilenameWrapper"): # this is a bad way to check for this, but problems importing class ( due to circular imports? ) dataset = dataset.dataset if dataset: for meta_key, meta_dict in filter_value.items(): - if hasattr( dataset, 'metadata' ) and hasattr( dataset.metadata, 'spec' ): - check_meta_val = dataset.metadata.spec[ meta_key ].param.to_string( dataset.metadata.get( meta_key ) ) + if hasattr(dataset, 'metadata') and hasattr(dataset.metadata, 'spec'): + check_meta_val = dataset.metadata.spec[meta_key].param.to_string(dataset.metadata.get(meta_key)) if check_meta_val in meta_dict: - options.extend( meta_dict[ check_meta_val ] ) + options.extend(meta_dict[check_meta_val]) return options return self.options - def get_legal_values( self, trans, other_values ): - def recurse_options( legal_values, options ): + def get_legal_values(self, trans, other_values): + def recurse_options(legal_values, options): for option in options: - legal_values.append( option['value'] ) - recurse_options( legal_values, option['options'] ) + legal_values.append(option['value']) + recurse_options(legal_values, option['options']) legal_values = [] - recurse_options( legal_values, self.get_options( trans=trans, other_values=other_values ) ) + recurse_options(legal_values, self.get_options(trans=trans, other_values=other_values)) return legal_values - def from_json( self, value, trans, other_values={} ): - legal_values = self.get_legal_values( trans, other_values ) - if len( list( legal_values ) ) == 0 and trans.workflow_building_mode: + def from_json(self, value, trans, other_values={}): + legal_values = self.get_legal_values(trans, other_values) + if len(list(legal_values)) == 0 and trans.workflow_building_mode: if self.multiple: if value == '': # No option selected value = None else: - value = value.split( "\n" ) + value = value.split("\n") return value if not value and not self.optional: - raise ValueError( "An invalid option was selected for %s, please verify." % (self.name) ) + raise ValueError("An invalid option was selected for %s, please verify." % (self.name)) if not value: return None - if not isinstance( value, list ): - value = [ value ] - if len( value ) > 1 and not self.multiple: - raise ValueError( "Multiple values provided but parameter %s is not expecting multiple values." % self.name ) + if not isinstance(value, list): + value = [value] + if len(value) > 1 and not self.multiple: + raise ValueError("Multiple values provided but parameter %s is not expecting multiple values." % self.name) rval = [] if not legal_values: - raise ValueError( "Parameter %s requires a value, but has no legal values defined." % self.name ) + raise ValueError("Parameter %s requires a value, but has no legal values defined." % self.name) for val in value: if val not in legal_values: - raise ValueError( "An invalid option was selected for %s, %r, please verify" % ( self.name, val ) ) - rval.append( val ) + raise ValueError("An invalid option was selected for %s, %r, please verify" % (self.name, val)) + rval.append(val) return rval - def to_param_dict_string( self, value, other_values={} ): - def get_options_list( value ): - def get_base_option( value, options ): + def to_param_dict_string(self, value, other_values={}): + def get_options_list(value): + def get_base_option(value, options): for option in options: if value == option['value']: return option - rval = get_base_option( value, option['options'] ) + rval = get_base_option(value, option['options']) if rval: return rval return None # not found - def recurse_option( option_list, option ): + def recurse_option(option_list, option): if not option['options']: - option_list.append( option['value'] ) + option_list.append(option['value']) else: for opt in option['options']: - recurse_option( option_list, opt ) + recurse_option(option_list, opt) rval = [] - recurse_option( rval, get_base_option( value, self.get_options( other_values=other_values ) ) ) + recurse_option(rval, get_base_option(value, self.get_options(other_values=other_values))) return rval or [value] if value is None: @@ -1329,87 +1354,87 @@ def recurse_option( option_list, option ): rval = value else: for val in value: - options = get_options_list( val ) - rval.extend( options ) - if len( rval ) > 1 and not self.multiple: - raise ValueError( "Multiple values provided but parameter %s is not expecting multiple values." % self.name ) - rval = self.separator.join( rval ) + options = get_options_list(val) + rval.extend(options) + if len(rval) > 1 and not self.multiple: + raise ValueError("Multiple values provided but parameter %s is not expecting multiple values." % self.name) + rval = self.separator.join(rval) if self.tool is None or self.tool.options.sanitize: if self.sanitizer: - rval = self.sanitizer.sanitize_param( rval ) + rval = self.sanitizer.sanitize_param(rval) else: - rval = sanitize_param( rval ) + rval = sanitize_param(rval) return rval - def get_initial_value( self, trans, other_values ): - def recurse_options( initial_values, options ): + def get_initial_value(self, trans, other_values): + def recurse_options(initial_values, options): for option in options: if option['selected']: - initial_values.append( option['value'] ) - recurse_options( initial_values, option['options'] ) + initial_values.append(option['value']) + recurse_options(initial_values, option['options']) # More working around dynamic options for workflow - options = self.get_options( trans=trans, other_values=other_values ) - if len( list( options ) ) == 0 and trans.workflow_building_mode: + options = self.get_options(trans=trans, other_values=other_values) + if len(list(options)) == 0 and trans.workflow_building_mode: return None initial_values = [] - recurse_options( initial_values, options ) - if len( initial_values ) == 0: + recurse_options(initial_values, options) + if len(initial_values) == 0: initial_values = None return initial_values - def to_text( self, value ): - def get_option_display( value, options ): + def to_text(self, value): + def get_option_display(value, options): for option in options: if value == option['value']: return option['name'] - rval = get_option_display( value, option['options'] ) + rval = get_option_display(value, option['options']) if rval: return rval return None # not found if not value: value = [] - elif not isinstance( value, list ): - value = [ value ] + elif not isinstance(value, list): + value = [value] # FIXME: Currently only translating values back to labels if they # are not dynamic if self.is_dynamic: if value: - if isinstance( value, list ): + if isinstance(value, list): rval = value else: - rval = [ value ] + rval = [value] else: rval = [] else: rval = [] for val in value: - rval.append( get_option_display( val, self.options ) or val ) + rval.append(get_option_display(val, self.options) or val) if rval: - return "\n".join( map( str, rval ) ) + return "\n".join(map(str, rval)) return "Nothing selected." - def get_dependencies( self ): + def get_dependencies(self): """ Get the *names* of the other params this param depends on. """ return list(self.filtered.keys()) - def to_dict( self, trans, other_values={} ): + def to_dict(self, trans, other_values={}): # skip SelectToolParameter (the immediate parent) bc we need to get options in a different way here - d = ToolParameter.to_dict( self, trans ) - d[ 'options' ] = self.get_options( trans=trans, other_values=other_values ) - d[ 'display' ] = self.display - d[ 'multiple' ] = self.multiple + d = ToolParameter.to_dict(self, trans) + d['options'] = self.get_options(trans=trans, other_values=other_values) + d['display'] = self.display + d['multiple'] = self.multiple return d -class BaseDataToolParameter( ToolParameter ): +class BaseDataToolParameter(ToolParameter): - def __init__( self, tool, input_source, trans ): - super(BaseDataToolParameter, self).__init__( tool, input_source ) + def __init__(self, tool, input_source, trans): + super(BaseDataToolParameter, self).__init__(tool, input_source) self.refresh_on_change = True - def _datatypes_registery( self, trans, tool ): + def _datatypes_registery(self, trans, tool): # Find datatypes_registry if tool is None: if trans: @@ -1425,11 +1450,11 @@ def _datatypes_registery( self, trans, tool ): datatypes_registry = tool.app.datatypes_registry return datatypes_registry - def _parse_formats( self, trans, tool, input_source ): - datatypes_registry = self._datatypes_registery( trans, tool ) + def _parse_formats(self, trans, tool, input_source): + datatypes_registry = self._datatypes_registery(trans, tool) # Build list of classes for supported data formats - self.extensions = input_source.get( 'format', 'data' ).split( "," ) + self.extensions = input_source.get('format', 'data').split(",") normalized_extensions = [extension.strip().lower() for extension in self.extensions] formats = [] for extension in normalized_extensions: @@ -1440,95 +1465,95 @@ def _parse_formats( self, trans, tool, input_source ): log.warning("Datatype class not found for extension '%s', which is used in the 'format' attribute of parameter '%s'" % (extension, self.name)) self.formats = formats - def _parse_options( self, input_source ): + def _parse_options(self, input_source): # TODO: Enhance dynamic options for DataToolParameters. Currently, # only the special case key='build' of type='data_meta' is # a valid filter self.options_filter_attribute = None - self.options = parse_dynamic_options( self, input_source ) + self.options = parse_dynamic_options(self, input_source) if self.options: # TODO: Abstract away XML handling here. options_elem = input_source.elem().find('options') - self.options_filter_attribute = options_elem.get( 'options_filter_attribute', None ) + self.options_filter_attribute = options_elem.get('options_filter_attribute', None) self.is_dynamic = self.options is not None - def get_initial_value( self, trans, other_values ): + def get_initial_value(self, trans, other_values): if trans.workflow_building_mode is workflow_building_modes.ENABLED or trans.app.name == 'tool_shed': return RuntimeValue() if self.optional: return None history = trans.history if history is not None: - dataset_matcher = DatasetMatcher( trans, self, None, other_values ) - if isinstance( self, DataToolParameter ): - for hda in reversed( history.active_datasets_and_roles ): - match = dataset_matcher.hda_match( hda, check_security=False ) + dataset_matcher = DatasetMatcher(trans, self, None, other_values) + if isinstance(self, DataToolParameter): + for hda in reversed(history.active_datasets_and_roles): + match = dataset_matcher.hda_match(hda, check_security=False) if match: return match.hda else: - dataset_collection_matcher = DatasetCollectionMatcher( dataset_matcher ) - for hdca in reversed( history.active_dataset_collections ): - if dataset_collection_matcher.hdca_match( hdca, reduction=self.multiple ): + dataset_collection_matcher = DatasetCollectionMatcher(dataset_matcher) + for hdca in reversed(history.active_dataset_collections): + if dataset_collection_matcher.hdca_match(hdca, reduction=self.multiple): return hdca - def to_json( self, value, app, use_security ): - def single_to_json( value ): + def to_json(self, value, app, use_security): + def single_to_json(value): src = None - if isinstance( value, dict ) and 'src' in value and 'id' in value: + if isinstance(value, dict) and 'src' in value and 'id' in value: return value - elif isinstance( value, galaxy.model.DatasetCollectionElement ): + elif isinstance(value, galaxy.model.DatasetCollectionElement): src = 'dce' - elif isinstance( value, app.model.HistoryDatasetCollectionAssociation ): + elif isinstance(value, app.model.HistoryDatasetCollectionAssociation): src = 'hdca' - elif hasattr( value, 'id' ): + elif hasattr(value, 'id'): src = 'hda' if src is not None: - return { 'id' : app.security.encode_id( value.id ) if use_security else value.id, 'src' : src } - if value not in [ None, '', 'None' ]: - if isinstance( value, list ) and len( value ) > 0: - values = [ single_to_json( v ) for v in value ] + return {'id' : app.security.encode_id(value.id) if use_security else value.id, 'src' : src} + if value not in [None, '', 'None']: + if isinstance(value, list) and len(value) > 0: + values = [single_to_json(v) for v in value] else: - values = [ single_to_json( value ) ] - return { 'values': values } + values = [single_to_json(value)] + return {'values': values} return None - def to_python( self, value, app ): - def single_to_python( value ): - if isinstance( value, dict ) and 'src' in value: - id = value[ 'id' ] if isinstance( value[ 'id' ], int ) else app.security.decode_id( value[ 'id' ] ) - if value[ 'src' ] == 'dce': - return app.model.context.query( app.model.DatasetCollectionElement ).get( id ) - elif value[ 'src' ] == 'hdca': - return app.model.context.query( app.model.HistoryDatasetCollectionAssociation ).get( id ) + def to_python(self, value, app): + def single_to_python(value): + if isinstance(value, dict) and 'src' in value: + id = value['id'] if isinstance(value['id'], int) else app.security.decode_id(value['id']) + if value['src'] == 'dce': + return app.model.context.query(app.model.DatasetCollectionElement).get(id) + elif value['src'] == 'hdca': + return app.model.context.query(app.model.HistoryDatasetCollectionAssociation).get(id) else: - return app.model.context.query( app.model.HistoryDatasetAssociation ).get( id ) + return app.model.context.query(app.model.HistoryDatasetAssociation).get(id) - if isinstance( value, dict ) and 'values' in value: - if hasattr( self, 'multiple' ) and self.multiple is True: - return [ single_to_python( v ) for v in value[ 'values' ] ] - elif len( value[ 'values' ] ) > 0: - return single_to_python( value[ 'values' ][ 0 ] ) + if isinstance(value, dict) and 'values' in value: + if hasattr(self, 'multiple') and self.multiple is True: + return [single_to_python(v) for v in value['values']] + elif len(value['values']) > 0: + return single_to_python(value['values'][0]) # Handle legacy string values potentially stored in databases - none_values = [ None, '', 'None' ] + none_values = [None, '', 'None'] if value in none_values: return None - if isinstance( value, string_types ) and value.find( ',' ) > -1: - return [ app.model.context.query( app.model.HistoryDatasetAssociation ).get( int( v ) ) for v in value.split( ',' ) if v not in none_values ] - elif str( value ).startswith( "__collection_reduce__|" ): - decoded_id = str( value )[ len( "__collection_reduce__|" ): ] + if isinstance(value, string_types) and value.find(',') > -1: + return [app.model.context.query(app.model.HistoryDatasetAssociation).get(int(v)) for v in value.split(',') if v not in none_values] + elif str(value).startswith("__collection_reduce__|"): + decoded_id = str(value)[len("__collection_reduce__|"):] if not decoded_id.isdigit(): - decoded_id = app.security.decode_id( decoded_id ) - return app.model.context.query( app.model.HistoryDatasetCollectionAssociation ).get( int( decoded_id ) ) - elif str( value ).startswith( "dce:" ): - return app.model.context.query( app.model.DatasetCollectionElement ).get( int( value[ len( "dce:" ): ] ) ) - elif str( value ).startswith( "hdca:" ): - return app.model.context.query( app.model.HistoryDatasetCollectionAssociation ).get( int( value[ len( "hdca:" ): ] ) ) + decoded_id = app.security.decode_id(decoded_id) + return app.model.context.query(app.model.HistoryDatasetCollectionAssociation).get(int(decoded_id)) + elif str(value).startswith("dce:"): + return app.model.context.query(app.model.DatasetCollectionElement).get(int(value[len("dce:"):])) + elif str(value).startswith("hdca:"): + return app.model.context.query(app.model.HistoryDatasetCollectionAssociation).get(int(value[len("hdca:"):])) else: - return app.model.context.query( app.model.HistoryDatasetAssociation ).get( int( value ) ) + return app.model.context.query(app.model.HistoryDatasetAssociation).get(int(value)) -class DataToolParameter( BaseDataToolParameter ): +class DataToolParameter(BaseDataToolParameter): # TODO, Nate: Make sure the following unit tests appropriately test the dataset security # components. Add as many additional tests as necessary. """ @@ -1542,170 +1567,170 @@ class DataToolParameter( BaseDataToolParameter ): security stuff will dramatically alter this anyway. """ - def __init__( self, tool, input_source, trans=None): - input_source = ensure_input_source( input_source ) - super(DataToolParameter, self).__init__( tool, input_source, trans ) + def __init__(self, tool, input_source, trans=None): + input_source = ensure_input_source(input_source) + super(DataToolParameter, self).__init__(tool, input_source, trans) # Add metadata validator - if not input_source.get_bool( 'no_validation', False ): - self.validators.append( validation.MetadataValidator() ) - self._parse_formats( trans, tool, input_source ) + if not input_source.get_bool('no_validation', False): + self.validators.append(validation.MetadataValidator()) + self._parse_formats(trans, tool, input_source) self.multiple = input_source.get_bool('multiple', False) - self.min = input_source.get( 'min' ) - self.max = input_source.get( 'max' ) + self.min = input_source.get('min') + self.max = input_source.get('max') if self.min: try: - self.min = int( self.min ) + self.min = int(self.min) except: - raise ValueError( "An integer is required for min property." ) + raise ValueError("An integer is required for min property.") if self.max: try: - self.max = int( self.max ) + self.max = int(self.max) except: - raise ValueError( "An integer is required for max property." ) + raise ValueError("An integer is required for max property.") if not self.multiple and (self.min is not None): - raise ValueError( "Cannot specify min property on single data parameter '%s'. Set multiple=\"true\" to enable this option." % self.name ) + raise ValueError("Cannot specify min property on single data parameter '%s'. Set multiple=\"true\" to enable this option." % self.name) if not self.multiple and (self.max is not None): - raise ValueError( "Cannot specify max property on single data parameter '%s'. Set multiple=\"true\" to enable this option." % self.name ) + raise ValueError("Cannot specify max property on single data parameter '%s'. Set multiple=\"true\" to enable this option." % self.name) self.is_dynamic = True - self._parse_options( input_source ) + self._parse_options(input_source) # Load conversions required for the dataset input self.conversions = [] for name, conv_extension in input_source.parse_conversion_tuples(): - assert None not in [ name, conv_extension ], 'A name (%s) and type (%s) are required for explicit conversion' % ( name, conv_extension ) - conv_type = tool.app.datatypes_registry.get_datatype_by_extension( conv_extension.lower() ) + assert None not in [name, conv_extension], 'A name (%s) and type (%s) are required for explicit conversion' % (name, conv_extension) + conv_type = tool.app.datatypes_registry.get_datatype_by_extension(conv_extension.lower()) if conv_type is None: raise ValueError("Datatype class not found for extension '%s', which is used as 'type' attribute in conversion of data parameter '%s'" % (conv_type, self.name)) - self.conversions.append( ( name, conv_extension, [conv_type] ) ) + self.conversions.append((name, conv_extension, [conv_type])) - def match_collections( self, history, dataset_matcher, reduction=True ): - dataset_collection_matcher = DatasetCollectionMatcher( dataset_matcher ) + def match_collections(self, history, dataset_matcher, reduction=True): + dataset_collection_matcher = DatasetCollectionMatcher(dataset_matcher) for history_dataset_collection in history.active_dataset_collections: - if dataset_collection_matcher.hdca_match( history_dataset_collection, reduction=reduction ): + if dataset_collection_matcher.hdca_match(history_dataset_collection, reduction=reduction): yield history_dataset_collection - def from_json( self, value, trans, other_values={} ): + def from_json(self, value, trans, other_values={}): if trans.workflow_building_mode is workflow_building_modes.ENABLED: return None if not value and not self.optional: - raise ValueError( "Specify a dataset of the required format / build." ) - if value in [ None, "None", '' ]: + raise ValueError("Specify a dataset of the required format / build.") + if value in [None, "None", '']: return None - if isinstance( value, dict ) and 'values' in value: - value = self.to_python( value, trans.app ) - if isinstance( value, string_types ) and value.find( "," ) > 0: - value = [ int( value_part ) for value_part in value.split( "," ) ] - if isinstance( value, list ): + if isinstance(value, dict) and 'values' in value: + value = self.to_python(value, trans.app) + if isinstance(value, string_types) and value.find(",") > 0: + value = [int(value_part) for value_part in value.split(",")] + if isinstance(value, list): rval = [] found_hdca = False for single_value in value: - if isinstance( single_value, dict ) and 'src' in single_value and 'id' in single_value: + if isinstance(single_value, dict) and 'src' in single_value and 'id' in single_value: if single_value['src'] == 'hda': - rval.append( trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( trans.security.decode_id(single_value['id']) )) + rval.append(trans.sa_session.query(trans.app.model.HistoryDatasetAssociation).get(trans.security.decode_id(single_value['id']))) elif single_value['src'] == 'hdca': found_hdca = True - decoded_id = trans.security.decode_id( single_value[ 'id' ] ) - rval.append( trans.sa_session.query( trans.app.model.HistoryDatasetCollectionAssociation ).get( decoded_id ) ) + decoded_id = trans.security.decode_id(single_value['id']) + rval.append(trans.sa_session.query(trans.app.model.HistoryDatasetCollectionAssociation).get(decoded_id)) else: raise ValueError("Unknown input source %s passed to job submission API." % single_value['src']) - elif isinstance( single_value, trans.app.model.HistoryDatasetCollectionAssociation ): - rval.append( single_value ) - elif isinstance( single_value, trans.app.model.HistoryDatasetAssociation ): - rval.append( single_value ) + elif isinstance(single_value, trans.app.model.HistoryDatasetCollectionAssociation): + rval.append(single_value) + elif isinstance(single_value, trans.app.model.HistoryDatasetAssociation): + rval.append(single_value) else: - rval.append( trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( single_value ) ) + rval.append(trans.sa_session.query(trans.app.model.HistoryDatasetAssociation).get(single_value)) if found_hdca: for val in rval: - if not isinstance( val, trans.app.model.HistoryDatasetCollectionAssociation ): - raise ValueError( "If collections are supplied to multiple data input parameter, only collections may be used." ) - elif isinstance( value, trans.app.model.HistoryDatasetAssociation ): + if not isinstance(val, trans.app.model.HistoryDatasetCollectionAssociation): + raise ValueError("If collections are supplied to multiple data input parameter, only collections may be used.") + elif isinstance(value, trans.app.model.HistoryDatasetAssociation): rval = value - elif isinstance( value, dict ) and 'src' in value and 'id' in value: + elif isinstance(value, dict) and 'src' in value and 'id' in value: if value['src'] == 'hda': - rval = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( trans.security.decode_id(value['id']) ) + rval = trans.sa_session.query(trans.app.model.HistoryDatasetAssociation).get(trans.security.decode_id(value['id'])) elif value['src'] == 'hdca': - decoded_id = trans.security.decode_id( value[ 'id' ] ) - rval = trans.sa_session.query( trans.app.model.HistoryDatasetCollectionAssociation ).get( decoded_id ) + decoded_id = trans.security.decode_id(value['id']) + rval = trans.sa_session.query(trans.app.model.HistoryDatasetCollectionAssociation).get(decoded_id) else: raise ValueError("Unknown input source %s passed to job submission API." % value['src']) - elif str( value ).startswith( "__collection_reduce__|" ): - encoded_ids = [ v[ len( "__collection_reduce__|" ): ] for v in str( value ).split(",") ] - decoded_ids = map( trans.security.decode_id, encoded_ids ) + elif str(value).startswith("__collection_reduce__|"): + encoded_ids = [v[len("__collection_reduce__|"):] for v in str(value).split(",")] + decoded_ids = map(trans.security.decode_id, encoded_ids) rval = [] for decoded_id in decoded_ids: - hdca = trans.sa_session.query( trans.app.model.HistoryDatasetCollectionAssociation ).get( decoded_id ) - rval.append( hdca ) - elif isinstance( value, trans.app.model.HistoryDatasetCollectionAssociation ): + hdca = trans.sa_session.query(trans.app.model.HistoryDatasetCollectionAssociation).get(decoded_id) + rval.append(hdca) + elif isinstance(value, trans.app.model.HistoryDatasetCollectionAssociation): rval = value else: - rval = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( value ) - if isinstance( rval, list ): + rval = trans.sa_session.query(trans.app.model.HistoryDatasetAssociation).get(value) + if isinstance(rval, list): values = rval else: - values = [ rval ] + values = [rval] for v in values: if v: if v.deleted: - raise ValueError( "The previously selected dataset has been deleted." ) - if hasattr( v, "dataset" ) and v.dataset.state in [ galaxy.model.Dataset.states.ERROR, galaxy.model.Dataset.states.DISCARDED ]: - raise ValueError( "The previously selected dataset has entered an unusable state" ) + raise ValueError("The previously selected dataset has been deleted.") + if hasattr(v, "dataset") and v.dataset.state in [galaxy.model.Dataset.states.ERROR, galaxy.model.Dataset.states.DISCARDED]: + raise ValueError("The previously selected dataset has entered an unusable state") if not self.multiple: - if len( values ) > 1: - raise ValueError( "More than one dataset supplied to single input dataset parameter." ) - if len( values ) > 0: - rval = values[ 0 ] + if len(values) > 1: + raise ValueError("More than one dataset supplied to single input dataset parameter.") + if len(values) > 0: + rval = values[0] else: - raise ValueError( "Invalid dataset supplied to single input dataset parameter." ) + raise ValueError("Invalid dataset supplied to single input dataset parameter.") return rval - def to_param_dict_string( self, value, other_values={} ): + def to_param_dict_string(self, value, other_values={}): if value is None: return "None" return value.file_name - def to_text( self, value ): - if value and not isinstance( value, list ): - value = [ value ] + def to_text(self, value): + if value and not isinstance(value, list): + value = [value] if value: try: - return ", ".join( [ "%s: %s" % ( item.hid, item.name ) for item in value ] ) + return ", ".join(["%s: %s" % (item.hid, item.name) for item in value]) except: pass return "No dataset." - def validate( self, value, trans=None ): + def validate(self, value, trans=None): dataset_count = 0 for validator in self.validators: - def do_validate( v ): - if validator.requires_dataset_metadata and v and hasattr( v, 'dataset' ) and v.dataset.state != galaxy.model.Dataset.states.OK: + def do_validate(v): + if validator.requires_dataset_metadata and v and hasattr(v, 'dataset') and v.dataset.state != galaxy.model.Dataset.states.OK: return else: - validator.validate( v, trans ) + validator.validate(v, trans) if value and self.multiple: - if not isinstance( value, list ): - value = [ value ] + if not isinstance(value, list): + value = [value] for v in value: if isinstance(v, galaxy.model.HistoryDatasetCollectionAssociation): for dataset_instance in v.collection.dataset_instances: dataset_count += 1 - do_validate( dataset_instance ) + do_validate(dataset_instance) else: dataset_count += 1 - do_validate( v ) + do_validate(v) else: if value: dataset_count += 1 - do_validate( value ) + do_validate(value) if self.min is not None: if self.min > dataset_count: - raise ValueError( "At least %d datasets are required." % self.min ) + raise ValueError("At least %d datasets are required." % self.min) if self.max is not None: if self.max < dataset_count: - raise ValueError( "At most %d datasets are required." % self.max ) + raise ValueError("At most %d datasets are required." % self.max) - def get_dependencies( self ): + def get_dependencies(self): """ Get the *names* of the other params this param depends on. """ @@ -1714,46 +1739,46 @@ def get_dependencies( self ): else: return [] - def converter_safe( self, other_values, trans ): - if self.tool is None or self.tool.has_multiple_pages or not hasattr( trans, 'workflow_building_mode' ) or trans.workflow_building_mode: + def converter_safe(self, other_values, trans): + if self.tool is None or self.tool.has_multiple_pages or not hasattr(trans, 'workflow_building_mode') or trans.workflow_building_mode: return False if other_values is None: return True # we don't know other values, so we can't check, assume ok converter_safe = [True] - def visitor( prefix, input, value, parent=None ): - if isinstance( input, SelectToolParameter ) and self.name in input.get_dependencies(): - if input.is_dynamic and ( input.dynamic_options or ( not input.dynamic_options and not input.options ) or not input.options.converter_safe ): + def visitor(prefix, input, value, parent=None): + if isinstance(input, SelectToolParameter) and self.name in input.get_dependencies(): + if input.is_dynamic and (input.dynamic_options or (not input.dynamic_options and not input.options) or not input.options.converter_safe): converter_safe[0] = False # This option does not allow for conversion, i.e. uses contents of dataset file to generate options - self.tool.visit_inputs( other_values, visitor ) + self.tool.visit_inputs(other_values, visitor) return False not in converter_safe - def get_options_filter_attribute( self, value ): + def get_options_filter_attribute(self, value): # HACK to get around current hardcoded limitation of when a set of dynamic options is defined for a DataToolParameter # it always causes available datasets to be filtered by dbkey # this behavior needs to be entirely reworked (in a backwards compatible manner) options_filter_attribute = self.options_filter_attribute if options_filter_attribute is None: return value.get_dbkey() - if options_filter_attribute.endswith( "()" ): + if options_filter_attribute.endswith("()"): call_attribute = True options_filter_attribute = options_filter_attribute[:-2] else: call_attribute = False ref = value - for attribute in options_filter_attribute.split( '.' ): - ref = getattr( ref, attribute ) + for attribute in options_filter_attribute.split('.'): + ref = getattr(ref, attribute) if call_attribute: ref = ref() return ref - def to_dict( self, trans, other_values={} ): + def to_dict(self, trans, other_values={}): # create dictionary and fill default parameters - d = super( DataToolParameter, self ).to_dict( trans ) + d = super(DataToolParameter, self).to_dict(trans) extensions = self.extensions - datatypes_registery = self._datatypes_registery( trans, self.tool ) - all_edam_formats = datatypes_registery.edam_formats if hasattr( datatypes_registery, 'edam_formats' ) else {} - all_edam_data = datatypes_registery.edam_data if hasattr( datatypes_registery, 'edam_formats' ) else {} + datatypes_registery = self._datatypes_registery(trans, self.tool) + all_edam_formats = datatypes_registery.edam_formats if hasattr(datatypes_registery, 'edam_formats') else {} + all_edam_data = datatypes_registery.edam_data if hasattr(datatypes_registery, 'edam_formats') else {} edam_formats = [all_edam_formats.get(ext, None) for ext in extensions] edam_data = [all_edam_data.get(ext, None) for ext in extensions] @@ -1772,42 +1797,42 @@ def to_dict( self, trans, other_values={} ): return d # prepare dataset/collection matching - dataset_matcher = DatasetMatcher( trans, self, None, other_values ) + dataset_matcher = DatasetMatcher(trans, self, None, other_values) multiple = self.multiple # build and append a new select option - def append( list, hda, name, src, keep=False ): - return list.append( { 'id' : trans.security.encode_id( hda.id ), - 'hid' : hda.hid, - 'name' : name, - 'tags' : [ t.user_tname if not t.value else "%s:%s" % (t.user_tname, t.value) for t in hda.tags ], - 'src' : src, - 'keep' : keep } ) + def append(list, hda, name, src, keep=False): + return list.append({'id' : trans.security.encode_id(hda.id), + 'hid' : hda.hid, + 'name' : name, + 'tags' : [t.user_tname if not t.value else "%s:%s" % (t.user_tname, t.value) for t in hda.tags], + 'src' : src, + 'keep' : keep}) # add datasets - hda_list = util.listify( other_values.get( self.name ) ) + hda_list = util.listify(other_values.get(self.name)) for hda in history.active_datasets_and_roles: - match = dataset_matcher.hda_match( hda, check_security=False ) + match = dataset_matcher.hda_match(hda, check_security=False) if match: m = match.hda - hda_list = [ h for h in hda_list if h != m and h != hda ] - m_name = '%s (as %s)' % ( match.original_hda.name, match.target_ext ) if match.implicit_conversion else m.name - append( d[ 'options' ][ 'hda' ], m, m_name, 'hda' ) + hda_list = [h for h in hda_list if h != m and h != hda] + m_name = '%s (as %s)' % (match.original_hda.name, match.target_ext) if match.implicit_conversion else m.name + append(d['options']['hda'], m, m_name, 'hda') for hda in hda_list: - if hasattr( hda, 'hid' ): + if hasattr(hda, 'hid'): if hda.deleted: hda_state = 'deleted' elif not hda.visible: hda_state = 'hidden' else: hda_state = 'unavailable' - append( d[ 'options' ][ 'hda' ], hda, '(%s) %s' % ( hda_state, hda.name ), 'hda', True ) + append(d['options']['hda'], hda, '(%s) %s' % (hda_state, hda.name), 'hda', True) # add dataset collections - dataset_collection_matcher = DatasetCollectionMatcher( dataset_matcher ) + dataset_collection_matcher = DatasetCollectionMatcher(dataset_matcher) for hdca in history.active_dataset_collections: - if dataset_collection_matcher.hdca_match( hdca, reduction=multiple ): - append( d[ 'options' ][ 'hdca' ], hdca, hdca.name, 'hdca' ) + if dataset_collection_matcher.hdca_match(hdca, reduction=multiple): + append(d['options']['hdca'], hdca, hdca.name, 'hdca') # sort both lists d['options']['hda'] = sorted(d['options']['hda'], key=lambda k: k['hid'], reverse=True) @@ -1817,108 +1842,108 @@ def append( list, hda, name, src, keep=False ): return d -class DataCollectionToolParameter( BaseDataToolParameter ): +class DataCollectionToolParameter(BaseDataToolParameter): """ """ - def __init__( self, tool, input_source, trans=None ): - input_source = ensure_input_source( input_source ) - super(DataCollectionToolParameter, self).__init__( tool, input_source, trans ) - self._parse_formats( trans, tool, input_source ) + def __init__(self, tool, input_source, trans=None): + input_source = ensure_input_source(input_source) + super(DataCollectionToolParameter, self).__init__(tool, input_source, trans) + self._parse_formats(trans, tool, input_source) collection_types = input_source.get("collection_type", None) if collection_types: collection_types = [t.strip() for t in collection_types.split(",")] self._collection_types = collection_types self.multiple = False # Accessed on DataToolParameter a lot, may want in future self.is_dynamic = True - self._parse_options( input_source ) # TODO: Review and test. + self._parse_options(input_source) # TODO: Review and test. @property - def collection_types( self ): + def collection_types(self): return self._collection_types - def _history_query( self, trans ): + def _history_query(self, trans): dataset_collection_type_descriptions = trans.app.dataset_collections_service.collection_type_descriptions - return history_query.HistoryQuery.from_parameter( self, dataset_collection_type_descriptions ) + return history_query.HistoryQuery.from_parameter(self, dataset_collection_type_descriptions) - def match_collections( self, trans, history, dataset_matcher ): - dataset_collections = trans.app.dataset_collections_service.history_dataset_collections( history, self._history_query( trans ) ) - dataset_collection_matcher = DatasetCollectionMatcher( dataset_matcher ) + def match_collections(self, trans, history, dataset_matcher): + dataset_collections = trans.app.dataset_collections_service.history_dataset_collections(history, self._history_query(trans)) + dataset_collection_matcher = DatasetCollectionMatcher(dataset_matcher) for dataset_collection_instance in dataset_collections: - if not dataset_collection_matcher.hdca_match( dataset_collection_instance ): + if not dataset_collection_matcher.hdca_match(dataset_collection_instance): continue yield dataset_collection_instance - def match_multirun_collections( self, trans, history, dataset_matcher ): - dataset_collection_matcher = DatasetCollectionMatcher( dataset_matcher ) + def match_multirun_collections(self, trans, history, dataset_matcher): + dataset_collection_matcher = DatasetCollectionMatcher(dataset_matcher) for history_dataset_collection in history.active_dataset_collections: - if not self._history_query( trans ).can_map_over( history_dataset_collection ): + if not self._history_query(trans).can_map_over(history_dataset_collection): continue - datasets_match = dataset_collection_matcher.hdca_match( history_dataset_collection ) + datasets_match = dataset_collection_matcher.hdca_match(history_dataset_collection) if datasets_match: yield history_dataset_collection - def from_json( self, value, trans, other_values={} ): + def from_json(self, value, trans, other_values={}): rval = None if trans.workflow_building_mode is workflow_building_modes.ENABLED: return None if not value and not self.optional: - raise ValueError( "Specify a dataset collection of the correct type." ) + raise ValueError("Specify a dataset collection of the correct type.") if value in [None, "None"]: return None - if isinstance( value, dict ) and 'values' in value: - value = self.to_python( value, trans.app ) - if isinstance( value, string_types ) and value.find( "," ) > 0: - value = [ int( value_part ) for value_part in value.split( "," ) ] - elif isinstance( value, trans.app.model.HistoryDatasetCollectionAssociation ): + if isinstance(value, dict) and 'values' in value: + value = self.to_python(value, trans.app) + if isinstance(value, string_types) and value.find(",") > 0: + value = [int(value_part) for value_part in value.split(",")] + elif isinstance(value, trans.app.model.HistoryDatasetCollectionAssociation): rval = value - elif isinstance( value, trans.app.model.DatasetCollectionElement ): + elif isinstance(value, trans.app.model.DatasetCollectionElement): # When mapping over nested collection - this paramter will recieve # a DatasetCollectionElement instead of a # HistoryDatasetCollectionAssociation. rval = value - elif isinstance( value, dict ) and 'src' in value and 'id' in value: + elif isinstance(value, dict) and 'src' in value and 'id' in value: if value['src'] == 'hdca': - rval = trans.sa_session.query( trans.app.model.HistoryDatasetCollectionAssociation ).get( trans.security.decode_id(value['id']) ) - elif isinstance( value, list ): - if len( value ) > 0: + rval = trans.sa_session.query(trans.app.model.HistoryDatasetCollectionAssociation).get(trans.security.decode_id(value['id'])) + elif isinstance(value, list): + if len(value) > 0: value = value[0] - if isinstance( value, dict ) and 'src' in value and 'id' in value: + if isinstance(value, dict) and 'src' in value and 'id' in value: if value['src'] == 'hdca': - rval = trans.sa_session.query( trans.app.model.HistoryDatasetCollectionAssociation ).get( trans.security.decode_id(value['id']) ) - elif isinstance( value, string_types ): - if value.startswith( "dce:" ): - rval = trans.sa_session.query( trans.app.model.DatasetCollectionElement ).get( value[ len( "dce:"): ] ) - elif value.startswith( "hdca:" ): - rval = trans.sa_session.query( trans.app.model.HistoryDatasetCollectionAssociation ).get( value[ len( "hdca:"): ] ) + rval = trans.sa_session.query(trans.app.model.HistoryDatasetCollectionAssociation).get(trans.security.decode_id(value['id'])) + elif isinstance(value, string_types): + if value.startswith("dce:"): + rval = trans.sa_session.query(trans.app.model.DatasetCollectionElement).get(value[len("dce:"):]) + elif value.startswith("hdca:"): + rval = trans.sa_session.query(trans.app.model.HistoryDatasetCollectionAssociation).get(value[len("hdca:"):]) else: - rval = trans.sa_session.query( trans.app.model.HistoryDatasetCollectionAssociation ).get( value ) - if rval and isinstance( rval, trans.app.model.HistoryDatasetCollectionAssociation ): + rval = trans.sa_session.query(trans.app.model.HistoryDatasetCollectionAssociation).get(value) + if rval and isinstance(rval, trans.app.model.HistoryDatasetCollectionAssociation): if rval.deleted: - raise ValueError( "The previously selected dataset collection has been deleted" ) + raise ValueError("The previously selected dataset collection has been deleted") # TODO: Handle error states, implement error states ... return rval - def to_text( self, value ): + def to_text(self, value): try: - if isinstance( value, galaxy.model.HistoryDatasetCollectionAssociation ): - display_text = "%s: %s" % ( value.hid, value.name ) + if isinstance(value, galaxy.model.HistoryDatasetCollectionAssociation): + display_text = "%s: %s" % (value.hid, value.name) else: - display_text = "Element %d:%s" % ( value.identifier_index, value.identifier_name ) + display_text = "Element %d:%s" % (value.identifier_index, value.identifier_name) except AttributeError: display_text = "No dataset collection." return display_text - def validate( self, value, trans=None ): + def validate(self, value, trans=None): return True # TODO - def to_dict( self, trans, other_values=None ): + def to_dict(self, trans, other_values=None): # create dictionary and fill default parameters other_values = other_values or {} - d = super( DataCollectionToolParameter, self ).to_dict( trans ) + d = super(DataCollectionToolParameter, self).to_dict(trans) d['extensions'] = self.extensions d['multiple'] = self.multiple d['options'] = {'hda': [], 'hdca': []} @@ -1929,27 +1954,27 @@ def to_dict( self, trans, other_values=None ): return d # prepare dataset/collection matching - dataset_matcher = DatasetMatcher( trans, self, None, other_values ) + dataset_matcher = DatasetMatcher(trans, self, None, other_values) # append directly matched collections - for hdca in self.match_collections( trans, history, dataset_matcher ): + for hdca in self.match_collections(trans, history, dataset_matcher): d['options']['hdca'].append({ - 'id' : trans.security.encode_id( hdca.id ), + 'id' : trans.security.encode_id(hdca.id), 'hid' : hdca.hid, 'name' : hdca.name, 'src' : 'hdca', - 'tags' : [ t.user_tname if not t.value else "%s:%s" % (t.user_tname, t.value) for t in hdca.tags ] + 'tags' : [t.user_tname if not t.value else "%s:%s" % (t.user_tname, t.value) for t in hdca.tags] }) # append matching subcollections - for hdca in self.match_multirun_collections( trans, history, dataset_matcher ): - subcollection_type = self._history_query( trans ).can_map_over( hdca ).collection_type + for hdca in self.match_multirun_collections(trans, history, dataset_matcher): + subcollection_type = self._history_query(trans).can_map_over(hdca).collection_type d['options']['hdca'].append({ - 'id' : trans.security.encode_id( hdca.id ), + 'id' : trans.security.encode_id(hdca.id), 'hid' : hdca.hid, 'name' : hdca.name, 'src' : 'hdca', - 'tags' : [ t.user_tname if not t.value else "%s:%s" % (t.user_tname, t.value) for t in hdca.tags ], + 'tags' : [t.user_tname if not t.value else "%s:%s" % (t.user_tname, t.value) for t in hdca.tags], 'map_over_type': subcollection_type }) @@ -1960,49 +1985,50 @@ def to_dict( self, trans, other_values=None ): return d -class HiddenDataToolParameter( HiddenToolParameter, DataToolParameter ): +class HiddenDataToolParameter(HiddenToolParameter, DataToolParameter): """ Hidden parameter that behaves as a DataToolParameter. As with all hidden parameters, this is a HACK. """ - def __init__( self, tool, elem ): - DataToolParameter.__init__( self, tool, elem ) + + def __init__(self, tool, elem): + DataToolParameter.__init__(self, tool, elem) self.value = "None" self.type = "hidden_data" self.hidden = True -class LibraryDatasetToolParameter( ToolParameter ): +class LibraryDatasetToolParameter(ToolParameter): """ Parameter that lets users select a LDDA from a modal window, then use it within the wrapper. """ - def __init__( self, tool, input_source, context=None ): - input_source = ensure_input_source( input_source ) - ToolParameter.__init__( self, tool, input_source ) - self.multiple = input_source.get_bool( 'multiple', True ) + def __init__(self, tool, input_source, context=None): + input_source = ensure_input_source(input_source) + ToolParameter.__init__(self, tool, input_source) + self.multiple = input_source.get_bool('multiple', True) - def from_json( self, value, trans, other_values={} ): - return self.to_python( value, trans.app, other_values=other_values, validate=True ) + def from_json(self, value, trans, other_values={}): + return self.to_python(value, trans.app, other_values=other_values, validate=True) - def to_param_dict_string( self, value, other_values={} ): + def to_param_dict_string(self, value, other_values={}): if value is None: return 'None' elif self.multiple: - return [ dataset.get_file_name() for dataset in value ] + return [dataset.get_file_name() for dataset in value] else: - return value[ 0 ].get_file_name() + return value[0].get_file_name() # converts values to json representation: # { id: LibraryDatasetDatasetAssociation.id, name: LibraryDatasetDatasetAssociation.name, src: 'lda' } - def to_json( self, value, app, use_security ): - if not isinstance( value, list ): + def to_json(self, value, app, use_security): + if not isinstance(value, list): value = [value] lst = [] for item in value: lda_id = lda_name = None if isinstance(item, app.model.LibraryDatasetDatasetAssociation): - lda_id = app.security.encode_id( item.id ) if use_security else item.id + lda_id = app.security.encode_id(item.id) if use_security else item.id lda_name = item.name elif isinstance(item, dict): lda_id = item.get('id') @@ -2011,12 +2037,12 @@ def to_json( self, value, app, use_security ): lst = [] break if lda_id is not None: - lst.append( { + lst.append({ 'id' : lda_id, 'name' : lda_name, 'src' : 'ldda' - } ) - if len( lst ) == 0: + }) + if len(lst) == 0: return None else: return lst @@ -2027,8 +2053,8 @@ def to_json( self, value, app, use_security ): # 1. LibraryDatasetDatasetAssociation # 2. LibraryDatasetDatasetAssociation.id # 3. { id: LibraryDatasetDatasetAssociation.id, ... } - def to_python( self, value, app, other_values={}, validate=False ): - if not isinstance( value, list ): + def to_python(self, value, app, other_values={}, validate=False): + if not isinstance(value, list): value = [value] lst = [] for item in value: @@ -2043,20 +2069,20 @@ def to_python( self, value, app, other_values={}, validate=False ): else: lst = [] break - lda = app.model.context.query( app.model.LibraryDatasetDatasetAssociation ).get( lda_id if isinstance( lda_id, int ) else app.security.decode_id( lda_id ) ) + lda = app.model.context.query(app.model.LibraryDatasetDatasetAssociation).get(lda_id if isinstance(lda_id, int) else app.security.decode_id(lda_id)) if lda is not None: - lst.append( lda ) + lst.append(lda) elif validate: - raise ValueError( "One of the selected library datasets is invalid or not available anymore." ) - if len( lst ) == 0: + raise ValueError("One of the selected library datasets is invalid or not available anymore.") + if len(lst) == 0: if not self.optional and validate: - raise ValueError( "Please select a valid library dataset." ) + raise ValueError("Please select a valid library dataset.") return None else: return lst - def to_dict( self, trans, other_values=None ): - d = super( LibraryDatasetToolParameter, self ).to_dict( trans ) + def to_dict(self, trans, other_values=None): + d = super(LibraryDatasetToolParameter, self).to_dict(trans) d['multiple'] = self.multiple return d @@ -2075,6 +2101,7 @@ def to_dict( self, trans, other_values=None ): baseurl=BaseURLToolParameter, file=FileToolParameter, ftpfile=FTPFileToolParameter, + genomespacefile=GenomespaceFileToolParameter, data=DataToolParameter, data_collection=DataCollectionToolParameter, library_data=LibraryDatasetToolParameter, @@ -2082,7 +2109,7 @@ def to_dict( self, trans, other_values=None ): ) -class RuntimeValue( object ): +class RuntimeValue(object): """ Wrapper to note a value that is not yet set, but will be required at runtime. """ diff --git a/lib/galaxy/tools/parameters/dataset_matcher.py b/lib/galaxy/tools/parameters/dataset_matcher.py index 9d3a0171b633..2cdb62770648 100644 --- a/lib/galaxy/tools/parameters/dataset_matcher.py +++ b/lib/galaxy/tools/parameters/dataset_matcher.py @@ -2,13 +2,13 @@ import galaxy.model -log = getLogger( __name__ ) +log = getLogger(__name__) ROLES_UNSET = object() -INVALID_STATES = [ galaxy.model.Dataset.states.ERROR, galaxy.model.Dataset.states.DISCARDED ] +INVALID_STATES = [galaxy.model.Dataset.states.ERROR, galaxy.model.Dataset.states.DISCARDED] -class DatasetMatcher( object ): +class DatasetMatcher(object): """ Utility class to aid DataToolParameter and similar classes in reasoning about what HDAs could match or are selected for a parameter and value. @@ -17,7 +17,7 @@ class DatasetMatcher( object ): and permission handling. """ - def __init__( self, trans, param, value, other_values ): + def __init__(self, trans, param, value, other_values): self.trans = trans self.param = param self.tool = param.tool @@ -26,12 +26,12 @@ def __init__( self, trans, param, value, other_values ): filter_value = None if param.options and other_values: try: - filter_value = param.options.get_options( trans, other_values )[0][0] + filter_value = param.options.get_options(trans, other_values)[0][0] except IndexError: pass # no valid options self.filter_value = filter_value - def hda_accessible( self, hda, check_security=True ): + def hda_accessible(self, hda, check_security=True): """ Does HDA correspond to dataset that is an a valid state and is accessible to user. """ @@ -42,9 +42,9 @@ def hda_accessible( self, hda, check_security=True ): else: valid_input_states = galaxy.model.Dataset.valid_input_states state_valid = dataset.state in valid_input_states - return state_valid and ( not check_security or self.__can_access_dataset( dataset ) ) + return state_valid and (not check_security or self.__can_access_dataset(dataset)) - def valid_hda_match( self, hda, check_implicit_conversions=True, check_security=False ): + def valid_hda_match(self, hda, check_implicit_conversions=True, check_security=False): """ Return False of this parameter can not be matched to the supplied HDA, otherwise return a description of the match (either a HdaDirectMatch describing a direct match or a HdaImplicitMatch @@ -52,98 +52,98 @@ def valid_hda_match( self, hda, check_implicit_conversions=True, check_security= """ rval = False formats = self.param.formats - if hda.datatype.matches_any( formats ): - rval = HdaDirectMatch( hda ) + if hda.datatype.matches_any(formats): + rval = HdaDirectMatch(hda) else: if not check_implicit_conversions: return False - target_ext, converted_dataset = hda.find_conversion_destination( formats ) + target_ext, converted_dataset = hda.find_conversion_destination(formats) if target_ext: original_hda = hda if converted_dataset: hda = converted_dataset - if check_security and not self.__can_access_dataset( hda.dataset ): + if check_security and not self.__can_access_dataset(hda.dataset): return False - rval = HdaImplicitMatch( hda, target_ext, original_hda ) + rval = HdaImplicitMatch(hda, target_ext, original_hda) else: return False - if self.filter( hda ): + if self.filter(hda): return False return rval - def hda_match( self, hda, check_implicit_conversions=True, check_security=True, ensure_visible=True ): + def hda_match(self, hda, check_implicit_conversions=True, check_security=True, ensure_visible=True): """ If HDA is accessible, return information about whether it could match this parameter and if so how. See valid_hda_match for more information. """ - accessible = self.hda_accessible( hda, check_security=check_security ) - if accessible and ( not ensure_visible or hda.visible or ( self.selected( hda ) and not hda.implicitly_converted_parent_datasets ) ): + accessible = self.hda_accessible(hda, check_security=check_security) + if accessible and (not ensure_visible or hda.visible or (self.selected(hda) and not hda.implicitly_converted_parent_datasets)): # If we are sending data to an external application, then we need to make sure there are no roles # associated with the dataset that restrict its access from "public". require_public = self.tool and self.tool.tool_type == 'data_destination' - if require_public and not self.trans.app.security_agent.dataset_is_public( hda.dataset ): + if require_public and not self.trans.app.security_agent.dataset_is_public(hda.dataset): return False - if self.filter( hda ): + if self.filter(hda): return False - return self.valid_hda_match( hda, check_implicit_conversions=check_implicit_conversions ) + return self.valid_hda_match(hda, check_implicit_conversions=check_implicit_conversions) - def selected( self, hda ): + def selected(self, hda): """ Given value for DataToolParameter, is this HDA "selected". """ value = self.value - if value and str( value[ 0 ] ).isdigit(): + if value and str(value[0]).isdigit(): return hda.id in map(int, value) else: return value and hda in value - def filter( self, hda ): + def filter(self, hda): """ Filter out this value based on other values for job (if applicable). """ param = self.param - return param.options and param.get_options_filter_attribute( hda ) != self.filter_value + return param.options and param.get_options_filter_attribute(hda) != self.filter_value - def __can_access_dataset( self, dataset ): + def __can_access_dataset(self, dataset): # Lazily cache current_user_roles. if self.current_user_roles is ROLES_UNSET: self.current_user_roles = self.trans.get_current_user_roles() - return self.trans.app.security_agent.can_access_dataset( self.current_user_roles, dataset ) + return self.trans.app.security_agent.can_access_dataset(self.current_user_roles, dataset) -class HdaDirectMatch( object ): +class HdaDirectMatch(object): """ Supplied HDA was a valid option directly (did not need to find implicit conversion). """ - def __init__( self, hda ): + def __init__(self, hda): self.hda = hda @property - def implicit_conversion( self ): + def implicit_conversion(self): return False -class HdaImplicitMatch( object ): +class HdaImplicitMatch(object): """ Supplied HDA was a valid option directly (did not need to find implicit conversion). """ - def __init__( self, hda, target_ext, original_hda ): + def __init__(self, hda, target_ext, original_hda): self.original_hda = original_hda self.hda = hda self.target_ext = target_ext @property - def implicit_conversion( self ): + def implicit_conversion(self): return True -class DatasetCollectionMatcher( object ): +class DatasetCollectionMatcher(object): - def __init__( self, dataset_matcher ): + def __init__(self, dataset_matcher): self.dataset_matcher = dataset_matcher - def __valid_element( self, element ): + def __valid_element(self, element): # Simplify things for now and assume these are hdas and not implicit # converts. One could imagine handling both of those cases down the # road. @@ -152,22 +152,22 @@ def __valid_element( self, element ): child_collection = element.child_collection if child_collection: - return self.dataset_collection_match( child_collection ) + return self.dataset_collection_match(child_collection) hda = element.hda if not hda: return False - hda_match = self.dataset_matcher.hda_match( hda, ensure_visible=False ) + hda_match = self.dataset_matcher.hda_match(hda, ensure_visible=False) return hda_match and not hda_match.implicit_conversion - def hdca_match( self, history_dataset_collection_association, reduction=False ): + def hdca_match(self, history_dataset_collection_association, reduction=False): dataset_collection = history_dataset_collection_association.collection - if reduction and dataset_collection.collection_type.find( ":" ) > 0: + if reduction and dataset_collection.collection_type.find(":") > 0: return False else: - return self.dataset_collection_match( dataset_collection ) + return self.dataset_collection_match(dataset_collection) - def dataset_collection_match( self, dataset_collection ): + def dataset_collection_match(self, dataset_collection): # If dataset collection not yet populated, cannot determine if it # would be a valid match for this parameter. if not dataset_collection.populated: @@ -175,10 +175,10 @@ def dataset_collection_match( self, dataset_collection ): valid = True for element in dataset_collection.elements: - if not self.__valid_element( element ): + if not self.__valid_element(element): valid = False break return valid -__all__ = ( 'DatasetMatcher', 'DatasetCollectionMatcher' ) +__all__ = ('DatasetMatcher', 'DatasetCollectionMatcher') diff --git a/lib/galaxy/tools/parameters/dynamic_options.py b/lib/galaxy/tools/parameters/dynamic_options.py index b32ea7518524..a55b7dad618a 100644 --- a/lib/galaxy/tools/parameters/dynamic_options.py +++ b/lib/galaxy/tools/parameters/dynamic_options.py @@ -20,31 +20,31 @@ log = logging.getLogger(__name__) -class Filter( object ): +class Filter(object): """ A filter takes the current options list and modifies it. """ @classmethod - def from_element( cls, d_option, elem ): + def from_element(cls, d_option, elem): """Loads the proper filter by the type attribute of elem""" - type = elem.get( 'type', None ) + type = elem.get('type', None) assert type is not None, "Required 'type' attribute missing from filter" - return filter_types[type.strip()]( d_option, elem ) + return filter_types[type.strip()](d_option, elem) - def __init__( self, d_option, elem ): + def __init__(self, d_option, elem): self.dynamic_option = d_option self.elem = elem - def get_dependency_name( self ): + def get_dependency_name(self): """Returns the name of any depedencies, otherwise None""" return None - def filter_options( self, options, trans, other_values ): + def filter_options(self, options, trans, other_values): """Returns a list of options after the filter is applied""" - raise TypeError( "Abstract Method" ) + raise TypeError("Abstract Method") -class StaticValueFilter( Filter ): +class StaticValueFilter(Filter): """ Filters a list of options on a column by a static value. @@ -57,29 +57,30 @@ class StaticValueFilter( Filter ): keep: Keep columns matching value (True) Discard columns matching value (False) """ - def __init__( self, d_option, elem ): - Filter.__init__( self, d_option, elem ) - self.value = elem.get( "value", None ) + + def __init__(self, d_option, elem): + Filter.__init__(self, d_option, elem) + self.value = elem.get("value", None) assert self.value is not None, "Required 'value' attribute missing from filter" - column = elem.get( "column", None ) + column = elem.get("column", None) assert column is not None, "Required 'column' attribute missing from filter, when loading from file" - self.column = d_option.column_spec_to_index( column ) - self.keep = string_as_bool( elem.get( "keep", 'True' ) ) + self.column = d_option.column_spec_to_index(column) + self.keep = string_as_bool(elem.get("keep", 'True')) - def filter_options( self, options, trans, other_values ): + def filter_options(self, options, trans, other_values): rval = [] filter_value = self.value try: - filter_value = User.expand_user_properties( trans.user, filter_value) + filter_value = User.expand_user_properties(trans.user, filter_value) except: pass for fields in options: - if ( self.keep and fields[self.column] == filter_value ) or ( not self.keep and fields[self.column] != filter_value ): - rval.append( fields ) + if (self.keep and fields[self.column] == filter_value) or (not self.keep and fields[self.column] != filter_value): + rval.append(fields) return rval -class DataMetaFilter( Filter ): +class DataMetaFilter(Filter): """ Filters a list of options on a column by a dataset metadata value. @@ -100,50 +101,51 @@ class DataMetaFilter( Filter ): - separator: When multiple split by this (,) """ - def __init__( self, d_option, elem ): - Filter.__init__( self, d_option, elem ) - self.ref_name = elem.get( "ref", None ) + + def __init__(self, d_option, elem): + Filter.__init__(self, d_option, elem) + self.ref_name = elem.get("ref", None) assert self.ref_name is not None, "Required 'ref' attribute missing from filter" d_option.has_dataset_dependencies = True - self.key = elem.get( "key", None ) + self.key = elem.get("key", None) assert self.key is not None, "Required 'key' attribute missing from filter" - self.column = elem.get( "column", None ) + self.column = elem.get("column", None) if self.column is None: assert self.dynamic_option.file_fields is None and self.dynamic_option.dataset_ref_name is None, "Required 'column' attribute missing from filter, when loading from file" else: - self.column = d_option.column_spec_to_index( self.column ) - self.multiple = string_as_bool( elem.get( "multiple", "False" ) ) - self.separator = elem.get( "separator", "," ) + self.column = d_option.column_spec_to_index(self.column) + self.multiple = string_as_bool(elem.get("multiple", "False")) + self.separator = elem.get("separator", ",") - def get_dependency_name( self ): + def get_dependency_name(self): return self.ref_name - def filter_options( self, options, trans, other_values ): - def compare_meta_value( file_value, dataset_value ): - if isinstance( dataset_value, list ): + def filter_options(self, options, trans, other_values): + def compare_meta_value(file_value, dataset_value): + if isinstance(dataset_value, list): if self.multiple: - file_value = file_value.split( self.separator ) + file_value = file_value.split(self.separator) for value in dataset_value: if value not in file_value: return False return True return file_value in dataset_value if self.multiple: - return dataset_value in file_value.split( self.separator ) + return dataset_value in file_value.split(self.separator) return file_value == dataset_value - ref = other_values.get( self.ref_name, None ) - if isinstance( ref, HistoryDatasetCollectionAssociation ): - ref = ref.to_hda_representative( self.multiple ) - is_data = isinstance( ref, galaxy.tools.wrappers.DatasetFilenameWrapper ) - is_data_list = isinstance( ref, galaxy.tools.wrappers.DatasetListWrapper ) or isinstance( ref, list ) + ref = other_values.get(self.ref_name, None) + if isinstance(ref, HistoryDatasetCollectionAssociation): + ref = ref.to_hda_representative(self.multiple) + is_data = isinstance(ref, galaxy.tools.wrappers.DatasetFilenameWrapper) + is_data_list = isinstance(ref, galaxy.tools.wrappers.DatasetListWrapper) or isinstance(ref, list) is_data_or_data_list = is_data or is_data_list - if not isinstance( ref, HistoryDatasetAssociation ) and not is_data_or_data_list: + if not isinstance(ref, HistoryDatasetAssociation) and not is_data_or_data_list: return [] # not a valid dataset if is_data_list: meta_value = None for single_ref in ref: - this_meta_value = single_ref.metadata.get( self.key, None ) + this_meta_value = single_ref.metadata.get(self.key, None) if this_meta_value == meta_value: continue elif meta_value is None: @@ -152,16 +154,16 @@ def compare_meta_value( file_value, dataset_value ): # Different values with mismatching metadata, return [] return [] else: - meta_value = ref.metadata.get( self.key, None ) + meta_value = ref.metadata.get(self.key, None) if meta_value is None: - return [ ( disp_name, optval, selected ) for disp_name, optval, selected in options ] + return [(disp_name, optval, selected) for disp_name, optval, selected in options] if self.column is not None: rval = [] for fields in options: - if compare_meta_value( fields[self.column], meta_value ): - rval.append( fields ) + if compare_meta_value(fields[self.column], meta_value): + rval.append(fields) return rval else: if not self.dynamic_option.columns: @@ -171,14 +173,14 @@ def compare_meta_value( file_value, dataset_value ): "selected" : 2 } self.dynamic_option.largest_index = 2 - if not isinstance( meta_value, list ): + if not isinstance(meta_value, list): meta_value = [meta_value] for value in meta_value: - options.append( ( value, value, False ) ) + options.append((value, value, False)) return options -class ParamValueFilter( Filter ): +class ParamValueFilter(Filter): """ Filters a list of options on a column by the value of another input. @@ -196,40 +198,41 @@ class ParamValueFilter( Filter ): - ref_attribute: Period (.) separated attribute chain of input (ref) to use as value for filter """ - def __init__( self, d_option, elem ): - Filter.__init__( self, d_option, elem ) - self.ref_name = elem.get( "ref", None ) + + def __init__(self, d_option, elem): + Filter.__init__(self, d_option, elem) + self.ref_name = elem.get("ref", None) assert self.ref_name is not None, "Required 'ref' attribute missing from filter" - column = elem.get( "column", None ) + column = elem.get("column", None) assert column is not None, "Required 'column' attribute missing from filter" - self.column = d_option.column_spec_to_index( column ) - self.keep = string_as_bool( elem.get( "keep", 'True' ) ) - self.ref_attribute = elem.get( "ref_attribute", None ) + self.column = d_option.column_spec_to_index(column) + self.keep = string_as_bool(elem.get("keep", 'True')) + self.ref_attribute = elem.get("ref_attribute", None) if self.ref_attribute: - self.ref_attribute = self.ref_attribute.split( '.' ) + self.ref_attribute = self.ref_attribute.split('.') else: self.ref_attribute = [] - def get_dependency_name( self ): + def get_dependency_name(self): return self.ref_name - def filter_options( self, options, trans, other_values ): + def filter_options(self, options, trans, other_values): if trans is not None and trans.workflow_building_mode: return [] - ref = other_values.get( self.ref_name, None ) + ref = other_values.get(self.ref_name, None) for ref_attribute in self.ref_attribute: - if not hasattr( ref, ref_attribute ): + if not hasattr(ref, ref_attribute): return [] # ref does not have attribute, so we cannot filter, return empty list - ref = getattr( ref, ref_attribute ) - ref = str( ref ) + ref = getattr(ref, ref_attribute) + ref = str(ref) rval = [] for fields in options: - if ( self.keep and fields[self.column] == ref ) or ( not self.keep and fields[self.column] != ref ): - rval.append( fields ) + if (self.keep and fields[self.column] == ref) or (not self.keep and fields[self.column] != ref): + rval.append(fields) return rval -class UniqueValueFilter( Filter ): +class UniqueValueFilter(Filter): """ Filters a list of options to be unique by a column value. @@ -238,26 +241,27 @@ class UniqueValueFilter( Filter ): Required Attributes: column: column in options to compare with """ - def __init__( self, d_option, elem ): - Filter.__init__( self, d_option, elem ) - column = elem.get( "column", None ) + + def __init__(self, d_option, elem): + Filter.__init__(self, d_option, elem) + column = elem.get("column", None) assert column is not None, "Required 'column' attribute missing from filter" - self.column = d_option.column_spec_to_index( column ) + self.column = d_option.column_spec_to_index(column) - def get_dependency_name( self ): + def get_dependency_name(self): return self.dynamic_option.dataset_ref_name - def filter_options( self, options, trans, other_values ): + def filter_options(self, options, trans, other_values): rval = [] skip_list = [] for fields in options: if fields[self.column] not in skip_list: - rval.append( fields ) - skip_list.append( fields[self.column] ) + rval.append(fields) + skip_list.append(fields[self.column]) return rval -class MultipleSplitterFilter( Filter ): +class MultipleSplitterFilter(Filter): """ Turns a single line of options into multiple lines, by splitting a column and creating a line for each item. @@ -268,23 +272,24 @@ class MultipleSplitterFilter( Filter ): Optional Attributes: separator: Split column by this (,) """ - def __init__( self, d_option, elem ): - Filter.__init__( self, d_option, elem ) - self.separator = elem.get( "separator", "," ) - columns = elem.get( "column", None ) + + def __init__(self, d_option, elem): + Filter.__init__(self, d_option, elem) + self.separator = elem.get("separator", ",") + columns = elem.get("column", None) assert columns is not None, "Required 'columns' attribute missing from filter" - self.columns = [ d_option.column_spec_to_index( column ) for column in columns.split( "," ) ] + self.columns = [d_option.column_spec_to_index(column) for column in columns.split(",")] - def filter_options( self, options, trans, other_values ): + def filter_options(self, options, trans, other_values): rval = [] for fields in options: for column in self.columns: - for field in fields[column].split( self.separator ): - rval.append( fields[0:column] + [field] + fields[column + 1:] ) + for field in fields[column].split(self.separator): + rval.append(fields[0:column] + [field] + fields[column + 1:]) return rval -class AttributeValueSplitterFilter( Filter ): +class AttributeValueSplitterFilter(Filter): """ Filters a list of attribute-value pairs to be unique attribute names. @@ -296,30 +301,31 @@ class AttributeValueSplitterFilter( Filter ): pair_separator: Split column by this (,) name_val_separator: Split name-value pair by this ( whitespace ) """ - def __init__( self, d_option, elem ): - Filter.__init__( self, d_option, elem ) - self.pair_separator = elem.get( "pair_separator", "," ) - self.name_val_separator = elem.get( "name_val_separator", None ) - self.columns = elem.get( "column", None ) + + def __init__(self, d_option, elem): + Filter.__init__(self, d_option, elem) + self.pair_separator = elem.get("pair_separator", ",") + self.name_val_separator = elem.get("name_val_separator", None) + self.columns = elem.get("column", None) assert self.columns is not None, "Required 'columns' attribute missing from filter" - self.columns = [ int( column ) for column in self.columns.split( "," ) ] + self.columns = [int(column) for column in self.columns.split(",")] - def filter_options( self, options, trans, other_values ): + def filter_options(self, options, trans, other_values): attr_names = [] rval = [] for fields in options: for column in self.columns: - for pair in fields[column].split( self.pair_separator ): - ary = pair.split( self.name_val_separator ) - if len( ary ) == 2: + for pair in fields[column].split(self.pair_separator): + ary = pair.split(self.name_val_separator) + if len(ary) == 2: name = ary[0] if name not in attr_names: - rval.append( fields[0:column] + [name] + fields[column:] ) - attr_names.append( name ) + rval.append(fields[0:column] + [name] + fields[column:]) + attr_names.append(name) return rval -class AdditionalValueFilter( Filter ): +class AdditionalValueFilter(Filter): """ Adds a single static value to an options list. @@ -331,35 +337,36 @@ class AdditionalValueFilter( Filter ): name: Display name to appear in select list (value) index: Index of option list to add value (APPEND) """ - def __init__( self, d_option, elem ): - Filter.__init__( self, d_option, elem ) - self.value = elem.get( "value", None ) + + def __init__(self, d_option, elem): + Filter.__init__(self, d_option, elem) + self.value = elem.get("value", None) assert self.value is not None, "Required 'value' attribute missing from filter" - self.name = elem.get( "name", None ) + self.name = elem.get("name", None) if self.name is None: self.name = self.value - self.index = elem.get( "index", None ) + self.index = elem.get("index", None) if self.index is not None: - self.index = int( self.index ) + self.index = int(self.index) - def filter_options( self, options, trans, other_values ): - rval = list( options ) + def filter_options(self, options, trans, other_values): + rval = list(options) add_value = [] - for _ in range( self.dynamic_option.largest_index + 1 ): - add_value.append( "" ) - value_col = self.dynamic_option.columns.get( 'value', 0 ) - name_col = self.dynamic_option.columns.get( 'name', value_col ) + for _ in range(self.dynamic_option.largest_index + 1): + add_value.append("") + value_col = self.dynamic_option.columns.get('value', 0) + name_col = self.dynamic_option.columns.get('name', value_col) # Set name first, then value, in case they are the same column - add_value[ name_col ] = self.name - add_value[ value_col ] = self.value + add_value[name_col] = self.name + add_value[value_col] = self.value if self.index is not None: - rval.insert( self.index, add_value ) + rval.insert(self.index, add_value) else: - rval.append( add_value ) + rval.append(add_value) return rval -class RemoveValueFilter( Filter ): +class RemoveValueFilter(Filter): """ Removes a value from an options list. @@ -375,47 +382,48 @@ class RemoveValueFilter( Filter ): key: metadata key to compare to """ - def __init__( self, d_option, elem ): - Filter.__init__( self, d_option, elem ) - self.value = elem.get( "value", None ) - self.ref_name = elem.get( "ref", None ) - self.meta_ref = elem.get( "meta_ref", None ) - self.metadata_key = elem.get( "key", None ) - assert self.value is not None or ( ( self.ref_name is not None or self.meta_ref is not None )and self.metadata_key is not None ), ValueError( "Required 'value' or 'ref' and 'key' attributes missing from filter" ) - self.multiple = string_as_bool( elem.get( "multiple", "False" ) ) - self.separator = elem.get( "separator", "," ) - - def filter_options( self, options, trans, other_values ): + + def __init__(self, d_option, elem): + Filter.__init__(self, d_option, elem) + self.value = elem.get("value", None) + self.ref_name = elem.get("ref", None) + self.meta_ref = elem.get("meta_ref", None) + self.metadata_key = elem.get("key", None) + assert self.value is not None or ((self.ref_name is not None or self.meta_ref is not None)and self.metadata_key is not None), ValueError("Required 'value' or 'ref' and 'key' attributes missing from filter") + self.multiple = string_as_bool(elem.get("multiple", "False")) + self.separator = elem.get("separator", ",") + + def filter_options(self, options, trans, other_values): if trans is not None and trans.workflow_building_mode: return options - def compare_value( option_value, filter_value ): - if isinstance( filter_value, list ): + def compare_value(option_value, filter_value): + if isinstance(filter_value, list): if self.multiple: - option_value = option_value.split( self.separator ) + option_value = option_value.split(self.separator) for value in filter_value: if value not in filter_value: return False return True return option_value in filter_value if self.multiple: - return filter_value in option_value.split( self.separator ) + return filter_value in option_value.split(self.separator) return option_value == filter_value value = self.value if value is None: if self.ref_name is not None: - value = other_values.get( self.ref_name ) + value = other_values.get(self.ref_name) else: - data_ref = other_values.get( self.meta_ref ) - if isinstance( data_ref, HistoryDatasetCollectionAssociation ): + data_ref = other_values.get(self.meta_ref) + if isinstance(data_ref, HistoryDatasetCollectionAssociation): data_ref = data_ref.to_hda_representative() - if not isinstance( data_ref, HistoryDatasetAssociation ) and not isinstance( data_ref, galaxy.tools.wrappers.DatasetFilenameWrapper ): + if not isinstance(data_ref, HistoryDatasetAssociation) and not isinstance(data_ref, galaxy.tools.wrappers.DatasetFilenameWrapper): return options # cannot modify options - value = data_ref.metadata.get( self.metadata_key, None ) - return [ ( disp_name, optval, selected ) for disp_name, optval, selected in options if not compare_value( optval, value ) ] + value = data_ref.metadata.get(self.metadata_key, None) + return [(disp_name, optval, selected) for disp_name, optval, selected in options if not compare_value(optval, value)] -class SortByColumnFilter( Filter ): +class SortByColumnFilter(Filter): """ Sorts an options list by a column @@ -424,45 +432,47 @@ class SortByColumnFilter( Filter ): Required Attributes: column: column to sort by """ - def __init__( self, d_option, elem ): - Filter.__init__( self, d_option, elem ) - column = elem.get( "column", None ) + + def __init__(self, d_option, elem): + Filter.__init__(self, d_option, elem) + column = elem.get("column", None) assert column is not None, "Required 'column' attribute missing from filter" - self.column = d_option.column_spec_to_index( column ) + self.column = d_option.column_spec_to_index(column) - def filter_options( self, options, trans, other_values ): + def filter_options(self, options, trans, other_values): rval = [] for fields in options: - for j in range( 0, len( rval ) ): + for j in range(0, len(rval)): if fields[self.column] < rval[j][self.column]: - rval.insert( j, fields ) + rval.insert(j, fields) break else: - rval.append( fields ) + rval.append(fields) return rval -filter_types = dict( data_meta=DataMetaFilter, - param_value=ParamValueFilter, - static_value=StaticValueFilter, - unique_value=UniqueValueFilter, - multiple_splitter=MultipleSplitterFilter, - attribute_value_splitter=AttributeValueSplitterFilter, - add_value=AdditionalValueFilter, - remove_value=RemoveValueFilter, - sort_by=SortByColumnFilter ) +filter_types = dict(data_meta=DataMetaFilter, + param_value=ParamValueFilter, + static_value=StaticValueFilter, + unique_value=UniqueValueFilter, + multiple_splitter=MultipleSplitterFilter, + attribute_value_splitter=AttributeValueSplitterFilter, + add_value=AdditionalValueFilter, + remove_value=RemoveValueFilter, + sort_by=SortByColumnFilter) -class DynamicOptions( object ): +class DynamicOptions(object): """Handles dynamically generated SelectToolParameter options""" - def __init__( self, elem, tool_param ): - def load_from_parameter( from_parameter, transform_lines=None ): + + def __init__(self, elem, tool_param): + def load_from_parameter(from_parameter, transform_lines=None): obj = self.tool_param - for field in from_parameter.split( '.' ): - obj = getattr( obj, field ) + for field in from_parameter.split('.'): + obj = getattr(obj, field) if transform_lines: - obj = eval( transform_lines ) - return self.parse_file_fields( obj ) + obj = eval(transform_lines) + return self.parse_file_fields(obj) self.tool_param = tool_param self.columns = {} self.filters = [] @@ -476,24 +486,24 @@ def load_from_parameter( from_parameter, transform_lines=None ): self.converter_safe = True # Parse the tag - self.separator = elem.get( 'separator', '\t' ) - self.line_startswith = elem.get( 'startswith', None ) - data_file = elem.get( 'from_file', None ) + self.separator = elem.get('separator', '\t') + self.line_startswith = elem.get('startswith', None) + data_file = elem.get('from_file', None) self.index_file = None self.missing_index_file = None - dataset_file = elem.get( 'from_dataset', None ) - from_parameter = elem.get( 'from_parameter', None ) - tool_data_table_name = elem.get( 'from_data_table', None ) + dataset_file = elem.get('from_dataset', None) + from_parameter = elem.get('from_parameter', None) + tool_data_table_name = elem.get('from_data_table', None) # Options are defined from a data table loaded by the app self.tool_data_table = None self.missing_tool_data_table_name = None if tool_data_table_name: app = tool_param.tool.app if tool_data_table_name in app.tool_data_tables: - self.tool_data_table = app.tool_data_tables[ tool_data_table_name ] + self.tool_data_table = app.tool_data_tables[tool_data_table_name] # Column definitions are optional, but if provided override those from the table - if elem.find( "column" ) is not None: - self.parse_column_definitions( elem ) + if elem.find("column") is not None: + self.parse_column_definitions(elem) else: self.columns = self.tool_data_table.columns # Set self.missing_index_file if the index file to @@ -502,18 +512,18 @@ def load_from_parameter( from_parameter, transform_lines=None ): self.missing_index_file = self.tool_data_table.missing_index_file else: self.missing_tool_data_table_name = tool_data_table_name - log.warning( "Data table named '%s' is required by tool but not configured" % tool_data_table_name ) + log.warning("Data table named '%s' is required by tool but not configured" % tool_data_table_name) # Options are defined by parsing tabular text data from a data file # on disk, a dataset, or the value of another parameter elif data_file is not None or dataset_file is not None or from_parameter is not None: - self.parse_column_definitions( elem ) + self.parse_column_definitions(elem) if data_file is not None: data_file = data_file.strip() - if not os.path.isabs( data_file ): - full_path = os.path.join( self.tool_param.tool.app.config.tool_data_path, data_file ) - if os.path.exists( full_path ): + if not os.path.isabs(data_file): + full_path = os.path.join(self.tool_param.tool.app.config.tool_data_path, data_file) + if os.path.exists(full_path): self.index_file = data_file - self.file_fields = self.parse_file_fields( open( full_path ) ) + self.file_fields = self.parse_file_fields(open(full_path)) else: self.missing_index_file = data_file elif dataset_file is not None: @@ -521,27 +531,27 @@ def load_from_parameter( from_parameter, transform_lines=None ): self.has_dataset_dependencies = True self.converter_safe = False elif from_parameter is not None: - transform_lines = elem.get( 'transform_lines', None ) - self.file_fields = list( load_from_parameter( from_parameter, transform_lines ) ) + transform_lines = elem.get('transform_lines', None) + self.file_fields = list(load_from_parameter(from_parameter, transform_lines)) # Load filters - for filter_elem in elem.findall( 'filter' ): - self.filters.append( Filter.from_element( self, filter_elem ) ) + for filter_elem in elem.findall('filter'): + self.filters.append(Filter.from_element(self, filter_elem)) # Load Validators - for validator in elem.findall( 'validator' ): - self.validators.append( validation.Validator.from_element( self.tool_param, validator ) ) + for validator in elem.findall('validator'): + self.validators.append(validation.Validator.from_element(self.tool_param, validator)) if self.dataset_ref_name: tool_param.data_ref = self.dataset_ref_name - def parse_column_definitions( self, elem ): - for column_elem in elem.findall( 'column' ): - name = column_elem.get( 'name', None ) + def parse_column_definitions(self, elem): + for column_elem in elem.findall('column'): + name = column_elem.get('name', None) assert name is not None, "Required 'name' attribute missing from column def" - index = column_elem.get( 'index', None ) + index = column_elem.get('index', None) assert index is not None, "Required 'index' attribute missing from column def" - index = int( index ) + index = int(index) self.columns[name] = index if index > self.largest_index: self.largest_index = index @@ -549,105 +559,105 @@ def parse_column_definitions( self, elem ): if 'name' not in self.columns: self.columns['name'] = self.columns['value'] - def parse_file_fields( self, reader ): + def parse_file_fields(self, reader): rval = [] field_count = None for line in reader: - if line.startswith( '#' ) or ( self.line_startswith and not line.startswith( self.line_startswith ) ): + if line.startswith('#') or (self.line_startswith and not line.startswith(self.line_startswith)): continue - line = line.rstrip( "\n\r" ) + line = line.rstrip("\n\r") if line: - fields = line.split( self.separator ) - if self.largest_index < len( fields ): + fields = line.split(self.separator) + if self.largest_index < len(fields): if not field_count: - field_count = len( fields ) - elif field_count != len( fields ): + field_count = len(fields) + elif field_count != len(fields): try: name = reader.name except AttributeError: name = "a configuration file" # Perhaps this should be an error, but even a warning is useful. - log.warning( "Inconsistent number of fields (%i vs %i) in %s using separator %r, check line: %r" % - ( field_count, len( fields ), name, self.separator, line ) ) - rval.append( fields ) + log.warning("Inconsistent number of fields (%i vs %i) in %s using separator %r, check line: %r" % + (field_count, len(fields), name, self.separator, line)) + rval.append(fields) return rval - def get_dependency_names( self ): + def get_dependency_names(self): """ Return the names of parameters these options depend on -- both data and other param types. """ rval = [] if self.dataset_ref_name: - rval.append( self.dataset_ref_name ) + rval.append(self.dataset_ref_name) for filter in self.filters: depend = filter.get_dependency_name() if depend: - rval.append( depend ) + rval.append(depend) return rval - def get_fields( self, trans, other_values ): + def get_fields(self, trans, other_values): if self.dataset_ref_name: - dataset = other_values.get( self.dataset_ref_name, None ) - if not dataset or not hasattr( dataset, 'file_name' ): + dataset = other_values.get(self.dataset_ref_name, None) + if not dataset or not hasattr(dataset, 'file_name'): return [] # no valid dataset in history # Ensure parsing dynamic options does not consume more than a megabyte worth memory. path = dataset.file_name - if os.path.getsize( path ) < 1048576: - options = self.parse_file_fields( open( path ) ) + if os.path.getsize(path) < 1048576: + options = self.parse_file_fields(open(path)) else: # Pass just the first megabyte to parse_file_fields. - log.warning( "Attempting to load options from large file, reading just first megabyte" ) - contents = open( path, 'r' ).read( 1048576 ) - options = self.parse_file_fields( StringIO( contents ) ) + log.warning("Attempting to load options from large file, reading just first megabyte") + contents = open(path, 'r').read(1048576) + options = self.parse_file_fields(StringIO(contents)) elif self.tool_data_table: options = self.tool_data_table.get_fields() else: - options = list( self.file_fields ) + options = list(self.file_fields) for filter in self.filters: - options = filter.filter_options( options, trans, other_values ) + options = filter.filter_options(options, trans, other_values) return options - def get_fields_by_value( self, value, trans, other_values ): + def get_fields_by_value(self, value, trans, other_values): """ Return a list of fields with column 'value' matching provided value. """ rval = [] - val_index = self.columns[ 'value' ] - for fields in self.get_fields( trans, other_values ): - if fields[ val_index ] == value: - rval.append( fields ) + val_index = self.columns['value'] + for fields in self.get_fields(trans, other_values): + if fields[val_index] == value: + rval.append(fields) return rval - def get_field_by_name_for_value( self, field_name, value, trans, other_values ): + def get_field_by_name_for_value(self, field_name, value, trans, other_values): """ Get contents of field by name for specified value. """ rval = [] - if isinstance( field_name, int ): + if isinstance(field_name, int): field_index = field_name else: assert field_name in self.columns, "Requested '%s' column missing from column def" % field_name - field_index = self.columns[ field_name ] - if not isinstance( value, list ): + field_index = self.columns[field_name] + if not isinstance(value, list): value = [value] for val in value: - for fields in self.get_fields_by_value( val, trans, other_values ): - rval.append( fields[ field_index ] ) + for fields in self.get_fields_by_value(val, trans, other_values): + rval.append(fields[field_index]) return rval - def get_options( self, trans, other_values ): + def get_options(self, trans, other_values): rval = [] if self.file_fields is not None or self.tool_data_table is not None or self.dataset_ref_name is not None: - options = self.get_fields( trans, other_values ) + options = self.get_fields(trans, other_values) for fields in options: - rval.append( ( fields[self.columns['name']], fields[self.columns['value']], False ) ) + rval.append((fields[self.columns['name']], fields[self.columns['value']], False)) else: for filter in self.filters: - rval = filter.filter_options( rval, trans, other_values ) + rval = filter.filter_options(rval, trans, other_values) return rval - def column_spec_to_index( self, column_spec ): + def column_spec_to_index(self, column_spec): """ Convert a column specification (as read from the config file), to an index. A column specification can just be a number, a column name, or @@ -657,4 +667,4 @@ def column_spec_to_index( self, column_spec ): if column_spec in self.columns: return self.columns[column_spec] # Int? - return int( column_spec ) + return int(column_spec) diff --git a/lib/galaxy/tools/parameters/grouping.py b/lib/galaxy/tools/parameters/grouping.py index f83d66490500..1531bcef5980 100644 --- a/lib/galaxy/tools/parameters/grouping.py +++ b/lib/galaxy/tools/parameters/grouping.py @@ -11,6 +11,10 @@ ) from galaxy.datatypes import sniff +from galaxy.exceptions import ( + AdminRequiredException, + ConfigDoesNotAllowException, +) from galaxy.util import ( inflector, relpath, @@ -20,52 +24,52 @@ from galaxy.util.dictifiable import Dictifiable from galaxy.util.expressions import ExpressionContext -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class Group( object, Dictifiable ): +class Group(object, Dictifiable): - dict_collection_visible_keys = ( 'name', 'type' ) + dict_collection_visible_keys = ('name', 'type') - def __init__( self ): + def __init__(self): self.name = None @property - def visible( self ): + def visible(self): return True - def value_to_basic( self, value, app ): + def value_to_basic(self, value, app): """ Convert value to a (possibly nested) representation using only basic types (dict, list, tuple, string_types, int, long, float, bool, None) """ return value - def value_from_basic( self, value, app, ignore_errors=False ): + def value_from_basic(self, value, app, ignore_errors=False): """ Convert a basic representation as produced by `value_to_basic` back into the preferred value form. """ return value - def get_initial_value( self, trans, context ): + def get_initial_value(self, trans, context): """ Return the initial state/value for this group """ - raise TypeError( "Not implemented" ) + raise TypeError("Not implemented") - def to_dict( self, trans ): - group_dict = super( Group, self ).to_dict() + def to_dict(self, trans): + group_dict = super(Group, self).to_dict() return group_dict -class Repeat( Group ): +class Repeat(Group): - dict_collection_visible_keys = ( 'name', 'type', 'title', 'help', 'default', 'min', 'max' ) + dict_collection_visible_keys = ('name', 'type', 'title', 'help', 'default', 'min', 'max') type = "repeat" - def __init__( self ): - Group.__init__( self ) + def __init__(self): + Group.__init__(self) self.title = None self.inputs = None self.help = None @@ -74,13 +78,13 @@ def __init__( self ): self.max = None @property - def title_plural( self ): - return inflector.pluralize( self.title ) + def title_plural(self): + return inflector.pluralize(self.title) - def label( self ): + def label(self): return "Repeat (%s)" % self.title - def value_to_basic( self, value, app ): + def value_to_basic(self, value, app): rval = [] for d in value: rval_dict = {} @@ -88,18 +92,18 @@ def value_to_basic( self, value, app ): if '__index__' in d: rval_dict['__index__'] = d['__index__'] for input in self.inputs.values(): - rval_dict[ input.name ] = input.value_to_basic( d[input.name], app ) - rval.append( rval_dict ) + rval_dict[input.name] = input.value_to_basic(d[input.name], app) + rval.append(rval_dict) return rval - def value_from_basic( self, value, app, ignore_errors=False ): + def value_from_basic(self, value, app, ignore_errors=False): rval = [] try: - for i, d in enumerate( value ): + for i, d in enumerate(value): rval_dict = {} # If the special __index__ key is not set, create it (for backward # compatibility) - rval_dict['__index__'] = d.get( '__index__', i ) + rval_dict['__index__'] = d.get('__index__', i) # Restore child inputs for input in self.inputs.values(): if ignore_errors and input.name not in d: @@ -108,146 +112,146 @@ def value_from_basic( self, value, app, ignore_errors=False ): # conditional's values dictionary. pass else: - rval_dict[ input.name ] = input.value_from_basic( d[input.name], app, ignore_errors ) - rval.append( rval_dict ) + rval_dict[input.name] = input.value_from_basic(d[input.name], app, ignore_errors) + rval.append(rval_dict) except Exception as e: if not ignore_errors: raise e return rval - def get_initial_value( self, trans, context ): + def get_initial_value(self, trans, context): rval = [] - for i in range( self.default ): - rval_dict = { '__index__': i} + for i in range(self.default): + rval_dict = {'__index__': i} for input in self.inputs.values(): - rval_dict[ input.name ] = input.get_initial_value( trans, context ) - rval.append( rval_dict ) + rval_dict[input.name] = input.get_initial_value(trans, context) + rval.append(rval_dict) return rval - def to_dict( self, trans ): - repeat_dict = super( Repeat, self ).to_dict( trans ) + def to_dict(self, trans): + repeat_dict = super(Repeat, self).to_dict(trans) - def input_to_dict( input ): - return input.to_dict( trans ) + def input_to_dict(input): + return input.to_dict(trans) - repeat_dict[ "inputs" ] = list(map( input_to_dict, self.inputs.values() )) + repeat_dict["inputs"] = list(map(input_to_dict, self.inputs.values())) return repeat_dict -class Section( Group ): +class Section(Group): - dict_collection_visible_keys = ( 'name', 'type', 'title', 'help', 'expanded') + dict_collection_visible_keys = ('name', 'type', 'title', 'help', 'expanded') type = "section" - def __init__( self ): - Group.__init__( self ) + def __init__(self): + Group.__init__(self) self.title = None self.inputs = None self.help = None self.expanded = False @property - def title_plural( self ): - return inflector.pluralize( self.title ) + def title_plural(self): + return inflector.pluralize(self.title) - def label( self ): + def label(self): return "Section (%s)" % self.title - def value_to_basic( self, value, app ): + def value_to_basic(self, value, app): rval = {} for input in self.inputs.values(): - rval[ input.name ] = input.value_to_basic( value[input.name], app ) + rval[input.name] = input.value_to_basic(value[input.name], app) return rval - def value_from_basic( self, value, app, ignore_errors=False ): + def value_from_basic(self, value, app, ignore_errors=False): rval = {} try: for input in self.inputs.values(): if not ignore_errors or input.name in value: - rval[ input.name ] = input.value_from_basic( value[ input.name ], app, ignore_errors ) + rval[input.name] = input.value_from_basic(value[input.name], app, ignore_errors) except Exception as e: if not ignore_errors: raise e return rval - def get_initial_value( self, trans, context ): + def get_initial_value(self, trans, context): rval = {} - child_context = ExpressionContext( rval, context ) + child_context = ExpressionContext(rval, context) for child_input in self.inputs.values(): - rval[ child_input.name ] = child_input.get_initial_value( trans, child_context ) + rval[child_input.name] = child_input.get_initial_value(trans, child_context) return rval - def to_dict( self, trans ): - section_dict = super( Section, self ).to_dict( trans ) + def to_dict(self, trans): + section_dict = super(Section, self).to_dict(trans) - def input_to_dict( input ): - return input.to_dict( trans ) + def input_to_dict(input): + return input.to_dict(trans) - section_dict[ "inputs" ] = list(map( input_to_dict, self.inputs.values() )) + section_dict["inputs"] = list(map(input_to_dict, self.inputs.values())) return section_dict -class UploadDataset( Group ): +class UploadDataset(Group): type = "upload_dataset" - def __init__( self ): - Group.__init__( self ) + def __init__(self): + Group.__init__(self) self.title = None self.inputs = None self.file_type_name = 'file_type' self.default_file_type = 'txt' - self.file_type_to_ext = { 'auto': self.default_file_type } + self.file_type_to_ext = {'auto': self.default_file_type} self.metadata_ref = 'files_metadata' - def get_composite_dataset_name( self, context ): + def get_composite_dataset_name(self, context): # FIXME: HACK # Special case of using 'base_name' metadata for use as Dataset name needs to be done in a General Fashion, as defined within a particular Datatype. # We get two different types of contexts here, one straight from submitted parameters, the other after being parsed into tool inputs - dataset_name = context.get('files_metadata|base_name', None ) + dataset_name = context.get('files_metadata|base_name', None) if dataset_name is None: - dataset_name = context.get('files_metadata', {} ).get( 'base_name', None ) + dataset_name = context.get('files_metadata', {}).get('base_name', None) if dataset_name is None: - dataset_name = 'Uploaded Composite Dataset (%s)' % self.get_file_type( context ) + dataset_name = 'Uploaded Composite Dataset (%s)' % self.get_file_type(context) return dataset_name - def get_file_base_name( self, context ): + def get_file_base_name(self, context): fd = context.get('files_metadata|base_name', 'Galaxy_Composite_file') return fd - def get_file_type( self, context ): - return context.get( self.file_type_name, self.default_file_type ) + def get_file_type(self, context): + return context.get(self.file_type_name, self.default_file_type) - def get_datatype_ext( self, trans, context ): - ext = self.get_file_type( context ) + def get_datatype_ext(self, trans, context): + ext = self.get_file_type(context) if ext in self.file_type_to_ext: ext = self.file_type_to_ext[ext] # when using autodetect, we will use composite info from 'text', i.e. only the main file return ext - def get_datatype( self, trans, context ): - ext = self.get_datatype_ext( trans, context ) - return trans.app.datatypes_registry.get_datatype_by_extension( ext ) + def get_datatype(self, trans, context): + ext = self.get_datatype_ext(trans, context) + return trans.app.datatypes_registry.get_datatype_by_extension(ext) @property - def title_plural( self ): + def title_plural(self): return inflector.pluralize(self.title) - def group_title( self, context ): - return "%s (%s)" % ( self.title, context.get( self.file_type_name, self.default_file_type ) ) + def group_title(self, context): + return "%s (%s)" % (self.title, context.get(self.file_type_name, self.default_file_type)) - def title_by_index( self, trans, index, context ): - d_type = self.get_datatype( trans, context ) - for i, ( composite_name, composite_file ) in enumerate( d_type.writable_files.items() ): + def title_by_index(self, trans, index, context): + d_type = self.get_datatype(trans, context) + for i, (composite_name, composite_file) in enumerate(d_type.writable_files.items()): if i == index: rval = composite_name if composite_file.description: - rval = "%s (%s)" % ( rval, composite_file.description ) + rval = "%s (%s)" % (rval, composite_file.description) if composite_file.optional: rval = "%s [optional]" % rval return rval return None - def value_to_basic( self, value, app ): + def value_to_basic(self, value, app): rval = [] for d in value: rval_dict = {} @@ -255,119 +259,135 @@ def value_to_basic( self, value, app ): if '__index__' in d: rval_dict['__index__'] = d['__index__'] for input in self.inputs.values(): - rval_dict[ input.name ] = input.value_to_basic( d[input.name], app ) - rval.append( rval_dict ) + rval_dict[input.name] = input.value_to_basic(d[input.name], app) + rval.append(rval_dict) return rval - def value_from_basic( self, value, app, ignore_errors=False ): + def value_from_basic(self, value, app, ignore_errors=False): rval = [] - for i, d in enumerate( value ): + for i, d in enumerate(value): rval_dict = {} # If the special __index__ key is not set, create it (for backward # compatibility) - rval_dict['__index__'] = d.get( '__index__', i ) + rval_dict['__index__'] = d.get('__index__', i) # Restore child inputs for input in self.inputs.values(): if ignore_errors and input.name not in d: # this wasn't tested - rval_dict[ input.name ] = input.get_initial_value( None, d ) + rval_dict[input.name] = input.get_initial_value(None, d) else: - rval_dict[ input.name ] = input.value_from_basic( d[input.name], app, ignore_errors ) - rval.append( rval_dict ) + rval_dict[input.name] = input.value_from_basic(d[input.name], app, ignore_errors) + rval.append(rval_dict) return rval - def get_initial_value( self, trans, context ): - d_type = self.get_datatype( trans, context ) + def get_initial_value(self, trans, context): + d_type = self.get_datatype(trans, context) rval = [] - for i, ( composite_name, composite_file ) in enumerate( d_type.writable_files.items() ): + for i, (composite_name, composite_file) in enumerate(d_type.writable_files.items()): rval_dict = {} rval_dict['__index__'] = i # create __index__ for input in self.inputs.values(): - rval_dict[ input.name ] = input.get_initial_value( trans, context ) - rval.append( rval_dict ) + rval_dict[input.name] = input.get_initial_value(trans, context) + rval.append(rval_dict) return rval - def get_uploaded_datasets( self, trans, context, override_name=None, override_info=None ): - def get_data_file_filename( data_file, override_name=None, override_info=None, purge=True ): + def get_uploaded_datasets(self, trans, context, override_name=None, override_info=None): + def get_data_file_filename(data_file, override_name=None, override_info=None, purge=True): dataset_name = override_name - dataset_info = override_info - def get_file_name( file_name ): - file_name = file_name.split( '\\' )[-1] - file_name = file_name.split( '/' )[-1] + def get_file_name(file_name): + file_name = file_name.split('\\')[-1] + file_name = file_name.split('/')[-1] return file_name try: # Use the existing file if not dataset_name and 'filename' in data_file: - dataset_name = get_file_name( data_file['filename'] ) - if not dataset_info: - dataset_info = 'uploaded file' - return Bunch( type='file', path=data_file['local_filename'], name=dataset_name, purge_source=purge ) + dataset_name = get_file_name(data_file['filename']) + return Bunch(type='file', path=data_file['local_filename'], name=dataset_name, purge_source=purge) except: # The uploaded file should've been persisted by the upload tool action - return Bunch( type=None, path=None, name=None ) + return Bunch(type=None, path=None, name=None) - def get_url_paste_urls_or_filename( group_incoming, override_name=None, override_info=None ): - url_paste_file = group_incoming.get( 'url_paste', None ) + def get_url_paste_urls_or_filename(group_incoming, override_name=None, override_info=None): + url_paste_file = group_incoming.get('url_paste', None) if url_paste_file is not None: - url_paste = open( url_paste_file, 'r' ).read( 1024 ) - if url_paste.lstrip().lower().startswith( 'http://' ) or url_paste.lstrip().lower().startswith( 'ftp://' ) or url_paste.lstrip().lower().startswith( 'https://' ): - url_paste = url_paste.replace( '\r', '' ).split( '\n' ) + url_paste = open(url_paste_file, 'r').read(1024) + + def start_of_url(content): + start_of_url_paste = content.lstrip()[0:8].lower() + looks_like_url = False + for url_prefix in ["http://", "https://", "ftp://", "file://"]: + if start_of_url_paste.startswith(url_prefix): + looks_like_url = True + break + + return looks_like_url + + if start_of_url(url_paste): + url_paste = url_paste.replace('\r', '').split('\n') for line in url_paste: line = line.strip() if line: - if not line.lower().startswith( 'http://' ) and not line.lower().startswith( 'ftp://' ) and not line.lower().startswith( 'https://' ): + if not start_of_url(line): continue # non-url line, ignore - dataset_name = override_name - if not dataset_name: + + if "file://" in line: + if not trans.user_is_admin(): + raise AdminRequiredException() + elif not trans.app.config.allow_path_paste: + raise ConfigDoesNotAllowException() + upload_path = line[len("file://"):] + dataset_name = os.path.basename(upload_path) + else: dataset_name = line - dataset_info = override_info - if not dataset_info: - dataset_info = 'uploaded url' - yield Bunch( type='url', path=line, name=dataset_name ) + + if override_name: + dataset_name = override_name + yield Bunch(type='url', path=line, name=dataset_name) else: - dataset_name = dataset_info = 'Pasted Entry' # we need to differentiate between various url pastes here + dataset_name = 'Pasted Entry' # we need to differentiate between various url pastes here if override_name: dataset_name = override_name - if override_info: - dataset_info = override_info - yield Bunch( type='file', path=url_paste_file, name=dataset_name ) + yield Bunch(type='file', path=url_paste_file, name=dataset_name) - def get_one_filename( context ): + def get_one_filename(context): data_file = context['file_data'] url_paste = context['url_paste'] ftp_files = context['ftp_files'] - name = context.get( 'NAME', None ) - info = context.get( 'INFO', None ) - uuid = context.get( 'uuid', None ) or None # Turn '' to None + name = context.get('NAME', None) + info = context.get('INFO', None) + uuid = context.get('uuid', None) or None # Turn '' to None warnings = [] to_posix_lines = False - if context.get( 'to_posix_lines', None ) not in [ "None", None, False ]: + if context.get('to_posix_lines', None) not in ["None", None, False]: to_posix_lines = True + auto_decompress = False + if context.get('auto_decompress', None) not in ["None", None, False]: + auto_decompress = True space_to_tab = False - if context.get( 'space_to_tab', None ) not in [ "None", None, False ]: + if context.get('space_to_tab', None) not in ["None", None, False]: space_to_tab = True - file_bunch = get_data_file_filename( data_file, override_name=name, override_info=info ) + file_bunch = get_data_file_filename(data_file, override_name=name, override_info=info) if file_bunch.path: if url_paste is not None and url_paste.strip(): - warnings.append( "All file contents specified in the paste box were ignored." ) + warnings.append("All file contents specified in the paste box were ignored.") if ftp_files: - warnings.append( "All FTP uploaded file selections were ignored." ) + warnings.append("All FTP uploaded file selections were ignored.") elif url_paste is not None and url_paste.strip(): # we need to use url_paste - for file_bunch in get_url_paste_urls_or_filename( context, override_name=name, override_info=info ): + for file_bunch in get_url_paste_urls_or_filename(context, override_name=name, override_info=info): if file_bunch.path: break if file_bunch.path and ftp_files is not None: - warnings.append( "All FTP uploaded file selections were ignored." ) + warnings.append("All FTP uploaded file selections were ignored.") elif ftp_files is not None and trans.user is not None: # look for files uploaded via FTP user_ftp_dir = trans.user_ftp_dir - for ( dirpath, dirnames, filenames ) in os.walk( user_ftp_dir ): + for (dirpath, dirnames, filenames) in os.walk(user_ftp_dir): for filename in filenames: for ftp_filename in ftp_files: if ftp_filename == filename: - path = relpath( os.path.join( dirpath, filename ), user_ftp_dir ) - if not os.path.islink( os.path.join( dirpath, filename ) ): - ftp_data_file = { 'local_filename' : os.path.abspath( os.path.join( user_ftp_dir, path ) ), - 'filename' : os.path.basename( path ) } + path = relpath(os.path.join(dirpath, filename), user_ftp_dir) + if not os.path.islink(os.path.join(dirpath, filename)): + ftp_data_file = {'local_filename' : os.path.abspath(os.path.join(user_ftp_dir, path)), + 'filename' : os.path.basename(path)} purge = getattr(trans.app.config, 'ftp_upload_purge', True) file_bunch = get_data_file_filename( ftp_data_file, @@ -382,35 +402,41 @@ def get_one_filename( context ): if file_bunch.path: break file_bunch.to_posix_lines = to_posix_lines + file_bunch.auto_decompress = auto_decompress file_bunch.space_to_tab = space_to_tab file_bunch.uuid = uuid return file_bunch, warnings - def get_filenames( context ): + def get_filenames(context): rval = [] data_file = context['file_data'] ftp_files = context['ftp_files'] - uuid = context.get( 'uuid', None ) or None # Turn '' to None - name = context.get( 'NAME', None ) - info = context.get( 'INFO', None ) + uuid = context.get('uuid', None) or None # Turn '' to None + name = context.get('NAME', None) + info = context.get('INFO', None) to_posix_lines = False - if context.get( 'to_posix_lines', None ) not in [ "None", None, False ]: + if context.get('to_posix_lines', None) not in ["None", None, False]: to_posix_lines = True + auto_decompress = False + if context.get('auto_decompress', None) not in ["None", None, False]: + auto_decompress = True space_to_tab = False - if context.get( 'space_to_tab', None ) not in [ "None", None, False ]: + if context.get('space_to_tab', None) not in ["None", None, False]: space_to_tab = True - file_bunch = get_data_file_filename( data_file, override_name=name, override_info=info ) + file_bunch = get_data_file_filename(data_file, override_name=name, override_info=info) file_bunch.uuid = uuid if file_bunch.path: file_bunch.to_posix_lines = to_posix_lines + file_bunch.auto_decompress = auto_decompress file_bunch.space_to_tab = space_to_tab - rval.append( file_bunch ) - for file_bunch in get_url_paste_urls_or_filename( context, override_name=name, override_info=info ): + rval.append(file_bunch) + for file_bunch in get_url_paste_urls_or_filename(context, override_name=name, override_info=info): if file_bunch.path: file_bunch.uuid = uuid file_bunch.to_posix_lines = to_posix_lines + file_bunch.auto_decompress = auto_decompress file_bunch.space_to_tab = space_to_tab - rval.append( file_bunch ) + rval.append(file_bunch) # look for files uploaded via FTP valid_files = [] if ftp_files is not None: @@ -418,18 +444,18 @@ def get_filenames( context ): # This allows for comparison when the filesystem uses a different encoding than the browser. ftp_files = [unicodedata.normalize('NFC', f) for f in ftp_files if isinstance(f, text_type)] if trans.user is None: - log.warning( 'Anonymous user passed values in ftp_files: %s' % ftp_files ) + log.warning('Anonymous user passed values in ftp_files: %s' % ftp_files) ftp_files = [] # TODO: warning to the user (could happen if session has become invalid) else: user_ftp_dir = trans.user_ftp_dir - for ( dirpath, dirnames, filenames ) in os.walk( user_ftp_dir ): + for (dirpath, dirnames, filenames) in os.walk(user_ftp_dir): for filename in filenames: - path = relpath( os.path.join( dirpath, filename ), user_ftp_dir ) - if not os.path.islink( os.path.join( dirpath, filename ) ): + path = relpath(os.path.join(dirpath, filename), user_ftp_dir) + if not os.path.islink(os.path.join(dirpath, filename)): # Normalize filesystem paths if isinstance(path, text_type): - valid_files.append(unicodedata.normalize('NFC', path )) + valid_files.append(unicodedata.normalize('NFC', path)) else: valid_files.append(path) @@ -437,27 +463,29 @@ def get_filenames( context ): ftp_files = [] for ftp_file in ftp_files: if ftp_file not in valid_files: - log.warning( 'User passed an invalid file path in ftp_files: %s' % ftp_file ) + log.warning('User passed an invalid file path in ftp_files: %s' % ftp_file) continue # TODO: warning to the user (could happen if file is already imported) - ftp_data_file = { 'local_filename' : os.path.abspath( os.path.join( user_ftp_dir, ftp_file ) ), - 'filename' : os.path.basename( ftp_file ) } + ftp_data_file = {'local_filename' : os.path.abspath(os.path.join(user_ftp_dir, ftp_file)), + 'filename' : os.path.basename(ftp_file)} purge = getattr(trans.app.config, 'ftp_upload_purge', True) - file_bunch = get_data_file_filename( ftp_data_file, override_name=name, override_info=info, purge=purge ) + file_bunch = get_data_file_filename(ftp_data_file, override_name=name, override_info=info, purge=purge) if file_bunch.path: file_bunch.to_posix_lines = to_posix_lines + file_bunch.auto_decompress = auto_decompress file_bunch.space_to_tab = space_to_tab - rval.append( file_bunch ) + rval.append(file_bunch) return rval - file_type = self.get_file_type( context ) - d_type = self.get_datatype( trans, context ) - dbkey = context.get( 'dbkey', None ) + file_type = self.get_file_type(context) + d_type = self.get_datatype(trans, context) + dbkey = context.get('dbkey', None) + tag_using_filenames = context.get('tag_using_filenames', False) writable_files = d_type.writable_files writable_files_offset = 0 - groups_incoming = [ None for _ in writable_files ] - for group_incoming in context.get( self.name, [] ): - i = int( group_incoming['__index__'] ) - groups_incoming[ i ] = group_incoming + groups_incoming = [None for _ in writable_files] + for group_incoming in context.get(self.name, []): + i = int(group_incoming['__index__']) + groups_incoming[i] = group_incoming if d_type.composite_type is not None: # handle uploading of composite datatypes # Only one Dataset can be created @@ -470,149 +498,153 @@ def get_filenames( context ): dataset.metadata = {} dataset.composite_files = {} dataset.uuid = None + dataset.tag_using_filenames = None # load metadata - files_metadata = context.get( self.metadata_ref, {} ) - metadata_name_substition_default_dict = dict( ( composite_file.substitute_name_with_metadata, d_type.metadata_spec[ composite_file.substitute_name_with_metadata ].default ) for composite_file in d_type.composite_files.values() if composite_file.substitute_name_with_metadata ) + files_metadata = context.get(self.metadata_ref, {}) + metadata_name_substition_default_dict = dict((composite_file.substitute_name_with_metadata, d_type.metadata_spec[composite_file.substitute_name_with_metadata].default) for composite_file in d_type.composite_files.values() if composite_file.substitute_name_with_metadata) for meta_name, meta_spec in d_type.metadata_spec.items(): if meta_spec.set_in_upload: if meta_name in files_metadata: - meta_value = files_metadata[ meta_name ] + meta_value = files_metadata[meta_name] if meta_name in metadata_name_substition_default_dict: - meta_value = sanitize_for_filename( meta_value, default=metadata_name_substition_default_dict[ meta_name ] ) - dataset.metadata[ meta_name ] = meta_value - dataset.precreated_name = dataset.name = self.get_composite_dataset_name( context ) + meta_value = sanitize_for_filename(meta_value, default=metadata_name_substition_default_dict[meta_name]) + dataset.metadata[meta_name] = meta_value + dataset.precreated_name = dataset.name = self.get_composite_dataset_name(context) if dataset.datatype.composite_type == 'auto_primary_file': # replace sniff here with just creating an empty file - temp_name, is_multi_byte = sniff.stream_to_file( StringIO( d_type.generate_primary_file( dataset ) ), prefix='upload_auto_primary_file' ) + temp_name, is_multi_byte = sniff.stream_to_file(StringIO(d_type.generate_primary_file(dataset)), prefix='upload_auto_primary_file') dataset.primary_file = temp_name dataset.to_posix_lines = True + dataset.auto_decompress = True dataset.space_to_tab = False else: - file_bunch, warnings = get_one_filename( groups_incoming[ 0 ] ) + file_bunch, warnings = get_one_filename(groups_incoming[0]) writable_files_offset = 1 dataset.primary_file = file_bunch.path dataset.to_posix_lines = file_bunch.to_posix_lines + dataset.auto_decompress = file_bunch.auto_decompress dataset.space_to_tab = file_bunch.space_to_tab - dataset.warnings.extend( warnings ) + dataset.warnings.extend(warnings) if dataset.primary_file is None: # remove this before finish, this should create an empty dataset - raise Exception( 'No primary dataset file was available for composite upload' ) - keys = [ value.name for value in writable_files.values() ] - for i, group_incoming in enumerate( groups_incoming[ writable_files_offset : ] ): - key = keys[ i + writable_files_offset ] - if group_incoming is None and not writable_files[ list(writable_files.keys())[ keys.index( key ) ] ].optional: - dataset.warnings.append( "A required composite file (%s) was not specified." % ( key ) ) - dataset.composite_files[ key ] = None + raise Exception('No primary dataset file was available for composite upload') + keys = [value.name for value in writable_files.values()] + for i, group_incoming in enumerate(groups_incoming[writable_files_offset :]): + key = keys[i + writable_files_offset] + if group_incoming is None and not writable_files[list(writable_files.keys())[keys.index(key)]].optional: + dataset.warnings.append("A required composite file (%s) was not specified." % (key)) + dataset.composite_files[key] = None else: - file_bunch, warnings = get_one_filename( group_incoming ) - dataset.warnings.extend( warnings ) + file_bunch, warnings = get_one_filename(group_incoming) + dataset.warnings.extend(warnings) if file_bunch.path: - dataset.composite_files[ key ] = file_bunch.__dict__ + dataset.composite_files[key] = file_bunch.__dict__ else: - dataset.composite_files[ key ] = None - if not writable_files[ list(writable_files.keys())[ keys.index( key ) ] ].optional: - dataset.warnings.append( "A required composite file (%s) was not specified." % ( key ) ) - return [ dataset ] + dataset.composite_files[key] = None + if not writable_files[list(writable_files.keys())[keys.index(key)]].optional: + dataset.warnings.append("A required composite file (%s) was not specified." % (key)) + return [dataset] else: - datasets = get_filenames( context[ self.name ][0] ) + datasets = get_filenames(context[self.name][0]) rval = [] for dataset in datasets: dataset.file_type = file_type dataset.datatype = d_type - dataset.ext = self.get_datatype_ext( trans, context ) + dataset.ext = self.get_datatype_ext(trans, context) dataset.dbkey = dbkey - rval.append( dataset ) + dataset.tag_using_filenames = tag_using_filenames + rval.append(dataset) return rval -class Conditional( Group ): +class Conditional(Group): type = "conditional" - def __init__( self ): - Group.__init__( self ) + def __init__(self): + Group.__init__(self) self.test_param = None self.cases = [] self.value_ref = None self.value_ref_in_group = True # When our test_param is not part of the conditional Group, this is False @property - def label( self ): + def label(self): return "Conditional (%s)" % self.name - def get_current_case( self, value ): + def get_current_case(self, value): # Convert value to user representation - str_value = self.test_param.to_param_dict_string( value ) + str_value = self.test_param.to_param_dict_string(value) # Find the matching case - for index, case in enumerate( self.cases ): + for index, case in enumerate(self.cases): if str_value == case.value: return index - raise ValueError( "No case matched value:", self.name, str_value ) + raise ValueError("No case matched value:", self.name, str_value) - def value_to_basic( self, value, app ): + def value_to_basic(self, value, app): rval = dict() - rval[ self.test_param.name ] = self.test_param.value_to_basic( value[ self.test_param.name ], app ) - current_case = rval[ '__current_case__' ] = self.get_current_case( value[ self.test_param.name ] ) - for input in self.cases[ current_case ].inputs.values(): + rval[self.test_param.name] = self.test_param.value_to_basic(value[self.test_param.name], app) + current_case = rval['__current_case__'] = self.get_current_case(value[self.test_param.name]) + for input in self.cases[current_case].inputs.values(): if input.name in value: # parameter might be absent in unverified workflow - rval[ input.name ] = input.value_to_basic( value[ input.name ], app ) + rval[input.name] = input.value_to_basic(value[input.name], app) return rval - def value_from_basic( self, value, app, ignore_errors=False ): + def value_from_basic(self, value, app, ignore_errors=False): rval = dict() try: - rval[ self.test_param.name ] = self.test_param.value_from_basic( value.get( self.test_param.name ), app, ignore_errors ) - current_case = rval[ '__current_case__' ] = self.get_current_case( rval[ self.test_param.name ] ) + rval[self.test_param.name] = self.test_param.value_from_basic(value.get(self.test_param.name), app, ignore_errors) + current_case = rval['__current_case__'] = self.get_current_case(rval[self.test_param.name]) # Inputs associated with current case - for input in self.cases[ current_case ].inputs.values(): + for input in self.cases[current_case].inputs.values(): # If we do not have a value, and are ignoring errors, we simply # do nothing. There will be no value for the parameter in the # conditional's values dictionary. if not ignore_errors or input.name in value: - rval[ input.name ] = input.value_from_basic( value[ input.name ], app, ignore_errors ) + rval[input.name] = input.value_from_basic(value[input.name], app, ignore_errors) except Exception as e: if not ignore_errors: raise e return rval - def get_initial_value( self, trans, context ): + def get_initial_value(self, trans, context): # State for a conditional is a plain dictionary. rval = {} # Get the default value for the 'test element' and use it # to determine the current case - test_value = self.test_param.get_initial_value( trans, context ) - current_case = self.get_current_case( test_value ) + test_value = self.test_param.get_initial_value(trans, context) + current_case = self.get_current_case(test_value) # Store the current case in a special value rval['__current_case__'] = current_case # Store the value of the test element - rval[ self.test_param.name ] = test_value + rval[self.test_param.name] = test_value # Fill in state for selected case - child_context = ExpressionContext( rval, context ) + child_context = ExpressionContext(rval, context) for child_input in self.cases[current_case].inputs.values(): - rval[ child_input.name ] = child_input.get_initial_value( trans, child_context ) + rval[child_input.name] = child_input.get_initial_value(trans, child_context) return rval - def to_dict( self, trans ): - cond_dict = super( Conditional, self ).to_dict( trans ) + def to_dict(self, trans): + cond_dict = super(Conditional, self).to_dict(trans) - def nested_to_dict( input ): - return input.to_dict( trans ) + def nested_to_dict(input): + return input.to_dict(trans) - cond_dict[ "cases" ] = list(map( nested_to_dict, self.cases )) - cond_dict[ "test_param" ] = nested_to_dict( self.test_param ) + cond_dict["cases"] = list(map(nested_to_dict, self.cases)) + cond_dict["test_param"] = nested_to_dict(self.test_param) return cond_dict -class ConditionalWhen( object, Dictifiable ): - dict_collection_visible_keys = ( 'value', ) +class ConditionalWhen(object, Dictifiable): + dict_collection_visible_keys = ('value', ) - def __init__( self ): + def __init__(self): self.value = None self.inputs = None - def to_dict( self, trans ): - when_dict = super( ConditionalWhen, self ).to_dict() + def to_dict(self, trans): + when_dict = super(ConditionalWhen, self).to_dict() - def input_to_dict( input ): - return input.to_dict( trans ) + def input_to_dict(input): + return input.to_dict(trans) - when_dict[ "inputs" ] = list(map( input_to_dict, self.inputs.values() )) + when_dict["inputs"] = list(map(input_to_dict, self.inputs.values())) return when_dict diff --git a/lib/galaxy/tools/parameters/history_query.py b/lib/galaxy/tools/parameters/history_query.py index b0057e32ddb0..35665fd2028e 100644 --- a/lib/galaxy/tools/parameters/history_query.py +++ b/lib/galaxy/tools/parameters/history_query.py @@ -1,17 +1,17 @@ import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class HistoryQuery( object ): +class HistoryQuery(object): """ An object for describing the collections to pull out of a history, used by DataCollectionToolParameter. """ - def __init__( self, **kwargs ): - self.collection_type_descriptions = kwargs.get( "collection_type_descriptions", None ) + def __init__(self, **kwargs): + self.collection_type_descriptions = kwargs.get("collection_type_descriptions", None) @staticmethod - def from_parameter( param, collection_type_descriptions ): + def from_parameter(param, collection_type_descriptions): """ Take in a tool parameter element. """ collection_types = param.collection_types @@ -21,24 +21,24 @@ def from_parameter( param, collection_type_descriptions ): # (until we expose it to the user) will default to providing tool as much # data as possible. So a list:list:paired mapped to a tool that takes # list,paired,list:paired - will map over list:paired and create a flat list. - collection_type_descriptions = sorted(collection_type_descriptions, lambda t: t.dimension, reverse=True ) + collection_type_descriptions = sorted(collection_type_descriptions, lambda t: t.dimension, reverse=True) else: collection_type_descriptions = None - kwargs = dict( collection_type_descriptions=collection_type_descriptions ) - return HistoryQuery( **kwargs ) + kwargs = dict(collection_type_descriptions=collection_type_descriptions) + return HistoryQuery(**kwargs) - def direct_match( self, hdca ): + def direct_match(self, hdca): collection_type_descriptions = self.collection_type_descriptions if collection_type_descriptions is not None: for collection_type_description in collection_type_descriptions: - if collection_type_description.can_match_type( hdca.collection.collection_type ): + if collection_type_description.can_match_type(hdca.collection.collection_type): return True return False return True - def can_map_over( self, hdca ): + def can_map_over(self, hdca): collection_type_descriptions = self.collection_type_descriptions if collection_type_descriptions is None: return False @@ -46,6 +46,6 @@ def can_map_over( self, hdca ): hdca_collection_type = hdca.collection.collection_type for collection_type_description in collection_type_descriptions: # See note about the way this is sorted above. - if collection_type_description.is_subcollection_of_type( hdca_collection_type ): + if collection_type_description.is_subcollection_of_type(hdca_collection_type): return collection_type_description return False diff --git a/lib/galaxy/tools/parameters/input_translation.py b/lib/galaxy/tools/parameters/input_translation.py index c282d8c9a3ed..22f9953e7fa8 100644 --- a/lib/galaxy/tools/parameters/input_translation.py +++ b/lib/galaxy/tools/parameters/input_translation.py @@ -5,10 +5,10 @@ from galaxy.util.bunch import Bunch -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class ToolInputTranslator( object ): +class ToolInputTranslator(object): """ Handles Tool input translation. This is used for data source tools @@ -49,61 +49,61 @@ class ToolInputTranslator( object ): True """ @classmethod - def from_element( cls, elem ): + def from_element(cls, elem): """Loads the proper filter by the type attribute of elem""" rval = ToolInputTranslator() - for req_param in elem.findall( "request_param" ): + for req_param in elem.findall("request_param"): # req_param tags must look like # trans_list = [] - remote_name = req_param.get( "remote_name" ) - galaxy_name = req_param.get( "galaxy_name" ) - missing = req_param.get( "missing" ) + remote_name = req_param.get("remote_name") + galaxy_name = req_param.get("galaxy_name") + missing = req_param.get("missing") value_trans = {} append_param = None - value_trans_elem = req_param.find( 'value_translation' ) + value_trans_elem = req_param.find('value_translation') if value_trans_elem is not None: - for value_elem in value_trans_elem.findall( 'value' ): - remote_value = value_elem.get( "remote_value" ) - galaxy_value = value_elem.get( "galaxy_value" ) - if None not in [ remote_value, galaxy_value ]: - value_trans[ remote_value ] = galaxy_value + for value_elem in value_trans_elem.findall('value'): + remote_value = value_elem.get("remote_value") + galaxy_value = value_elem.get("galaxy_value") + if None not in [remote_value, galaxy_value]: + value_trans[remote_value] = galaxy_value - append_param_elem = req_param.find( "append_param" ) + append_param_elem = req_param.find("append_param") if append_param_elem is not None: - separator = append_param_elem.get( 'separator', ',' ) - first_separator = append_param_elem.get( 'first_separator', None ) - join_str = append_param_elem.get( 'join', '=' ) + separator = append_param_elem.get('separator', ',') + first_separator = append_param_elem.get('first_separator', None) + join_str = append_param_elem.get('join', '=') append_dict = {} - for value_elem in append_param_elem.findall( 'value' ): - value_name = value_elem.get( 'name' ) - value_missing = value_elem.get( 'missing' ) - if None not in [ value_name, value_missing ]: - append_dict[ value_name ] = value_missing - append_param = Bunch( separator=separator, first_separator=first_separator, join_str=join_str, append_dict=append_dict ) + for value_elem in append_param_elem.findall('value'): + value_name = value_elem.get('name') + value_missing = value_elem.get('missing') + if None not in [value_name, value_missing]: + append_dict[value_name] = value_missing + append_param = Bunch(separator=separator, first_separator=first_separator, join_str=join_str, append_dict=append_dict) - rval.param_trans_dict[ remote_name ] = Bunch( galaxy_name=galaxy_name, missing=missing, value_trans=value_trans, append_param=append_param ) + rval.param_trans_dict[remote_name] = Bunch(galaxy_name=galaxy_name, missing=missing, value_trans=value_trans, append_param=append_param) return rval - def __init__( self ): + def __init__(self): self.param_trans_dict = {} - def translate( self, params ): + def translate(self, params): """ update params in-place """ for remote_name, translator in self.param_trans_dict.items(): galaxy_name = translator.galaxy_name # NB: if a param by name galaxy_name is provided, it is always thrown away unless galaxy_name == remote_name - value = params.get( remote_name, translator.missing ) # get value from input params, or use default value specified in tool config + value = params.get(remote_name, translator.missing) # get value from input params, or use default value specified in tool config if translator.value_trans and value in translator.value_trans: - value = translator.value_trans[ value ] + value = translator.value_trans[value] if translator.append_param: for param_name, missing_value in translator.append_param.append_dict.items(): - param_value = params.get( param_name, missing_value ) + param_value = params.get(param_name, missing_value) if translator.append_param.first_separator and translator.append_param.first_separator not in value: sep = translator.append_param.first_separator else: sep = translator.append_param.separator - value += '%s%s%s%s' % ( sep, param_name, translator.append_param.join_str, param_value ) - params.update( { galaxy_name: value } ) + value += '%s%s%s%s' % (sep, param_name, translator.append_param.join_str, param_value) + params.update({galaxy_name: value}) diff --git a/lib/galaxy/tools/parameters/meta.py b/lib/galaxy/tools/parameters/meta.py index 7243a2f335a7..2130fcb43b81 100644 --- a/lib/galaxy/tools/parameters/meta.py +++ b/lib/galaxy/tools/parameters/meta.py @@ -9,10 +9,10 @@ ) from galaxy.util import permutations -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -def expand_workflow_inputs( inputs ): +def expand_workflow_inputs(inputs): """ Expands incoming encoded multiple payloads, into the set of all individual payload combinations >>> params, param_keys = expand_workflow_inputs( {'1': {'input': {'batch': True, 'product': True, 'values': [{'hid': '1'}, {'hid': '2'}] }}} ) @@ -36,39 +36,39 @@ def expand_workflow_inputs( inputs ): product = [] linked_keys = [] product_keys = [] - for step_id, step in sorted( inputs.items() ): - for key, value in sorted( step.items() ): - if isinstance( value, dict ) and 'batch' in value and value[ 'batch' ] is True and 'values' in value and isinstance( value[ 'values' ], list ): - nval = len( value[ 'values' ] ) - if 'product' in value and value[ 'product' ] is True: - product.append( value[ 'values' ] ) - product_keys.append( ( step_id, key ) ) + for step_id, step in sorted(inputs.items()): + for key, value in sorted(step.items()): + if isinstance(value, dict) and 'batch' in value and value['batch'] is True and 'values' in value and isinstance(value['values'], list): + nval = len(value['values']) + if 'product' in value and value['product'] is True: + product.append(value['values']) + product_keys.append((step_id, key)) else: if linked_n is None: linked_n = nval elif linked_n != nval or nval is 0: - raise exceptions.RequestParameterInvalidException( 'Failed to match linked batch selections. Please select equal number of data files.' ) - linked.append( value[ 'values' ] ) - linked_keys.append( ( step_id, key ) ) + raise exceptions.RequestParameterInvalidException('Failed to match linked batch selections. Please select equal number of data files.') + linked.append(value['values']) + linked_keys.append((step_id, key)) params = [] params_keys = [] - linked = linked or [ [ None ] ] - product = product or [ [ None ] ] - linked_keys = linked_keys or [ ( None, None ) ] - product_keys = product_keys or [ ( None, None ) ] - for linked_values, product_values in itertools.product( zip( *linked ), itertools.product( *product ) ): - new_params = copy.deepcopy( inputs ) + linked = linked or [[None]] + product = product or [[None]] + linked_keys = linked_keys or [(None, None)] + product_keys = product_keys or [(None, None)] + for linked_values, product_values in itertools.product(zip(*linked), itertools.product(*product)): + new_params = copy.deepcopy(inputs) new_keys = [] - for ( step_id, key ), value in list(zip( linked_keys, linked_values )) + list(zip( product_keys, product_values )): + for (step_id, key), value in list(zip(linked_keys, linked_values)) + list(zip(product_keys, product_values)): if step_id is not None: - new_params[ step_id ][ key ] = value - new_keys.append( str( value[ 'hid' ] ) ) - params_keys.append( new_keys ) - params.append( new_params ) + new_params[step_id][key] = value + new_keys.append(str(value['hid'])) + params_keys.append(new_keys) + params.append(new_params) return params, params_keys -def expand_meta_parameters( trans, tool, incoming ): +def expand_meta_parameters(trans, tool, incoming): """ Take in a dictionary of raw incoming parameters and expand to a list of expanded incoming parameters (one set of parameters per tool @@ -82,23 +82,23 @@ def expand_meta_parameters( trans, tool, incoming ): for key in to_remove: incoming.pop(key) - def classifier( input_key ): - value = incoming[ input_key ] - if isinstance( value, dict ) and 'values' in value: + def classifier(input_key): + value = incoming[input_key] + if isinstance(value, dict) and 'values' in value: # Explicit meta wrapper for inputs... - is_batch = value.get( 'batch', False ) - is_linked = value.get( 'linked', True ) + is_batch = value.get('batch', False) + is_linked = value.get('linked', True) if is_batch and is_linked: classification = permutations.input_classification.MATCHED elif is_batch: classification = permutations.input_classification.MULTIPLIED else: classification = permutations.input_classification.SINGLE - if __collection_multirun_parameter( value ): - collection_value = value[ 'values' ][ 0 ] - values = __expand_collection_parameter( trans, input_key, collection_value, collections_to_match, linked=is_linked ) + if __collection_multirun_parameter(value): + collection_value = value['values'][0] + values = __expand_collection_parameter(trans, input_key, collection_value, collections_to_match, linked=is_linked) else: - values = value[ 'values' ] + values = value['values'] else: classification = permutations.input_classification.SINGLE values = value @@ -111,53 +111,53 @@ def classifier( input_key ): # by expand_mult_inputs. incoming_template = incoming.copy() - expanded_incomings = permutations.expand_multi_inputs( incoming_template, classifier ) + expanded_incomings = permutations.expand_multi_inputs(incoming_template, classifier) if collections_to_match.has_collections(): - collection_info = trans.app.dataset_collections_service.match_collections( collections_to_match ) + collection_info = trans.app.dataset_collections_service.match_collections(collections_to_match) else: collection_info = None return expanded_incomings, collection_info -def __expand_collection_parameter( trans, input_key, incoming_val, collections_to_match, linked=False ): +def __expand_collection_parameter(trans, input_key, incoming_val, collections_to_match, linked=False): # If subcollectin multirun of data_collection param - value will # be "hdca_id|subcollection_type" else it will just be hdca_id if "|" in incoming_val: - encoded_hdc_id, subcollection_type = incoming_val.split( "|", 1 ) + encoded_hdc_id, subcollection_type = incoming_val.split("|", 1) else: try: - src = incoming_val[ "src" ] + src = incoming_val["src"] if src != "hdca": - raise exceptions.ToolMetaParameterException( "Invalid dataset collection source type %s" % src ) - encoded_hdc_id = incoming_val[ "id" ] - subcollection_type = incoming_val.get( 'map_over_type', None ) + raise exceptions.ToolMetaParameterException("Invalid dataset collection source type %s" % src) + encoded_hdc_id = incoming_val["id"] + subcollection_type = incoming_val.get('map_over_type', None) except TypeError: encoded_hdc_id = incoming_val subcollection_type = None - hdc_id = trans.app.security.decode_id( encoded_hdc_id ) - hdc = trans.sa_session.query( model.HistoryDatasetCollectionAssociation ).get( hdc_id ) - collections_to_match.add( input_key, hdc, subcollection_type=subcollection_type, linked=linked ) + hdc_id = trans.app.security.decode_id(encoded_hdc_id) + hdc = trans.sa_session.query(model.HistoryDatasetCollectionAssociation).get(hdc_id) + collections_to_match.add(input_key, hdc, subcollection_type=subcollection_type, linked=linked) if subcollection_type is not None: from galaxy.dataset_collections import subcollections - subcollection_elements = subcollections.split_dataset_collection_instance( hdc, subcollection_type ) + subcollection_elements = subcollections.split_dataset_collection_instance(hdc, subcollection_type) return subcollection_elements else: hdas = [] for element in hdc.collection.dataset_elements: hda = element.dataset_instance hda.element_identifier = element.element_identifier - hdas.append( hda ) + hdas.append(hda) return hdas -def __collection_multirun_parameter( value ): - is_batch = value.get( 'batch', False ) +def __collection_multirun_parameter(value): + is_batch = value.get('batch', False) if not is_batch: return False - batch_values = util.listify( value[ 'values' ] ) - if len( batch_values ) == 1: - batch_over = batch_values[ 0 ] - if isinstance( batch_over, dict ) and ('src' in batch_over) and (batch_over[ 'src' ] == 'hdca'): + batch_values = util.listify(value['values']) + if len(batch_values) == 1: + batch_over = batch_values[0] + if isinstance(batch_over, dict) and ('src' in batch_over) and (batch_over['src'] == 'hdca'): return True return False diff --git a/lib/galaxy/tools/parameters/output_collect.py b/lib/galaxy/tools/parameters/output_collect.py index 91a4f00401d5..cefa90132e9c 100644 --- a/lib/galaxy/tools/parameters/output_collect.py +++ b/lib/galaxy/tools/parameters/output_collect.py @@ -7,7 +7,8 @@ import os import re -from galaxy import jobs +from collections import namedtuple + from galaxy import util from galaxy.tools.parser.output_collection_def import ( DEFAULT_DATASET_COLLECTOR_DESCRIPTION, @@ -20,12 +21,102 @@ DATASET_ID_TOKEN = "DATASET_ID" -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) + + +class NullToolProvidedMetadata(object): + + def get_new_datasets(self, output_name): + return [] + + def get_new_dataset_meta_by_basename(self, output_name, basename): + return {} + + +class LegacyToolProvidedMetadata(object): + + def __init__(self, job_wrapper, meta_file): + self.job_wrapper = job_wrapper + self.tool_provided_job_metadata = [] + + with open(meta_file, 'r') as f: + for line in f: + try: + line = json.loads(line) + assert 'type' in line + except Exception: + log.exception('(%s) Got JSON data from tool, but data is improperly formatted or no "type" key in data' % job_wrapper.job_id) + log.debug('Offending data was: %s' % line) + continue + # Set the dataset id if it's a dataset entry and isn't set. + # This isn't insecure. We loop the job's output datasets in + # the finish method, so if a tool writes out metadata for a + # dataset id that it doesn't own, it'll just be ignored. + if line['type'] == 'dataset' and 'dataset_id' not in line: + try: + line['dataset_id'] = job_wrapper.get_output_file_id(line['dataset']) + except KeyError: + log.warning('(%s) Tool provided job dataset-specific metadata without specifying a dataset' % job_wrapper.job_id) + continue + self.tool_provided_job_metadata.append(line) + + def get_meta_by_dataset_id(self, dataset_id): + for meta in self.tool_provided_job_metadata: + if meta['type'] == 'dataset' and meta['dataset_id'] == dataset_id: + return meta + + def get_new_dataset_meta_by_basename(self, output_name, basename): + for meta in self.tool_provided_job_metadata: + if meta['type'] == 'new_primary_dataset' and meta['filename'] == basename: + return meta + + def get_new_datasets(self, output_name): + log.warning("Called get_new_datasets with legacy tool metadata provider - that is unimplemented.") + return [] + + +class ToolProvidedMetadata(object): + + def __init__(self, job_wrapper, meta_file): + self.job_wrapper = job_wrapper + with open(meta_file, 'r') as f: + self.tool_provided_job_metadata = json.load(f) + + def get_meta_by_name(self, name): + return self.tool_provided_job_metadata.get(name, {}) + + def get_new_dataset_meta_by_basename(self, output_name, basename): + datasets = self.tool_provided_job_metadata.get(output_name, {}).get("datasets", []) + for meta in datasets: + if meta['filename'] == basename: + return meta + + def get_new_datasets(self, output_name): + datasets = self.tool_provided_job_metadata.get(output_name, {}).get("datasets", []) + if not datasets: + elements = self.tool_provided_job_metadata.get(output_name, {}).get("elements", []) + if elements: + datasets = self._elements_to_datasets(elements) + return datasets + + def _elements_to_datasets(self, elements, level=0): + for element in elements: + extra_kwds = {"identifier_%d" % level: element["name"]} + if "elements" in element: + for inner_element in self._elements_to_datasets(element["elements"], level=level + 1): + dataset = extra_kwds.copy() + dataset.update(inner_element) + yield dataset + else: + dataset = extra_kwds + extra_kwds.update(element) + yield extra_kwds def collect_dynamic_collections( tool, output_collections, + tool_provided_metadata, job_working_directory, inp_data={}, job=None, @@ -34,6 +125,7 @@ def collect_dynamic_collections( collections_service = tool.app.dataset_collections_service job_context = JobContext( tool, + tool_provided_metadata, job, job_working_directory, inp_data, @@ -43,7 +135,7 @@ def collect_dynamic_collections( for name, has_collection in output_collections.items(): if name not in tool.output_collections: continue - output_collection_def = tool.output_collections[ name ] + output_collection_def = tool.output_collections[name] if not output_collection_def.dynamic_structure: continue @@ -69,48 +161,50 @@ def collect_dynamic_collections( collection.handle_population_failed("Problem building datasets for collection.") -class JobContext( object ): +class JobContext(object): - def __init__( self, tool, job, job_working_directory, inp_data, input_dbkey ): + def __init__(self, tool, tool_provided_metadata, job, job_working_directory, inp_data, input_dbkey): self.inp_data = inp_data self.input_dbkey = input_dbkey self.app = tool.app self.sa_session = tool.sa_session self.job = job self.job_working_directory = job_working_directory + self.tool_provided_metadata = tool_provided_metadata @property - def permissions( self ): + def permissions(self): inp_data = self.inp_data - existing_datasets = [ inp for inp in inp_data.values() if inp ] + existing_datasets = [inp for inp in inp_data.values() if inp] if existing_datasets: - permissions = self.app.security_agent.guess_derived_permissions_for_datasets( existing_datasets ) + permissions = self.app.security_agent.guess_derived_permissions_for_datasets(existing_datasets) else: # No valid inputs, we will use history defaults - permissions = self.app.security_agent.history_get_default_permissions( self.job.history ) + permissions = self.app.security_agent.history_get_default_permissions(self.job.history) return permissions - def find_files( self, collection, dataset_collectors ): + def find_files(self, output_name, collection, dataset_collectors): filenames = odict.odict() - for path, extra_file_collector in walk_over_extra_files( dataset_collectors, self.job_working_directory, collection ): - filenames[ path ] = extra_file_collector + for discovered_file in discover_files(output_name, self.tool_provided_metadata, dataset_collectors, self.job_working_directory, collection): + filenames[discovered_file.path] = discovered_file return filenames - def populate_collection_elements( self, collection, root_collection_builder, output_collection_def ): + def populate_collection_elements(self, collection, root_collection_builder, output_collection_def): # TODO: allow configurable sorting. # # # # dataset_collectors = map(dataset_collector, output_collection_def.dataset_collector_descriptions) - filenames = self.find_files( collection, dataset_collectors ) + output_name = output_collection_def.name + filenames = self.find_files(output_name, collection, dataset_collectors) element_datasets = [] - for filename, extra_file_collector in filenames.items(): + for filename, discovered_file in filenames.items(): create_dataset_timer = ExecutionTimer() - fields_match = extra_file_collector.match( collection, os.path.basename( filename ) ) + fields_match = discovered_file.match if not fields_match: - raise Exception( "Problem parsing metadata fields for file %s" % filename ) + raise Exception("Problem parsing metadata fields for file %s" % filename) element_identifiers = fields_match.element_identifiers designation = fields_match.designation visible = fields_match.visible @@ -159,15 +253,15 @@ def populate_collection_elements( self, collection, root_collection_builder, out current_builder = root_collection_builder for element_identifier in element_identifiers[:-1]: current_builder = current_builder.get_level(element_identifier) - current_builder.add_dataset( element_identifiers[-1], dataset ) + current_builder.add_dataset(element_identifiers[-1], dataset) # Associate new dataset with job if job: element_identifier_str = ":".join(element_identifiers) - # Below was changed from '__new_primary_file_%s|%s__' % ( name, designation ) - assoc = app.model.JobToOutputDatasetAssociation( '__new_primary_file_%s|%s__' % ( name, element_identifier_str ), dataset ) + # Below was changed from '__new_primary_file_%s|%s__' % (name, designation ) + assoc = app.model.JobToOutputDatasetAssociation('__new_primary_file_%s|%s__' % (name, element_identifier_str), dataset) assoc.job = self.job - sa_session.add( assoc ) + sa_session.add(assoc) dataset.raw_set_dataset_state('ok') @@ -191,7 +285,7 @@ def create_dataset( # Copy metadata from one of the inputs if requested. metadata_source = None if metadata_source_name: - metadata_source = self.inp_data[ metadata_source_name ] + metadata_source = self.inp_data[metadata_source_name] sa_session.flush() # Move data from temp location to dataset location @@ -202,7 +296,7 @@ def create_dataset( primary_data.name = name if metadata_source: - primary_data.init_meta( copy_from=metadata_source ) + primary_data.init_meta(copy_from=metadata_source) else: primary_data.init_meta() @@ -212,57 +306,61 @@ def create_dataset( return primary_data -def collect_primary_datasets( tool, output, job_working_directory, input_ext, input_dbkey="?" ): +def collect_primary_datasets(tool, output, tool_provided_metadata, job_working_directory, input_ext, input_dbkey="?"): app = tool.app sa_session = tool.sa_session new_primary_datasets = {} try: - galaxy_json_path = os.path.join( job_working_directory, "working", jobs.TOOL_PROVIDED_JOB_METADATA_FILE ) + galaxy_json_path = os.path.join(job_working_directory, "working", tool.provide_metadata_file) # LEGACY: Remove in 17.XX - if not os.path.exists( galaxy_json_path ): + if not os.path.exists(galaxy_json_path): # Maybe this is a legacy job, use the job working directory instead - galaxy_json_path = os.path.join( job_working_directory, jobs.TOOL_PROVIDED_JOB_METADATA_FILE ) - json_file = open( galaxy_json_path, 'r' ) + galaxy_json_path = os.path.join(job_working_directory, tool.provide_metadata_file) + json_file = open(galaxy_json_path, 'r') for line in json_file: - line = json.loads( line ) - if line.get( 'type' ) == 'new_primary_dataset': - new_primary_datasets[ os.path.split( line.get( 'filename' ) )[-1] ] = line + line = json.loads(line) + if line.get('type') == 'new_primary_dataset': + new_primary_datasets[os.path.split(line.get('filename'))[-1]] = line except Exception: # This should not be considered an error or warning condition, this file is optional pass # Loop through output file names, looking for generated primary - # datasets in form of: - # 'primary_associatedWithDatasetID_designation_visibility_extension(_DBKEY)' + # datasets in form specified by discover dataset patterns or in tool provided metadata. primary_output_assigned = False new_outdata_name = None primary_datasets = {} - for output_index, ( name, outdata ) in enumerate( output.items() ): - dataset_collectors = map(dataset_collector, tool.outputs[ name ].dataset_collector_descriptions) if name in tool.outputs else [ DEFAULT_DATASET_COLLECTOR ] + for output_index, (name, outdata) in enumerate(output.items()): + dataset_collectors = map(dataset_collector, tool.outputs[name].dataset_collector_descriptions) if name in tool.outputs else [DEFAULT_DATASET_COLLECTOR] filenames = odict.odict() if 'new_file_path' in app.config.collect_outputs_from: if DEFAULT_DATASET_COLLECTOR in dataset_collectors: # 'new_file_path' collection should be considered deprecated, # only use old-style matching (glob instead of regex and only # using default collector - if enabled). - for filename in glob.glob(os.path.join(app.config.new_file_path, "primary_%i_*" % outdata.id) ): - filenames[ filename ] = DEFAULT_DATASET_COLLECTOR + for filename in glob.glob(os.path.join(app.config.new_file_path, "primary_%i_*" % outdata.id)): + filenames[filename] = DiscoveredFile( + filename, + DEFAULT_DATASET_COLLECTOR, + DEFAULT_DATASET_COLLECTOR.match(outdata, os.path.basename(filename)) + ) if 'job_working_directory' in app.config.collect_outputs_from: - for path, extra_file_collector in walk_over_extra_files( dataset_collectors, job_working_directory, outdata ): - filenames[ path ] = extra_file_collector - for filename_index, ( filename, extra_file_collector ) in enumerate( filenames.items() ): - fields_match = extra_file_collector.match( outdata, os.path.basename( filename ) ) + for discovered_file in discover_files(name, tool_provided_metadata, dataset_collectors, job_working_directory, outdata): + filenames[discovered_file.path] = discovered_file + for filename_index, (filename, discovered_file) in enumerate(filenames.items()): + extra_file_collector = discovered_file.collector + fields_match = discovered_file.match if not fields_match: # Before I guess pop() would just have thrown an IndexError - raise Exception( "Problem parsing metadata fields for file %s" % filename ) + raise Exception("Problem parsing metadata fields for file %s" % filename) designation = fields_match.designation if filename_index == 0 and extra_file_collector.assign_primary_output and output_index == 0: - new_outdata_name = fields_match.name or "%s (%s)" % ( outdata.name, designation ) + new_outdata_name = fields_match.name or "%s (%s)" % (outdata.name, designation) # Move data from temp location to dataset location - app.object_store.update_from_file( outdata.dataset, file_name=filename, create=True ) + app.object_store.update_from_file(outdata.dataset, file_name=filename, create=True) primary_output_assigned = True continue if name not in primary_datasets: - primary_datasets[ name ] = odict.odict() + primary_datasets[name] = odict.odict() visible = fields_match.visible ext = fields_match.ext if ext == "input": @@ -272,16 +370,16 @@ def collect_primary_datasets( tool, output, job_working_directory, input_ext, in dbkey = input_dbkey # Create new primary dataset primary_data = _new_hda(app, sa_session, ext, designation, visible, dbkey) - app.security_agent.copy_dataset_permissions( outdata.dataset, primary_data.dataset ) + app.security_agent.copy_dataset_permissions(outdata.dataset, primary_data.dataset) sa_session.flush() # Move data from temp location to dataset location app.object_store.update_from_file(primary_data.dataset, file_name=filename, create=True) primary_data.set_size() # If match specified a name use otherwise generate one from # designation. - primary_data.name = fields_match.name or "%s (%s)" % ( outdata.name, designation ) + primary_data.name = fields_match.name or "%s (%s)" % (outdata.name, designation) primary_data.info = outdata.info - primary_data.init_meta( copy_from=outdata ) + primary_data.init_meta(copy_from=outdata) primary_data.dbkey = dbkey # Associate new dataset with job job = None @@ -289,44 +387,46 @@ def collect_primary_datasets( tool, output, job_working_directory, input_ext, in job = assoc.job break if job: - assoc = app.model.JobToOutputDatasetAssociation( '__new_primary_file_%s|%s__' % ( name, designation ), primary_data ) + assoc = app.model.JobToOutputDatasetAssociation('__new_primary_file_%s|%s__' % (name, designation), primary_data) assoc.job = job - sa_session.add( assoc ) + sa_session.add(assoc) sa_session.flush() primary_data.state = outdata.state + # TODO: should be able to disambiguate files in different directories... + new_primary_filename = os.path.split(filename)[-1] + new_primary_datasets_attributes = tool_provided_metadata.get_new_dataset_meta_by_basename(name, new_primary_filename) # add tool/metadata provided information - new_primary_datasets_attributes = new_primary_datasets.get( os.path.split( filename )[-1], {} ) if new_primary_datasets_attributes: - dataset_att_by_name = dict( ext='extension' ) - for att_set in [ 'name', 'info', 'ext', 'dbkey' ]: - dataset_att_name = dataset_att_by_name.get( att_set, att_set ) - setattr( primary_data, dataset_att_name, new_primary_datasets_attributes.get( att_set, getattr( primary_data, dataset_att_name ) ) ) - extra_files_path = new_primary_datasets_attributes.get( 'extra_files', None ) + dataset_att_by_name = dict(ext='extension') + for att_set in ['name', 'info', 'ext', 'dbkey']: + dataset_att_name = dataset_att_by_name.get(att_set, att_set) + setattr(primary_data, dataset_att_name, new_primary_datasets_attributes.get(att_set, getattr(primary_data, dataset_att_name))) + extra_files_path = new_primary_datasets_attributes.get('extra_files', None) if extra_files_path: - extra_files_path_joined = os.path.join( job_working_directory, extra_files_path ) - for root, dirs, files in os.walk( extra_files_path_joined ): - extra_dir = os.path.join( primary_data.extra_files_path, root.replace( extra_files_path_joined, '', 1 ).lstrip( os.path.sep ) ) + extra_files_path_joined = os.path.join(job_working_directory, extra_files_path) + for root, dirs, files in os.walk(extra_files_path_joined): + extra_dir = os.path.join(primary_data.extra_files_path, root.replace(extra_files_path_joined, '', 1).lstrip(os.path.sep)) for f in files: app.object_store.update_from_file( primary_data.dataset, extra_dir=extra_dir, alt_name=f, - file_name=os.path.join( root, f ), + file_name=os.path.join(root, f), create=True, dir_only=True, preserve_symlinks=True ) - metadata_dict = new_primary_datasets_attributes.get( 'metadata', None ) + metadata_dict = new_primary_datasets_attributes.get('metadata', None) if metadata_dict: if "dbkey" in new_primary_datasets_attributes: metadata_dict["dbkey"] = new_primary_datasets_attributes["dbkey"] - primary_data.metadata.from_JSON_dict( json_dict=metadata_dict ) + primary_data.metadata.from_JSON_dict(json_dict=metadata_dict) else: primary_data.set_meta() primary_data.set_peek() - sa_session.add( primary_data ) + sa_session.add(primary_data) sa_session.flush() - outdata.history.add_dataset( primary_data ) + outdata.history.add_dataset(primary_data) # Add dataset to return dict primary_datasets[name][designation] = primary_data # Need to update all associated output hdas, i.e. history was @@ -335,53 +435,93 @@ def collect_primary_datasets( tool, output, job_working_directory, input_ext, in if outdata == dataset: continue new_data = primary_data.copy() - dataset.history.add_dataset( new_data ) - sa_session.add( new_data ) + dataset.history.add_dataset(new_data) + sa_session.add(new_data) sa_session.flush() if primary_output_assigned: outdata.name = new_outdata_name outdata.init_meta() outdata.set_meta() outdata.set_peek() - sa_session.add( outdata ) + sa_session.add(outdata) sa_session.flush() return primary_datasets -def walk_over_extra_files( extra_file_collectors, job_working_directory, matchable ): +DiscoveredFile = namedtuple('DiscoveredFile', ['path', 'collector', 'match']) + + +def discover_files(output_name, tool_provided_metadata, extra_file_collectors, job_working_directory, matchable): + if extra_file_collectors and extra_file_collectors[0].discover_via == "tool_provided_metadata": + # just load entries from tool provided metadata... + assert len(extra_file_collectors) == 1 + extra_file_collector = extra_file_collectors[0] + target_directory = discover_target_directory(extra_file_collector, job_working_directory) + for dataset in tool_provided_metadata.get_new_datasets(output_name): + filename = dataset["filename"] + path = os.path.join(target_directory, filename) + yield DiscoveredFile(path, extra_file_collector, JsonCollectedDatasetMatch(dataset, extra_file_collector, filename, path=path)) + else: + for (match, collector) in walk_over_extra_files(extra_file_collectors, job_working_directory, matchable): + yield DiscoveredFile(match.path, collector, match) + + +def discover_target_directory(extra_file_collector, job_working_directory): + directory = job_working_directory + if extra_file_collector.directory: + directory = os.path.join(directory, extra_file_collector.directory) + if not util.in_directory(directory, job_working_directory): + raise Exception("Problem with tool configuration, attempting to pull in datasets from outside working directory.") + return directory + + +def walk_over_extra_files(extra_file_collectors, job_working_directory, matchable): + for extra_file_collector in extra_file_collectors: + assert extra_file_collector.discover_via == "pattern" matches = [] - directory = job_working_directory - if extra_file_collector.directory: - directory = os.path.join( directory, extra_file_collector.directory ) - if not util.in_directory( directory, job_working_directory ): - raise Exception( "Problem with tool configuration, attempting to pull in datasets from outside working directory." ) - if not os.path.isdir( directory ): + directory = discover_target_directory(extra_file_collector, job_working_directory) + if not os.path.isdir(directory): continue - for filename in os.listdir( directory ): - path = os.path.join( directory, filename ) - if not os.path.isfile( path ): + for filename in os.listdir(directory): + path = os.path.join(directory, filename) + if not os.path.isfile(path): continue - match = extra_file_collector.match( matchable, filename, path=path ) + match = extra_file_collector.match(matchable, filename, path=path) if match: matches.append(match) for match in extra_file_collector.sort(matches): - yield match.path, extra_file_collector + yield match, extra_file_collector -def dataset_collector( dataset_collection_description ): +def dataset_collector(dataset_collection_description): if dataset_collection_description is DEFAULT_DATASET_COLLECTOR_DESCRIPTION: # Use 'is' and 'in' operators, so lets ensure this is # treated like a singleton. return DEFAULT_DATASET_COLLECTOR else: - return DatasetCollector( dataset_collection_description ) + if dataset_collection_description.discover_via == "pattern": + return DatasetCollector(dataset_collection_description) + else: + return ToolMetadataDatasetCollector(dataset_collection_description) + + +class ToolMetadataDatasetCollector(object): + + def __init__(self, dataset_collection_description): + self.discover_via = dataset_collection_description.discover_via + self.default_dbkey = dataset_collection_description.default_dbkey + self.default_ext = dataset_collection_description.default_ext + self.default_visible = dataset_collection_description.default_visible + self.directory = dataset_collection_description.directory + self.assign_primary_output = dataset_collection_description.assign_primary_output -class DatasetCollector( object ): +class DatasetCollector(object): - def __init__( self, dataset_collection_description ): + def __init__(self, dataset_collection_description): + self.discover_via = dataset_collection_description.discover_via # dataset_collection_description is an abstract description # built from the tool parsing module - see galaxy.tools.parser.output_colleciton_def self.sort_key = dataset_collection_description.sort_key @@ -394,21 +534,21 @@ def __init__( self, dataset_collection_description ): self.directory = dataset_collection_description.directory self.assign_primary_output = dataset_collection_description.assign_primary_output - def pattern_for_dataset( self, dataset_instance=None ): + def _pattern_for_dataset(self, dataset_instance=None): token_replacement = r'\d+' if dataset_instance: - token_replacement = str( dataset_instance.id ) - return self.pattern.replace( DATASET_ID_TOKEN, token_replacement ) + token_replacement = str(dataset_instance.id) + return self.pattern.replace(DATASET_ID_TOKEN, token_replacement) - def match( self, dataset_instance, filename, path=None ): - pattern = self.pattern_for_dataset( dataset_instance ) - re_match = re.match( pattern, filename ) + def match(self, dataset_instance, filename, path=None): + pattern = self._pattern_for_dataset(dataset_instance) + re_match = re.match(pattern, filename) match_object = None if re_match: - match_object = CollectedDatasetMatch( re_match, self, filename, path=path ) + match_object = RegexCollectedDatasetMatch(re_match, self, filename, path=path) return match_object - def sort( self, matches ): + def sort(self, matches): reverse = self.sort_reverse sort_key = self.sort_key sort_comp = self.sort_comp @@ -425,42 +565,40 @@ def _compose(f, g): return lambda x: f(g(x)) -class CollectedDatasetMatch( object ): +class JsonCollectedDatasetMatch(object): - def __init__( self, re_match, collector, filename, path=None ): - self.re_match = re_match + def __init__(self, as_dict, collector, filename, path=None): + self.as_dict = as_dict self.collector = collector self.filename = filename self.path = path @property - def designation( self ): - re_match = self.re_match - # If collecting nested collection, grap identifier_0, + def designation(self): + # If collecting nested collection, grab identifier_0, # identifier_1, etc... and join on : to build designation. element_identifiers = self.raw_element_identifiers if element_identifiers: return ":".join(element_identifiers) - elif "designation" in re_match.groupdict(): - return re_match.group( "designation" ) - elif "name" in re_match.groupdict(): - return re_match.group( "name" ) + elif "designation" in self.as_dict: + return self.as_dict.get("designation") + elif "name" in self.as_dict: + return self.as_dict.get("name") else: return None @property - def element_identifiers( self ): + def element_identifiers(self): return self.raw_element_identifiers or [self.designation] @property - def raw_element_identifiers( self ): - re_match = self.re_match + def raw_element_identifiers(self): identifiers = [] i = 0 while True: key = "identifier_%d" % i - if key in re_match.groupdict(): - identifiers.append(re_match.group(key)) + if key in self.as_dict: + identifiers.append(self.as_dict.get(key)) else: break i += 1 @@ -468,37 +606,35 @@ def raw_element_identifiers( self ): return identifiers @property - def name( self ): + def name(self): """ Return name or None if not defined by the discovery pattern. """ - re_match = self.re_match - name = None - if "name" in re_match.groupdict(): - name = re_match.group( "name" ) - return name + return self.as_dict.get("name") @property - def dbkey( self ): - try: - return self.re_match.group( "dbkey" ) - except IndexError: - return self.collector.default_dbkey + def dbkey(self): + return self.as_dict.get("dbkey", self.collector.default_dbkey) @property - def ext( self ): - try: - return self.re_match.group( "ext" ) - except IndexError: - return self.collector.default_ext + def ext(self): + return self.as_dict.get("ext", self.collector.default_ext) @property - def visible( self ): + def visible(self): try: - return self.re_match.group( "visible" ).lower() == "visible" - except IndexError: + return self.as_dict["visible"].lower() == "visible" + except KeyError: return self.collector.default_visible +class RegexCollectedDatasetMatch(JsonCollectedDatasetMatch): + + def __init__(self, re_match, collector, filename, path=None): + super(RegexCollectedDatasetMatch, self).__init__( + re_match.groupdict(), collector, filename, path=path + ) + + UNSET = object() @@ -514,16 +650,16 @@ def _new_hda( """Return a new unflushed HDA with dataset and permissions setup. """ # Create new primary dataset - primary_data = app.model.HistoryDatasetAssociation( extension=ext, - designation=designation, - visible=visible, - dbkey=dbkey, - create_dataset=True, - flush=False, - sa_session=sa_session ) + primary_data = app.model.HistoryDatasetAssociation(extension=ext, + designation=designation, + visible=visible, + dbkey=dbkey, + create_dataset=True, + flush=False, + sa_session=sa_session) if permissions is not UNSET: - app.security_agent.set_all_dataset_permissions( primary_data.dataset, permissions, new=True, flush=False ) - sa_session.add( primary_data ) + app.security_agent.set_all_dataset_permissions(primary_data.dataset, permissions, new=True, flush=False) + sa_session.add(primary_data) return primary_data diff --git a/lib/galaxy/tools/parameters/sanitize.py b/lib/galaxy/tools/parameters/sanitize.py index f5c1be0b008c..c6967993f18c 100644 --- a/lib/galaxy/tools/parameters/sanitize.py +++ b/lib/galaxy/tools/parameters/sanitize.py @@ -8,10 +8,10 @@ import galaxy.util -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class ToolParameterSanitizer( object ): +class ToolParameterSanitizer(object): """ Handles tool parameter specific sanitizing. @@ -44,117 +44,117 @@ class ToolParameterSanitizer( object ): True """ - VALID_PRESET = { 'default': ( string.letters + string.digits + " -=_.()/+*^,:?!" ), 'none': '' } - MAPPING_PRESET = { 'default': galaxy.util.mapped_chars, 'none': {} } + VALID_PRESET = {'default': (string.letters + string.digits + " -=_.()/+*^,:?!"), 'none': ''} + MAPPING_PRESET = {'default': galaxy.util.mapped_chars, 'none': {}} DEFAULT_INVALID_CHAR = 'X' # class methods @classmethod - def from_element( cls, elem ): + def from_element(cls, elem): """Loads the proper filter by the type attribute of elem""" # TODO: Add ability to generically specify a method to use for sanitizing input via specification in tool XML rval = ToolParameterSanitizer() - rval._invalid_char = elem.get( 'invalid_char', cls.DEFAULT_INVALID_CHAR ) - rval.sanitize = galaxy.util.string_as_bool( elem.get( 'sanitize', 'True' ) ) - for valid_elem in elem.findall( 'valid' ): - rval._valid_chars = rval.get_valid_by_name( valid_elem.get( 'initial', 'default' ) ) + rval._invalid_char = elem.get('invalid_char', cls.DEFAULT_INVALID_CHAR) + rval.sanitize = galaxy.util.string_as_bool(elem.get('sanitize', 'True')) + for valid_elem in elem.findall('valid'): + rval._valid_chars = rval.get_valid_by_name(valid_elem.get('initial', 'default')) for action_elem in valid_elem: - preset = rval.get_valid_by_name( action_elem.get( 'preset', 'none' ) ) - valid_value = [ val for val in action_elem.get( 'value', [] ) ] + preset = rval.get_valid_by_name(action_elem.get('preset', 'none')) + valid_value = [val for val in action_elem.get('value', [])] if action_elem.tag.lower() == 'add': for val in preset + valid_value: if val not in rval._valid_chars: - rval._valid_chars.append( val ) + rval._valid_chars.append(val) elif action_elem.tag.lower() == 'remove': for val in preset + valid_value: while val in rval._valid_chars: - rval._valid_chars.remove( val ) + rval._valid_chars.remove(val) else: - log.debug( 'Invalid action tag in valid: %s' % action_elem.tag ) - for mapping_elem in elem.findall( 'mapping' ): - rval._mapped_chars = rval.get_mapping_by_name( mapping_elem.get( 'initial', 'default' ) ) + log.debug('Invalid action tag in valid: %s' % action_elem.tag) + for mapping_elem in elem.findall('mapping'): + rval._mapped_chars = rval.get_mapping_by_name(mapping_elem.get('initial', 'default')) for action_elem in mapping_elem: - map_source = action_elem.get( 'source', None ) - map_target = action_elem.get( 'target', None ) - preset = rval.get_mapping_by_name( action_elem.get( 'preset', 'none' ) ) + map_source = action_elem.get('source', None) + map_target = action_elem.get('target', None) + preset = rval.get_mapping_by_name(action_elem.get('preset', 'none')) if action_elem.tag.lower() == 'add': - rval._mapped_chars.update( preset ) - if None not in [ map_source, map_target ]: - rval._mapped_chars[ map_source ] = map_target + rval._mapped_chars.update(preset) + if None not in [map_source, map_target]: + rval._mapped_chars[map_source] = map_target elif action_elem.tag.lower() == 'remove': for map_key in preset.keys(): if map_key in rval._mapped_chars: - del rval._mapped_chars[ map_key ] + del rval._mapped_chars[map_key] if map_source is not None and map_key in rval._mapped_chars: - del rval._mapped_chars[ map_key ] + del rval._mapped_chars[map_key] else: - log.debug( 'Invalid action tag in mapping: %s' % action_elem.tag ) + log.debug('Invalid action tag in mapping: %s' % action_elem.tag) return rval @classmethod - def get_valid_by_name( cls, name ): + def get_valid_by_name(cls, name): rval = [] - for split_name in name.split( ',' ): + for split_name in name.split(','): split_name = split_name.strip() value = [] - if split_name.startswith( 'string.' ): + if split_name.startswith('string.'): try: - value = eval( split_name ) + value = eval(split_name) except NameError as e: - log.debug( 'Invalid string preset specified: %s' % e ) + log.debug('Invalid string preset specified: %s' % e) elif split_name in cls.VALID_PRESET: - value = cls.VALID_PRESET[ split_name ] + value = cls.VALID_PRESET[split_name] else: - log.debug( 'Invalid preset name specified: %s' % split_name ) - rval.extend( [ val for val in value if val not in rval ] ) + log.debug('Invalid preset name specified: %s' % split_name) + rval.extend([val for val in value if val not in rval]) return rval @classmethod - def get_mapping_by_name( cls, name ): + def get_mapping_by_name(cls, name): rval = {} - for split_name in name.split( ',' ): + for split_name in name.split(','): split_name = split_name.strip() if split_name in cls.MAPPING_PRESET: - rval.update( cls.MAPPING_PRESET[ split_name ] ) + rval.update(cls.MAPPING_PRESET[split_name]) else: - log.debug( 'Invalid preset name specified: %s' % split_name ) + log.debug('Invalid preset name specified: %s' % split_name) return rval # end class methods - def __init__( self ): + def __init__(self): self._valid_chars = [] # List of valid characters self._mapped_chars = {} # Replace a char with a any number of characters self._invalid_char = self.DEFAULT_INVALID_CHAR # Replace invalid characters with this character self.sanitize = True # Simply pass back the passed in value - def restore_text( self, text ): + def restore_text(self, text): """Restores sanitized text""" if self.sanitize: for key, value in self._mapped_chars.items(): - text = text.replace( value, key ) + text = text.replace(value, key) return text - def sanitize_text( self, text ): + def sanitize_text(self, text): """Restricts the characters that are allowed in a text""" if not self.sanitize: return text rval = [] for c in text: if c in self._valid_chars: - rval.append( c ) + rval.append(c) elif c in self._mapped_chars: - rval.append( self._mapped_chars[ c ] ) + rval.append(self._mapped_chars[c]) else: - rval.append( self._invalid_char ) - return ''.join( rval ) + rval.append(self._invalid_char) + return ''.join(rval) - def sanitize_param( self, value ): + def sanitize_param(self, value): """Clean incoming parameters (strings or lists)""" if not self.sanitize: return value - if isinstance( value, string_types ): - return self.sanitize_text( value ) - elif isinstance( value, list ): - return list( map( self.sanitize_text, value ) ) + if isinstance(value, string_types): + return self.sanitize_text(value) + elif isinstance(value, list): + return list(map(self.sanitize_text, value)) else: - raise Exception('Unknown parameter type (%s:%s)' % ( type( value ), value )) + raise Exception('Unknown parameter type (%s:%s)' % (type(value), value)) diff --git a/lib/galaxy/tools/parameters/validation.py b/lib/galaxy/tools/parameters/validation.py index 481186e2843c..531b4ef8b51f 100644 --- a/lib/galaxy/tools/parameters/validation.py +++ b/lib/galaxy/tools/parameters/validation.py @@ -11,26 +11,26 @@ util ) -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class Validator( object ): +class Validator(object): """ A validator checks that a value meets some conditions OR raises ValueError """ requires_dataset_metadata = False @classmethod - def from_element( cls, param, elem ): - type = elem.get( 'type', None ) + def from_element(cls, param, elem): + type = elem.get('type', None) assert type is not None, "Required 'type' attribute missing from validator" - return validator_types[type].from_element( param, elem ) + return validator_types[type].from_element(param, elem) - def validate( self, value, trans=None ): - raise TypeError( "Abstract Method" ) + def validate(self, value, trans=None): + raise TypeError("Abstract Method") -class RegexValidator( Validator ): +class RegexValidator(Validator): """ Validator that evaluates a regular expression @@ -50,21 +50,21 @@ class RegexValidator( Validator ): """ @classmethod - def from_element( cls, param, elem ): - return cls( elem.get( 'message' ), elem.text ) + def from_element(cls, param, elem): + return cls(elem.get('message'), elem.text) - def __init__( self, message, expression ): + def __init__(self, message, expression): self.message = message # Compile later. RE objects used to not be thread safe. Not sure about # the sre module. self.expression = expression - def validate( self, value, trans=None ): - if re.match( self.expression, value or '' ) is None: - raise ValueError( self.message ) + def validate(self, value, trans=None): + if re.match(self.expression, value or '') is None: + raise ValueError(self.message) -class ExpressionValidator( Validator ): +class ExpressionValidator(Validator): """ Validator that evaluates a python expression using the value @@ -84,24 +84,24 @@ class ExpressionValidator( Validator ): """ @classmethod - def from_element( cls, param, elem ): - return cls( elem.get( 'message' ), elem.text, elem.get( 'substitute_value_in_message' ) ) + def from_element(cls, param, elem): + return cls(elem.get('message'), elem.text, elem.get('substitute_value_in_message')) - def __init__( self, message, expression, substitute_value_in_message ): + def __init__(self, message, expression, substitute_value_in_message): self.message = message self.substitute_value_in_message = substitute_value_in_message # Save compiled expression, code objects are thread safe (right?) - self.expression = compile( expression, '', 'eval' ) + self.expression = compile(expression, '', 'eval') - def validate( self, value, trans=None ): - if not( eval( self.expression, dict( value=value ) ) ): + def validate(self, value, trans=None): + if not(eval(self.expression, dict(value=value))): message = self.message if self.substitute_value_in_message: message = message % value - raise ValueError( message ) + raise ValueError(message) -class InRangeValidator( Validator ): +class InRangeValidator(Validator): """ Validator that ensures a number is in a specific range @@ -125,12 +125,12 @@ class InRangeValidator( Validator ): """ @classmethod - def from_element( cls, param, elem ): - return cls( elem.get( 'message', None ), elem.get( 'min' ), - elem.get( 'max' ), elem.get( 'exclude_min', 'false' ), - elem.get( 'exclude_max', 'false' ) ) + def from_element(cls, param, elem): + return cls(elem.get('message', None), elem.get('min'), + elem.get('max'), elem.get('exclude_min', 'false'), + elem.get('exclude_max', 'false')) - def __init__( self, message, range_min, range_max, exclude_min=False, exclude_max=False ): + def __init__(self, message, range_min, range_max, exclude_min=False, exclude_max=False): """ When the optional exclude_min and exclude_max attributes are set to true, the range excludes the end points (i.e., min < value < max), @@ -138,38 +138,38 @@ def __init__( self, message, range_min, range_max, exclude_min=False, exclude_ma (1.e., min <= value <= max). Combinations of exclude_min and exclude_max values are allowed. """ - self.min = float( range_min if range_min is not None else '-inf' ) - self.exclude_min = util.asbool( exclude_min ) - self.max = float( range_max if range_max is not None else 'inf' ) - self.exclude_max = util.asbool( exclude_max ) + self.min = float(range_min if range_min is not None else '-inf') + self.exclude_min = util.asbool(exclude_min) + self.max = float(range_max if range_max is not None else 'inf') + self.exclude_max = util.asbool(exclude_max) assert self.min <= self.max, 'min must be less than or equal to max' # Remove unneeded 0s and decimal from floats to make message pretty. - self_min_str = str( self.min ).rstrip( '0' ).rstrip( '.' ) - self_max_str = str( self.max ).rstrip( '0' ).rstrip( '.' ) + self_min_str = str(self.min).rstrip('0').rstrip('.') + self_max_str = str(self.max).rstrip('0').rstrip('.') op1 = '>=' op2 = '<=' if self.exclude_min: op1 = '>' if self.exclude_max: op2 = '<' - self.message = message or "Value must be %s %s and %s %s" % ( op1, self_min_str, op2, self_max_str ) + self.message = message or "Value must be %s %s and %s %s" % (op1, self_min_str, op2, self_max_str) - def validate( self, value, trans=None ): + def validate(self, value, trans=None): if self.exclude_min: - if not self.min < float( value ): - raise ValueError( self.message ) + if not self.min < float(value): + raise ValueError(self.message) else: - if not self.min <= float( value ): - raise ValueError( self.message ) + if not self.min <= float(value): + raise ValueError(self.message) if self.exclude_max: - if not float( value ) < self.max: - raise ValueError( self.message ) + if not float(value) < self.max: + raise ValueError(self.message) else: - if not float( value ) <= self.max: - raise ValueError( self.message ) + if not float(value) <= self.max: + raise ValueError(self.message) -class LengthValidator( Validator ): +class LengthValidator(Validator): """ Validator that ensures the length of the provided string (value) is in a specific range @@ -193,276 +193,278 @@ class LengthValidator( Validator ): """ @classmethod - def from_element( cls, param, elem ): - return cls( elem.get( 'message', None ), elem.get( 'min', None ), elem.get( 'max', None ) ) + def from_element(cls, param, elem): + return cls(elem.get('message', None), elem.get('min', None), elem.get('max', None)) - def __init__( self, message, length_min, length_max ): + def __init__(self, message, length_min, length_max): self.message = message if length_min is not None: - length_min = int( length_min ) + length_min = int(length_min) if length_max is not None: - length_max = int( length_max ) + length_max = int(length_max) self.min = length_min self.max = length_max - def validate( self, value, trans=None ): - if self.min is not None and len( value ) < self.min: - raise ValueError( self.message or ( "Must have length of at least %d" % self.min ) ) - if self.max is not None and len( value ) > self.max: - raise ValueError( self.message or ( "Must have length no more than %d" % self.max ) ) + def validate(self, value, trans=None): + if self.min is not None and len(value) < self.min: + raise ValueError(self.message or ("Must have length of at least %d" % self.min)) + if self.max is not None and len(value) > self.max: + raise ValueError(self.message or ("Must have length no more than %d" % self.max)) -class DatasetOkValidator( Validator ): +class DatasetOkValidator(Validator): """ Validator that checks if a dataset is in an 'ok' state """ - def __init__( self, message=None ): + def __init__(self, message=None): self.message = message @classmethod - def from_element( cls, param, elem ): - return cls( elem.get( 'message', None ) ) + def from_element(cls, param, elem): + return cls(elem.get('message', None)) - def validate( self, value, trans=None ): + def validate(self, value, trans=None): if value and value.state != model.Dataset.states.OK: if self.message is None: self.message = "The selected dataset is still being generated, select another dataset or wait until it is completed" - raise ValueError( self.message ) + raise ValueError(self.message) -class DatasetEmptyValidator( Validator ): +class DatasetEmptyValidator(Validator): """Validator that checks if a dataset has a positive file size.""" - def __init__( self, message=None ): + + def __init__(self, message=None): self.message = message @classmethod - def from_element( cls, param, elem ): - return cls( elem.get( 'message', None ) ) + def from_element(cls, param, elem): + return cls(elem.get('message', None)) - def validate( self, value, trans=None ): + def validate(self, value, trans=None): if value: if value.get_size() == 0: if self.message is None: self.message = "The selected dataset is empty, this tool expects non-empty files." - raise ValueError( self.message ) + raise ValueError(self.message) -class DatasetExtraFilesPathEmptyValidator( Validator ): +class DatasetExtraFilesPathEmptyValidator(Validator): """Validator that checks if a dataset's extra_files_path exists and is not empty.""" - def __init__( self, message=None ): + + def __init__(self, message=None): self.message = message @classmethod - def from_element( cls, param, elem ): - return cls( elem.get( 'message', None ) ) + def from_element(cls, param, elem): + return cls(elem.get('message', None)) - def validate( self, value, trans=None ): + def validate(self, value, trans=None): if value: if value.get_total_size() == value.get_size(): if self.message is None: self.message = "The selected dataset's extra_files_path directory is empty or does not exist, this tool expects non-empty extra_files_path directories associated with the selected input." - raise ValueError( self.message ) + raise ValueError(self.message) -class MetadataValidator( Validator ): +class MetadataValidator(Validator): """ Validator that checks for missing metadata """ requires_dataset_metadata = True - def __init__( self, message=None, check="", skip="" ): + def __init__(self, message=None, check="", skip=""): self.message = message - self.check = check.split( "," ) - self.skip = skip.split( "," ) + self.check = check.split(",") + self.skip = skip.split(",") @classmethod - def from_element( cls, param, elem ): - return cls( message=elem.get( 'message', None ), check=elem.get( 'check', "" ), skip=elem.get( 'skip', "" ) ) + def from_element(cls, param, elem): + return cls(message=elem.get('message', None), check=elem.get('check', ""), skip=elem.get('skip', "")) - def validate( self, value, trans=None ): + def validate(self, value, trans=None): if value: - if not isinstance( value, model.DatasetInstance ): - raise ValueError( 'A non-dataset value was provided.' ) - if value.missing_meta( check=self.check, skip=self.skip ): + if not isinstance(value, model.DatasetInstance): + raise ValueError('A non-dataset value was provided.') + if value.missing_meta(check=self.check, skip=self.skip): if self.message is None: self.message = "Metadata missing, click the pencil icon in the history item to edit / save the metadata attributes" - raise ValueError( self.message ) + raise ValueError(self.message) -class UnspecifiedBuildValidator( Validator ): +class UnspecifiedBuildValidator(Validator): """ Validator that checks for dbkey not equal to '?' """ requires_dataset_metadata = True - def __init__( self, message=None ): + def __init__(self, message=None): if message is None: self.message = "Unspecified genome build, click the pencil icon in the history item to set the genome build" else: self.message = message @classmethod - def from_element( cls, param, elem ): - return cls( elem.get( 'message', None ) ) + def from_element(cls, param, elem): + return cls(elem.get('message', None)) - def validate( self, value, trans=None ): + def validate(self, value, trans=None): # if value is None, we cannot validate if value: dbkey = value.metadata.dbkey - if isinstance( dbkey, list ): + if isinstance(dbkey, list): dbkey = dbkey[0] if dbkey == '?': - raise ValueError( self.message ) + raise ValueError(self.message) -class NoOptionsValidator( Validator ): +class NoOptionsValidator(Validator): """Validator that checks for empty select list""" - def __init__( self, message=None ): + def __init__(self, message=None): self.message = message @classmethod - def from_element( cls, param, elem ): - return cls( elem.get( 'message', None ) ) + def from_element(cls, param, elem): + return cls(elem.get('message', None)) - def validate( self, value, trans=None ): + def validate(self, value, trans=None): if value is None: if self.message is None: self.message = "No options available for selection" - raise ValueError( self.message ) + raise ValueError(self.message) -class EmptyTextfieldValidator( Validator ): +class EmptyTextfieldValidator(Validator): """Validator that checks for empty text field""" - def __init__( self, message=None ): + def __init__(self, message=None): self.message = message @classmethod - def from_element( cls, param, elem ): - return cls( elem.get( 'message', None ) ) + def from_element(cls, param, elem): + return cls(elem.get('message', None)) - def validate( self, value, trans=None ): + def validate(self, value, trans=None): if value == '': if self.message is None: self.message = "Field requires a value" - raise ValueError( self.message ) + raise ValueError(self.message) -class MetadataInFileColumnValidator( Validator ): +class MetadataInFileColumnValidator(Validator): """ Validator that checks if the value for a dataset's metadata item exists in a file. """ requires_dataset_metadata = True @classmethod - def from_element( cls, param, elem ): - filename = elem.get( "filename", None ) + def from_element(cls, param, elem): + filename = elem.get("filename", None) if filename: - filename = "%s/%s" % ( param.tool.app.config.tool_data_path, filename.strip() ) - metadata_name = elem.get( "metadata_name", None ) + filename = "%s/%s" % (param.tool.app.config.tool_data_path, filename.strip()) + metadata_name = elem.get("metadata_name", None) if metadata_name: metadata_name = metadata_name.strip() - metadata_column = int( elem.get( "metadata_column", 0 ) ) - split = elem.get( "split", "\t" ) - message = elem.get( "message", "Value for metadata %s was not found in %s." % ( metadata_name, filename ) ) - line_startswith = elem.get( "line_startswith", None ) + metadata_column = int(elem.get("metadata_column", 0)) + split = elem.get("split", "\t") + message = elem.get("message", "Value for metadata %s was not found in %s." % (metadata_name, filename)) + line_startswith = elem.get("line_startswith", None) if line_startswith: line_startswith = line_startswith.strip() - return cls( filename, metadata_name, metadata_column, message, line_startswith, split ) + return cls(filename, metadata_name, metadata_column, message, line_startswith, split) - def __init__( self, filename, metadata_name, metadata_column, message="Value for metadata not found.", line_startswith=None, split="\t" ): + def __init__(self, filename, metadata_name, metadata_column, message="Value for metadata not found.", line_startswith=None, split="\t"): self.metadata_name = metadata_name self.message = message self.valid_values = [] - for line in open( filename ): - if line_startswith is None or line.startswith( line_startswith ): - fields = line.split( split ) - if metadata_column < len( fields ): - self.valid_values.append( fields[metadata_column].strip() ) + for line in open(filename): + if line_startswith is None or line.startswith(line_startswith): + fields = line.split(split) + if metadata_column < len(fields): + self.valid_values.append(fields[metadata_column].strip()) - def validate( self, value, trans=None ): + def validate(self, value, trans=None): if not value: return - if hasattr( value, "metadata" ): - if value.metadata.spec[self.metadata_name].param.to_string( value.metadata.get( self.metadata_name ) ) in self.valid_values: + if hasattr(value, "metadata"): + if value.metadata.spec[self.metadata_name].param.to_string(value.metadata.get(self.metadata_name)) in self.valid_values: return - raise ValueError( self.message ) + raise ValueError(self.message) -class MetadataInDataTableColumnValidator( Validator ): +class MetadataInDataTableColumnValidator(Validator): """ Validator that checks if the value for a dataset's metadata item exists in a file. """ requires_dataset_metadata = True @classmethod - def from_element( cls, param, elem ): - table_name = elem.get( "table_name", None ) + def from_element(cls, param, elem): + table_name = elem.get("table_name", None) assert table_name, 'You must specify a table_name.' - tool_data_table = param.tool.app.tool_data_tables[ table_name ] - metadata_name = elem.get( "metadata_name", None ) + tool_data_table = param.tool.app.tool_data_tables[table_name] + metadata_name = elem.get("metadata_name", None) if metadata_name: metadata_name = metadata_name.strip() - metadata_column = elem.get( "metadata_column", 0 ) + metadata_column = elem.get("metadata_column", 0) try: - metadata_column = int( metadata_column ) + metadata_column = int(metadata_column) except: pass - message = elem.get( "message", "Value for metadata %s was not found in %s." % ( metadata_name, table_name ) ) - line_startswith = elem.get( "line_startswith", None ) + message = elem.get("message", "Value for metadata %s was not found in %s." % (metadata_name, table_name)) + line_startswith = elem.get("line_startswith", None) if line_startswith: line_startswith = line_startswith.strip() - return cls( tool_data_table, metadata_name, metadata_column, message, line_startswith ) + return cls(tool_data_table, metadata_name, metadata_column, message, line_startswith) - def __init__( self, tool_data_table, metadata_name, metadata_column, message="Value for metadata not found.", line_startswith=None ): + def __init__(self, tool_data_table, metadata_name, metadata_column, message="Value for metadata not found.", line_startswith=None): self.metadata_name = metadata_name self.message = message self.valid_values = [] self._data_table_content_version = None self._tool_data_table = tool_data_table - if isinstance( metadata_column, string_types ): - metadata_column = tool_data_table.columns[ metadata_column ] + if isinstance(metadata_column, string_types): + metadata_column = tool_data_table.columns[metadata_column] self._metadata_column = metadata_column self._load_values() - def _load_values( self ): + def _load_values(self): self._data_table_content_version, data_fields = self._tool_data_table.get_version_fields() self.valid_values = [] for fields in data_fields: - if self._metadata_column < len( fields ): - self.valid_values.append( fields[ self._metadata_column ] ) + if self._metadata_column < len(fields): + self.valid_values.append(fields[self._metadata_column]) - def validate( self, value, trans=None ): + def validate(self, value, trans=None): if not value: return - if hasattr( value, "metadata" ): - if not self._tool_data_table.is_current_version( self._data_table_content_version ): - log.debug( 'MetadataInDataTableColumnValidator values are out of sync with data table (%s), updating validator.', self._tool_data_table.name ) + if hasattr(value, "metadata"): + if not self._tool_data_table.is_current_version(self._data_table_content_version): + log.debug('MetadataInDataTableColumnValidator values are out of sync with data table (%s), updating validator.', self._tool_data_table.name) self._load_values() - if value.metadata.spec[self.metadata_name].param.to_string( value.metadata.get( self.metadata_name ) ) in self.valid_values: + if value.metadata.spec[self.metadata_name].param.to_string(value.metadata.get(self.metadata_name)) in self.valid_values: return - raise ValueError( self.message ) + raise ValueError(self.message) -validator_types = dict( expression=ExpressionValidator, - regex=RegexValidator, - in_range=InRangeValidator, - length=LengthValidator, - metadata=MetadataValidator, - unspecified_build=UnspecifiedBuildValidator, - no_options=NoOptionsValidator, - empty_field=EmptyTextfieldValidator, - empty_dataset=DatasetEmptyValidator, - empty_extra_files_path=DatasetExtraFilesPathEmptyValidator, - dataset_metadata_in_file=MetadataInFileColumnValidator, - dataset_metadata_in_data_table=MetadataInDataTableColumnValidator, - dataset_ok_validator=DatasetOkValidator, ) +validator_types = dict(expression=ExpressionValidator, + regex=RegexValidator, + in_range=InRangeValidator, + length=LengthValidator, + metadata=MetadataValidator, + unspecified_build=UnspecifiedBuildValidator, + no_options=NoOptionsValidator, + empty_field=EmptyTextfieldValidator, + empty_dataset=DatasetEmptyValidator, + empty_extra_files_path=DatasetExtraFilesPathEmptyValidator, + dataset_metadata_in_file=MetadataInFileColumnValidator, + dataset_metadata_in_data_table=MetadataInDataTableColumnValidator, + dataset_ok_validator=DatasetOkValidator, ) def get_suite(): """Get unittest suite for this module""" import doctest import sys - return doctest.DocTestSuite( sys.modules[__name__] ) + return doctest.DocTestSuite(sys.modules[__name__]) diff --git a/lib/galaxy/tools/parameters/wrapped.py b/lib/galaxy/tools/parameters/wrapped.py index 70502f7ca7dd..c93b69e065b3 100644 --- a/lib/galaxy/tools/parameters/wrapped.py +++ b/lib/galaxy/tools/parameters/wrapped.py @@ -19,23 +19,23 @@ PARAMS_UNWRAPPED = object() -class WrappedParameters( object ): +class WrappedParameters(object): - def __init__( self, trans, tool, incoming ): + def __init__(self, trans, tool, incoming): self.trans = trans self.tool = tool self.incoming = incoming self._params = PARAMS_UNWRAPPED @property - def params( self ): + def params(self): if self._params is PARAMS_UNWRAPPED: - params = make_dict_copy( self.incoming ) - self.wrap_values( self.tool.inputs, params, skip_missing_values=not self.tool.check_values ) + params = make_dict_copy(self.incoming) + self.wrap_values(self.tool.inputs, params, skip_missing_values=not self.tool.check_values) self._params = params return self._params - def wrap_values( self, inputs, input_values, skip_missing_values=False ): + def wrap_values(self, inputs, input_values, skip_missing_values=False): trans = self.trans tool = self.tool incoming = self.incoming @@ -44,35 +44,35 @@ def wrap_values( self, inputs, input_values, skip_missing_values=False ): for input in inputs.values(): if input.name not in input_values and skip_missing_values: continue - value = input_values[ input.name ] - if isinstance( input, Repeat ): - for d in input_values[ input.name ]: - self.wrap_values( input.inputs, d, skip_missing_values=skip_missing_values ) - elif isinstance( input, Conditional ): - values = input_values[ input.name ] - current = values[ "__current_case__" ] - self.wrap_values( input.cases[current].inputs, values, skip_missing_values=skip_missing_values ) - elif isinstance( input, Section ): + value = input_values[input.name] + if isinstance(input, Repeat): + for d in input_values[input.name]: + self.wrap_values(input.inputs, d, skip_missing_values=skip_missing_values) + elif isinstance(input, Conditional): + values = input_values[input.name] + current = values["__current_case__"] + self.wrap_values(input.cases[current].inputs, values, skip_missing_values=skip_missing_values) + elif isinstance(input, Section): values = value - self.wrap_values( input.inputs, values, skip_missing_values=skip_missing_values ) - elif isinstance( input, DataToolParameter ) and input.multiple: - dataset_instances = DatasetListWrapper.to_dataset_instances( value ) - input_values[ input.name ] = \ - DatasetListWrapper( None, - dataset_instances, - datatypes_registry=trans.app.datatypes_registry, - tool=tool, - name=input.name ) - elif isinstance( input, DataToolParameter ): - input_values[ input.name ] = \ - DatasetFilenameWrapper( value, - datatypes_registry=trans.app.datatypes_registry, - tool=tool, - name=input.name ) - elif isinstance( input, SelectToolParameter ): - input_values[ input.name ] = SelectToolParameterWrapper( input, input_values[ input.name ], other_values=incoming ) - elif isinstance( input, DataCollectionToolParameter ): - input_values[ input.name ] = DatasetCollectionWrapper( + self.wrap_values(input.inputs, values, skip_missing_values=skip_missing_values) + elif isinstance(input, DataToolParameter) and input.multiple: + dataset_instances = DatasetListWrapper.to_dataset_instances(value) + input_values[input.name] = \ + DatasetListWrapper(None, + dataset_instances, + datatypes_registry=trans.app.datatypes_registry, + tool=tool, + name=input.name) + elif isinstance(input, DataToolParameter): + input_values[input.name] = \ + DatasetFilenameWrapper(value, + datatypes_registry=trans.app.datatypes_registry, + tool=tool, + name=input.name) + elif isinstance(input, SelectToolParameter): + input_values[input.name] = SelectToolParameterWrapper(input, input_values[input.name], other_values=incoming) + elif isinstance(input, DataCollectionToolParameter): + input_values[input.name] = DatasetCollectionWrapper( None, value, datatypes_registry=trans.app.datatypes_registry, @@ -80,10 +80,10 @@ def wrap_values( self, inputs, input_values, skip_missing_values=False ): name=input.name, ) else: - input_values[ input.name ] = InputValueWrapper( input, value, incoming ) + input_values[input.name] = InputValueWrapper(input, value, incoming) -def make_dict_copy( from_dict ): +def make_dict_copy(from_dict): """ Makes a copy of input dictionary from_dict such that all values that are dictionaries result in creation of a new dictionary ( a sort of deepcopy ). We may need to handle @@ -92,25 +92,25 @@ def make_dict_copy( from_dict ): """ copy_from_dict = {} for key, value in from_dict.items(): - if type( value ).__name__ == 'dict': - copy_from_dict[ key ] = make_dict_copy( value ) - elif isinstance( value, list ): - copy_from_dict[ key ] = make_list_copy( value ) + if type(value).__name__ == 'dict': + copy_from_dict[key] = make_dict_copy(value) + elif isinstance(value, list): + copy_from_dict[key] = make_list_copy(value) else: - copy_from_dict[ key ] = value + copy_from_dict[key] = value return copy_from_dict -def make_list_copy( from_list ): +def make_list_copy(from_list): new_list = [] for value in from_list: - if isinstance( value, dict ): - new_list.append( make_dict_copy( value ) ) - elif isinstance( value, list ): - new_list.append( make_list_copy( value ) ) + if isinstance(value, dict): + new_list.append(make_dict_copy(value)) + elif isinstance(value, list): + new_list.append(make_list_copy(value)) else: - new_list.append( value ) + new_list.append(value) return new_list -__all__ = ( 'WrappedParameters', 'make_dict_copy' ) +__all__ = ('WrappedParameters', 'make_dict_copy') diff --git a/lib/galaxy/tools/parser/cwl.py b/lib/galaxy/tools/parser/cwl.py index a981bafdb076..2c9a1444cc05 100644 --- a/lib/galaxy/tools/parser/cwl.py +++ b/lib/galaxy/tools/parser/cwl.py @@ -116,7 +116,7 @@ def parse_outputs(self, tool): def _parse_output(self, tool, output_instance): name = output_instance.name # TODO: handle filters, actions, change_format - output = ToolOutput( name ) + output = ToolOutput(name) if "File" in output_instance.output_data_type: output.format = "_sniff_" else: @@ -131,7 +131,7 @@ def _parse_output(self, tool, output_instance): output.tool = tool output.hidden = "" output.dataset_collector_descriptions = [] - output.actions = ToolOutputActionGroup( output, None ) + output.actions = ToolOutputActionGroup(output, None) return output def parse_requirements_and_containers(self): diff --git a/lib/galaxy/tools/parser/interface.py b/lib/galaxy/tools/parser/interface.py index b16f807ae7e2..75e77a5b1bc1 100644 --- a/lib/galaxy/tools/parser/interface.py +++ b/lib/galaxy/tools/parser/interface.py @@ -8,8 +8,8 @@ NOT_IMPLEMENTED_MESSAGE = "Galaxy tool format does not yet support this tool feature." -@six.add_metaclass(ABCMeta) @six.python_2_unicode_compatible +@six.add_metaclass(ABCMeta) class ToolSource(object): """ This interface represents an abstract source to parse tool information from. @@ -148,6 +148,22 @@ def parse_requirements_and_containers(self): def parse_input_pages(self): """ Return a PagesSource representing inputs by page for tool. """ + def parse_provided_metadata_style(self): + """Return style of tool provided metadata file (e.g. galaxy.json). + + A value of of "default" indicates the newer galaxy.json style + (the default for XML-based tools with profile >= 17.09) and a value + of "legacy" indicates the older galaxy.json style. + + A short description of these two styles can be found at + https://github.com/galaxyproject/galaxy/pull/4437. + """ + return "default" + + def parse_provided_metadata_file(self): + """Return location of provided metadata file (e.g. galaxy.json).""" + return "galaxy.json" + @abstractmethod def parse_outputs(self, tool): """ Return a pair of output and output collections ordered @@ -196,6 +212,7 @@ class PagesSource(object): Pages are deprecated so ideally this outer list will always be exactly a singleton. """ + def __init__(self, page_sources): self.page_sources = page_sources @@ -261,7 +278,7 @@ def parse_optional(self, default=None): """ Return boolean indicating wheter parameter is optional. """ if default is None: default = self.default_optional - return self.get_bool( "optional", default ) + return self.get_bool("optional", default) def parse_dynamic_options_elem(self): """ Return an XML elemnt describing dynamic options. @@ -291,14 +308,15 @@ def parse_when_input_sources(self): raise NotImplementedError(NOT_IMPLEMENTED_MESSAGE) -class ToolStdioRegex( object ): +class ToolStdioRegex(object): """ This is a container for the element's regex subelement. The regex subelement has a "match" attribute, a "sources" attribute that contains "output" and/or "error", and a "level" attribute that contains "warning" or "fatal". """ - def __init__( self ): + + def __init__(self): self.match = "" self.stdout_match = False self.stderr_match = False @@ -307,52 +325,53 @@ def __init__( self ): self.desc = "" -class ToolStdioExitCode( object ): +class ToolStdioExitCode(object): """ This is a container for the element's subelement. The exit_code element has a range of exit codes and the error level. """ - def __init__( self ): - self.range_start = float( "-inf" ) - self.range_end = float( "inf" ) + + def __init__(self): + self.range_start = float("-inf") + self.range_end = float("inf") # TODO: Define a common class or constant for error level: self.error_level = "fatal" self.desc = "" -class TestCollectionDef( object ): +class TestCollectionDef(object): # TODO: do not require XML directly here. - def __init__( self, elem, parse_param_elem ): + def __init__(self, elem, parse_param_elem): self.elements = [] - attrib = dict( elem.attrib ) - self.collection_type = attrib[ "type" ] - self.name = attrib.get( "name", "Unnamed Collection" ) - for element in elem.findall( "element" ): - element_attrib = dict( element.attrib ) - element_identifier = element_attrib[ "name" ] - nested_collection_elem = element.find( "collection" ) + attrib = dict(elem.attrib) + self.collection_type = attrib["type"] + self.name = attrib.get("name", "Unnamed Collection") + for element in elem.findall("element"): + element_attrib = dict(element.attrib) + element_identifier = element_attrib["name"] + nested_collection_elem = element.find("collection") if nested_collection_elem is not None: - self.elements.append( ( element_identifier, TestCollectionDef( nested_collection_elem, parse_param_elem ) ) ) + self.elements.append((element_identifier, TestCollectionDef(nested_collection_elem, parse_param_elem))) else: - self.elements.append( ( element_identifier, parse_param_elem( element ) ) ) + self.elements.append((element_identifier, parse_param_elem(element))) - def collect_inputs( self ): + def collect_inputs(self): inputs = [] for element in self.elements: - value = element[ 1 ] - if isinstance( value, TestCollectionDef ): - inputs.extend( value.collect_inputs() ) + value = element[1] + if isinstance(value, TestCollectionDef): + inputs.extend(value.collect_inputs()) else: - inputs.append( value ) + inputs.append(value) return inputs -class TestCollectionOutputDef( object ): +class TestCollectionOutputDef(object): - def __init__( self, name, attrib, element_tests ): + def __init__(self, name, attrib, element_tests): self.name = name - self.collection_type = attrib.get( "type", None ) + self.collection_type = attrib.get("type", None) count = attrib.get("count", None) self.count = int(count) if count is not None else None self.attrib = attrib diff --git a/lib/galaxy/tools/parser/output_actions.py b/lib/galaxy/tools/parser/output_actions.py index 2234cd467252..9dfacff74b5b 100644 --- a/lib/galaxy/tools/parser/output_actions.py +++ b/lib/galaxy/tools/parser/output_actions.py @@ -9,481 +9,481 @@ from galaxy import util -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) # Attributes tool developer may want to query on dataset collections. -COLLECTION_ATTRIBUTES = [ "collection_type" ] +COLLECTION_ATTRIBUTES = ["collection_type"] -class ToolOutputActionGroup( object ): +class ToolOutputActionGroup(object): """ Manages a set of tool output dataset actions directives """ tag = "group" - def __init__( self, parent, config_elem ): + def __init__(self, parent, config_elem): self.parent = parent self.actions = [] if config_elem is not None: for elem in config_elem: if elem.tag == "conditional": - self.actions.append( ToolOutputActionConditional( self, elem ) ) + self.actions.append(ToolOutputActionConditional(self, elem)) elif elem.tag == "action": - self.actions.append( ToolOutputAction.from_elem( self, elem ) ) + self.actions.append(ToolOutputAction.from_elem(self, elem)) else: - log.debug( "Unknown ToolOutputAction tag specified: %s" % elem.tag ) + log.debug("Unknown ToolOutputAction tag specified: %s" % elem.tag) - def apply_action( self, output_dataset, other_values ): + def apply_action(self, output_dataset, other_values): for action in self.actions: - action.apply_action( output_dataset, other_values ) + action.apply_action(output_dataset, other_values) @property - def tool( self ): + def tool(self): return self.parent.tool - def __len__( self ): - return len( self.actions ) + def __len__(self): + return len(self.actions) -class ToolOutputActionConditionalWhen( ToolOutputActionGroup ): +class ToolOutputActionConditionalWhen(ToolOutputActionGroup): tag = "when" @classmethod - def from_elem( cls, parent, when_elem ): + def from_elem(cls, parent, when_elem): """Loads the proper when by attributes of elem""" - when_value = when_elem.get( "value", None ) + when_value = when_elem.get("value", None) if when_value is not None: - return ValueToolOutputActionConditionalWhen( parent, when_elem, when_value ) + return ValueToolOutputActionConditionalWhen(parent, when_elem, when_value) else: - when_value = when_elem.get( "datatype_isinstance", None ) + when_value = when_elem.get("datatype_isinstance", None) if when_value is not None: - return DatatypeIsInstanceToolOutputActionConditionalWhen( parent, when_elem, when_value ) - raise TypeError( "When type not implemented" ) + return DatatypeIsInstanceToolOutputActionConditionalWhen(parent, when_elem, when_value) + raise TypeError("When type not implemented") - def __init__( self, parent, config_elem, value ): - super( ToolOutputActionConditionalWhen, self ).__init__( parent, config_elem ) + def __init__(self, parent, config_elem, value): + super(ToolOutputActionConditionalWhen, self).__init__(parent, config_elem) self.value = value - def is_case( self, output_dataset, other_values ): - raise TypeError( "Not implemented" ) + def is_case(self, output_dataset, other_values): + raise TypeError("Not implemented") - def get_ref( self, output_dataset, other_values ): + def get_ref(self, output_dataset, other_values): ref = other_values for ref_name in self.parent.name: assert ref_name in ref, "Required dependency '%s' not found in incoming values" % ref_name - ref = ref.get( ref_name ) + ref = ref.get(ref_name) return ref - def apply_action( self, output_dataset, other_values ): - if self.is_case( output_dataset, other_values ): - return super( ToolOutputActionConditionalWhen, self ).apply_action( output_dataset, other_values ) + def apply_action(self, output_dataset, other_values): + if self.is_case(output_dataset, other_values): + return super(ToolOutputActionConditionalWhen, self).apply_action(output_dataset, other_values) -class ValueToolOutputActionConditionalWhen( ToolOutputActionConditionalWhen ): +class ValueToolOutputActionConditionalWhen(ToolOutputActionConditionalWhen): tag = "when value" - def is_case( self, output_dataset, other_values ): - ref = self.get_ref( output_dataset, other_values ) - return bool( str( ref ) == self.value ) + def is_case(self, output_dataset, other_values): + ref = self.get_ref(output_dataset, other_values) + return bool(str(ref) == self.value) -class DatatypeIsInstanceToolOutputActionConditionalWhen( ToolOutputActionConditionalWhen ): +class DatatypeIsInstanceToolOutputActionConditionalWhen(ToolOutputActionConditionalWhen): tag = "when datatype_isinstance" - def __init__( self, parent, config_elem, value ): - super( DatatypeIsInstanceToolOutputActionConditionalWhen, self ).__init__( parent, config_elem, value ) - self.value = type( self.tool.app.datatypes_registry.get_datatype_by_extension( value ) ) + def __init__(self, parent, config_elem, value): + super(DatatypeIsInstanceToolOutputActionConditionalWhen, self).__init__(parent, config_elem, value) + self.value = type(self.tool.app.datatypes_registry.get_datatype_by_extension(value)) - def is_case( self, output_dataset, other_values ): - ref = self.get_ref( output_dataset, other_values ) - return isinstance( ref.datatype, self.value ) + def is_case(self, output_dataset, other_values): + ref = self.get_ref(output_dataset, other_values) + return isinstance(ref.datatype, self.value) -class ToolOutputActionConditional( object ): +class ToolOutputActionConditional(object): tag = "conditional" - def __init__( self, parent, config_elem ): + def __init__(self, parent, config_elem): self.parent = parent - self.name = config_elem.get( 'name', None ) + self.name = config_elem.get('name', None) assert self.name is not None, "Required 'name' attribute missing from ToolOutputActionConditional" - self.name = self.name.split( '.' ) + self.name = self.name.split('.') self.cases = [] - for when_elem in config_elem.findall( 'when' ): - self.cases.append( ToolOutputActionConditionalWhen.from_elem( self, when_elem ) ) + for when_elem in config_elem.findall('when'): + self.cases.append(ToolOutputActionConditionalWhen.from_elem(self, when_elem)) - def apply_action( self, output_dataset, other_values ): + def apply_action(self, output_dataset, other_values): for case in self.cases: - case.apply_action( output_dataset, other_values ) + case.apply_action(output_dataset, other_values) @property - def tool( self ): + def tool(self): return self.parent.tool -class ToolOutputAction( object ): +class ToolOutputAction(object): tag = "action" @classmethod - def from_elem( cls, parent, elem ): + def from_elem(cls, parent, elem): """Loads the proper action by the type attribute of elem""" - action_type = elem.get( 'type', None ) + action_type = elem.get('type', None) assert action_type is not None, "Required 'type' attribute missing from ToolOutputAction" - return action_types[ action_type ]( parent, elem ) + return action_types[action_type](parent, elem) - def __init__( self, parent, elem ): + def __init__(self, parent, elem): self.parent = parent - self.default = elem.get( 'default', None ) - option_elem = elem.find( 'option' ) - self.option = ToolOutputActionOption.from_elem( self, option_elem ) + self.default = elem.get('default', None) + option_elem = elem.find('option') + self.option = ToolOutputActionOption.from_elem(self, option_elem) - def apply_action( self, output_dataset, other_values ): - raise TypeError( "Not implemented" ) + def apply_action(self, output_dataset, other_values): + raise TypeError("Not implemented") @property - def tool( self ): + def tool(self): return self.parent.tool -class ToolOutputActionOption( object ): +class ToolOutputActionOption(object): tag = "object" @classmethod - def from_elem( cls, parent, elem ): + def from_elem(cls, parent, elem): """Loads the proper action by the type attribute of elem""" if elem is None: option_type = NullToolOutputActionOption.tag # no ToolOutputActionOption's have been defined, use implicit NullToolOutputActionOption else: - option_type = elem.get( 'type', None ) + option_type = elem.get('type', None) assert option_type is not None, "Required 'type' attribute missing from ToolOutputActionOption" - return option_types[ option_type ]( parent, elem ) + return option_types[option_type](parent, elem) - def __init__( self, parent, elem ): + def __init__(self, parent, elem): self.parent = parent self.filters = [] if elem is not None: - for filter_elem in elem.findall( 'filter' ): - self.filters.append( ToolOutputActionOptionFilter.from_elem( self, filter_elem ) ) + for filter_elem in elem.findall('filter'): + self.filters.append(ToolOutputActionOptionFilter.from_elem(self, filter_elem)) - def get_value( self, other_values ): - raise TypeError( "Not implemented" ) + def get_value(self, other_values): + raise TypeError("Not implemented") @property - def tool( self ): + def tool(self): return self.parent.tool -class NullToolOutputActionOption( ToolOutputActionOption ): +class NullToolOutputActionOption(ToolOutputActionOption): tag = "null_option" - def get_value( self, other_values ): + def get_value(self, other_values): return None -class FromFileToolOutputActionOption( ToolOutputActionOption ): +class FromFileToolOutputActionOption(ToolOutputActionOption): tag = "from_file" - def __init__( self, parent, elem ): - super( FromFileToolOutputActionOption, self ).__init__( parent, elem ) - self.name = elem.get( 'name', None ) + def __init__(self, parent, elem): + super(FromFileToolOutputActionOption, self).__init__(parent, elem) + self.name = elem.get('name', None) assert self.name is not None, "Required 'name' attribute missing from FromFileToolOutputActionOption" - self.column = elem.get( 'column', None ) + self.column = elem.get('column', None) assert self.column is not None, "Required 'column' attribute missing from FromFileToolOutputActionOption" - self.column = int( self.column ) - self.offset = elem.get( 'offset', -1 ) - self.offset = int( self.offset ) - self.separator = elem.get( 'separator', '\t' ) + self.column = int(self.column) + self.offset = elem.get('offset', -1) + self.offset = int(self.offset) + self.separator = elem.get('separator', '\t') self.options = [] data_file = self.name - if not os.path.isabs( data_file ): - data_file = os.path.join( self.tool.app.config.tool_data_path, data_file ) - for line in open( data_file ): - self.options.append( line.rstrip( '\n\r' ).split( self.separator ) ) + if not os.path.isabs(data_file): + data_file = os.path.join(self.tool.app.config.tool_data_path, data_file) + for line in open(data_file): + self.options.append(line.rstrip('\n\r').split(self.separator)) - def get_value( self, other_values ): + def get_value(self, other_values): options = self.options for filter in self.filters: - options = filter.filter_options( options, other_values ) + options = filter.filter_options(options, other_values) try: if options: - return str( options[ self.offset ][ self.column ] ) + return str(options[self.offset][self.column]) except Exception as e: - log.debug( "Error in FromFileToolOutputActionOption get_value: %s" % e ) + log.debug("Error in FromFileToolOutputActionOption get_value: %s" % e) return None -class FromParamToolOutputActionOption( ToolOutputActionOption ): +class FromParamToolOutputActionOption(ToolOutputActionOption): tag = "from_param" - def __init__( self, parent, elem ): - super( FromParamToolOutputActionOption, self ).__init__( parent, elem ) - self.name = elem.get( 'name', None ) + def __init__(self, parent, elem): + super(FromParamToolOutputActionOption, self).__init__(parent, elem) + self.name = elem.get('name', None) assert self.name is not None, "Required 'name' attribute missing from FromFileToolOutputActionOption" - self.name = self.name.split( '.' ) - self.column = elem.get( 'column', 0 ) - self.column = int( self.column ) - self.offset = elem.get( 'offset', -1 ) - self.offset = int( self.offset ) - self.param_attribute = elem.get( 'param_attribute', [] ) + self.name = self.name.split('.') + self.column = elem.get('column', 0) + self.column = int(self.column) + self.offset = elem.get('offset', -1) + self.offset = int(self.offset) + self.param_attribute = elem.get('param_attribute', []) if self.param_attribute: - self.param_attribute = self.param_attribute.split( '.' ) + self.param_attribute = self.param_attribute.split('.') - def get_value( self, other_values ): + def get_value(self, other_values): value = other_values for ref_name in self.name: assert ref_name in value, "Required dependency '%s' not found in incoming values" % ref_name - value = value.get( ref_name ) + value = value.get(ref_name) for attr_name in self.param_attribute: # if the value is a list from a repeat tag you can access the first element of the repeat with # artifical 'first' attribute_name. For example: .. param_attribute="first.input_mate1.ext" if isinstance(value, list) and attr_name == 'first': value = value[0] elif isinstance(value, dict): - value = value[ attr_name ] - elif hasattr( value, "collection" ) and value not in COLLECTION_ATTRIBUTES: + value = value[attr_name] + elif hasattr(value, "collection") and value not in COLLECTION_ATTRIBUTES: # if this is an HDCA for instance let reverse.ext grab # the reverse element and then continue for loop to grab # dataset extension - value = value.collection[ attr_name ].element_object - elif hasattr( value, "collection" ) and value in COLLECTION_ATTRIBUTES: - value = getattr( value.collection, attr_name ) + value = value.collection[attr_name].element_object + elif hasattr(value, "collection") and value in COLLECTION_ATTRIBUTES: + value = getattr(value.collection, attr_name) else: - value = getattr( value, attr_name ) - options = [ [ str( value ) ] ] + value = getattr(value, attr_name) + options = [[str(value)]] for filter in self.filters: - options = filter.filter_options( options, other_values ) + options = filter.filter_options(options, other_values) try: if options: - return str( options[ self.offset ][ self.column ] ) + return str(options[self.offset][self.column]) except Exception as e: - log.debug( "Error in FromParamToolOutputActionOption get_value: %s" % e ) + log.debug("Error in FromParamToolOutputActionOption get_value: %s" % e) return None -class FromDataTableOutputActionOption( ToolOutputActionOption ): +class FromDataTableOutputActionOption(ToolOutputActionOption): tag = "from_data_table" # TODO: allow accessing by column 'name' not just index - def __init__( self, parent, elem ): - super( FromDataTableOutputActionOption, self ).__init__( parent, elem ) - self.name = elem.get( 'name', None ) + def __init__(self, parent, elem): + super(FromDataTableOutputActionOption, self).__init__(parent, elem) + self.name = elem.get('name', None) assert self.name is not None, "Required 'name' attribute missing from FromDataTableOutputActionOption" self.missing_tool_data_table_name = None if self.name in self.tool.app.tool_data_tables: - self.options = self.tool.app.tool_data_tables[ self.name ].get_fields() - self.column = elem.get( 'column', None ) + self.options = self.tool.app.tool_data_tables[self.name].get_fields() + self.column = elem.get('column', None) assert self.column is not None, "Required 'column' attribute missing from FromDataTableOutputActionOption" - self.column = int( self.column ) - self.offset = elem.get( 'offset', -1 ) - self.offset = int( self.offset ) + self.column = int(self.column) + self.offset = elem.get('offset', -1) + self.offset = int(self.offset) else: self.options = [] self.missing_tool_data_table_name = self.name - def get_value( self, other_values ): + def get_value(self, other_values): if self.options: options = self.options else: options = [] for filter in self.filters: - options = filter.filter_options( options, other_values ) + options = filter.filter_options(options, other_values) try: if options: - return str( options[ self.offset ][ self.column ] ) + return str(options[self.offset][self.column]) except Exception as e: - log.debug( "Error in FromDataTableOutputActionOption get_value: %s" % e ) + log.debug("Error in FromDataTableOutputActionOption get_value: %s" % e) return None -class MetadataToolOutputAction( ToolOutputAction ): +class MetadataToolOutputAction(ToolOutputAction): tag = "metadata" - def __init__( self, parent, elem ): - super( MetadataToolOutputAction, self ).__init__( parent, elem ) - self.name = elem.get( 'name', None ) + def __init__(self, parent, elem): + super(MetadataToolOutputAction, self).__init__(parent, elem) + self.name = elem.get('name', None) assert self.name is not None, "Required 'name' attribute missing from MetadataToolOutputAction" - def apply_action( self, output_dataset, other_values ): - value = self.option.get_value( other_values ) + def apply_action(self, output_dataset, other_values): + value = self.option.get_value(other_values) if value is None and self.default is not None: value = self.default if value is not None: - setattr( output_dataset.metadata, self.name, value ) + setattr(output_dataset.metadata, self.name, value) -class FormatToolOutputAction( ToolOutputAction ): +class FormatToolOutputAction(ToolOutputAction): tag = "format" - def __init__( self, parent, elem ): - super( FormatToolOutputAction, self ).__init__( parent, elem ) - self.default = elem.get( 'default', None ) + def __init__(self, parent, elem): + super(FormatToolOutputAction, self).__init__(parent, elem) + self.default = elem.get('default', None) - def apply_action( self, output_dataset, other_values ): - value = self.option.get_value( other_values ) + def apply_action(self, output_dataset, other_values): + value = self.option.get_value(other_values) if value is None and self.default is not None: value = self.default if value is not None: output_dataset.extension = value -class ToolOutputActionOptionFilter( object ): +class ToolOutputActionOptionFilter(object): tag = "filter" @classmethod - def from_elem( cls, parent, elem ): + def from_elem(cls, parent, elem): """Loads the proper action by the type attribute of elem""" - filter_type = elem.get( 'type', None ) + filter_type = elem.get('type', None) assert filter_type is not None, "Required 'type' attribute missing from ToolOutputActionOptionFilter" - return filter_types[ filter_type ]( parent, elem ) + return filter_types[filter_type](parent, elem) - def __init__( self, parent, elem ): + def __init__(self, parent, elem): self.parent = parent - def filter_options( self, options, other_values ): - raise TypeError( "Not implemented" ) + def filter_options(self, options, other_values): + raise TypeError("Not implemented") @property - def tool( self ): + def tool(self): return self.parent.tool -class ParamValueToolOutputActionOptionFilter( ToolOutputActionOptionFilter ): +class ParamValueToolOutputActionOptionFilter(ToolOutputActionOptionFilter): tag = "param_value" - def __init__( self, parent, elem ): - super( ParamValueToolOutputActionOptionFilter, self ).__init__( parent, elem ) - self.ref = elem.get( 'ref', None ) + def __init__(self, parent, elem): + super(ParamValueToolOutputActionOptionFilter, self).__init__(parent, elem) + self.ref = elem.get('ref', None) if self.ref: - self.ref = self.ref.split( '.' ) - self.value = elem.get( 'value', None ) + self.ref = self.ref.split('.') + self.value = elem.get('value', None) assert self.ref != self.value, "Required 'ref' or 'value' attribute missing from ParamValueToolOutputActionOptionFilter" - self.column = elem.get( 'column', None ) + self.column = elem.get('column', None) assert self.column is not None, "Required 'column' attribute missing from ParamValueToolOutputActionOptionFilter" - self.column = int( self.column ) - self.keep = util.string_as_bool( elem.get( "keep", 'True' ) ) - self.compare = parse_compare_type( elem.get( 'compare', None ) ) - self.cast = parse_cast_attribute( elem.get( "cast", None ) ) - self.param_attribute = elem.get( 'param_attribute', [] ) + self.column = int(self.column) + self.keep = util.string_as_bool(elem.get("keep", 'True')) + self.compare = parse_compare_type(elem.get('compare', None)) + self.cast = parse_cast_attribute(elem.get("cast", None)) + self.param_attribute = elem.get('param_attribute', []) if self.param_attribute: - self.param_attribute = self.param_attribute.split( '.' ) + self.param_attribute = self.param_attribute.split('.') - def filter_options( self, options, other_values ): + def filter_options(self, options, other_values): if self.ref: # find ref value value = other_values for ref_name in self.ref: assert ref_name in value, "Required dependency '%s' not found in incoming values" % ref_name - value = value.get( ref_name ) + value = value.get(ref_name) for attr_name in self.param_attribute: - value = getattr( value, attr_name ) - value = str( value ) + value = getattr(value, attr_name) + value = str(value) else: value = self.value - value = self.cast( value ) + value = self.cast(value) rval = [] for fields in options: try: - if self.keep == ( self.compare( self.cast( fields[self.column] ), value ) ): - rval.append( fields ) + if self.keep == (self.compare(self.cast(fields[self.column]), value)): + rval.append(fields) except Exception as e: log.debug(e) continue # likely a bad cast or column out of range return rval -class InsertColumnToolOutputActionOptionFilter( ToolOutputActionOptionFilter ): +class InsertColumnToolOutputActionOptionFilter(ToolOutputActionOptionFilter): tag = "insert_column" - def __init__( self, parent, elem ): - super( InsertColumnToolOutputActionOptionFilter, self ).__init__( parent, elem ) - self.ref = elem.get( 'ref', None ) + def __init__(self, parent, elem): + super(InsertColumnToolOutputActionOptionFilter, self).__init__(parent, elem) + self.ref = elem.get('ref', None) if self.ref: - self.ref = self.ref.split( '.' ) - self.value = elem.get( 'value', None ) + self.ref = self.ref.split('.') + self.value = elem.get('value', None) assert self.ref != self.value, "Required 'ref' or 'value' attribute missing from InsertColumnToolOutputActionOptionFilter" - self.column = elem.get( 'column', None ) # None is append + self.column = elem.get('column', None) # None is append if self.column: - self.column = int( self.column ) - self.iterate = util.string_as_bool( elem.get( "iterate", 'False' ) ) + self.column = int(self.column) + self.iterate = util.string_as_bool(elem.get("iterate", 'False')) - def filter_options( self, options, other_values ): + def filter_options(self, options, other_values): if self.ref: # find ref value value = other_values for ref_name in self.ref: assert ref_name in value, "Required dependency '%s' not found in incoming values" % ref_name - value = value.get( ref_name ) - value = str( value ) + value = value.get(ref_name) + value = str(value) else: value = self.value if self.iterate: - value = int( value ) + value = int(value) rval = [] for fields in options: if self.column is None: - rval.append( fields + [ str( value ) ] ) + rval.append(fields + [str(value)]) else: - fields = list( fields ) - fields.insert( self.column, str( value ) ) - rval.append( fields ) + fields = list(fields) + fields.insert(self.column, str(value)) + rval.append(fields) if self.iterate: value += 1 return rval -class MultipleSplitterFilter( ToolOutputActionOptionFilter ): +class MultipleSplitterFilter(ToolOutputActionOptionFilter): tag = "multiple_splitter" - def __init__( self, parent, elem ): - super( MultipleSplitterFilter, self ).__init__( parent, elem ) - self.column = elem.get( 'column', None ) + def __init__(self, parent, elem): + super(MultipleSplitterFilter, self).__init__(parent, elem) + self.column = elem.get('column', None) assert self.column is not None, "Required 'column' attribute missing from MultipleSplitterFilter" - self.column = int( self.column ) - self.separator = elem.get( "separator", "," ) + self.column = int(self.column) + self.separator = elem.get("separator", ",") - def filter_options( self, options, other_values ): + def filter_options(self, options, other_values): rval = [] for fields in options: - for field in fields[self.column].split( self.separator ): - rval.append( fields[0:self.column] + [field] + fields[self.column + 1:] ) + for field in fields[self.column].split(self.separator): + rval.append(fields[0:self.column] + [field] + fields[self.column + 1:]) return rval -class ColumnStripFilter( ToolOutputActionOptionFilter ): +class ColumnStripFilter(ToolOutputActionOptionFilter): tag = "column_strip" - def __init__( self, parent, elem ): - super( ColumnStripFilter, self ).__init__( parent, elem ) - self.column = elem.get( 'column', None ) + def __init__(self, parent, elem): + super(ColumnStripFilter, self).__init__(parent, elem) + self.column = elem.get('column', None) assert self.column is not None, "Required 'column' attribute missing from ColumnStripFilter" - self.column = int( self.column ) - self.strip = elem.get( "strip", None ) + self.column = int(self.column) + self.strip = elem.get("strip", None) - def filter_options( self, options, other_values ): + def filter_options(self, options, other_values): rval = [] for fields in options: - rval.append( fields[0:self.column] + [ fields[self.column].strip( self.strip ) ] + fields[self.column + 1:] ) + rval.append(fields[0:self.column] + [fields[self.column].strip(self.strip)] + fields[self.column + 1:]) return rval -class ColumnReplaceFilter( ToolOutputActionOptionFilter ): +class ColumnReplaceFilter(ToolOutputActionOptionFilter): tag = "column_replace" - def __init__( self, parent, elem ): - super( ColumnReplaceFilter, self ).__init__( parent, elem ) - self.old_column = elem.get( 'old_column', None ) - self.old_value = elem.get( "old_value", None ) - self.new_value = elem.get( "new_value", None ) - self.new_column = elem.get( 'new_column', None ) - assert ( bool( self.old_column ) ^ bool( self.old_value ) and bool( self.new_column ) ^ bool( self.new_value ) ), "Required 'old_column' or 'old_value' and 'new_column' or 'new_value' attribute missing from ColumnReplaceFilter" - self.column = elem.get( 'column', None ) + def __init__(self, parent, elem): + super(ColumnReplaceFilter, self).__init__(parent, elem) + self.old_column = elem.get('old_column', None) + self.old_value = elem.get("old_value", None) + self.new_value = elem.get("new_value", None) + self.new_column = elem.get('new_column', None) + assert (bool(self.old_column) ^ bool(self.old_value) and bool(self.new_column) ^ bool(self.new_value)), "Required 'old_column' or 'old_value' and 'new_column' or 'new_value' attribute missing from ColumnReplaceFilter" + self.column = elem.get('column', None) assert self.column is not None, "Required 'column' attribute missing from ColumnReplaceFilter" - self.column = int( self.column ) + self.column = int(self.column) if self.old_column is not None: - self.old_column = int( self.old_column ) + self.old_column = int(self.old_column) if self.new_column is not None: - self.new_column = int( self.new_column ) + self.new_column = int(self.new_column) - def filter_options( self, options, other_values ): + def filter_options(self, options, other_values): rval = [] for fields in options: if self.old_column: @@ -494,99 +494,99 @@ def filter_options( self, options, other_values ): new_value = fields[self.new_column] else: new_value = self.new_value - rval.append( fields[0:self.column] + [ fields[self.column].replace( old_value, new_value ) ] + fields[self.column + 1:] ) + rval.append(fields[0:self.column] + [fields[self.column].replace(old_value, new_value)] + fields[self.column + 1:]) return rval -class MetadataValueFilter( ToolOutputActionOptionFilter ): +class MetadataValueFilter(ToolOutputActionOptionFilter): tag = "metadata_value" - def __init__( self, parent, elem ): - super( MetadataValueFilter, self ).__init__( parent, elem ) - self.ref = elem.get( 'ref', None ) + def __init__(self, parent, elem): + super(MetadataValueFilter, self).__init__(parent, elem) + self.ref = elem.get('ref', None) assert self.ref is not None, "Required 'ref' attribute missing from MetadataValueFilter" - self.ref = self.ref.split( '.' ) - self.name = elem.get( 'name', None ) + self.ref = self.ref.split('.') + self.name = elem.get('name', None) assert self.name is not None, "Required 'name' attribute missing from MetadataValueFilter" - self.column = elem.get( 'column', None ) + self.column = elem.get('column', None) assert self.column is not None, "Required 'column' attribute missing from MetadataValueFilter" - self.column = int( self.column ) - self.keep = util.string_as_bool( elem.get( "keep", 'True' ) ) - self.compare = parse_compare_type( elem.get( 'compare', None ) ) + self.column = int(self.column) + self.keep = util.string_as_bool(elem.get("keep", 'True')) + self.compare = parse_compare_type(elem.get('compare', None)) - def filter_options( self, options, other_values ): + def filter_options(self, options, other_values): ref = other_values for ref_name in self.ref: assert ref_name in ref, "Required dependency '%s' not found in incoming values" % ref_name - ref = ref.get( ref_name ) - value = str( getattr( ref.metadata, self.name ) ) + ref = ref.get(ref_name) + value = str(getattr(ref.metadata, self.name)) rval = [] for fields in options: - if self.keep == ( self.compare( fields[self.column], value ) ): - rval.append( fields ) + if self.keep == (self.compare(fields[self.column], value)): + rval.append(fields) return rval -class BooleanFilter( ToolOutputActionOptionFilter ): +class BooleanFilter(ToolOutputActionOptionFilter): tag = "boolean" - def __init__( self, parent, elem ): - super( BooleanFilter, self ).__init__( parent, elem ) - self.column = elem.get( 'column', None ) + def __init__(self, parent, elem): + super(BooleanFilter, self).__init__(parent, elem) + self.column = elem.get('column', None) assert self.column is not None, "Required 'column' attribute missing from BooleanFilter" - self.column = int( self.column ) - self.keep = util.string_as_bool( elem.get( "keep", 'True' ) ) - self.cast = parse_cast_attribute( elem.get( "cast", None ) ) + self.column = int(self.column) + self.keep = util.string_as_bool(elem.get("keep", 'True')) + self.cast = parse_cast_attribute(elem.get("cast", None)) - def filter_options( self, options, other_values ): + def filter_options(self, options, other_values): rval = [] for fields in options: try: value = fields[self.column] - value = self.cast( value ) + value = self.cast(value) except: value = False # unable to cast or access value; treat as false - if self.keep == bool( value ): - rval.append( fields ) + if self.keep == bool(value): + rval.append(fields) return rval -class StringFunctionFilter( ToolOutputActionOptionFilter ): +class StringFunctionFilter(ToolOutputActionOptionFilter): tag = "string_function" - def __init__( self, parent, elem ): - super( StringFunctionFilter, self ).__init__( parent, elem ) - self.column = elem.get( 'column', None ) + def __init__(self, parent, elem): + super(StringFunctionFilter, self).__init__(parent, elem) + self.column = elem.get('column', None) assert self.column is not None, "Required 'column' attribute missing from StringFunctionFilter" - self.column = int( self.column ) - self.function = elem.get( "name", None ) - assert self.function in [ 'lower', 'upper' ], "Required function 'name' missing or invalid from StringFunctionFilter" # add function names as needed - self.function = getattr( string, self.function ) + self.column = int(self.column) + self.function = elem.get("name", None) + assert self.function in ['lower', 'upper'], "Required function 'name' missing or invalid from StringFunctionFilter" # add function names as needed + self.function = getattr(string, self.function) - def filter_options( self, options, other_values ): + def filter_options(self, options, other_values): rval = [] for fields in options: - rval.append( fields[0:self.column] + [ self.function( fields[self.column] ) ] + fields[self.column + 1:] ) + rval.append(fields[0:self.column] + [self.function(fields[self.column])] + fields[self.column + 1:]) return rval # tag to class lookups action_types = {} -for action_type in [ MetadataToolOutputAction, FormatToolOutputAction ]: - action_types[ action_type.tag ] = action_type +for action_type in [MetadataToolOutputAction, FormatToolOutputAction]: + action_types[action_type.tag] = action_type option_types = {} -for option_type in [ NullToolOutputActionOption, FromFileToolOutputActionOption, FromParamToolOutputActionOption, FromDataTableOutputActionOption ]: - option_types[ option_type.tag ] = option_type +for option_type in [NullToolOutputActionOption, FromFileToolOutputActionOption, FromParamToolOutputActionOption, FromDataTableOutputActionOption]: + option_types[option_type.tag] = option_type filter_types = {} -for filter_type in [ ParamValueToolOutputActionOptionFilter, InsertColumnToolOutputActionOptionFilter, MultipleSplitterFilter, ColumnStripFilter, MetadataValueFilter, BooleanFilter, StringFunctionFilter, ColumnReplaceFilter ]: - filter_types[ filter_type.tag ] = filter_type +for filter_type in [ParamValueToolOutputActionOptionFilter, InsertColumnToolOutputActionOptionFilter, MultipleSplitterFilter, ColumnStripFilter, MetadataValueFilter, BooleanFilter, StringFunctionFilter, ColumnReplaceFilter]: + filter_types[filter_type.tag] = filter_type # helper classes # determine cast function -def parse_cast_attribute( cast ): +def parse_cast_attribute(cast): if cast == 'string_as_bool': cast = util.string_as_bool elif cast == 'int': @@ -601,52 +601,52 @@ def cast(x): # comparison -def parse_compare_type( compare ): +def parse_compare_type(compare): if compare is None: compare = 'eq' assert compare in compare_types, "Invalid compare type specified: %s" % compare - return compare_types[ compare ] + return compare_types[compare] -def compare_eq( value1, value2 ): +def compare_eq(value1, value2): return value1 == value2 -def compare_neq( value1, value2 ): +def compare_neq(value1, value2): return value1 != value2 -def compare_gt( value1, value2 ): +def compare_gt(value1, value2): return value1 > value2 -def compare_gte( value1, value2 ): +def compare_gte(value1, value2): return value1 >= value2 -def compare_lt( value1, value2 ): +def compare_lt(value1, value2): return value1 < value2 -def compare_lte( value1, value2 ): +def compare_lte(value1, value2): return value1 <= value2 -def compare_in( value1, value2 ): +def compare_in(value1, value2): return value1 in value2 -def compare_startswith( value1, value2 ): - return value1.startswith( value2 ) +def compare_startswith(value1, value2): + return value1.startswith(value2) -def compare_endswith( value1, value2 ): - return value1.endswith( value2 ) +def compare_endswith(value1, value2): + return value1.endswith(value2) -def compare_re_search( value1, value2 ): +def compare_re_search(value1, value2): # checks pattern=value2 in value1 - return bool( re.search( value2, value1 ) ) + return bool(re.search(value2, value1)) compare_types = { diff --git a/lib/galaxy/tools/parser/output_collection_def.py b/lib/galaxy/tools/parser/output_collection_def.py index cd175a2f6cc5..d80f52a3042c 100644 --- a/lib/galaxy/tools/parser/output_collection_def.py +++ b/lib/galaxy/tools/parser/output_collection_def.py @@ -23,33 +23,64 @@ LEGACY_DEFAULT_DBKEY = None # don't use __input__ for legacy default collection -def dataset_collector_descriptions_from_elem( elem, legacy=True ): - primary_dataset_elems = elem.findall( "discover_datasets" ) - if len(primary_dataset_elems) == 0 and legacy: - return [ DEFAULT_DATASET_COLLECTOR_DESCRIPTION ] +def dataset_collector_descriptions_from_elem(elem, legacy=True): + primary_dataset_elems = elem.findall("discover_datasets") + num_discover_dataset_blocks = len(primary_dataset_elems) + if num_discover_dataset_blocks == 0 and legacy: + collectors = [DEFAULT_DATASET_COLLECTOR_DESCRIPTION] else: - return map( lambda elem: DatasetCollectionDescription( **elem.attrib ), primary_dataset_elems ) + collectors = map(lambda elem: dataset_collection_description(**elem.attrib), primary_dataset_elems) + if num_discover_dataset_blocks > 1: + for collector in collectors: + if collector.discover_via == "tool_provided_metadata": + raise Exception("Cannot specify more than one discover dataset condition if any of them specify tool_provided_metadata.") -def dataset_collector_descriptions_from_list( discover_datasets_dicts ): - return map( lambda kwds: DatasetCollectionDescription( **kwds ), discover_datasets_dicts ) + return collectors + + +def dataset_collector_descriptions_from_list(discover_datasets_dicts): + return map(lambda kwds: dataset_collection_description(**kwds), discover_datasets_dicts) + + +def dataset_collection_description(**kwargs): + if asbool(kwargs.get("from_provided_metadata", False)): + for key in ["pattern", "sort_by"]: + if kwargs.get(key): + raise Exception("Cannot specify attribute [%s] if from_provided_metadata is True" % key) + return ToolProvidedMetadataDatasetCollection(**kwargs) + else: + return FilePatternDatasetCollectionDescription(**kwargs) class DatasetCollectionDescription(object): - def __init__( self, **kwargs ): - pattern = kwargs.get( "pattern", "__default__" ) + def __init__(self, **kwargs): + self.default_dbkey = kwargs.get("dbkey", INPUT_DBKEY_TOKEN) + self.default_ext = kwargs.get("ext", None) + if self.default_ext is None and "format" in kwargs: + self.default_ext = kwargs.get("format") + self.default_visible = asbool(kwargs.get("visible", None)) + self.assign_primary_output = asbool(kwargs.get('assign_primary_output', False)) + self.directory = kwargs.get("directory", None) + + +class ToolProvidedMetadataDatasetCollection(DatasetCollectionDescription): + + discover_via = "tool_provided_metadata" + + +class FilePatternDatasetCollectionDescription(DatasetCollectionDescription): + + discover_via = "pattern" + + def __init__(self, **kwargs): + super(FilePatternDatasetCollectionDescription, self).__init__(**kwargs) + pattern = kwargs.get("pattern", "__default__") if pattern in NAMED_PATTERNS: - pattern = NAMED_PATTERNS.get( pattern ) + pattern = NAMED_PATTERNS.get(pattern) self.pattern = pattern - self.default_dbkey = kwargs.get( "dbkey", INPUT_DBKEY_TOKEN ) - self.default_ext = kwargs.get( "ext", None ) - if self.default_ext is None and "format" in kwargs: - self.default_ext = kwargs.get( "format" ) - self.default_visible = asbool( kwargs.get( "visible", None ) ) - self.directory = kwargs.get( "directory", None ) - self.assign_primary_output = asbool( kwargs.get( 'assign_primary_output', False ) ) - sort_by = kwargs.get( "sort_by", DEFAULT_SORT_BY ) + sort_by = kwargs.get("sort_by", DEFAULT_SORT_BY) if sort_by.startswith("reverse_"): self.sort_reverse = True sort_by = sort_by[len("reverse_"):] @@ -70,6 +101,6 @@ def __init__( self, **kwargs ): self.sort_comp = sort_comp -DEFAULT_DATASET_COLLECTOR_DESCRIPTION = DatasetCollectionDescription( +DEFAULT_DATASET_COLLECTOR_DESCRIPTION = FilePatternDatasetCollectionDescription( default_dbkey=LEGACY_DEFAULT_DBKEY, ) diff --git a/lib/galaxy/tools/parser/output_objects.py b/lib/galaxy/tools/parser/output_objects.py index 32421f288fc6..605eb807c78a 100644 --- a/lib/galaxy/tools/parser/output_objects.py +++ b/lib/galaxy/tools/parser/output_objects.py @@ -2,10 +2,10 @@ from galaxy.util.odict import odict -class ToolOutputBase( Dictifiable, object ): +class ToolOutputBase(Dictifiable, object): - def __init__( self, name, label=None, filters=None, hidden=False ): - super( ToolOutputBase, self ).__init__() + def __init__(self, name, label=None, filters=None, hidden=False): + super(ToolOutputBase, self).__init__() self.name = name self.label = label self.filters = filters or [] @@ -13,7 +13,7 @@ def __init__( self, name, label=None, filters=None, hidden=False ): self.collection = False -class ToolOutput( ToolOutputBase ): +class ToolOutput(ToolOutputBase): """ Represents an output datasets produced by a tool. For backward compatibility this behaves as if it were the tuple:: @@ -21,12 +21,12 @@ class ToolOutput( ToolOutputBase ): (format, metadata_source, parent) """ - dict_collection_visible_keys = ( 'name', 'format', 'label', 'hidden' ) + dict_collection_visible_keys = ('name', 'format', 'label', 'hidden') - def __init__( self, name, format=None, format_source=None, metadata_source=None, - parent=None, label=None, filters=None, actions=None, hidden=False, - implicit=False ): - super( ToolOutput, self ).__init__( name, label=label, filters=filters, hidden=hidden ) + def __init__(self, name, format=None, format_source=None, metadata_source=None, + parent=None, label=None, filters=None, actions=None, hidden=False, + implicit=False): + super(ToolOutput, self).__init__(name, label=label, filters=filters, hidden=hidden) self.format = format self.format_source = format_source self.metadata_source = metadata_source @@ -40,10 +40,10 @@ def __init__( self, name, format=None, format_source=None, metadata_source=None, # Tuple emulation - def __len__( self ): + def __len__(self): return 3 - def __getitem__( self, index ): + def __getitem__(self, index): if index == 0: return self.format elif index == 1: @@ -51,13 +51,13 @@ def __getitem__( self, index ): elif index == 2: return self.parent else: - raise IndexError( index ) + raise IndexError(index) - def __iter__( self ): - return iter( ( self.format, self.metadata_source, self.parent ) ) + def __iter__(self): + return iter((self.format, self.metadata_source, self.parent)) - def to_dict( self, view='collection', value_mapper=None, app=None ): - as_dict = super( ToolOutput, self ).to_dict( view=view, value_mapper=value_mapper ) + def to_dict(self, view='collection', value_mapper=None, app=None): + as_dict = super(ToolOutput, self).to_dict(view=view, value_mapper=value_mapper) format = self.format if format and format != "input" and app: edam_format = app.datatypes_registry.edam_formats.get(self.format) @@ -67,7 +67,7 @@ def to_dict( self, view='collection', value_mapper=None, app=None ): return as_dict -class ToolOutputCollection( ToolOutputBase ): +class ToolOutputCollection(ToolOutputBase): """ Represents a HistoryDatasetCollectionAssociation of output datasets produced by a tool. @@ -96,7 +96,7 @@ def __init__( inherit_format=False, inherit_metadata=False ): - super( ToolOutputCollection, self ).__init__( name, label=label, filters=filters, hidden=hidden ) + super(ToolOutputCollection, self).__init__(name, label=label, filters=filters, hidden=hidden) self.collection = True self.default_format = default_format self.structure = structure @@ -109,22 +109,18 @@ def __init__( self.format_source = default_format_source self.change_format = [] # TODO - def known_outputs( self, inputs, type_registry ): + def known_outputs(self, inputs, type_registry): if self.dynamic_structure: return [] # This line is probably not right - should verify structured_like # or have outputs and all outputs have name. - if len( self.outputs ) > 1: + if len(self.outputs) > 1: output_parts = [ToolOutputCollectionPart(self, k, v) for k, v in self.outputs.items()] else: - # either must have specified structured_like or something worse - if self.structure.structured_like: - collection_prototype = inputs[ self.structure.structured_like ].collection - else: - collection_prototype = type_registry.prototype( self.structure.collection_type ) + collection_prototype = self.structure.collection_prototype(inputs, type_registry) - def prototype_dataset_element_to_output( element, parent_ids=[] ): + def prototype_dataset_element_to_output(element, parent_ids=[]): name = element.element_identifier format = self.default_format if self.inherit_format: @@ -145,12 +141,12 @@ def prototype_dataset_element_to_output( element, parent_ids=[] ): parent_ids=parent_ids, ) - def prototype_collection_to_output( collection_prototype, parent_ids=[] ): + def prototype_collection_to_output(collection_prototype, parent_ids=[]): output_parts = [] for element in collection_prototype.elements: element_parts = [] if not element.is_collection: - element_parts.append(prototype_dataset_element_to_output( element, parent_ids )) + element_parts.append(prototype_dataset_element_to_output(element, parent_ids)) else: new_parent_ids = parent_ids[:] + [element.element_identifier] element_parts.extend(prototype_collection_to_output(element.element_object, new_parent_ids)) @@ -158,7 +154,7 @@ def prototype_collection_to_output( collection_prototype, parent_ids=[] ): return output_parts - output_parts = prototype_collection_to_output( collection_prototype ) + output_parts = prototype_collection_to_output(collection_prototype) return output_parts @@ -173,14 +169,14 @@ def dataset_collector_descriptions(self): return self.structure.dataset_collector_descriptions -class ToolOutputCollectionStructure( object ): +class ToolOutputCollectionStructure(object): def __init__( self, collection_type, - collection_type_source, - structured_like, - dataset_collector_descriptions, + collection_type_source=None, + structured_like=None, + dataset_collector_descriptions=None, ): self.collection_type = collection_type self.collection_type_source = collection_type_source @@ -189,32 +185,40 @@ def __init__( if collection_type and collection_type_source: raise ValueError("Cannot set both type and type_source on collection output.") if collection_type is None and structured_like is None and dataset_collector_descriptions is None and collection_type_source is None: - raise ValueError( "Output collection types must be specify type of structured_like" ) + raise ValueError("Output collection types must be specify type of structured_like") if dataset_collector_descriptions and structured_like: - raise ValueError( "Cannot specify dynamic structure (discovered_datasets) and structured_like attribute." ) + raise ValueError("Cannot specify dynamic structure (discovered_datasets) and structured_like attribute.") self.dynamic = dataset_collector_descriptions is not None + def collection_prototype(self, inputs, type_registry): + # either must have specified structured_like or something worse + if self.structured_like: + collection_prototype = inputs[self.structured_like].collection + else: + collection_prototype = type_registry.prototype(self.collection_type) + return collection_prototype + -class ToolOutputCollectionPart( object ): +class ToolOutputCollectionPart(object): - def __init__( self, output_collection_def, element_identifier, output_def, parent_ids=[] ): + def __init__(self, output_collection_def, element_identifier, output_def, parent_ids=[]): self.output_collection_def = output_collection_def self.element_identifier = element_identifier self.output_def = output_def self.parent_ids = parent_ids @property - def effective_output_name( self ): + def effective_output_name(self): name = self.output_collection_def.name part_name = self.element_identifier - effective_output_name = "%s|__part__|%s" % ( name, part_name ) + effective_output_name = "%s|__part__|%s" % (name, part_name) return effective_output_name @staticmethod - def is_named_collection_part_name( name ): + def is_named_collection_part_name(name): return "|__part__|" in name @staticmethod - def split_output_name( name ): - assert ToolOutputCollectionPart.is_named_collection_part_name( name ) + def split_output_name(name): + assert ToolOutputCollectionPart.is_named_collection_part_name(name) return name.split("|__part__|") diff --git a/lib/galaxy/tools/parser/xml.py b/lib/galaxy/tools/parser/xml.py index 00e9d0eee083..114b45988792 100644 --- a/lib/galaxy/tools/parser/xml.py +++ b/lib/galaxy/tools/parser/xml.py @@ -33,7 +33,7 @@ ) -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) class XmlToolSource(ToolSource): @@ -54,62 +54,62 @@ def parse_id(self): def parse_tool_module(self): root = self.root - if root.find( "type" ) is not None: - type_elem = root.find( "type" ) - module = type_elem.get( 'module', 'galaxy.tools' ) - cls = type_elem.get( 'class' ) + if root.find("type") is not None: + type_elem = root.find("type") + module = type_elem.get('module', 'galaxy.tools') + cls = type_elem.get('class') return module, cls return None def parse_action_module(self): root = self.root - action_elem = root.find( "action" ) + action_elem = root.find("action") if action_elem is not None: - module = action_elem.get( 'module' ) - cls = action_elem.get( 'class' ) + module = action_elem.get('module') + cls = action_elem.get('class') return module, cls else: return None def parse_tool_type(self): root = self.root - if root.get( 'tool_type', None ) is not None: - return root.get( 'tool_type' ) + if root.get('tool_type', None) is not None: + return root.get('tool_type') def parse_name(self): - return self.root.get( "name" ) + return self.root.get("name") def parse_edam_operations(self): edam_ops = self.root.find("edam_operations") if edam_ops is None: return [] - return [ edam_op.text for edam_op in edam_ops.findall("edam_operation") ] + return [edam_op.text for edam_op in edam_ops.findall("edam_operation")] def parse_edam_topics(self): edam_topics = self.root.find("edam_topics") if edam_topics is None: return [] - return [ edam_topic.text for edam_topic in edam_topics.findall("edam_topic") ] + return [edam_topic.text for edam_topic in edam_topics.findall("edam_topic")] def parse_description(self): return xml_text(self.root, "description") def parse_is_multi_byte(self): - return self._get_attribute_as_bool( "is_multi_byte", self.default_is_multi_byte ) + return self._get_attribute_as_bool("is_multi_byte", self.default_is_multi_byte) def parse_display_interface(self, default): - return self._get_attribute_as_bool( "display_interface", default ) + return self._get_attribute_as_bool("display_interface", default) def parse_require_login(self, default): - return self._get_attribute_as_bool( "require_login", default ) + return self._get_attribute_as_bool("require_login", default) def parse_request_param_translation_elem(self): - return self.root.find( "request_param_translation" ) + return self.root.find("request_param_translation") def parse_command(self): command_el = self._command_el - return ( ( command_el is not None ) and command_el.text ) or None + return ((command_el is not None) and command_el.text) or None def parse_environment_variables(self): environment_variables_el = self.root.find("environment_variables") @@ -146,7 +146,7 @@ def parse_version_command(self): def parse_version_command_interpreter(self): if self.parse_version_command() is not None: version_cmd = self.root.find("version_command") - version_cmd_interpreter = version_cmd.get( "interpreter", None ) + version_cmd_interpreter = version_cmd.get("interpreter", None) if version_cmd_interpreter: return version_cmd_interpreter return None @@ -186,10 +186,10 @@ def _get_option_value(self, key, default): def _command_el(self): return self.root.find("command") - def _get_attribute_as_bool( self, attribute, default, elem=None ): + def _get_attribute_as_bool(self, attribute, default, elem=None): if elem is None: elem = self.root - return string_as_bool( elem.get( attribute, default ) ) + return string_as_bool(elem.get(attribute, default)) def parse_requirements_and_containers(self): return requirements.parse_requirements_from_xml(self.root) @@ -197,6 +197,26 @@ def parse_requirements_and_containers(self): def parse_input_pages(self): return XmlPagesSource(self.root) + def parse_provided_metadata_style(self): + style = None + out_elem = self.root.find("outputs") + if out_elem and "provided_metadata_style" in out_elem.attrib: + style = out_elem.attrib["provided_metadata_style"] + + if style is None: + style = "legacy" if self.parse_profile() < "17.09" else "default" + + assert style in ["legacy", "default"] + return style + + def parse_provided_metadata_file(self): + provided_metadata_file = "galaxy.json" + out_elem = self.root.find("outputs") + if out_elem and "provided_metadata_file" in out_elem.attrib: + provided_metadata_file = out_elem.attrib["provided_metadata_file"] + + return provided_metadata_file + def parse_outputs(self, tool): out_elem = self.root.find("outputs") outputs = odict() @@ -214,24 +234,24 @@ def _parse(data_elem, **kwds): map(_parse, out_elem.findall("data")) for collection_elem in out_elem.findall("collection"): - name = collection_elem.get( "name" ) - label = xml_text( collection_elem, "label" ) - default_format = collection_elem.get( "format", "data" ) - collection_type = collection_elem.get( "type", None ) - collection_type_source = collection_elem.get( "type_source", None ) - structured_like = collection_elem.get( "structured_like", None ) + name = collection_elem.get("name") + label = xml_text(collection_elem, "label") + default_format = collection_elem.get("format", "data") + collection_type = collection_elem.get("type", None) + collection_type_source = collection_elem.get("type_source", None) + structured_like = collection_elem.get("structured_like", None) inherit_format = False inherit_metadata = False if structured_like: - inherit_format = string_as_bool( collection_elem.get( "inherit_format", None ) ) - inherit_metadata = string_as_bool( collection_elem.get( "inherit_metadata", None ) ) - default_format_source = collection_elem.get( "format_source", None ) - default_metadata_source = collection_elem.get( "metadata_source", "" ) - filters = collection_elem.findall( 'filter' ) + inherit_format = string_as_bool(collection_elem.get("inherit_format", None)) + inherit_metadata = string_as_bool(collection_elem.get("inherit_metadata", None)) + default_format_source = collection_elem.get("format_source", None) + default_metadata_source = collection_elem.get("metadata_source", "") + filters = collection_elem.findall('filter') dataset_collector_descriptions = None - if collection_elem.find( "discover_datasets" ) is not None: - dataset_collector_descriptions = dataset_collector_descriptions_from_elem( collection_elem, legacy=False ) + if collection_elem.find("discover_datasets") is not None: + dataset_collector_descriptions = dataset_collector_descriptions_from_elem(collection_elem, legacy=False) structure = ToolOutputCollectionStructure( collection_type=collection_type, collection_type_source=collection_type_source, @@ -265,7 +285,7 @@ def _parse(data_elem, **kwds): assert data del data_dict[output_name] output_collection.outputs[output_name] = data - output_collections[ name ] = output_collection + output_collections[name] = output_collection for output_def in data_dict.values(): outputs[output_def.name] = output_def @@ -279,9 +299,9 @@ def _parse_output( default_format_source=None, default_metadata_source="", ): - output = ToolOutput( data_elem.get("name") ) + output = ToolOutput(data_elem.get("name")) output_format = data_elem.get("format", default_format) - auto_format = string_as_bool( data_elem.get( "auto_format", "false" ) ) + auto_format = string_as_bool(data_elem.get("auto_format", "false")) if auto_format and output_format != "data": raise ValueError("Setting format and auto_format is not supported at this time.") elif auto_format: @@ -289,16 +309,17 @@ def _parse_output( output.format = output_format output.change_format = data_elem.findall("change_format") output.format_source = data_elem.get("format_source", default_format_source) + output.default_identifier_source = data_elem.get("default_identifier_source", 'None') output.metadata_source = data_elem.get("metadata_source", default_metadata_source) output.parent = data_elem.get("parent", None) - output.label = xml_text( data_elem, "label" ) - output.count = int( data_elem.get("count", 1) ) - output.filters = data_elem.findall( 'filter' ) + output.label = xml_text(data_elem, "label") + output.count = int(data_elem.get("count", 1)) + output.filters = data_elem.findall('filter') output.tool = tool output.from_work_dir = data_elem.get("from_work_dir", None) - output.hidden = string_as_bool( data_elem.get("hidden", "") ) - output.actions = ToolOutputActionGroup( output, data_elem.find( 'actions' ) ) - output.dataset_collector_descriptions = dataset_collector_descriptions_from_elem( data_elem, legacy=self.legacy_defaults ) + output.hidden = string_as_bool(data_elem.get("hidden", "")) + output.actions = ToolOutputActionGroup(output, data_elem.find('actions')) + output.dataset_collector_descriptions = dataset_collector_descriptions_from_elem(data_elem, legacy=self.legacy_defaults) return output def parse_stdio(self): @@ -329,7 +350,7 @@ def parse_strict_shell(self): return True def parse_help(self): - help_elem = self.root.find( 'help' ) + help_elem = self.root.find('help') return help_elem.text if help_elem is not None else None def parse_tests_to_dict(self): @@ -363,9 +384,9 @@ def _test_elem_to_dict(test_elem, i): output_collections=__parse_output_collection_elems(test_elem), inputs=__parse_input_elems(test_elem, i), expect_num_outputs=test_elem.get("expect_num_outputs"), - command=__parse_assert_list_from_elem( test_elem.find("assert_command") ), - stdout=__parse_assert_list_from_elem( test_elem.find("assert_stdout") ), - stderr=__parse_assert_list_from_elem( test_elem.find("assert_stderr") ), + command=__parse_assert_list_from_elem(test_elem.find("assert_command")), + stdout=__parse_assert_list_from_elem(test_elem.find("assert_stdout")), + stderr=__parse_assert_list_from_elem(test_elem.find("assert_stderr")), expect_exit_code=test_elem.get("expect_exit_code"), expect_failure=string_as_bool(test_elem.get("expect_failure", False)), maxseconds=test_elem.get("maxseconds", None), @@ -375,105 +396,105 @@ def _test_elem_to_dict(test_elem, i): def __parse_input_elems(test_elem, i): - __expand_input_elems( test_elem ) - return __parse_inputs_elems( test_elem, i ) + __expand_input_elems(test_elem) + return __parse_inputs_elems(test_elem, i) -def __parse_output_elems( test_elem ): +def __parse_output_elems(test_elem): outputs = [] - for output_elem in test_elem.findall( "output" ): - name, file, attributes = __parse_output_elem( output_elem ) - outputs.append( ( name, file, attributes ) ) + for output_elem in test_elem.findall("output"): + name, file, attributes = __parse_output_elem(output_elem) + outputs.append((name, file, attributes)) return outputs -def __parse_output_elem( output_elem ): - attrib = dict( output_elem.attrib ) - name = attrib.pop( 'name', None ) +def __parse_output_elem(output_elem): + attrib = dict(output_elem.attrib) + name = attrib.pop('name', None) if name is None: - raise Exception( "Test output does not have a 'name'" ) + raise Exception("Test output does not have a 'name'") - file, attributes = __parse_test_attributes( output_elem, attrib, parse_discovered_datasets=True ) + file, attributes = __parse_test_attributes(output_elem, attrib, parse_discovered_datasets=True) return name, file, attributes -def __parse_command_elem( test_elem ): +def __parse_command_elem(test_elem): assert_elem = test_elem.find("command") - return __parse_assert_list_from_elem( assert_elem ) + return __parse_assert_list_from_elem(assert_elem) -def __parse_output_collection_elems( test_elem ): +def __parse_output_collection_elems(test_elem): output_collections = [] - for output_collection_elem in test_elem.findall( "output_collection" ): - output_collection_def = __parse_output_collection_elem( output_collection_elem ) - output_collections.append( output_collection_def ) + for output_collection_elem in test_elem.findall("output_collection"): + output_collection_def = __parse_output_collection_elem(output_collection_elem) + output_collections.append(output_collection_def) return output_collections -def __parse_output_collection_elem( output_collection_elem ): - attrib = dict( output_collection_elem.attrib ) - name = attrib.pop( 'name', None ) +def __parse_output_collection_elem(output_collection_elem): + attrib = dict(output_collection_elem.attrib) + name = attrib.pop('name', None) if name is None: - raise Exception( "Test output collection does not have a 'name'" ) - element_tests = __parse_element_tests( output_collection_elem ) - return TestCollectionOutputDef( name, attrib, element_tests ) + raise Exception("Test output collection does not have a 'name'") + element_tests = __parse_element_tests(output_collection_elem) + return TestCollectionOutputDef(name, attrib, element_tests) -def __parse_element_tests( parent_element ): +def __parse_element_tests(parent_element): element_tests = {} for element in parent_element.findall("element"): - element_attrib = dict( element.attrib ) - identifier = element_attrib.pop( 'name', None ) + element_attrib = dict(element.attrib) + identifier = element_attrib.pop('name', None) if identifier is None: - raise Exception( "Test primary dataset does not have a 'identifier'" ) - element_tests[ identifier ] = __parse_test_attributes( element, element_attrib, parse_elements=True ) + raise Exception("Test primary dataset does not have a 'identifier'") + element_tests[identifier] = __parse_test_attributes(element, element_attrib, parse_elements=True) return element_tests -def __parse_test_attributes( output_elem, attrib, parse_elements=False, parse_discovered_datasets=False ): - assert_list = __parse_assert_list( output_elem ) +def __parse_test_attributes(output_elem, attrib, parse_elements=False, parse_discovered_datasets=False): + assert_list = __parse_assert_list(output_elem) # Allow either file or value to specify a target file to compare result with # file was traditionally used by outputs and value by extra files. - file = attrib.pop( 'file', attrib.pop( 'value', None ) ) + file = attrib.pop('file', attrib.pop('value', None)) # File no longer required if an list of assertions was present. attributes = {} # Method of comparison - attributes['compare'] = attrib.pop( 'compare', 'diff' ).lower() + attributes['compare'] = attrib.pop('compare', 'diff').lower() # Number of lines to allow to vary in logs (for dates, etc) - attributes['lines_diff'] = int( attrib.pop( 'lines_diff', '0' ) ) + attributes['lines_diff'] = int(attrib.pop('lines_diff', '0')) # Allow a file size to vary if sim_size compare - attributes['delta'] = int( attrib.pop( 'delta', '10000' ) ) - attributes['sort'] = string_as_bool( attrib.pop( 'sort', False ) ) - attributes['decompress'] = string_as_bool( attrib.pop( 'decompress', False ) ) + attributes['delta'] = int(attrib.pop('delta', '10000')) + attributes['sort'] = string_as_bool(attrib.pop('sort', False)) + attributes['decompress'] = string_as_bool(attrib.pop('decompress', False)) extra_files = [] if 'ftype' in attrib: attributes['ftype'] = attrib['ftype'] - for extra in output_elem.findall( 'extra_files' ): - extra_files.append( __parse_extra_files_elem( extra ) ) + for extra in output_elem.findall('extra_files'): + extra_files.append(__parse_extra_files_elem(extra)) metadata = {} - for metadata_elem in output_elem.findall( 'metadata' ): - metadata[ metadata_elem.get('name') ] = metadata_elem.get( 'value' ) + for metadata_elem in output_elem.findall('metadata'): + metadata[metadata_elem.get('name')] = metadata_elem.get('value') md5sum = attrib.get("md5", None) checksum = attrib.get("checksum", None) element_tests = {} if parse_elements: - element_tests = __parse_element_tests( output_elem ) + element_tests = __parse_element_tests(output_elem) primary_datasets = {} if parse_discovered_datasets: - for primary_elem in ( output_elem.findall( "discovered_dataset" ) or [] ): - primary_attrib = dict( primary_elem.attrib ) - designation = primary_attrib.pop( 'designation', None ) + for primary_elem in (output_elem.findall("discovered_dataset") or []): + primary_attrib = dict(primary_elem.attrib) + designation = primary_attrib.pop('designation', None) if designation is None: - raise Exception( "Test primary dataset does not have a 'designation'" ) - primary_datasets[ designation ] = __parse_test_attributes( primary_elem, primary_attrib ) + raise Exception("Test primary dataset does not have a 'designation'") + primary_datasets[designation] = __parse_test_attributes(primary_elem, primary_attrib) has_checksum = md5sum or checksum has_nested_tests = extra_files or element_tests or primary_datasets if not (assert_list or file or metadata or has_checksum or has_nested_tests): - raise Exception( "Test output defines nothing to check (e.g. must have a 'file' check against, assertions to check, metadata or checksum tests, etc...)") + raise Exception("Test output defines nothing to check (e.g. must have a 'file' check against, assertions to check, metadata or checksum tests, etc...)") attributes['assert_list'] = assert_list attributes['extra_files'] = extra_files attributes['metadata'] = metadata @@ -484,21 +505,21 @@ def __parse_test_attributes( output_elem, attrib, parse_elements=False, parse_di return file, attributes -def __parse_assert_list( output_elem ): +def __parse_assert_list(output_elem): assert_elem = output_elem.find("assert_contents") - return __parse_assert_list_from_elem( assert_elem ) + return __parse_assert_list_from_elem(assert_elem) -def __parse_assert_list_from_elem( assert_elem ): +def __parse_assert_list_from_elem(assert_elem): assert_list = None def convert_elem(elem): """ Converts and XML element to a dictionary format, used by assertion checking code. """ tag = elem.tag - attributes = dict( elem.attrib ) + attributes = dict(elem.attrib) converted_children = [] for child_elem in elem: - converted_children.append( convert_elem(child_elem) ) + converted_children.append(convert_elem(child_elem)) return {"tag": tag, "attributes": attributes, "children": converted_children} if assert_elem is not None: assert_list = [] @@ -520,75 +541,75 @@ def __parse_extra_files_elem(extra): return extra_type, extra_value, extra_name, extra_attributes -def __expand_input_elems( root_elem, prefix="" ): - __append_prefix_to_params( root_elem, prefix ) +def __expand_input_elems(root_elem, prefix=""): + __append_prefix_to_params(root_elem, prefix) - repeat_elems = root_elem.findall( 'repeat' ) + repeat_elems = root_elem.findall('repeat') indices = {} for repeat_elem in repeat_elems: - name = repeat_elem.get( "name" ) + name = repeat_elem.get("name") if name not in indices: - indices[ name ] = 0 + indices[name] = 0 index = 0 else: - index = indices[ name ] + 1 - indices[ name ] = index + index = indices[name] + 1 + indices[name] = index - new_prefix = __prefix_join( prefix, name, index=index ) - __expand_input_elems( repeat_elem, new_prefix ) - __pull_up_params( root_elem, repeat_elem ) - root_elem.remove( repeat_elem ) + new_prefix = __prefix_join(prefix, name, index=index) + __expand_input_elems(repeat_elem, new_prefix) + __pull_up_params(root_elem, repeat_elem) + root_elem.remove(repeat_elem) - cond_elems = root_elem.findall( 'conditional' ) + cond_elems = root_elem.findall('conditional') for cond_elem in cond_elems: - new_prefix = __prefix_join( prefix, cond_elem.get( "name" ) ) - __expand_input_elems( cond_elem, new_prefix ) - __pull_up_params( root_elem, cond_elem ) - root_elem.remove( cond_elem ) + new_prefix = __prefix_join(prefix, cond_elem.get("name")) + __expand_input_elems(cond_elem, new_prefix) + __pull_up_params(root_elem, cond_elem) + root_elem.remove(cond_elem) - section_elems = root_elem.findall( 'section' ) + section_elems = root_elem.findall('section') for section_elem in section_elems: - new_prefix = __prefix_join( prefix, section_elem.get( "name" ) ) - __expand_input_elems( section_elem, new_prefix ) - __pull_up_params( root_elem, section_elem ) - root_elem.remove( section_elem ) + new_prefix = __prefix_join(prefix, section_elem.get("name")) + __expand_input_elems(section_elem, new_prefix) + __pull_up_params(root_elem, section_elem) + root_elem.remove(section_elem) -def __append_prefix_to_params( elem, prefix ): - for param_elem in elem.findall( 'param' ): - param_elem.set( "name", __prefix_join( prefix, param_elem.get( "name" ) ) ) +def __append_prefix_to_params(elem, prefix): + for param_elem in elem.findall('param'): + param_elem.set("name", __prefix_join(prefix, param_elem.get("name"))) -def __pull_up_params( parent_elem, child_elem ): - for param_elem in child_elem.findall( 'param' ): - parent_elem.append( param_elem ) - child_elem.remove( param_elem ) +def __pull_up_params(parent_elem, child_elem): + for param_elem in child_elem.findall('param'): + parent_elem.append(param_elem) + child_elem.remove(param_elem) -def __prefix_join( prefix, name, index=None ): - name = name if index is None else "%s_%d" % ( name, index ) - return name if not prefix else "%s|%s" % ( prefix, name ) +def __prefix_join(prefix, name, index=None): + name = name if index is None else "%s_%d" % (name, index) + return name if not prefix else "%s|%s" % (prefix, name) -def _copy_to_dict_if_present( elem, rval, attributes ): +def _copy_to_dict_if_present(elem, rval, attributes): for attribute in attributes: if attribute in elem.attrib: rval[attribute] = elem.get(attribute) return rval -def __parse_inputs_elems( test_elem, i ): +def __parse_inputs_elems(test_elem, i): raw_inputs = [] - for param_elem in test_elem.findall( "param" ): - name, value, attrib = __parse_param_elem( param_elem, i ) - raw_inputs.append( ( name, value, attrib ) ) + for param_elem in test_elem.findall("param"): + name, value, attrib = __parse_param_elem(param_elem, i) + raw_inputs.append((name, value, attrib)) return raw_inputs -def __parse_param_elem( param_elem, i=0 ): - attrib = dict( param_elem.attrib ) +def __parse_param_elem(param_elem, i=0): + attrib = dict(param_elem.attrib) if 'values' in attrib: - value = attrib[ 'values' ].split( ',' ) + value = attrib['values'].split(',') elif 'value' in attrib: value = attrib['value'] else: @@ -606,27 +627,27 @@ def __parse_param_elem( param_elem, i=0 ): composite_data_name = None for child in attrib['children']: if child.tag == 'composite_data': - attrib['composite_data'].append( child ) + attrib['composite_data'].append(child) if composite_data_name is None: # Generate a unique name; each test uses a # fresh history. composite_data_name = '_COMPOSITE_RENAMED_t%d_%s' \ - % ( i, uuid.uuid1().hex ) + % (i, uuid.uuid1().hex) elif child.tag == 'metadata': - attrib['metadata'].append( child ) + attrib['metadata'].append(child) elif child.tag == 'metadata': - attrib['metadata'].append( child ) + attrib['metadata'].append(child) elif child.tag == 'edit_attributes': - attrib['edit_attributes'].append( child ) + attrib['edit_attributes'].append(child) elif child.tag == 'collection': - attrib[ 'collection' ] = TestCollectionDef( child, __parse_param_elem ) + attrib['collection'] = TestCollectionDef(child, __parse_param_elem) if composite_data_name: # Composite datasets need implicit renaming; # inserted at front of list so explicit declarations # take precedence - attrib['edit_attributes'].insert( 0, { 'type': 'name', 'value': composite_data_name } ) - name = attrib.pop( 'name' ) - return ( name, value, attrib ) + attrib['edit_attributes'].insert(0, {'type': 'name', 'value': composite_data_name}) + name = attrib.pop('name') + return (name, value, attrib) class StdioParser(object): @@ -640,13 +661,13 @@ def __init__(self, root): # multiples. # For every stdio element, add all of the exit_code and regex # subelements that we find: - for stdio_elem in ( root.findall( 'stdio' ) ): - self.parse_stdio_exit_codes( stdio_elem ) - self.parse_stdio_regexes( stdio_elem ) + for stdio_elem in (root.findall('stdio')): + self.parse_stdio_exit_codes(stdio_elem) + self.parse_stdio_regexes(stdio_elem) except Exception: - log.error( "Exception in parse_stdio! " + str(sys.exc_info()) ) + log.error("Exception in parse_stdio! " + str(sys.exc_info())) - def parse_stdio_exit_codes( self, stdio_elem ): + def parse_stdio_exit_codes(self, stdio_elem): """ Parse the tool's element's subelements. This will add all of those elements, if any, to self.stdio_exit_codes. @@ -658,22 +679,22 @@ def parse_stdio_exit_codes( self, stdio_elem ): # So if there are value and range attributes, we use the range # attribute. If there is neither a range nor a value, then print # a warning and skip to the next. - for exit_code_elem in ( stdio_elem.findall( "exit_code" ) ): + for exit_code_elem in (stdio_elem.findall("exit_code")): exit_code = ToolStdioExitCode() # Each exit code has an optional description that can be # part of the "desc" or "description" attributes: - exit_code.desc = exit_code_elem.get( "desc" ) + exit_code.desc = exit_code_elem.get("desc") if exit_code.desc is None: - exit_code.desc = exit_code_elem.get( "description" ) + exit_code.desc = exit_code_elem.get("description") # Parse the error level: exit_code.error_level = ( - self.parse_error_level( exit_code_elem.get( "level" ))) - code_range = exit_code_elem.get( "range", "" ) + self.parse_error_level(exit_code_elem.get("level"))) + code_range = exit_code_elem.get("range", "") if code_range is None: - code_range = exit_code_elem.get( "value", "" ) + code_range = exit_code_elem.get("value", "") if code_range is None: - log.warning( "Tool stdio exit codes must have " + - "a range or value" ) + log.warning("Tool stdio exit codes must have " + + "a range or value") continue # Parse the range. We look for: # :Y @@ -683,30 +704,30 @@ def parse_stdio_exit_codes( self, stdio_elem ): # Also note that whitespace is eliminated. # TODO: Turn this into a single match - it should be # more efficient. - code_range = re.sub( "\s", "", code_range ) - code_ranges = re.split( ":", code_range ) - if ( len( code_ranges ) == 2 ): - if ( code_ranges[0] is None or '' == code_ranges[0] ): - exit_code.range_start = float( "-inf" ) + code_range = re.sub("\s", "", code_range) + code_ranges = re.split(":", code_range) + if (len(code_ranges) == 2): + if (code_ranges[0] is None or '' == code_ranges[0]): + exit_code.range_start = float("-inf") else: - exit_code.range_start = int( code_ranges[0] ) - if ( code_ranges[1] is None or '' == code_ranges[1] ): - exit_code.range_end = float( "inf" ) + exit_code.range_start = int(code_ranges[0]) + if (code_ranges[1] is None or '' == code_ranges[1]): + exit_code.range_end = float("inf") else: - exit_code.range_end = int( code_ranges[1] ) + exit_code.range_end = int(code_ranges[1]) # If we got more than one colon, then ignore the exit code. - elif ( len( code_ranges ) > 2 ): - log.warning( "Invalid tool exit_code range %s - ignored" - % code_range ) + elif (len(code_ranges) > 2): + log.warning("Invalid tool exit_code range %s - ignored" + % code_range) continue # Else we have a singular value. If it's not an integer, then # we'll just write a log message and skip this exit_code. else: try: - exit_code.range_start = int( code_range ) + exit_code.range_start = int(code_range) except: - log.error( code_range ) - log.warning( "Invalid range start for tool's exit_code %s: exit_code ignored" % code_range ) + log.error(code_range) + log.warning("Invalid range start for tool's exit_code %s: exit_code ignored" % code_range) continue exit_code.range_end = exit_code.range_start # TODO: Check if we got ">", ">=", "<", or "<=": @@ -714,20 +735,20 @@ def parse_stdio_exit_codes( self, stdio_elem ): # isn't bogus. If we have two infinite values, then # the start must be -inf and the end must be +inf. # So at least warn about this situation: - if ( isinf( exit_code.range_start ) and - isinf( exit_code.range_end ) ): - log.warning( "Tool exit_code range %s will match on " + - "all exit codes" % code_range ) - self.stdio_exit_codes.append( exit_code ) + if (isinf(exit_code.range_start) and + isinf(exit_code.range_end)): + log.warning("Tool exit_code range %s will match on " + + "all exit codes" % code_range) + self.stdio_exit_codes.append(exit_code) except Exception: - log.error( "Exception in parse_stdio_exit_codes! " + - str(sys.exc_info()) ) + log.error("Exception in parse_stdio_exit_codes! " + + str(sys.exc_info())) trace = sys.exc_info()[2] if trace is not None: - trace_msg = repr( traceback.format_tb( trace ) ) - log.error( "Traceback: %s" % trace_msg ) + trace_msg = repr(traceback.format_tb(trace)) + log.error("Traceback: %s" % trace_msg) - def parse_stdio_regexes( self, stdio_elem ): + def parse_stdio_regexes(self, stdio_elem): """ Look in the tool's elem for all subelements that define how to look for warnings and fatal errors in @@ -737,66 +758,66 @@ def parse_stdio_regexes( self, stdio_elem ): try: # Look for every subelement. The regular expression # will have "match" and "source" (or "src") attributes. - for regex_elem in ( stdio_elem.findall( "regex" ) ): + for regex_elem in (stdio_elem.findall("regex")): # TODO: Fill in ToolStdioRegex regex = ToolStdioRegex() # Each regex has an optional description that can be # part of the "desc" or "description" attributes: - regex.desc = regex_elem.get( "desc" ) + regex.desc = regex_elem.get("desc") if regex.desc is None: - regex.desc = regex_elem.get( "description" ) + regex.desc = regex_elem.get("description") # Parse the error level regex.error_level = ( - self.parse_error_level( regex_elem.get( "level" ) ) ) - regex.match = regex_elem.get( "match", "" ) + self.parse_error_level(regex_elem.get("level"))) + regex.match = regex_elem.get("match", "") if regex.match is None: # TODO: Convert the offending XML element to a string - log.warning( "Ignoring tool's stdio regex element %s - " - "the 'match' attribute must exist" ) + log.warning("Ignoring tool's stdio regex element %s - " + "the 'match' attribute must exist") continue # Parse the output sources. We look for the "src", "source", # and "sources" attributes, in that order. If there is no # such source, then the source defaults to stderr & stdout. # Look for a comma and then look for "err", "error", "out", # and "output": - output_srcs = regex_elem.get( "src" ) + output_srcs = regex_elem.get("src") if output_srcs is None: - output_srcs = regex_elem.get( "source" ) + output_srcs = regex_elem.get("source") if output_srcs is None: - output_srcs = regex_elem.get( "sources" ) + output_srcs = regex_elem.get("sources") if output_srcs is None: output_srcs = "output,error" - output_srcs = re.sub( "\s", "", output_srcs ) - src_list = re.split( ",", output_srcs ) + output_srcs = re.sub("\s", "", output_srcs) + src_list = re.split(",", output_srcs) # Just put together anything to do with "out", including # "stdout", "output", etc. Repeat for "stderr", "error", # and anything to do with "err". If neither stdout nor # stderr were specified, then raise a warning and scan both. for src in src_list: - if re.search( "both", src, re.IGNORECASE ): + if re.search("both", src, re.IGNORECASE): regex.stdout_match = True regex.stderr_match = True - if re.search( "out", src, re.IGNORECASE ): + if re.search("out", src, re.IGNORECASE): regex.stdout_match = True - if re.search( "err", src, re.IGNORECASE ): + if re.search("err", src, re.IGNORECASE): regex.stderr_match = True if (not regex.stdout_match and not regex.stderr_match): - log.warning( "Tool id %s: unable to determine if tool " - "stream source scanning is output, error, " - "or both. Defaulting to use both." % self.id ) + log.warning("Tool id %s: unable to determine if tool " + "stream source scanning is output, error, " + "or both. Defaulting to use both." % self.id) regex.stdout_match = True regex.stderr_match = True - self.stdio_regexes.append( regex ) + self.stdio_regexes.append(regex) except Exception: - log.error( "Exception in parse_stdio_exit_codes! " + - str(sys.exc_info()) ) + log.error("Exception in parse_stdio_exit_codes! " + + str(sys.exc_info())) trace = sys.exc_info()[2] if trace is not None: - trace_msg = repr( traceback.format_tb( trace ) ) - log.error( "Traceback: %s" % trace_msg ) + trace_msg = repr(traceback.format_tb(trace)) + log.error("Traceback: %s" % trace_msg) # TODO: This method doesn't have to be part of the Tool class. - def parse_error_level( self, err_level ): + def parse_error_level(self, err_level): """ Parses error level and returns error level enumeration. If unparsable, returns 'fatal' @@ -805,22 +826,22 @@ def parse_error_level( self, err_level ): return_level = StdioErrorLevel.FATAL try: if err_level: - if ( re.search( "log", err_level, re.IGNORECASE ) ): + if (re.search("log", err_level, re.IGNORECASE)): return_level = StdioErrorLevel.LOG - elif ( re.search( "warning", err_level, re.IGNORECASE ) ): + elif (re.search("warning", err_level, re.IGNORECASE)): return_level = StdioErrorLevel.WARNING - elif ( re.search( "fatal", err_level, re.IGNORECASE ) ): + elif (re.search("fatal", err_level, re.IGNORECASE)): return_level = StdioErrorLevel.FATAL else: - log.debug( "Tool %s: error level %s did not match log/warning/fatal" % - ( self.id, err_level ) ) + log.debug("Tool %s: error level %s did not match log/warning/fatal" % + (self.id, err_level)) except Exception: - log.error( "Exception in parse_error_level " + - str(sys.exc_info() ) ) + log.error("Exception in parse_error_level " + + str(sys.exc_info())) trace = sys.exc_info()[2] if trace is not None: - trace_msg = repr( traceback.format_tb( trace ) ) - log.error( "Traceback: %s" % trace_msg ) + trace_msg = repr(traceback.format_tb(trace)) + log.error("Traceback: %s" % trace_msg) return return_level @@ -830,8 +851,8 @@ def __init__(self, root): self.input_elem = root.find("inputs") page_sources = [] if self.input_elem is not None: - pages_elem = self.input_elem.findall( "page" ) - for page in ( pages_elem or [ self.input_elem ] ): + pages_elem = self.input_elem.findall("page") + for page in (pages_elem or [self.input_elem]): page_sources.append(XmlPageSource(page)) super(XmlPagesSource, self).__init__(page_sources) @@ -873,7 +894,7 @@ def get(self, key, value=None): return self.input_elem.get(key, value) def get_bool(self, key, default): - return string_as_bool( self.get(key, default ) ) + return string_as_bool(self.get(key, default)) def parse_label(self): return xml_text(self.input_elem, "label") @@ -891,16 +912,16 @@ def parse_dynamic_options_elem(self): """ Return a galaxy.tools.parameters.dynamic_options.DynamicOptions if appropriate. """ - options_elem = self.input_elem.find( 'options' ) + options_elem = self.input_elem.find('options') return options_elem def parse_static_options(self): static_options = list() elem = self.input_elem - for index, option in enumerate( elem.findall( "option" ) ): - value = option.get( "value" ) - selected = string_as_bool( option.get( "selected", False ) ) - static_options.append( ( option.text or value, value, selected ) ) + for index, option in enumerate(elem.findall("option")): + value = option.get("value") + selected = string_as_bool(option.get("selected", False)) + static_options.append((option.text or value, value, selected)) return static_options def parse_optional(self, default=None): @@ -911,21 +932,21 @@ def parse_optional(self, default=None): # should use optional going forward for consistency with other # parameters. if "force_select" in elem.attrib: - force_select = string_as_bool( elem.get( "force_select" ) ) + force_select = string_as_bool(elem.get("force_select")) else: - force_select = not string_as_bool( elem.get( "optional", False ) ) + force_select = not string_as_bool(elem.get("optional", False)) return not force_select if default is None: default = self.default_optional - return self.get_bool( "optional", default ) + return self.get_bool("optional", default) def parse_conversion_tuples(self): elem = self.input_elem conversions = [] - for conv_elem in elem.findall( "conversion" ): - name = conv_elem.get( "name" ) # name for commandline substitution - conv_extensions = conv_elem.get( "type" ) # target datatype extension + for conv_elem in elem.findall("conversion"): + name = conv_elem.get("name") # name for commandline substitution + conv_extensions = conv_elem.get("type") # target datatype extension conversions.append((name, conv_extensions)) return conversions @@ -935,7 +956,7 @@ def parse_nested_inputs_source(self): def parse_test_input_source(self): elem = self.input_elem - input_elem = elem.find( "param" ) + input_elem = elem.find("param") assert input_elem is not None, " must have a child " return XmlInputSource(input_elem) @@ -943,8 +964,8 @@ def parse_when_input_sources(self): elem = self.input_elem sources = [] - for case_elem in elem.findall( "when" ): - value = case_elem.get( "value" ) + for case_elem in elem.findall("when"): + value = case_elem.get("value") case_page_source = XmlPageSource(case_elem) sources.append((value, case_page_source)) return sources diff --git a/lib/galaxy/tools/parser/yaml.py b/lib/galaxy/tools/parser/yaml.py index 4450fb8f6d04..c8a18f23adf4 100644 --- a/lib/galaxy/tools/parser/yaml.py +++ b/lib/galaxy/tools/parser/yaml.py @@ -109,39 +109,40 @@ def parse_outputs(self, tool): def _parse_output(self, tool, name, output_dict): # TODO: handle filters, actions, change_format - output = ToolOutput( name ) + output = ToolOutput(name) output.format = output_dict.get("format", "data") output.change_format = [] output.format_source = output_dict.get("format_source", None) + output.default_identifier_source = output_dict.get("default_identifier_source", None) output.metadata_source = output_dict.get("metadata_source", "") output.parent = output_dict.get("parent", None) - output.label = output_dict.get( "label", None ) + output.label = output_dict.get("label", None) output.count = output_dict.get("count", 1) output.filters = [] output.tool = tool output.from_work_dir = output_dict.get("from_work_dir", None) output.hidden = output_dict.get("hidden", "") # TODO: implement tool output action group fixes - output.actions = ToolOutputActionGroup( output, None ) - output.dataset_collector_descriptions = self._dataset_collector_descriptions( output_dict ) + output.actions = ToolOutputActionGroup(output, None) + output.dataset_collector_descriptions = self._dataset_collector_descriptions(output_dict) return output def _parse_output_collection(self, tool, name, output_dict): name = output_dict.get("name") label = output_dict.get("label") - default_format = output_dict.get( "format", "data" ) - collection_type = output_dict.get( "type", None ) - collection_type_source = output_dict.get( "type_source", None ) - structured_like = output_dict.get( "structured_like", None ) + default_format = output_dict.get("format", "data") + collection_type = output_dict.get("type", None) + collection_type_source = output_dict.get("type_source", None) + structured_like = output_dict.get("structured_like", None) inherit_format = False inherit_metadata = False if structured_like: - inherit_format = output_dict.get( "inherit_format", None ) - inherit_metadata = output_dict.get( "inherit_metadata", None ) - default_format_source = output_dict.get( "format_source", None ) - default_metadata_source = output_dict.get( "metadata_source", "" ) + inherit_format = output_dict.get("inherit_format", None) + inherit_metadata = output_dict.get("inherit_metadata", None) + default_format_source = output_dict.get("format_source", None) + default_metadata_source = output_dict.get("metadata_source", "") filters = [] - dataset_collector_descriptions = self._dataset_collector_descriptions( output_dict ) + dataset_collector_descriptions = self._dataset_collector_descriptions(output_dict) structure = ToolOutputCollectionStructure( collection_type=collection_type, @@ -164,8 +165,8 @@ def _parse_output_collection(self, tool, name, output_dict): def _dataset_collector_descriptions(self, discover_datasets_dicts): if _is_dict(discover_datasets_dicts): - discover_datasets_dicts = [ discover_datasets_dicts ] - dataset_collector_descriptions = dataset_collector_descriptions_from_list( discover_datasets_dicts ) + discover_datasets_dicts = [discover_datasets_dicts] + dataset_collector_descriptions = dataset_collector_descriptions_from_list(discover_datasets_dicts) return dataset_collector_descriptions def parse_tests_to_dict(self): @@ -224,18 +225,18 @@ def _parse_test(i, test_dict): attributes["metadata"] = {} # TODO assert_list = [] - assert_list = __to_test_assert_list( attributes.get("asserts", [] ) ) + assert_list = __to_test_assert_list(attributes.get("asserts", [])) attributes["assert_list"] = assert_list _ensure_has(attributes, defaults) test_dict["outputs"] = new_outputs # TODO: implement output collections for YAML tools. test_dict["output_collections"] = [] - test_dict["command"] = __to_test_assert_list( test_dict.get( "command", [] ) ) - test_dict["stdout"] = __to_test_assert_list( test_dict.get( "stdout", [] ) ) - test_dict["stderr"] = __to_test_assert_list( test_dict.get( "stderr", [] ) ) - test_dict["expect_exit_code"] = test_dict.get( "expect_exit_code", None ) - test_dict["expect_failure"] = test_dict.get( "expect_exit_code", False ) + test_dict["command"] = __to_test_assert_list(test_dict.get("command", [])) + test_dict["stdout"] = __to_test_assert_list(test_dict.get("stdout", [])) + test_dict["stderr"] = __to_test_assert_list(test_dict.get("stderr", [])) + test_dict["expect_exit_code"] = test_dict.get("expect_exit_code", None) + test_dict["expect_failure"] = test_dict.get("expect_exit_code", False) return test_dict @@ -251,7 +252,7 @@ def expand_dict_form(item): return new_value if _is_dict(assertions): - assertions = map(expand_dict_form, assertions.items() ) + assertions = map(expand_dict_form, assertions.items()) assert_list = [] for assertion in assertions: @@ -305,7 +306,7 @@ def parse_nested_inputs_source(self): return YamlPageSource(self.input_dict["blocks"]) def parse_test_input_source(self): - test_dict = self.input_dict.get( "test", None ) + test_dict = self.input_dict.get("test", None) assert test_dict is not None, "conditional must contain a `test` definition" return YamlInputSource(test_dict) @@ -332,10 +333,10 @@ def parse_static_options(self): static_options = list() input_dict = self.input_dict for index, option in enumerate(input_dict.get("options", {})): - value = option.get( "value" ) - label = option.get( "label", value ) - selected = option.get( "selected", False ) - static_options.append( ( label, value, selected ) ) + value = option.get("value") + label = option.get("label", value) + selected = option.get("selected", False) + static_options.append((label, value, selected)) return static_options diff --git a/lib/galaxy/tools/search/__init__.py b/lib/galaxy/tools/search/__init__.py index 32ef818ad374..fe59c89d0d0f 100644 --- a/lib/galaxy/tools/search/__init__.py +++ b/lib/galaxy/tools/search/__init__.py @@ -24,26 +24,26 @@ from galaxy.util import ExecutionTimer from galaxy.web.framework.helpers import to_unicode -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class ToolBoxSearch( object ): +class ToolBoxSearch(object): """ Support searching tools in a toolbox. This implementation uses the Whoosh search library. """ - def __init__( self, toolbox, index_help=True ): + def __init__(self, toolbox, index_help=True): """ Create a searcher for `toolbox`. """ - self.schema = Schema( id=STORED, - stub=KEYWORD, - name=TEXT( analyzer=analysis.SimpleAnalyzer() ), - description=TEXT, - section=TEXT, - help=TEXT, - labels=KEYWORD ) + self.schema = Schema(id=STORED, + stub=KEYWORD, + name=TEXT(analyzer=analysis.SimpleAnalyzer()), + description=TEXT, + section=TEXT, + help=TEXT, + labels=KEYWORD) self.rex = analysis.RegexTokenizer() self.toolbox = toolbox self.storage, self.index = self._index_setup() @@ -112,54 +112,54 @@ def _create_doc(self, tool_id, tool, index_help=True): pass return add_doc_kwds - def search( self, q, tool_name_boost, tool_section_boost, tool_description_boost, tool_label_boost, tool_stub_boost, tool_help_boost, tool_search_limit, tool_enable_ngram_search, tool_ngram_minsize, tool_ngram_maxsize ): + def search(self, q, tool_name_boost, tool_section_boost, tool_description_boost, tool_label_boost, tool_stub_boost, tool_help_boost, tool_search_limit, tool_enable_ngram_search, tool_ngram_minsize, tool_ngram_maxsize): """ Perform search on the in-memory index. Weight in the given boosts. """ # Change field boosts for searcher searcher = self.index.searcher( weighting=BM25F( - field_B={ 'name_B': float( tool_name_boost ), - 'section_B': float( tool_section_boost ), - 'description_B': float( tool_description_boost ), - 'labels_B': float( tool_label_boost ), - 'stub_B': float( tool_stub_boost ), - 'help_B': float( tool_help_boost ) } + field_B={'name_B': float(tool_name_boost), + 'section_B': float(tool_section_boost), + 'description_B': float(tool_description_boost), + 'labels_B': float(tool_label_boost), + 'stub_B': float(tool_stub_boost), + 'help_B': float(tool_help_boost)} ) ) # Set query to search name, description, section, help, and labels. - parser = MultifieldParser( [ 'name', 'description', 'section', 'help', 'labels', 'stub' ], schema=self.schema ) + parser = MultifieldParser(['name', 'description', 'section', 'help', 'labels', 'stub'], schema=self.schema) # Hyphens are wildcards in Whoosh causing bad things - if q.find( '-' ) != -1: - q = (' ').join( [ token.text for token in self.rex( to_unicode( q ) ) ] ) + if q.find('-') != -1: + q = (' ').join([token.text for token in self.rex(to_unicode(q))]) # Perform tool search with ngrams if set to true in the config file - if ( tool_enable_ngram_search is True or tool_enable_ngram_search == "True" ): + if (tool_enable_ngram_search is True or tool_enable_ngram_search == "True"): hits_with_score = {} - token_analyzer = StandardAnalyzer() | analysis.NgramFilter( minsize=int( tool_ngram_minsize ), maxsize=int( tool_ngram_maxsize ) ) - ngrams = [ token.text for token in token_analyzer( q ) ] + token_analyzer = StandardAnalyzer() | analysis.NgramFilter(minsize=int(tool_ngram_minsize), maxsize=int(tool_ngram_maxsize)) + ngrams = [token.text for token in token_analyzer(q)] for query in ngrams: # Get the tool list with respective scores for each qgram - curr_hits = searcher.search( parser.parse( '*' + query + '*' ), limit=float( tool_search_limit ) ) - for i, curr_hit in enumerate( curr_hits ): + curr_hits = searcher.search(parser.parse('*' + query + '*'), limit=float(tool_search_limit)) + for i, curr_hit in enumerate(curr_hits): is_present = False for prev_hit in hits_with_score: # Check if the tool appears again for the next qgram search - if curr_hit[ 'id' ] == prev_hit: + if curr_hit['id'] == prev_hit: is_present = True # Add the current score with the previous one if the # tool appears again for the next qgram - hits_with_score[ prev_hit ] = curr_hits.score(i) + hits_with_score[ prev_hit ] + hits_with_score[prev_hit] = curr_hits.score(i) + hits_with_score[prev_hit] # Add the tool if not present to the collection with its score if not is_present: - hits_with_score[ curr_hit[ 'id' ] ] = curr_hits.score(i) + hits_with_score[curr_hit['id']] = curr_hits.score(i) # Sort the results based on aggregated BM25 score in decreasing order of scores - hits_with_score = sorted( hits_with_score.items(), key=lambda x: x[1], reverse=True ) + hits_with_score = sorted(hits_with_score.items(), key=lambda x: x[1], reverse=True) # Return the tool ids - return [ item[0] for item in hits_with_score[ 0:int( tool_search_limit ) ] ] + return [item[0] for item in hits_with_score[0:int(tool_search_limit)]] else: # Perform the search - hits = searcher.search( parser.parse( '*' + q + '*' ), limit=float( tool_search_limit ) ) - return [ hit[ 'id' ] for hit in hits ] + hits = searcher.search(parser.parse('*' + q + '*'), limit=float(tool_search_limit)) + return [hit['id'] for hit in hits] def _temp_storage(self, name=None): diff --git a/lib/galaxy/tools/sort_collection_list.xml b/lib/galaxy/tools/sort_collection_list.xml new file mode 100644 index 000000000000..f884b0392db4 --- /dev/null +++ b/lib/galaxy/tools/sort_collection_list.xml @@ -0,0 +1,73 @@ + + of list of datasets + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/galaxy/tools/special_tools.py b/lib/galaxy/tools/special_tools.py index 4aa82a0f1d17..953e69dee647 100644 --- a/lib/galaxy/tools/special_tools.py +++ b/lib/galaxy/tools/special_tools.py @@ -1,5 +1,5 @@ import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) SPECIAL_TOOLS = { "history export": "galaxy/tools/imp_exp/exp_history_to_archive.xml", @@ -7,7 +7,7 @@ } -def load_lib_tools( toolbox ): +def load_lib_tools(toolbox): for name, path in SPECIAL_TOOLS.items(): - tool = toolbox.load_hidden_lib_tool( path ) - log.debug( "Loaded %s tool: %s", name, tool.id ) + tool = toolbox.load_hidden_lib_tool(path) + log.debug("Loaded %s tool: %s", name, tool.id) diff --git a/lib/galaxy/tools/test.py b/lib/galaxy/tools/test.py index 7a176b39598b..82ab9061dee0 100644 --- a/lib/galaxy/tools/test.py +++ b/lib/galaxy/tools/test.py @@ -14,7 +14,7 @@ def nottest(x): return x -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) DEFAULT_FTYPE = 'auto' DEFAULT_DBKEY = 'hg17' @@ -28,28 +28,28 @@ def parse_tests(tool, tests_source): Build ToolTestBuilder objects for each "" elements and return default interactor (if any). """ - default_interactor = os.environ.get( 'GALAXY_TEST_DEFAULT_INTERACTOR', DEFAULT_INTERACTOR ) + default_interactor = os.environ.get('GALAXY_TEST_DEFAULT_INTERACTOR', DEFAULT_INTERACTOR) tests_dict = tests_source.parse_tests_to_dict() - tests_default_interactor = tests_dict.get( 'interactor', default_interactor ) + tests_default_interactor = tests_dict.get('interactor', default_interactor) tests = [] for i, test_dict in enumerate(tests_dict.get('tests', [])): - test = ToolTestBuilder( tool, test_dict, i, default_interactor=tests_default_interactor ) - tests.append( test ) + test = ToolTestBuilder(tool, test_dict, i, default_interactor=tests_default_interactor) + tests.append(test) return tests -class ToolTestBuilder( object ): +class ToolTestBuilder(object): """ Encapsulates information about a tool test, and allows creation of a dynamic TestCase class (the unittest framework is very class oriented, doing dynamic tests in this way allows better integration) """ - def __init__( self, tool, test_dict, i, default_interactor ): - name = test_dict.get( 'name', 'Test-%d' % (i + 1) ) - maxseconds = test_dict.get( 'maxseconds', DEFAULT_MAX_SECS ) + def __init__(self, tool, test_dict, i, default_interactor): + name = test_dict.get('name', 'Test-%d' % (i + 1)) + maxseconds = test_dict.get('maxseconds', DEFAULT_MAX_SECS) if maxseconds is not None: - maxseconds = int( maxseconds ) + maxseconds = int(maxseconds) self.tool = tool self.name = name @@ -63,25 +63,25 @@ def __init__( self, tool, test_dict, i, default_interactor ): self.error = False self.exception = None - self.__handle_test_dict( test_dict, i, default_interactor ) + self.__handle_test_dict(test_dict, i, default_interactor) - def test_data( self ): + def test_data(self): """ Iterator over metadata representing the required files for upload. """ - return test_data_iter( self.required_files ) + return test_data_iter(self.required_files) - def __matching_case_for_value( self, cond, declared_value ): + def __matching_case_for_value(self, cond, declared_value): test_param = cond.test_param if isinstance(test_param, galaxy.tools.parameters.basic.BooleanToolParameter): if declared_value is None: # No explicit value for param in test case, determine from default query_value = test_param.checked else: - query_value = _process_bool_param_value( test_param, declared_value ) + query_value = _process_bool_param_value(test_param, declared_value) def matches_declared_value(case_value): - return _process_bool_param_value( test_param, case_value ) == query_value + return _process_bool_param_value(test_param, case_value) == query_value elif isinstance(test_param, galaxy.tools.parameters.basic.SelectToolParameter): if declared_value is not None: # Test case supplied explicit value to check against. @@ -107,38 +107,38 @@ def matches_declared_value(case_value): # default - give up. Previously this would just result in a key # error exception. msg = "Failed to find test parameter value specification required for conditional %s" % cond.name - raise Exception( msg ) + raise Exception(msg) # Check the tool's defined cases against predicate to determine # selected or default. - for i, case in enumerate( cond.cases ): - if matches_declared_value( case.value ): + for i, case in enumerate(cond.cases): + if matches_declared_value(case.value): return case else: msg_template = "%s - Failed to find case matching value (%s) for test parameter specification for conditional %s. Remainder of test behavior is unspecified." - msg = msg_template % ( self.tool.id, declared_value, cond.name ) - log.info( msg ) + msg = msg_template % (self.tool.id, declared_value, cond.name) + log.info(msg) - def __split_if_str( self, value ): + def __split_if_str(self, value): split = isinstance(value, string_types) if split: value = value.split(",") return value - def __handle_test_dict( self, test_dict, i, default_interactor ): + def __handle_test_dict(self, test_dict, i, default_interactor): try: # Mechanism test code uses for interacting with Galaxy instance, # until 'api' is the default switch this to API to use its new # features. Once 'api' is the default set to 'twill' to use legacy # features or workarounds. - self.interactor = test_dict.get( 'interactor', default_interactor ) + self.interactor = test_dict.get('interactor', default_interactor) - self.inputs = self.__process_raw_inputs( self.tool.inputs, test_dict["inputs"] ) + self.inputs = self.__process_raw_inputs(self.tool.inputs, test_dict["inputs"]) self.outputs = test_dict["outputs"] self.output_collections = test_dict["output_collections"] - num_outputs = test_dict.get( 'expect_num_outputs', None ) + num_outputs = test_dict.get('expect_num_outputs', None) if num_outputs: - num_outputs = int( num_outputs ) + num_outputs = int(num_outputs) self.num_outputs = num_outputs self.command_line = test_dict.get("command", None) self.stdout = test_dict.get("stdout", None) @@ -151,7 +151,7 @@ def __handle_test_dict( self, test_dict, i, default_interactor ): self.error = True self.exception = e - def __process_raw_inputs( self, tool_inputs, raw_inputs, parent_context=None ): + def __process_raw_inputs(self, tool_inputs, raw_inputs, parent_context=None): """ Recursively expand flat list of inputs into "tree" form of flat list (| using to nest to new levels) structure and expand dataset @@ -160,18 +160,18 @@ def __process_raw_inputs( self, tool_inputs, raw_inputs, parent_context=None ): parent_context = parent_context or RootParamContext() expanded_inputs = {} for key, value in tool_inputs.items(): - if isinstance( value, galaxy.tools.parameters.grouping.Conditional ): - cond_context = ParamContext( name=value.name, parent_context=parent_context ) - case_context = ParamContext( name=value.test_param.name, parent_context=cond_context ) - raw_input = case_context.extract_value( raw_inputs ) - case_value = raw_input[ 1 ] if raw_input else None - case = self.__matching_case_for_value( value, case_value ) + if isinstance(value, galaxy.tools.parameters.grouping.Conditional): + cond_context = ParamContext(name=value.name, parent_context=parent_context) + case_context = ParamContext(name=value.test_param.name, parent_context=cond_context) + raw_input = case_context.extract_value(raw_inputs) + case_value = raw_input[1] if raw_input else None + case = self.__matching_case_for_value(value, case_value) if case: for input_name, input_value in case.inputs.items(): - case_inputs = self.__process_raw_inputs( { input_name: input_value }, raw_inputs, parent_context=cond_context ) - expanded_inputs.update( case_inputs ) + case_inputs = self.__process_raw_inputs({input_name: input_value}, raw_inputs, parent_context=cond_context) + expanded_inputs.update(case_inputs) if not value.type == "text": - expanded_case_value = self.__split_if_str( case.value ) + expanded_case_value = self.__split_if_str(case.value) if case_value is not None: # A bit tricky here - we are growing inputs with value # that may be implicit (i.e. not defined by user just @@ -180,64 +180,64 @@ def __process_raw_inputs( self, tool_inputs, raw_inputs, parent_context=None ): # as a new instance with value defined and hence enter # an infinite loop - hence the "case_value is not None" # check. - processed_value = _process_simple_value( value.test_param, expanded_case_value ) - expanded_inputs[ case_context.for_state() ] = processed_value - elif isinstance( value, galaxy.tools.parameters.grouping.Section ): - context = ParamContext( name=value.name, parent_context=parent_context ) + processed_value = _process_simple_value(value.test_param, expanded_case_value) + expanded_inputs[case_context.for_state()] = processed_value + elif isinstance(value, galaxy.tools.parameters.grouping.Section): + context = ParamContext(name=value.name, parent_context=parent_context) for r_name, r_value in value.inputs.items(): - expanded_input = self.__process_raw_inputs( { context.for_state(): r_value }, raw_inputs, parent_context=context ) + expanded_input = self.__process_raw_inputs({context.for_state(): r_value}, raw_inputs, parent_context=context) if expanded_input: - expanded_inputs.update( expanded_input ) - elif isinstance( value, galaxy.tools.parameters.grouping.Repeat ): + expanded_inputs.update(expanded_input) + elif isinstance(value, galaxy.tools.parameters.grouping.Repeat): repeat_index = 0 while True: - context = ParamContext( name=value.name, index=repeat_index, parent_context=parent_context ) + context = ParamContext(name=value.name, index=repeat_index, parent_context=parent_context) updated = False for r_name, r_value in value.inputs.items(): - expanded_input = self.__process_raw_inputs( { context.for_state(): r_value }, raw_inputs, parent_context=context ) + expanded_input = self.__process_raw_inputs({context.for_state(): r_value}, raw_inputs, parent_context=context) if expanded_input: - expanded_inputs.update( expanded_input ) + expanded_inputs.update(expanded_input) updated = True if not updated: break repeat_index += 1 else: - context = ParamContext( name=value.name, parent_context=parent_context ) - raw_input = context.extract_value( raw_inputs ) + context = ParamContext(name=value.name, parent_context=parent_context) + raw_input = context.extract_value(raw_inputs) if raw_input: (name, param_value, param_extra) = raw_input if not value.type == "text": - param_value = self.__split_if_str( param_value ) - if isinstance( value, galaxy.tools.parameters.basic.DataToolParameter ): + param_value = self.__split_if_str(param_value) + if isinstance(value, galaxy.tools.parameters.basic.DataToolParameter): if not isinstance(param_value, list): - param_value = [ param_value ] - map( lambda v: self.__add_uploaded_dataset( context.for_state(), v, param_extra, value ), param_value ) + param_value = [param_value] + map(lambda v: self.__add_uploaded_dataset(context.for_state(), v, param_extra, value), param_value) processed_value = param_value - elif isinstance( value, galaxy.tools.parameters.basic.DataCollectionToolParameter ): + elif isinstance(value, galaxy.tools.parameters.basic.DataCollectionToolParameter): assert 'collection' in param_extra - collection_def = param_extra[ 'collection' ] - for ( name, value, extra ) in collection_def.collect_inputs(): - require_file( name, value, extra, self.required_files ) + collection_def = param_extra['collection'] + for (name, value, extra) in collection_def.collect_inputs(): + require_file(name, value, extra, self.required_files) processed_value = collection_def else: - processed_value = _process_simple_value( value, param_value ) - expanded_inputs[ context.for_state() ] = processed_value + processed_value = _process_simple_value(value, param_value) + expanded_inputs[context.for_state()] = processed_value return expanded_inputs - def __add_uploaded_dataset( self, name, value, extra, input_parameter ): + def __add_uploaded_dataset(self, name, value, extra, input_parameter): if value is None: assert input_parameter.optional, '%s is not optional. You must provide a valid filename.' % name return value - return require_file( name, value, extra, self.required_files ) + return require_file(name, value, extra, self.required_files) -def _process_simple_value( param, param_value ): - if isinstance( param, galaxy.tools.parameters.basic.SelectToolParameter ) and hasattr( param, 'static_options' ): +def _process_simple_value(param, param_value): + if isinstance(param, galaxy.tools.parameters.basic.SelectToolParameter) and hasattr(param, 'static_options'): # Tests may specify values as either raw value or the value # as they appear in the list - the API doesn't and shouldn't # accept the text value - so we need to convert the text # into the form value. - def process_param_value( param_value ): + def process_param_value(param_value): found_value = False value_for_text = None if param.static_options: @@ -253,23 +253,23 @@ def process_param_value( param_value ): return processed_value # Do replacement described above for lists or singleton # values. - if isinstance( param_value, list ): - processed_value = list( map( process_param_value, param_value ) ) + if isinstance(param_value, list): + processed_value = list(map(process_param_value, param_value)) else: - processed_value = process_param_value( param_value ) - elif isinstance( param, galaxy.tools.parameters.basic.BooleanToolParameter ): + processed_value = process_param_value(param_value) + elif isinstance(param, galaxy.tools.parameters.basic.BooleanToolParameter): # Like above, tests may use the tool define values of simply # true/false. - processed_value = _process_bool_param_value( param, param_value ) + processed_value = _process_bool_param_value(param, param_value) else: processed_value = param_value return processed_value -def _process_bool_param_value( param, param_value ): - assert isinstance( param, galaxy.tools.parameters.basic.BooleanToolParameter ) +def _process_bool_param_value(param, param_value): + assert isinstance(param, galaxy.tools.parameters.basic.BooleanToolParameter) was_list = False - if isinstance( param_value, list ): + if isinstance(param_value, list): was_list = True param_value = param_value[0] if param.truevalue == param_value: @@ -277,94 +277,94 @@ def _process_bool_param_value( param, param_value ): elif param.falsevalue == param_value: processed_value = False else: - processed_value = string_as_bool( param_value ) - return [ processed_value ] if was_list else processed_value + processed_value = string_as_bool(param_value) + return [processed_value] if was_list else processed_value @nottest -def test_data_iter( required_files ): +def test_data_iter(required_files): for fname, extra in required_files: data_dict = dict( fname=fname, - metadata=extra.get( 'metadata', [] ), - composite_data=extra.get( 'composite_data', [] ), - ftype=extra.get( 'ftype', DEFAULT_FTYPE ), - dbkey=extra.get( 'dbkey', DEFAULT_DBKEY ), + metadata=extra.get('metadata', []), + composite_data=extra.get('composite_data', []), + ftype=extra.get('ftype', DEFAULT_FTYPE), + dbkey=extra.get('dbkey', DEFAULT_DBKEY), ) - edit_attributes = extra.get( 'edit_attributes', [] ) + edit_attributes = extra.get('edit_attributes', []) # currently only renaming is supported for edit_att in edit_attributes: - if edit_att.get( 'type', None ) == 'name': - new_name = edit_att.get( 'value', None ) + if edit_att.get('type', None) == 'name': + new_name = edit_att.get('value', None) assert new_name, 'You must supply the new dataset name as the value tag of the edit_attributes tag' data_dict['name'] = new_name else: - raise Exception( 'edit_attributes type (%s) is unimplemented' % edit_att.get( 'type', None ) ) + raise Exception('edit_attributes type (%s) is unimplemented' % edit_att.get('type', None)) yield data_dict -def require_file( name, value, extra, required_files ): - if ( value, extra ) not in required_files: - required_files.append( ( value, extra ) ) # these files will be uploaded - name_change = [ att for att in extra.get( 'edit_attributes', [] ) if att.get( 'type' ) == 'name' ] +def require_file(name, value, extra, required_files): + if (value, extra) not in required_files: + required_files.append((value, extra)) # these files will be uploaded + name_change = [att for att in extra.get('edit_attributes', []) if att.get('type') == 'name'] if name_change: - name_change = name_change[-1].get( 'value' ) # only the last name change really matters + name_change = name_change[-1].get('value') # only the last name change really matters value = name_change # change value for select to renamed uploaded file for e.g. composite dataset else: - for end in [ '.zip', '.gz' ]: - if value.endswith( end ): - value = value[ :-len( end ) ] + for end in ['.zip', '.gz']: + if value.endswith(end): + value = value[:-len(end)] break - value = os.path.basename( value ) # if uploading a file in a path other than root of test-data + value = os.path.basename(value) # if uploading a file in a path other than root of test-data return value class ParamContext(object): - def __init__( self, name, index=None, parent_context=None ): + def __init__(self, name, index=None, parent_context=None): self.parent_context = parent_context self.name = name - self.index = None if index is None else int( index ) + self.index = None if index is None else int(index) - def for_state( self ): - name = self.name if self.index is None else "%s_%d" % ( self.name, self.index ) + def for_state(self): + name = self.name if self.index is None else "%s_%d" % (self.name, self.index) parent_for_state = self.parent_context.for_state() if parent_for_state: - return "%s|%s" % ( parent_for_state, name ) + return "%s|%s" % (parent_for_state, name) else: return name - def __str__( self ): + def __str__(self): return "Context[for_state=%s]" % self.for_state() - def param_names( self ): + def param_names(self): for parent_context_param in self.parent_context.param_names(): if self.index is not None: - yield "%s|%s_%d" % ( parent_context_param, self.name, self.index ) + yield "%s|%s_%d" % (parent_context_param, self.name, self.index) else: - yield "%s|%s" % ( parent_context_param, self.name ) + yield "%s|%s" % (parent_context_param, self.name) if self.index is not None: - yield "%s_%d" % ( self.name, self.index ) + yield "%s_%d" % (self.name, self.index) else: yield self.name - def extract_value( self, raw_inputs ): + def extract_value(self, raw_inputs): for param_name in self.param_names(): - value = self.__raw_param_found( param_name, raw_inputs) + value = self.__raw_param_found(param_name, raw_inputs) if value: return value return None - def __raw_param_found( self, param_name, raw_inputs ): + def __raw_param_found(self, param_name, raw_inputs): index = None - for i, raw_input in enumerate( raw_inputs ): - if raw_input[ 0 ] == param_name: + for i, raw_input in enumerate(raw_inputs): + if raw_input[0] == param_name: index = i if index is not None: - raw_input = raw_inputs[ index ] - del raw_inputs[ index ] + raw_input = raw_inputs[index] + del raw_inputs[index] return raw_input else: return None @@ -372,14 +372,14 @@ def __raw_param_found( self, param_name, raw_inputs ): class RootParamContext(object): - def __init__( self ): + def __init__(self): pass - def for_state( self ): + def for_state(self): return "" - def param_names( self ): + def param_names(self): return [] - def get_index( self ): + def get_index(self): return 0 diff --git a/lib/galaxy/tools/toolbox/base.py b/lib/galaxy/tools/toolbox/base.py index 21b13fb47ed3..f6ea1e88768a 100644 --- a/lib/galaxy/tools/toolbox/base.py +++ b/lib/galaxy/tools/toolbox/base.py @@ -35,16 +35,16 @@ from .parser import ensure_tool_conf_item, get_toolbox_parser from .tags import tool_tag_manager -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class AbstractToolBox( Dictifiable, ManagesIntegratedToolPanelMixin, object ): +class AbstractToolBox(Dictifiable, ManagesIntegratedToolPanelMixin, object): """ Abstract container for managing a ToolPanel - containing tools and workflows optionally in labelled sections. """ - def __init__( self, config_filenames, tool_root_dir, app ): + def __init__(self, config_filenames, tool_root_dir, app): """ Create a toolbox from the config files named by `config_filenames`, using `tool_root_dir` as the base directory for finding individual tool config files. @@ -64,9 +64,9 @@ def __init__( self, config_filenames, tool_root_dir, app ): self._tool_panel = ToolPanelElements() self._index = 0 self.data_manager_tools = odict() - self._lineage_map = LineageMap( app ) + self._lineage_map = LineageMap(app) # Sets self._integrated_tool_panel and self._integrated_tool_panel_config_has_contents - self._init_integrated_tool_panel( app.config ) + self._init_integrated_tool_panel(app.config) # The following refers to the tool_path config setting for backward compatibility. The shed-related # (e.g., shed_tool_conf.xml) files include the tool_path attribute within the tag. self._tool_root_dir = tool_root_dir @@ -76,9 +76,9 @@ def __init__( self, config_filenames, tool_root_dir, app ): else: # Toolbox is loaded but not used during toolshed tests self._tool_watcher = None - self._filter_factory = FilterFactory( self ) - self._tool_tag_manager = tool_tag_manager( app ) - self._init_tools_from_configs( config_filenames ) + self._filter_factory = FilterFactory(self) + self._tool_tag_manager = tool_tag_manager(app) + self._init_tools_from_configs(config_filenames) if self.app.name == 'galaxy' and self._integrated_tool_panel_config_has_contents: # Load self._tool_panel based on the order in self._integrated_tool_panel. self._load_tool_panel() @@ -91,25 +91,25 @@ def handle_panel_update(self, section_dict): interacting with the rest of the Galaxy app or message queues, etc.... """ - def create_tool( self, config_file, repository_id=None, guid=None, **kwds ): + def create_tool(self, config_file, repository_id=None, guid=None, **kwds): raise NotImplementedError() - def _init_tools_from_configs( self, config_filenames ): + def _init_tools_from_configs(self, config_filenames): """ Read through all tool config files and initialize tools in each with init_tools_from_config below. """ execution_timer = ExecutionTimer() self._tool_tag_manager.reset_tags() - config_filenames = listify( config_filenames ) + config_filenames = listify(config_filenames) for config_filename in config_filenames: - if os.path.isdir( config_filename ): - directory_contents = sorted( os.listdir( config_filename ) ) - directory_config_files = [ config_file for config_file in directory_contents if config_file.endswith( ".xml" ) ] - config_filenames.remove( config_filename ) - config_filenames.extend( directory_config_files ) + if os.path.isdir(config_filename): + directory_contents = sorted(os.listdir(config_filename)) + directory_config_files = [config_file for config_file in directory_contents if config_file.endswith(".xml")] + config_filenames.remove(config_filename) + config_filenames.extend(directory_config_files) for config_filename in config_filenames: try: - self._init_tools_from_config( config_filename ) + self._init_tools_from_config(config_filename) except ParseError: # Occasionally we experience "Missing required parameter 'shed_tool_conf'." # This happens if parsing the shed_tool_conf fails, so we just sleep a second and try again. @@ -120,10 +120,10 @@ def _init_tools_from_configs( self, config_filenames ): except Exception: raise except Exception: - log.exception( "Error loading tools defined in config %s", config_filename ) + log.exception("Error loading tools defined in config %s", config_filename) log.debug("Reading tools from config files finshed %s", execution_timer) - def _init_tools_from_config( self, config_filename ): + def _init_tools_from_config(self, config_filename): """ Read the configuration file and load each tool. The following tags are currently supported: @@ -142,7 +142,7 @@ def _init_tools_from_config( self, config_filename ): """ - log.info( "Parsing the tool configuration %s" % config_filename ) + log.info("Parsing the tool configuration %s" % config_filename) tool_conf_source = get_toolbox_parser(config_filename) tool_path = tool_conf_source.parse_tool_path() parsing_shed_tool_conf = tool_conf_source.is_shed_tool_conf() @@ -156,23 +156,23 @@ def _init_tools_from_config( self, config_filename ): index = self._index self._index += 1 if parsing_shed_tool_conf: - config_elems.append( item.elem ) + config_elems.append(item.elem) self.load_item( item, tool_path=tool_path, load_panel_dict=load_panel_dict, - guid=item.get( 'guid' ), + guid=item.get('guid'), index=index, internal=True ) if parsing_shed_tool_conf: - shed_tool_conf_dict = dict( config_filename=config_filename, - tool_path=tool_path, - config_elems=config_elems ) - self._dynamic_tool_confs.append( shed_tool_conf_dict ) + shed_tool_conf_dict = dict(config_filename=config_filename, + tool_path=tool_path, + config_elems=config_elems) + self._dynamic_tool_confs.append(shed_tool_conf_dict) - def load_item( self, item, tool_path, panel_dict=None, integrated_panel_dict=None, load_panel_dict=True, guid=None, index=None, internal=False ): + def load_item(self, item, tool_path, panel_dict=None, integrated_panel_dict=None, load_panel_dict=True, guid=None, index=None, internal=False): with self.app._toolbox_lock: item = ensure_tool_conf_item(item) item_type = item.type @@ -186,19 +186,19 @@ def load_item( self, item, tool_path, panel_dict=None, integrated_panel_dict=Non if integrated_panel_dict is None: integrated_panel_dict = self._integrated_tool_panel if item_type == 'tool': - self._load_tool_tag_set( item, panel_dict=panel_dict, integrated_panel_dict=integrated_panel_dict, tool_path=tool_path, load_panel_dict=load_panel_dict, guid=guid, index=index, internal=internal ) + self._load_tool_tag_set(item, panel_dict=panel_dict, integrated_panel_dict=integrated_panel_dict, tool_path=tool_path, load_panel_dict=load_panel_dict, guid=guid, index=index, internal=internal) elif item_type == 'workflow': - self._load_workflow_tag_set( item, panel_dict=panel_dict, integrated_panel_dict=integrated_panel_dict, load_panel_dict=load_panel_dict, index=index ) + self._load_workflow_tag_set(item, panel_dict=panel_dict, integrated_panel_dict=integrated_panel_dict, load_panel_dict=load_panel_dict, index=index) elif item_type == 'section': - self._load_section_tag_set( item, tool_path=tool_path, load_panel_dict=load_panel_dict, index=index, internal=internal ) + self._load_section_tag_set(item, tool_path=tool_path, load_panel_dict=load_panel_dict, index=index, internal=internal) elif item_type == 'label': - self._load_label_tag_set( item, panel_dict=panel_dict, integrated_panel_dict=integrated_panel_dict, load_panel_dict=load_panel_dict, index=index ) + self._load_label_tag_set(item, panel_dict=panel_dict, integrated_panel_dict=integrated_panel_dict, load_panel_dict=load_panel_dict, index=index) elif item_type == 'tool_dir': - self._load_tooldir_tag_set( item, panel_dict, tool_path, integrated_panel_dict, load_panel_dict=load_panel_dict ) + self._load_tooldir_tag_set(item, panel_dict, tool_path, integrated_panel_dict, load_panel_dict=load_panel_dict) - def get_shed_config_dict_by_filename( self, filename, default=None ): + def get_shed_config_dict_by_filename(self, filename, default=None): for shed_config_dict in self._dynamic_tool_confs: - if shed_config_dict[ 'config_filename' ] == filename: + if shed_config_dict['config_filename'] == filename: return shed_config_dict return default @@ -212,12 +212,12 @@ def update_shed_config(self, shed_conf): self._dynamic_tool_confs[index] = shed_conf self._save_integrated_tool_panel() - def get_section( self, section_id, new_label=None, create_if_needed=False ): - tool_panel_section_key = str( section_id ) + def get_section(self, section_id, new_label=None, create_if_needed=False): + tool_panel_section_key = str(section_id) if tool_panel_section_key in self._tool_panel: # Appending a tool to an existing section in toolbox._tool_panel - tool_section = self._tool_panel[ tool_panel_section_key ] - log.debug( "Appending to tool panel section: %s" % str( tool_section.name ) ) + tool_section = self._tool_panel[tool_panel_section_key] + log.debug("Appending to tool panel section: %s" % str(tool_section.name)) elif create_if_needed: # Appending a new section to toolbox._tool_panel if new_label is None: @@ -229,7 +229,7 @@ def get_section( self, section_id, new_label=None, create_if_needed=False ): 'version': '', } self.handle_panel_update(section_dict) - tool_section = self._tool_panel[ tool_panel_section_key ] + tool_section = self._tool_panel[tool_panel_section_key] self._save_integrated_tool_panel() else: tool_section = None @@ -241,7 +241,7 @@ def create_section(self, section_dict): log.debug("Loading new tool panel section: %s" % str(tool_section.name)) return tool_section - def get_integrated_section_for_tool( self, tool ): + def get_integrated_section_for_tool(self, tool): tool_id = tool.id if tool_id in self._integrated_section_by_tool: @@ -297,6 +297,8 @@ def __add_tool_to_tool_panel(self, tool, panel_component, section=False): if index: panel_dict.insert_tool(index, tool) inserted = True + else: + log.warning("Could not find lineage for tool '%s'", tool.id) if not inserted: if ( tool.guid is None or @@ -322,91 +324,91 @@ def __add_tool_to_tool_panel(self, tool, panel_component, section=False): if not hasattr(self.app, 'tool_cache') or tool_id in self.app.tool_cache._new_tool_ids: log.debug(log_msg) - def _load_tool_panel( self ): + def _load_tool_panel(self): execution_timer = ExecutionTimer() for key, item_type, val in self._integrated_tool_panel.panel_items_iter(): if item_type == panel_item_types.TOOL: - tool_id = key.replace( 'tool_', '', 1 ) + tool_id = key.replace('tool_', '', 1) if tool_id in self._tools_by_id: - self.__add_tool_to_tool_panel( val, self._tool_panel, section=False ) + self.__add_tool_to_tool_panel(val, self._tool_panel, section=False) self._integrated_section_by_tool[tool_id] = '', '' elif item_type == panel_item_types.WORKFLOW: - workflow_id = key.replace( 'workflow_', '', 1 ) + workflow_id = key.replace('workflow_', '', 1) if workflow_id in self._workflows_by_id: - workflow = self._workflows_by_id[ workflow_id ] - self._tool_panel[ key ] = workflow - log.debug( "Loaded workflow: %s %s" % ( workflow_id, workflow.name ) ) + workflow = self._workflows_by_id[workflow_id] + self._tool_panel[key] = workflow + log.debug("Loaded workflow: %s %s" % (workflow_id, workflow.name)) elif item_type == panel_item_types.LABEL: - self._tool_panel[ key ] = val + self._tool_panel[key] = val elif item_type == panel_item_types.SECTION: section_dict = { 'id': val.id or '', 'name': val.name or '', 'version': val.version or '', } - section = ToolSection( section_dict ) - log.debug( "Loading section: %s" % section_dict.get( 'name' ) ) + section = ToolSection(section_dict) + log.debug("Loading section: %s" % section_dict.get('name')) for section_key, section_item_type, section_val in val.panel_items_iter(): if section_item_type == panel_item_types.TOOL: - tool_id = section_key.replace( 'tool_', '', 1 ) + tool_id = section_key.replace('tool_', '', 1) if tool_id in self._tools_by_id: - self.__add_tool_to_tool_panel( section_val, section, section=True ) + self.__add_tool_to_tool_panel(section_val, section, section=True) self._integrated_section_by_tool[tool_id] = key, val.name elif section_item_type == panel_item_types.WORKFLOW: - workflow_id = section_key.replace( 'workflow_', '', 1 ) + workflow_id = section_key.replace('workflow_', '', 1) if workflow_id in self._workflows_by_id: - workflow = self._workflows_by_id[ workflow_id ] - section.elems[ section_key ] = workflow - log.debug( "Loaded workflow: %s %s" % ( workflow_id, workflow.name ) ) + workflow = self._workflows_by_id[workflow_id] + section.elems[section_key] = workflow + log.debug("Loaded workflow: %s %s" % (workflow_id, workflow.name)) elif section_item_type == panel_item_types.LABEL: if section_val: - section.elems[ section_key ] = section_val - log.debug( "Loaded label: %s" % ( section_val.text ) ) - self._tool_panel[ key ] = section + section.elems[section_key] = section_val + log.debug("Loaded label: %s" % (section_val.text)) + self._tool_panel[key] = section log.debug("Loading tool panel finished %s", execution_timer) - def _load_integrated_tool_panel_keys( self ): + def _load_integrated_tool_panel_keys(self): """ Load the integrated tool panel keys, setting values for tools and workflows to None. The values will be reset when the various tool panel config files are parsed, at which time the tools and workflows are loaded. """ - tree = parse_xml( self._integrated_tool_panel_config ) + tree = parse_xml(self._integrated_tool_panel_config) root = tree.getroot() for elem in root: - key = elem.get( 'id' ) + key = elem.get('id') if elem.tag == 'tool': - self._integrated_tool_panel.stub_tool( key ) + self._integrated_tool_panel.stub_tool(key) elif elem.tag == 'workflow': - self._integrated_tool_panel.stub_workflow( key ) + self._integrated_tool_panel.stub_workflow(key) elif elem.tag == 'section': - section = ToolSection( elem ) + section = ToolSection(elem) for section_elem in elem: - section_id = section_elem.get( 'id' ) + section_id = section_elem.get('id') if section_elem.tag == 'tool': - section.elems.stub_tool( section_id ) + section.elems.stub_tool(section_id) elif section_elem.tag == 'workflow': - section.elems.stub_workflow( section_id ) + section.elems.stub_workflow(section_id) elif section_elem.tag == 'label': - section.elems.stub_label( section_id ) - self._integrated_tool_panel.append_section( key, section ) + section.elems.stub_label(section_id) + self._integrated_tool_panel.append_section(key, section) elif elem.tag == 'label': - self._integrated_tool_panel.stub_label( key ) + self._integrated_tool_panel.stub_label(key) - def get_tool( self, tool_id, tool_version=None, get_all_versions=False, exact=False ): - """Attempt to locate a tool in the tool box.""" + def get_tool(self, tool_id, tool_version=None, get_all_versions=False, exact=False): + """Attempt to locate a tool in the tool box. Note that `exact` only refers to the `tool_id`, not the `tool_version`.""" if tool_version: - tool_version = str( tool_version ) + tool_version = str(tool_version) if get_all_versions and exact: raise AssertionError("Cannot specify get_tool with both get_all_versions and exact as True") if "/repos/" in tool_id: # test if tool came from a toolshed tool_id_without_tool_shed = tool_id.split("/repos/")[1] - available_tool_sheds = [ urlparse(_) for _ in self.app.tool_shed_registry.tool_sheds.values() ] - available_tool_sheds = [ url.geturl().replace(url.scheme + "://", '', 1) for url in available_tool_sheds] - tool_ids = [ tool_shed + "repos/" + tool_id_without_tool_shed for tool_shed in available_tool_sheds] + available_tool_sheds = [urlparse(_) for _ in self.app.tool_shed_registry.tool_sheds.values()] + available_tool_sheds = [url.geturl().replace(url.scheme + "://", '', 1) for url in available_tool_sheds] + tool_ids = [tool_shed + "repos/" + tool_id_without_tool_shed for tool_shed in available_tool_sheds] if tool_id in tool_ids: # move original tool_id to the top of tool_ids tool_ids.remove(tool_id) tool_ids.insert(0, tool_id) @@ -416,9 +418,9 @@ def get_tool( self, tool_id, tool_version=None, get_all_versions=False, exact=Fa if tool_id in self._tools_by_id and not get_all_versions: # tool_id exactly matches an available tool by id (which is 'old' tool_id or guid) if not tool_version: - return self._tools_by_id[ tool_id ] - elif tool_version in self._tool_versions_by_id[ tool_id ]: - return self._tool_versions_by_id[ tool_id ][ tool_version ] + return self._tools_by_id[tool_id] + elif tool_version in self._tool_versions_by_id[tool_id]: + return self._tool_versions_by_id[tool_id][tool_version] elif exact: # We're looking for an exact match, so we skip lineage and # versionless mapping, though we may want to check duplicate @@ -426,18 +428,18 @@ def get_tool( self, tool_id, tool_version=None, get_all_versions=False, exact=Fa continue # exact tool id match not found, or all versions requested, search for other options, e.g. migrated tools or different versions rval = [] - tool_lineage = self._lineage_map.get( tool_id ) + tool_lineage = self._lineage_map.get(tool_id) if tool_lineage: - lineage_tool_versions = tool_lineage.get_versions( ) + lineage_tool_versions = tool_lineage.get_versions() for lineage_tool_version in lineage_tool_versions: - lineage_tool = self._tool_from_lineage_version( lineage_tool_version ) + lineage_tool = self._tool_from_lineage_version(lineage_tool_version) if lineage_tool: - rval.append( lineage_tool ) + rval.append(lineage_tool) if not rval: # still no tool, do a deeper search and try to match by old ids for tool in self._tools_by_id.values(): if tool.old_id == tool_id: - rval.append( tool ) + rval.append(tool) if rval: if get_all_versions: return rval @@ -452,13 +454,16 @@ def get_tool( self, tool_id, tool_version=None, get_all_versions=False, exact=Fa # We now likely have a Toolshed guid passed in, but no supporting database entries # If the tool exists by exact id and is loaded then provide exact match within a list if tool_id in self._tools_by_id: - return[ self._tools_by_id[ tool_id ] ] + if get_all_versions: + return [self._tools_by_id[tool_id]] + else: + return self._tools_by_id[tool_id] return None - def has_tool( self, tool_id, tool_version=None, exact=False ): - return self.get_tool( tool_id, tool_version=tool_version, exact=exact ) is not None + def has_tool(self, tool_id, tool_version=None, exact=False): + return self.get_tool(tool_id, tool_version=tool_version, exact=exact) is not None - def is_missing_shed_tool( self, tool_id ): + def is_missing_shed_tool(self, tool_id): """Confirm that the tool ID does reference a shed tool and is not installed.""" if tool_id is None: # This is not a tool ID. @@ -472,14 +477,14 @@ def is_missing_shed_tool( self, tool_id ): return True return False - def get_tool_id( self, tool_id ): + def get_tool_id(self, tool_id): """ Take a tool id - potentially from a different Galaxy instance or that is no longer loaded - and find the closest match to the currently loaded tools (using get_tool for inexact matches which currently returns the oldest tool shed installed tool with the same short id). """ if tool_id not in self._tools_by_id: - tool = self.get_tool( tool_id ) + tool = self.get_tool(tool_id) if tool: tool_id = tool.id else: @@ -487,48 +492,48 @@ def get_tool_id( self, tool_id ): # else exact match - leave unmodified. return tool_id - def get_loaded_tools_by_lineage( self, tool_id ): + def get_loaded_tools_by_lineage(self, tool_id): """Get all loaded tools associated by lineage to the tool whose id is tool_id.""" - tool_lineage = self._lineage_map.get( tool_id ) + tool_lineage = self._lineage_map.get(tool_id) if tool_lineage: - lineage_tool_versions = tool_lineage.get_versions( ) + lineage_tool_versions = tool_lineage.get_versions() available_tool_versions = [] for lineage_tool_version in lineage_tool_versions: - tool = self._tool_from_lineage_version( lineage_tool_version ) + tool = self._tool_from_lineage_version(lineage_tool_version) if tool: - available_tool_versions.append( tool ) + available_tool_versions.append(tool) return available_tool_versions else: if tool_id in self._tools_by_id: - tool = self._tools_by_id[ tool_id ] - return [ tool ] + tool = self._tools_by_id[tool_id] + return [tool] return [] - def tools( self ): + def tools(self): return iteritems(self._tools_by_id) - def dynamic_confs( self, include_migrated_tool_conf=False ): + def dynamic_confs(self, include_migrated_tool_conf=False): confs = [] for dynamic_tool_conf_dict in self._dynamic_tool_confs: - dynamic_tool_conf_filename = dynamic_tool_conf_dict[ 'config_filename' ] + dynamic_tool_conf_filename = dynamic_tool_conf_dict['config_filename'] if include_migrated_tool_conf or (dynamic_tool_conf_filename != self.app.config.migrated_tools_config): - confs.append( dynamic_tool_conf_dict ) + confs.append(dynamic_tool_conf_dict) return confs - def dynamic_conf_filenames( self, include_migrated_tool_conf=False ): + def dynamic_conf_filenames(self, include_migrated_tool_conf=False): """ Return list of dynamic tool configuration filenames (shed_tools). These must be used with various dynamic tool configuration update operations (e.g. with update_shed_config). """ - for dynamic_tool_conf_dict in self.dynamic_confs( include_migrated_tool_conf=include_migrated_tool_conf ): - yield dynamic_tool_conf_dict[ 'config_filename' ] + for dynamic_tool_conf_dict in self.dynamic_confs(include_migrated_tool_conf=include_migrated_tool_conf): + yield dynamic_tool_conf_dict['config_filename'] - def _path_template_kwds( self ): + def _path_template_kwds(self): return {} - def _load_tool_tag_set( self, item, panel_dict, integrated_panel_dict, tool_path, load_panel_dict, guid=None, index=None, internal=False ): + def _load_tool_tag_set(self, item, panel_dict, integrated_panel_dict, tool_path, load_panel_dict, guid=None, index=None, internal=False): try: - path_template = item.get( "file" ) + path_template = item.get("file") template_kwds = self._path_template_kwds() path = string.Template(path_template).safe_substitute(**template_kwds) concrete_path = os.path.join(tool_path, path) @@ -554,7 +559,7 @@ def _load_tool_tag_set( self, item, panel_dict, integrated_panel_dict, tool_path tool = self.load_tool(concrete_path, guid=guid, repository_id=repository_id, use_cached=False) if not tool: # tool was not in cache and is not a tool shed tool. tool = self.load_tool(concrete_path, use_cached=False) - if string_as_bool(item.get( 'hidden', False )): + if string_as_bool(item.get('hidden', False)): tool.hidden = True key = 'tool_%s' % str(tool.id) if can_load_into_panel_dict: @@ -564,23 +569,21 @@ def _load_tool_tag_set( self, item, panel_dict, integrated_panel_dict, tool_path tool.repository_owner = tool_shed_repository.owner tool.installed_changeset_revision = tool_shed_repository.installed_changeset_revision tool.guid = guid - tool.version = item.elem.find( "version" ).text - # Make sure tools are registered in self._lineage_map. - tool._lineage = self._lineage_map.register( tool ) + tool.version = item.elem.find("version").text if item.has_elem: - self._tool_tag_manager.handle_tags( tool.id, item.elem ) - self.__add_tool( tool, load_panel_dict, panel_dict ) + self._tool_tag_manager.handle_tags(tool.id, item.elem) + self.__add_tool(tool, load_panel_dict, panel_dict) # Always load the tool into the integrated_panel_dict, or it will not be included in the integrated_tool_panel.xml file. - integrated_panel_dict.update_or_append( index, key, tool ) + integrated_panel_dict.update_or_append(index, key, tool) # If labels were specified in the toolbox config, attach them to # the tool. labels = item.labels if labels is not None: tool.labels = labels except IOError: - log.error( "Error reading tool configuration file from path: %s" % path ) + log.error("Error reading tool configuration file from path: %s" % path) except Exception: - log.exception( "Error reading tool from path: %s", path ) + log.exception("Error reading tool from path: %s", path) def get_tool_repository_from_xml_item(self, item, path): tool_shed = item.elem.find("tool_shed").text @@ -617,12 +620,12 @@ def get_tool_repository_from_xml_item(self, item, path): raise Exception(msg) return repository - def _get_tool_shed_repository( self, tool_shed, name, owner, installed_changeset_revision ): + def _get_tool_shed_repository(self, tool_shed, name, owner, installed_changeset_revision): # Abstract class doesn't have a dependency on the database, for full Tool Shed # support the actual Galaxy ToolBox implements this method and returns a Tool Shed repository. return None - def __add_tool( self, tool, load_panel_dict, panel_dict ): + def __add_tool(self, tool, load_panel_dict, panel_dict): # Allow for the same tool to be loaded into multiple places in the # tool panel. We have to handle the case where the tool is contained # in a repository installed from the tool shed, and the Galaxy @@ -631,53 +634,54 @@ def __add_tool( self, tool, load_panel_dict, panel_dict ): # changed, so the tool should always be reloaded here. We used to # only load the tool if it was not found in self._tools_by_id, but # performing that check did not enable this scenario. - self.register_tool( tool ) + tool._lineage = self._lineage_map.register(tool) + self.register_tool(tool) if load_panel_dict: - self.__add_tool_to_tool_panel( tool, panel_dict, section=isinstance( panel_dict, ToolSection ) ) + self.__add_tool_to_tool_panel(tool, panel_dict, section=isinstance(panel_dict, ToolSection)) - def _load_workflow_tag_set( self, item, panel_dict, integrated_panel_dict, load_panel_dict, index=None ): + def _load_workflow_tag_set(self, item, panel_dict, integrated_panel_dict, load_panel_dict, index=None): try: # TODO: should id be encoded? - workflow_id = item.get( 'id' ) - workflow = self._load_workflow( workflow_id ) - self._workflows_by_id[ workflow_id ] = workflow + workflow_id = item.get('id') + workflow = self._load_workflow(workflow_id) + self._workflows_by_id[workflow_id] = workflow key = 'workflow_' + workflow_id if load_panel_dict: - panel_dict[ key ] = workflow + panel_dict[key] = workflow # Always load workflows into the integrated_panel_dict. - integrated_panel_dict.update_or_append( index, key, workflow ) + integrated_panel_dict.update_or_append(index, key, workflow) except: - log.exception( "Error loading workflow: %s", workflow_id ) + log.exception("Error loading workflow: %s", workflow_id) - def _load_label_tag_set( self, item, panel_dict, integrated_panel_dict, load_panel_dict, index=None ): - label = ToolSectionLabel( item ) + def _load_label_tag_set(self, item, panel_dict, integrated_panel_dict, load_panel_dict, index=None): + label = ToolSectionLabel(item) key = 'label_' + label.id if load_panel_dict: - panel_dict[ key ] = label - integrated_panel_dict.update_or_append( index, key, label ) + panel_dict[key] = label + integrated_panel_dict.update_or_append(index, key, label) - def _load_section_tag_set( self, item, tool_path, load_panel_dict, index=None, internal=False ): - key = item.get( "id" ) + def _load_section_tag_set(self, item, tool_path, load_panel_dict, index=None, internal=False): + key = item.get("id") if key in self._tool_panel: - section = self._tool_panel[ key ] + section = self._tool_panel[key] elems = section.elems else: - section = ToolSection( item ) + section = ToolSection(item) elems = section.elems if key in self._integrated_tool_panel: - integrated_section = self._integrated_tool_panel[ key ] + integrated_section = self._integrated_tool_panel[key] integrated_elems = integrated_section.elems else: - integrated_section = ToolSection( item ) + integrated_section = ToolSection(item) integrated_elems = integrated_section.elems - for sub_index, sub_item in enumerate( item.items ): + for sub_index, sub_item in enumerate(item.items): self.load_item( sub_item, tool_path=tool_path, panel_dict=elems, integrated_panel_dict=integrated_elems, load_panel_dict=load_panel_dict, - guid=sub_item.get( 'guid' ), + guid=sub_item.get('guid'), index=sub_index, internal=internal, ) @@ -686,28 +690,28 @@ def _load_section_tag_set( self, item, tool_path, load_panel_dict, index=None, i for section_key, section_item_type, section_item in integrated_elems.panel_items_iter(): if section_item_type == panel_item_types.TOOL: if section_item: - tool_id = section_key.replace( 'tool_', '', 1 ) + tool_id = section_key.replace('tool_', '', 1) self._integrated_section_by_tool[tool_id] = integrated_section.id, integrated_section.name if load_panel_dict: - self._tool_panel[ key ] = section + self._tool_panel[key] = section # Always load sections into the integrated_tool_panel. - self._integrated_tool_panel.update_or_append( index, key, integrated_section ) + self._integrated_tool_panel.update_or_append(index, key, integrated_section) def _load_tooldir_tag_set(self, item, elems, tool_path, integrated_elems, load_panel_dict): - directory = os.path.join( tool_path, item.get("dir") ) - recursive = string_as_bool( item.get("recursive", True) ) - self.__watch_directory( directory, elems, integrated_elems, load_panel_dict, recursive, force_watch=True ) + directory = os.path.join(tool_path, item.get("dir")) + recursive = string_as_bool(item.get("recursive", True)) + self.__watch_directory(directory, elems, integrated_elems, load_panel_dict, recursive, force_watch=True) - def __watch_directory( self, directory, elems, integrated_elems, load_panel_dict, recursive, force_watch=False ): + def __watch_directory(self, directory, elems, integrated_elems, load_panel_dict, recursive, force_watch=False): - def quick_load( tool_file, async=True ): + def quick_load(tool_file, async=True): try: - tool = self.load_tool( tool_file ) - self.__add_tool( tool, load_panel_dict, elems ) + tool = self.load_tool(tool_file) + self.__add_tool(tool, load_panel_dict, elems) # Always load the tool into the integrated_panel_dict, or it will not be included in the integrated_tool_panel.xml file. - key = 'tool_%s' % str( tool.id ) - integrated_elems[ key ] = tool + key = 'tool_%s' % str(tool.id) + integrated_elems[key] = tool if async: self._load_tool_panel() @@ -718,7 +722,7 @@ def quick_load( tool_file, async=True ): return None tool_loaded = False - for name in os.listdir( directory ): + for name in os.listdir(directory): if name.startswith('.' or '_'): # Very unlikely that we want to load tools from a hidden or private folder continue @@ -726,25 +730,25 @@ def quick_load( tool_file, async=True ): if os.path.isdir(child_path) and recursive: self.__watch_directory(child_path, elems, integrated_elems, load_panel_dict, recursive) elif self._looks_like_a_tool(child_path): - quick_load( child_path, async=False ) + quick_load(child_path, async=False) tool_loaded = True if (tool_loaded or force_watch) and self._tool_watcher: - self._tool_watcher.watch_directory( directory, quick_load ) + self._tool_watcher.watch_directory(directory, quick_load) - def load_tool( self, config_file, guid=None, repository_id=None, use_cached=False, **kwds ): + def load_tool(self, config_file, guid=None, repository_id=None, use_cached=False, **kwds): """Load a single tool from the file named by `config_file` and return an instance of `Tool`.""" # Parse XML configuration file and get the root element tool = None if use_cached: tool = self.load_tool_from_cache(config_file) if not tool: - tool = self.create_tool( config_file=config_file, repository_id=repository_id, guid=guid, **kwds ) + tool = self.create_tool(config_file=config_file, repository_id=repository_id, guid=guid, **kwds) if tool.tool_shed_repository or not guid: self.add_tool_to_cache(tool, config_file) if not tool.id.startswith("__") and self._tool_watcher: # do not monitor special tools written to tmp directory - no reason # to monitor such a large directory. - self._tool_watcher.watch_file( config_file, tool.id ) + self._tool_watcher.watch_file(config_file, tool.id) return tool def add_tool_to_cache(self, tool, config_file): @@ -753,39 +757,39 @@ def add_tool_to_cache(self, tool, config_file): self.app.tool_cache.cache_tool(config_file, tool) def load_tool_from_cache(self, config_file): - tool_cache = getattr( self.app, 'tool_cache', None ) - tool = tool_cache and tool_cache.get_tool( config_file ) + tool_cache = getattr(self.app, 'tool_cache', None) + tool = tool_cache and tool_cache.get_tool(config_file) return tool - def load_hidden_lib_tool( self, path ): - tool_xml = os.path.join( os.getcwd(), "lib", path ) - return self.load_hidden_tool( tool_xml ) + def load_hidden_lib_tool(self, path): + tool_xml = os.path.join(os.getcwd(), "lib", path) + return self.load_hidden_tool(tool_xml) - def load_hidden_tool( self, config_file, **kwds ): + def load_hidden_tool(self, config_file, **kwds): """ Load a hidden tool (in this context meaning one that does not appear in the tool panel) and register it in _tools_by_id. """ - tool = self.load_tool( config_file, **kwds ) - self.register_tool( tool ) + tool = self.load_tool(config_file, **kwds) + self.register_tool(tool) return tool - def register_tool( self, tool ): + def register_tool(self, tool): tool_id = tool.id version = tool.version or None if tool_id not in self._tool_versions_by_id: - self._tool_versions_by_id[ tool_id ] = { version: tool } + self._tool_versions_by_id[tool_id] = {version: tool} else: - self._tool_versions_by_id[ tool_id ][ version ] = tool + self._tool_versions_by_id[tool_id][version] = tool if tool_id in self._tools_by_id: - related_tool = self._tools_by_id[ tool_id ] + related_tool = self._tools_by_id[tool_id] # This one becomes the default un-versioned tool # if newer. - if self._newer_tool( tool, related_tool ): - self._tools_by_id[ tool_id ] = tool + if self._newer_tool(tool, related_tool): + self._tools_by_id[tool_id] = tool else: - self._tools_by_id[ tool_id ] = tool + self._tools_by_id[tool_id] = tool - def package_tool( self, trans, tool_id ): + def package_tool(self, trans, tool_id): """ Create a tarball with the tool's xml, help images, and test data. :param trans: the web transaction @@ -794,22 +798,22 @@ def package_tool( self, trans, tool_id ): """ # Make sure the tool is actually loaded. if tool_id not in self._tools_by_id: - raise ObjectNotFound("No tool found with id '%s'." % escape( tool_id )) + raise ObjectNotFound("No tool found with id '%s'." % escape(tool_id)) else: - tool = self._tools_by_id[ tool_id ] + tool = self._tools_by_id[tool_id] return tool.to_archive() - def reload_tool_by_id( self, tool_id ): + def reload_tool_by_id(self, tool_id): """ Attempt to reload the tool identified by 'tool_id', if successful replace the old tool. """ if tool_id not in self._tools_by_id: - message = "No tool with id '%s'." % escape( tool_id ) + message = "No tool with id '%s'." % escape(tool_id) status = 'error' else: - old_tool = self._tools_by_id[ tool_id ] - new_tool = self.load_tool( old_tool.config_file, use_cached=False ) + old_tool = self._tools_by_id[tool_id] + new_tool = self.load_tool(old_tool.config_file, use_cached=False) # The tool may have been installed from a tool shed, so set the tool shed attributes. # Since the tool version may have changed, we don't override it here. new_tool.id = old_tool.id @@ -823,20 +827,20 @@ def reload_tool_by_id( self, tool_id ): tool_key = 'tool_' + tool_id for key, val in self._tool_panel.items(): if key == tool_key: - self._tool_panel[ key ] = new_tool + self._tool_panel[key] = new_tool break - elif key.startswith( 'section' ): + elif key.startswith('section'): if tool_key in val.elems: - self._tool_panel[ key ].elems[ tool_key ] = new_tool + self._tool_panel[key].elems[tool_key] = new_tool break # (Re-)Register the reloaded tool, this will handle # _tools_by_id and _tool_versions_by_id - self.register_tool( new_tool ) - message = { 'name': old_tool.name, 'id': old_tool.id, 'version': old_tool.version } + self.register_tool(new_tool) + message = {'name': old_tool.name, 'id': old_tool.id, 'version': old_tool.version} status = 'done' return message, status - def remove_tool_by_id( self, tool_id, remove_from_panel=True ): + def remove_tool_by_id(self, tool_id, remove_from_panel=True): """ Attempt to remove the tool identified by 'tool_id'. Ignores tool lineage - so to remove a tool with potentially multiple @@ -845,40 +849,40 @@ def remove_tool_by_id( self, tool_id, remove_from_panel=True ): if needed. """ if tool_id not in self._tools_by_id: - message = "No tool with id %s" % escape( tool_id ) + message = "No tool with id %s" % escape(tool_id) status = 'error' else: - tool = self._tools_by_id[ tool_id ] - del self._tools_by_id[ tool_id ] - tool_cache = getattr( self.app, 'tool_cache', None ) + tool = self._tools_by_id[tool_id] + del self._tools_by_id[tool_id] + tool_cache = getattr(self.app, 'tool_cache', None) if tool_cache: - tool_cache.expire_tool( tool_id ) + tool_cache.expire_tool(tool_id) if remove_from_panel: tool_key = 'tool_' + tool_id for key, val in self._tool_panel.items(): if key == tool_key: - del self._tool_panel[ key ] + del self._tool_panel[key] break - elif key.startswith( 'section' ): + elif key.startswith('section'): if tool_key in val.elems: - del self._tool_panel[ key ].elems[ tool_key ] + del self._tool_panel[key].elems[tool_key] break if tool_id in self.data_manager_tools: - del self.data_manager_tools[ tool_id ] + del self.data_manager_tools[tool_id] # TODO: do we need to manually remove from the integrated panel here? message = "Removed the tool:
    " - message += "name: %s
    " % escape( tool.name ) - message += "id: %s
    " % escape( tool.id ) - message += "version: %s" % escape( tool.version ) + message += "name: %s
    " % escape(tool.name) + message += "id: %s
    " % escape(tool.id) + message += "version: %s" % escape(tool.version) status = 'done' return message, status - def get_sections( self ): + def get_sections(self): for k, v in self._tool_panel.items(): - if isinstance( v, ToolSection ): + if isinstance(v, ToolSection): yield (v.id, v.name) - def find_section_id( self, tool_panel_section_id ): + def find_section_id(self, tool_panel_section_id): """ Find the section ID referenced by the key or return '' indicating no such section id. @@ -896,30 +900,30 @@ def find_section_id( self, tool_panel_section_id ): tool_panel_section_id = '' return tool_panel_section_id - def _load_workflow( self, workflow_id ): + def _load_workflow(self, workflow_id): """ Return an instance of 'Workflow' identified by `id`, which is encoded in the tool panel. """ - id = self.app.security.decode_id( workflow_id ) - stored = self.app.model.context.query( self.app.model.StoredWorkflow ).get( id ) + id = self.app.security.decode_id(workflow_id) + stored = self.app.model.context.query(self.app.model.StoredWorkflow).get(id) return stored.latest_workflow - def tool_panel_contents( self, trans, **kwds ): + def tool_panel_contents(self, trans, **kwds): """ Filter tool_panel contents for displaying for user. """ - filter_method = self._build_filter_method( trans ) + filter_method = self._build_filter_method(trans) for _, item_type, elt in self._tool_panel.panel_items_iter(): - elt = filter_method( elt, item_type ) + elt = filter_method(elt, item_type) if elt: yield elt - def to_dict( self, trans, in_panel=True, **kwds ): + def to_dict(self, trans, in_panel=True, **kwds): """ to_dict toolbox. """ if in_panel: - panel_elts = list( self.tool_panel_contents( trans, **kwds ) ) + panel_elts = list(self.tool_panel_contents(trans, **kwds)) # Produce panel. rval = [] kwargs = dict( @@ -927,20 +931,20 @@ def to_dict( self, trans, in_panel=True, **kwds ): link_details=True ) for elt in panel_elts: - rval.append( elt.to_dict( **kwargs ) ) + rval.append(elt.to_dict(**kwargs)) else: - filter_method = self._build_filter_method( trans ) + filter_method = self._build_filter_method(trans) tools = [] for id, tool in self._tools_by_id.items(): - tool = filter_method( tool, panel_item_types.TOOL ) + tool = filter_method(tool, panel_item_types.TOOL) if not tool: continue - tools.append( tool.to_dict( trans, link_details=True ) ) + tools.append(tool.to_dict(trans, link_details=True)) rval = tools return rval - def _lineage_in_panel( self, panel_dict, tool=None, tool_lineage=None ): + def _lineage_in_panel(self, panel_dict, tool=None, tool_lineage=None): """ If tool with same lineage already in panel (or section) - find and return it. Otherwise return None. """ @@ -950,70 +954,72 @@ def _lineage_in_panel( self, panel_dict, tool=None, tool_lineage=None ): if tool_lineage is not None: lineage_tool_versions = reversed(tool_lineage.get_versions()) for lineage_tool_version in lineage_tool_versions: - lineage_tool = self._tool_from_lineage_version( lineage_tool_version ) + lineage_tool = self._tool_from_lineage_version(lineage_tool_version) if lineage_tool: lineage_id = lineage_tool.id - if panel_dict.has_tool_with_id( lineage_id ): - return panel_dict.get_tool_with_id( lineage_id ) + if panel_dict.has_tool_with_id(lineage_id): + return panel_dict.get_tool_with_id(lineage_id) + else: + log.warning("Could not find lineage for tool '%s'", tool.id) return None - def _newer_tool( self, tool1, tool2 ): + def _newer_tool(self, tool1, tool2): """ Return True if tool1 is considered "newer" given its own lineage description. """ return tool1.version_object > tool2.version_object - def _tool_from_lineage_version( self, lineage_tool_version ): + def _tool_from_lineage_version(self, lineage_tool_version): if lineage_tool_version.id_based: - return self._tools_by_id.get( lineage_tool_version.id, None ) + return self._tools_by_id.get(lineage_tool_version.id, None) else: - return self._tool_versions_by_id.get( lineage_tool_version.id, {} ).get( lineage_tool_version.version, None ) + return self._tool_versions_by_id.get(lineage_tool_version.id, {}).get(lineage_tool_version.version, None) - def _build_filter_method( self, trans ): - context = Bunch( toolbox=self, trans=trans ) - filters = self._filter_factory.build_filters( trans ) + def _build_filter_method(self, trans): + context = Bunch(toolbox=self, trans=trans) + filters = self._filter_factory.build_filters(trans) return lambda element, item_type: _filter_for_panel(element, item_type, filters, context) -def _filter_for_panel( item, item_type, filters, context ): +def _filter_for_panel(item, item_type, filters, context): """ Filters tool panel elements so that only those that are compatible with provided filters are kept. """ - def _apply_filter( filter_item, filter_list ): + def _apply_filter(filter_item, filter_list): for filter_method in filter_list: try: - if not filter_method( context, filter_item ): + if not filter_method(context, filter_item): return False except Exception as e: - raise MessageException( "Toolbox filter exception from '%s': %s." % ( filter_method.__name__, e ) ) + raise MessageException("Toolbox filter exception from '%s': %s." % (filter_method.__name__, e)) return True if item_type == panel_item_types.TOOL: - if _apply_filter( item, filters[ 'tool' ] ): + if _apply_filter(item, filters['tool']): return item elif item_type == panel_item_types.LABEL: - if _apply_filter( item, filters[ 'label' ] ): + if _apply_filter(item, filters['label']): return item elif item_type == panel_item_types.SECTION: # Filter section item-by-item. Only show a label if there are # non-filtered tools below it. - if _apply_filter( item, filters[ 'section' ] ): + if _apply_filter(item, filters['section']): cur_label_key = None tools_under_label = False filtered_elems = item.elems.copy() for key, section_item_type, section_item in item.panel_items_iter(): if section_item_type == panel_item_types.TOOL: # Filter tool. - if _apply_filter( section_item, filters[ 'tool' ] ): + if _apply_filter(section_item, filters['tool']): tools_under_label = True else: - del filtered_elems[ key ] + del filtered_elems[key] elif section_item_type == panel_item_types.LABEL: # If there is a label and it does not have tools, # remove it. - if cur_label_key and ( not tools_under_label or not _apply_filter( section_item, filters[ 'label' ] ) ): - del filtered_elems[ cur_label_key ] + if cur_label_key and (not tools_under_label or not _apply_filter(section_item, filters['label'])): + del filtered_elems[cur_label_key] # Reset attributes for new label. cur_label_key = key @@ -1021,10 +1027,10 @@ def _apply_filter( filter_item, filter_list ): # Handle last label. if cur_label_key and not tools_under_label: - del filtered_elems[ cur_label_key ] + del filtered_elems[cur_label_key] # Only return section if there are elements. - if len( filtered_elems ) != 0: + if len(filtered_elems) != 0: copy = item.copy() copy.elems = filtered_elems return copy @@ -1045,7 +1051,7 @@ def __init__(self, config_filenames, tool_root_dir, app): self._init_dependency_manager() @property - def sa_session( self ): + def sa_session(self): """ Returns a SQLAlchemy session """ @@ -1054,8 +1060,8 @@ def sa_session( self ): def _looks_like_a_tool(self, path): return looks_like_a_tool(path, enable_beta_formats=getattr(self.app.config, "enable_beta_tool_formats", False)) - def _init_dependency_manager( self ): - self.dependency_manager = build_dependency_manager( self.app.config ) + def _init_dependency_manager(self): + self.dependency_manager = build_dependency_manager(self.app.config) def reload_dependency_manager(self): self._init_dependency_manager() diff --git a/lib/galaxy/tools/toolbox/filters/__init__.py b/lib/galaxy/tools/toolbox/filters/__init__.py index ec96cdc54b93..6e6380764828 100644 --- a/lib/galaxy/tools/toolbox/filters/__init__.py +++ b/lib/galaxy/tools/toolbox/filters/__init__.py @@ -5,38 +5,38 @@ from galaxy.util import listify -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class FilterFactory( object ): +class FilterFactory(object): """ An instance of this class is responsible for filtering the list of tools presented to a given user in a given context. """ - def __init__( self, toolbox ): + def __init__(self, toolbox): self.toolbox = toolbox # Prepopulate dict containing filters that are always checked, # other filters that get checked depending on context (e.g. coming from # trackster or no user found are added in build filters). - self.default_filters = dict( tool=[ _not_hidden, _handle_authorization ], section=[], label=[] ) + self.default_filters = dict(tool=[_not_hidden, _handle_authorization], section=[], label=[]) # Add dynamic filters to these default filters. config = toolbox.app.config - self.__base_modules = listify( getattr( config, "toolbox_filter_base_modules", "galaxy.tools.filters" ) ) - self.__init_filters( "tool", getattr( config, "tool_filters", "" ), self.default_filters ) - self.__init_filters( "section", getattr( config, "tool_section_filters", "" ), self.default_filters ) - self.__init_filters( "label", getattr( config, "tool_label_filters", "" ), self.default_filters ) + self.__base_modules = listify(getattr(config, "toolbox_filter_base_modules", "galaxy.tools.filters")) + self.__init_filters("tool", getattr(config, "tool_filters", ""), self.default_filters) + self.__init_filters("section", getattr(config, "tool_section_filters", ""), self.default_filters) + self.__init_filters("label", getattr(config, "tool_label_filters", ""), self.default_filters) - def build_filters( self, trans, **kwds ): + def build_filters(self, trans, **kwds): """ Build list of filters to check tools against given current context. """ - filters = deepcopy( self.default_filters ) + filters = deepcopy(self.default_filters) if trans.user: for name, value in trans.user.preferences.items(): if value.strip(): - user_filters = listify( value, do_strip=True ) + user_filters = listify(value, do_strip=True) category = '' if name == 'toolbox_tool_filters': category = "tool" @@ -45,65 +45,65 @@ def build_filters( self, trans, **kwds ): elif name == 'toolbox_label_filters': category = "label" if category: - validate = getattr( trans.app.config, 'user_%s_filters' % category, [] ) - self.__init_filters( category, user_filters, filters, validate=validate ) + validate = getattr(trans.app.config, 'user_%s_filters' % category, []) + self.__init_filters(category, user_filters, filters, validate=validate) else: - if kwds.get( "trackster", False ): - filters[ "tool" ].append( _has_trackster_conf ) + if kwds.get("trackster", False): + filters["tool"].append(_has_trackster_conf) return filters - def __init_filters( self, key, filters, toolbox_filters, validate=None ): + def __init_filters(self, key, filters, toolbox_filters, validate=None): for filter in filters: if validate is None or filter in validate or filter in self.default_filters: - filter_function = self.build_filter_function( filter ) - toolbox_filters[ key ].append( filter_function ) + filter_function = self.build_filter_function(filter) + toolbox_filters[key].append(filter_function) else: - log.warning( "Refusing to load %s filter '%s' which is not defined in config", key, filter ) + log.warning("Refusing to load %s filter '%s' which is not defined in config", key, filter) return toolbox_filters - def build_filter_function( self, filter_name ): + def build_filter_function(self, filter_name): """Obtain python function (importing a submodule if needed) corresponding to filter_name. """ if ":" in filter_name: # Should be a submodule of filters (e.g. examples:restrict_development_tools) (module_name, function_name) = filter_name.rsplit(":", 1) - function = self._import_filter( module_name, function_name ) + function = self._import_filter(module_name, function_name) else: # No module found, just load a function from this file or # one that has be explicitly imported. - function = getattr( globals(), filter_name.strip() ) + function = getattr(globals(), filter_name.strip()) return function - def _import_filter( self, module_name, function_name ): + def _import_filter(self, module_name, function_name): function_name = function_name.strip() for base_module in self.__base_modules: - full_module_name = "%s.%s" % ( base_module, module_name.strip() ) + full_module_name = "%s.%s" % (base_module, module_name.strip()) try: - __import__( full_module_name ) + __import__(full_module_name) except ImportError: # log.debug("Failed to load module.", exc_info=True) continue - module = sys.modules[ full_module_name ] - if hasattr( module, function_name ): - return getattr( module, function_name ) + module = sys.modules[full_module_name] + if hasattr(module, function_name): + return getattr(module, function_name) raise Exception("Failed to find filter %s.%s" % (module_name, function_name)) # Stock Filter Functions -def _not_hidden( context, tool ): +def _not_hidden(context, tool): return not tool.hidden -def _handle_authorization( context, tool ): +def _handle_authorization(context, tool): user = context.trans.user if tool.require_login and not user: return False - if not tool.allow_user_access( user, attempting_access=False ): + if not tool.allow_user_access(user, attempting_access=False): return False return True -def _has_trackster_conf( context, tool ): +def _has_trackster_conf(context, tool): return tool.trackster_conf diff --git a/lib/galaxy/tools/toolbox/integrated_panel.py b/lib/galaxy/tools/toolbox/integrated_panel.py index e9985706b5ea..555168fde7ba 100644 --- a/lib/galaxy/tools/toolbox/integrated_panel.py +++ b/lib/galaxy/tools/toolbox/integrated_panel.py @@ -30,10 +30,10 @@ class ManagesIntegratedToolPanelMixin: def _init_integrated_tool_panel(self, config): self.update_integrated_tool_panel = config.update_integrated_tool_panel self._integrated_tool_panel_config = config.integrated_tool_panel_config - self._integrated_tool_panel_tracking_directory = getattr( config, "integrated_tool_panel_tracking_directory", None ) + self._integrated_tool_panel_tracking_directory = getattr(config, "integrated_tool_panel_tracking_directory", None) # In-memory dictionary that defines the layout of the tool_panel.xml file on disk. self._integrated_tool_panel = ToolPanelElements() - self._integrated_tool_panel_config_has_contents = os.path.exists( self._integrated_tool_panel_config ) and os.stat( self._integrated_tool_panel_config ).st_size > 0 + self._integrated_tool_panel_config_has_contents = os.path.exists(self._integrated_tool_panel_config) and os.stat(self._integrated_tool_panel_config).st_size > 0 if self._integrated_tool_panel_config_has_contents: self._load_integrated_tool_panel_keys() @@ -45,7 +45,7 @@ def _save_integrated_tool_panel(self): # will be False when things like functional tests are the caller. self._write_integrated_tool_panel_config_file() - def _write_integrated_tool_panel_config_file( self ): + def _write_integrated_tool_panel_config_file(self): """ Write the current in-memory version of the integrated_tool_panel.xml file to disk. Since Galaxy administrators use this file to manage the tool panel, we'll not use xml_to_string() since it doesn't write XML quite right. @@ -60,47 +60,47 @@ def _write_integrated_tool_panel_config_file( self ): filename = os.path.join(tracking_directory, name) open_file = open(filename, "w") fd = open_file.fileno() - os.write( fd, '\n' ) - os.write( fd, '\n' ) - os.write( fd, ' \n') + os.write(fd, '\n') + os.write(fd, '\n') + os.write(fd, ' \n') for key, item_type, item in self._integrated_tool_panel.panel_items_iter(): if item: if item_type == panel_item_types.TOOL: - os.write( fd, ' \n' % item.id ) + os.write(fd, ' \n' % item.id) elif item_type == panel_item_types.WORKFLOW: - os.write( fd, ' \n' % item.id ) + os.write(fd, ' \n' % item.id) elif item_type == panel_item_types.LABEL: label_id = item.id or '' label_text = item.text or '' label_version = item.version or '' - os.write( fd, ' \n') + os.close(fd) + destination = os.path.abspath(self._integrated_tool_panel_config) if tracking_directory: open(filename + ".stack", "w").write(''.join(traceback.format_stack())) - shutil.copy( filename, filename + ".copy" ) + shutil.copy(filename, filename + ".copy") filename = filename + ".copy" - shutil.move( filename, destination ) - os.chmod( self._integrated_tool_panel_config, 0o644 ) + shutil.move(filename, destination) + os.chmod(self._integrated_tool_panel_config, 0o644) diff --git a/lib/galaxy/tools/toolbox/lineages/factory.py b/lib/galaxy/tools/toolbox/lineages/factory.py index 9bfd9aca5c65..0893f9471b7e 100644 --- a/lib/galaxy/tools/toolbox/lineages/factory.py +++ b/lib/galaxy/tools/toolbox/lineages/factory.py @@ -16,7 +16,7 @@ def register(self, tool): versionless_tool_id = remove_version_from_guid(tool_id) lineage = self.lineage_map.get(versionless_tool_id) if not lineage: - lineage = ToolLineage.from_tool( tool ) + lineage = ToolLineage.from_tool(tool) else: # A lineage for a tool with the same versionless_tool_id exists, # but this lineage may not have the current tools' version, @@ -42,7 +42,7 @@ def get(self, tool_id): if tool_id not in self.lineage_map: tool = self.app.toolbox._tools_by_id.get(tool_id) if tool: - lineage = ToolLineage.from_tool( tool ) + lineage = ToolLineage.from_tool(tool) if lineage: self.lineage_map[tool_id] = lineage return self.lineage_map.get(tool_id) diff --git a/lib/galaxy/tools/toolbox/lineages/interface.py b/lib/galaxy/tools/toolbox/lineages/interface.py index 6a691a359e85..44018a9070ce 100644 --- a/lib/galaxy/tools/toolbox/lineages/interface.py +++ b/lib/galaxy/tools/toolbox/lineages/interface.py @@ -15,7 +15,7 @@ def __init__(self, id, version): self.version = version @property - def id_based( self ): + def id_based(self): """ Return True if the lineage is defined by GUIDs (in this case the indexer of the tools (i.e. the ToolBox) should ignore the tool_version (because it is encoded in the GUID and managed @@ -52,26 +52,26 @@ def tool_ids(self): return ["%s/%s" % (tool_id, version) for version in self.tool_versions] @staticmethod - def from_tool( tool ): + def from_tool(tool): tool_id = tool.id lineages_by_id = ToolLineage.lineages_by_id with ToolLineage.lock: if tool_id not in lineages_by_id: - lineages_by_id[ tool_id ] = ToolLineage( tool_id ) - lineage = lineages_by_id[ tool_id ] - lineage.register_version( tool.version ) + lineages_by_id[tool_id] = ToolLineage(tool_id) + lineage = lineages_by_id[tool_id] + lineage.register_version(tool.version) return lineage - def register_version( self, tool_version ): + def register_version(self, tool_version): assert tool_version is not None - self._tool_versions.add( str(tool_version) ) + self._tool_versions.add(str(tool_version)) - def get_versions( self ): + def get_versions(self): """ Return an ordered list of lineages (ToolLineageVersion) in this chain, from oldest to newest. """ - return [ ToolLineageVersion( tool_id, tool_version ) for tool_id, tool_version in zip(self.tool_ids, self.tool_versions) ] + return [ToolLineageVersion(tool_id, tool_version) for tool_id, tool_version in zip(self.tool_ids, self.tool_versions)] def get_version_ids(self, reverse=False): if reverse: diff --git a/lib/galaxy/tools/toolbox/panel.py b/lib/galaxy/tools/toolbox/panel.py index 12936de1298a..8e553bc655b1 100644 --- a/lib/galaxy/tools/toolbox/panel.py +++ b/lib/galaxy/tools/toolbox/panel.py @@ -22,12 +22,12 @@ class HasPanelItems: """ @abstractmethod - def panel_items( self ): + def panel_items(self): """ Return an ordered dictionary-like object describing tool panel items (such as workflows, tools, labels, and sections). """ - def panel_items_iter( self ): + def panel_items_iter(self): """ Iterate through panel items each represented as a tuple of (panel_key, panel_type, panel_content). """ @@ -44,15 +44,15 @@ def panel_items_iter( self ): yield (panel_key, panel_type, panel_value) -class ToolSection( Dictifiable, HasPanelItems, object ): +class ToolSection(Dictifiable, HasPanelItems, object): """ A group of tools with similar type/purpose that will be displayed as a group in the user interface. """ - dict_collection_visible_keys = ( 'id', 'name', 'version' ) + dict_collection_visible_keys = ('id', 'name', 'version') - def __init__( self, item=None ): + def __init__(self, item=None): """ Build a ToolSection from an ElementTree element or a dictionary. """ if item is None: @@ -62,7 +62,7 @@ def __init__( self, item=None ): self.version = item.get('version') or '' self.elems = ToolPanelElements() - def copy( self ): + def copy(self): copy = ToolSection() copy.name = self.name copy.id = self.id @@ -70,101 +70,101 @@ def copy( self ): copy.elems = self.elems.copy() return copy - def to_dict( self, trans, link_details=False ): + def to_dict(self, trans, link_details=False): """ Return a dict that includes section's attributes. """ - section_dict = super( ToolSection, self ).to_dict() + section_dict = super(ToolSection, self).to_dict() section_elts = [] kwargs = dict( trans=trans, link_details=link_details ) for elt in self.elems.values(): - section_elts.append( elt.to_dict( **kwargs ) ) - section_dict[ 'elems' ] = section_elts + section_elts.append(elt.to_dict(**kwargs)) + section_dict['elems'] = section_elts return section_dict - def panel_items( self ): + def panel_items(self): return self.elems -class ToolSectionLabel( Dictifiable, object ): +class ToolSectionLabel(Dictifiable, object): """ A label for a set of tools that can be displayed above groups of tools and sections in the user interface """ - dict_collection_visible_keys = ( 'id', 'text', 'version' ) + dict_collection_visible_keys = ('id', 'text', 'version') - def __init__( self, item ): + def __init__(self, item): """ Build a ToolSectionLabel from an ElementTree element or a dictionary. """ item = ensure_tool_conf_item(item) - self.text = item.get( "text" ) - self.id = item.get( "id" ) - self.version = item.get( "version" ) or '' + self.text = item.get("text") + self.id = item.get("id") + self.version = item.get("version") or '' - def to_dict( self, **kwds ): - return super( ToolSectionLabel, self ).to_dict() + def to_dict(self, **kwds): + return super(ToolSectionLabel, self).to_dict() -class ToolPanelElements( HasPanelItems, odict ): +class ToolPanelElements(HasPanelItems, odict): """ Represents an ordered dictionary of tool entries - abstraction used both by tool panel itself (normal and integrated) and its sections. """ - def update_or_append( self, index, key, value ): + def update_or_append(self, index, key, value): if key in self or index is None: - self[ key ] = value + self[key] = value else: - self.insert( index, key, value ) + self.insert(index, key, value) - def has_tool_with_id( self, tool_id ): + def has_tool_with_id(self, tool_id): key = 'tool_%s' % tool_id return key in self - def replace_tool( self, previous_tool_id, new_tool_id, tool ): + def replace_tool(self, previous_tool_id, new_tool_id, tool): previous_key = 'tool_%s' % previous_tool_id new_key = 'tool_%s' % new_tool_id - index = self.keys().index( previous_key ) - del self[ previous_key ] - self.insert( index, new_key, tool ) + index = self.keys().index(previous_key) + del self[previous_key] + self.insert(index, new_key, tool) - def index_of_tool_id( self, tool_id ): + def index_of_tool_id(self, tool_id): query_key = 'tool_%s' % tool_id - for index, target_key in enumerate( self.keys() ): + for index, target_key in enumerate(self.keys()): if query_key == target_key: return index return None - def insert_tool( self, index, tool ): + def insert_tool(self, index, tool): key = "tool_%s" % tool.id - self.insert( index, key, tool ) + self.insert(index, key, tool) - def get_tool_with_id( self, tool_id ): + def get_tool_with_id(self, tool_id): key = "tool_%s" % tool_id - return self[ key ] + return self[key] - def append_tool( self, tool ): + def append_tool(self, tool): key = "tool_%s" % tool.id - self[ key ] = tool + self[key] = tool - def stub_tool( self, key ): + def stub_tool(self, key): key = "tool_%s" % key - self[ key ] = None + self[key] = None - def stub_workflow( self, key ): + def stub_workflow(self, key): key = 'workflow_%s' % key - self[ key ] = None + self[key] = None - def stub_label( self, key ): + def stub_label(self, key): key = 'label_%s' % key - self[ key ] = None + self[key] = None - def append_section( self, key, section_elems ): - self[ key ] = section_elems + def append_section(self, key, section_elems): + self[key] = section_elems - def panel_items( self ): + def panel_items(self): return self diff --git a/lib/galaxy/tools/toolbox/parser.py b/lib/galaxy/tools/toolbox/parser.py index 67afbbe98170..3c79a8b4d7bf 100644 --- a/lib/galaxy/tools/toolbox/parser.py +++ b/lib/galaxy/tools/toolbox/parser.py @@ -117,7 +117,7 @@ def elem(self): def labels(self): labels = None if "labels" in self.attributes: - labels = [ label.strip() for label in self.attributes["labels"].split( "," ) ] + labels = [label.strip() for label in self.attributes["labels"].split(",")] return labels diff --git a/lib/galaxy/tools/toolbox/tags.py b/lib/galaxy/tools/toolbox/tags.py index 33f1adf50a20..f6a48ac0f7d8 100644 --- a/lib/galaxy/tools/toolbox/tags.py +++ b/lib/galaxy/tools/toolbox/tags.py @@ -8,73 +8,73 @@ import six -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -def tool_tag_manager( app ): +def tool_tag_manager(app): """ Build a tool tag manager according to app's configuration and return it. """ - if hasattr( app.config, "get_bool" ) and app.config.get_bool( 'enable_tool_tags', False ): - return PersistentToolTagManager( app ) + if hasattr(app.config, "get_bool") and app.config.get_bool('enable_tool_tags', False): + return PersistentToolTagManager(app) else: return NullToolTagManager() @six.add_metaclass(ABCMeta) -class AbstractToolTagManager( object ): +class AbstractToolTagManager(object): @abstractmethod - def reset_tags( self ): + def reset_tags(self): """ Starting to load tool panels, reset all tags. """ @abstractmethod - def handle_tags( self, tool_id, tool_definition_source ): + def handle_tags(self, tool_id, tool_definition_source): """ Parse out tags and persist them. """ -class NullToolTagManager( AbstractToolTagManager ): +class NullToolTagManager(AbstractToolTagManager): - def reset_tags( self ): + def reset_tags(self): return None - def handle_tags( self, tool_id, tool_definition_source ): + def handle_tags(self, tool_id, tool_definition_source): return None -class PersistentToolTagManager( AbstractToolTagManager ): +class PersistentToolTagManager(AbstractToolTagManager): - def __init__( self, app ): + def __init__(self, app): self.app = app self.sa_session = app.model.context - def reset_tags( self ): - log.info("removing all tool tag associations (" + str( self.sa_session.query( self.app.model.ToolTagAssociation ).count() ) + ")" ) - self.sa_session.query( self.app.model.ToolTagAssociation ).delete() + def reset_tags(self): + log.info("removing all tool tag associations (" + str(self.sa_session.query(self.app.model.ToolTagAssociation).count()) + ")") + self.sa_session.query(self.app.model.ToolTagAssociation).delete() self.sa_session.flush() - def handle_tags( self, tool_id, tool_definition_source ): + def handle_tags(self, tool_id, tool_definition_source): elem = tool_definition_source - if self.app.config.get_bool( 'enable_tool_tags', False ): - tag_names = elem.get( "tags", "" ).split( "," ) + if self.app.config.get_bool('enable_tool_tags', False): + tag_names = elem.get("tags", "").split(",") for tag_name in tag_names: if tag_name == '': continue - tag = self.sa_session.query( self.app.model.Tag ).filter_by( name=tag_name ).first() + tag = self.sa_session.query(self.app.model.Tag).filter_by(name=tag_name).first() if not tag: - tag = self.app.model.Tag( name=tag_name ) - self.sa_session.add( tag ) + tag = self.app.model.Tag(name=tag_name) + self.sa_session.add(tag) self.sa_session.flush() - tta = self.app.model.ToolTagAssociation( tool_id=tool_id, tag_id=tag.id ) - self.sa_session.add( tta ) + tta = self.app.model.ToolTagAssociation(tool_id=tool_id, tag_id=tag.id) + self.sa_session.add(tta) self.sa_session.flush() else: for tagged_tool in tag.tagged_tools: if tagged_tool.tool_id == tool_id: break else: - tta = self.app.model.ToolTagAssociation( tool_id=tool_id, tag_id=tag.id ) - self.sa_session.add( tta ) + tta = self.app.model.ToolTagAssociation(tool_id=tool_id, tag_id=tag.id) + self.sa_session.add(tta) self.sa_session.flush() diff --git a/lib/galaxy/tools/toolbox/watcher.py b/lib/galaxy/tools/toolbox/watcher.py index d658f0fab151..4e810baf13cb 100644 --- a/lib/galaxy/tools/toolbox/watcher.py +++ b/lib/galaxy/tools/toolbox/watcher.py @@ -17,7 +17,7 @@ from galaxy.util.hash_util import md5_hash_file from galaxy.web.stack import register_postfork_function -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) def get_observer_class(config_value, default, monitor_what_str): @@ -94,7 +94,7 @@ def shutdown(self): def check(self): """Check for changes in self.paths or self.cache and call the event handler.""" - hashes = { key: None for key in self.paths.keys() } + hashes = {key: None for key in self.paths.keys()} while self._active: do_reload = False with self._lock: @@ -175,19 +175,19 @@ def monitor(self, dir): self.observer.schedule(self.event_handler, dir, recursive=False) def watch_file(self, tool_file, tool_id): - tool_file = os.path.abspath( tool_file ) + tool_file = os.path.abspath(tool_file) self.tool_file_ids[tool_file] = tool_id - tool_dir = os.path.dirname( tool_file ) + tool_dir = os.path.dirname(tool_file) if tool_dir not in self.monitored_dirs: - self.monitored_dirs[ tool_dir ] = tool_dir - self.monitor( tool_dir ) + self.monitored_dirs[tool_dir] = tool_dir + self.monitor(tool_dir) def watch_directory(self, tool_dir, callback): - tool_dir = os.path.abspath( tool_dir ) + tool_dir = os.path.abspath(tool_dir) self.tool_dir_callbacks[tool_dir] = callback if tool_dir not in self.monitored_dirs: - self.monitored_dirs[ tool_dir ] = tool_dir - self.monitor( tool_dir ) + self.monitored_dirs[tool_dir] = tool_dir + self.monitor(tool_dir) class ToolDataWatcher(object): @@ -211,10 +211,10 @@ def monitor(self, dir): self.observer.schedule(self.event_handler, dir, recursive=True) def watch_directory(self, tool_data_dir): - tool_data_dir = os.path.abspath( tool_data_dir ) + tool_data_dir = os.path.abspath(tool_data_dir) if tool_data_dir not in self.monitored_dirs: - self.monitored_dirs[ tool_data_dir ] = tool_data_dir - self.monitor( tool_data_dir ) + self.monitored_dirs[tool_data_dir] = tool_data_dir + self.monitor(tool_data_dir) class LocFileEventHandler(FileSystemEventHandler): @@ -229,8 +229,8 @@ def _handle(self, event): # modified events will only have src path, move events will # have dest_path and src_path but we only care about dest. So # look at dest if it exists else use src. - path = getattr( event, 'dest_path', None ) or event.src_path - path = os.path.abspath( path ) + path = getattr(event, 'dest_path', None) or event.src_path + path = os.path.abspath(path) if path.endswith(".loc"): cur_hash = md5_hash_file(path) if self.loc_watcher.path_hash.get(path) == cur_hash: @@ -252,22 +252,22 @@ def _handle(self, event): # modified events will only have src path, move events will # have dest_path and src_path but we only care about dest. So # look at dest if it exists else use src. - path = getattr( event, 'dest_path', None ) or event.src_path - path = os.path.abspath( path ) - tool_id = self.tool_watcher.tool_file_ids.get( path, None ) + path = getattr(event, 'dest_path', None) or event.src_path + path = os.path.abspath(path) + tool_id = self.tool_watcher.tool_file_ids.get(path, None) if tool_id: try: self.tool_watcher.toolbox.reload_tool_by_id(tool_id) except Exception: pass elif path.endswith(".xml"): - directory = os.path.dirname( path ) - dir_callback = self.tool_watcher.tool_dir_callbacks.get( directory, None ) + directory = os.path.dirname(path) + dir_callback = self.tool_watcher.tool_dir_callbacks.get(directory, None) if dir_callback: tool_file = event.src_path - tool_id = dir_callback( tool_file ) + tool_id = dir_callback(tool_file) if tool_id: - self.tool_watcher.tool_file_ids[ tool_file ] = tool_id + self.tool_watcher.tool_file_ids[tool_file] = tool_id class NullWatcher(object): diff --git a/lib/galaxy/tools/util/galaxyops/__init__.py b/lib/galaxy/tools/util/galaxyops/__init__.py index 7ec5e7590544..9cb5d1d96ab0 100644 --- a/lib/galaxy/tools/util/galaxyops/__init__.py +++ b/lib/galaxy/tools/util/galaxyops/__init__.py @@ -4,40 +4,40 @@ import sys -def warn( msg ): +def warn(msg): # TODO: since everything printed to stderr results in job.state = error, we # don't need both a warn and a fail... print(msg, file=sys.stderr) - sys.exit( 1 ) + sys.exit(1) -def fail( msg ): +def fail(msg): print(msg, file=sys.stderr) - sys.exit( 1 ) + sys.exit(1) # Default chrom, start, end, strand cols for a bed file BED_DEFAULT_COLS = 0, 1, 2, 5 -def parse_cols_arg( cols ): +def parse_cols_arg(cols): """Parse a columns command line argument into a four-tuple""" if cols: # Handle case where no strand column included - in this case, cols # looks something like 1,2,3, - if cols.endswith( ',' ): + if cols.endswith(','): cols += '0' - col_list = [int( x ) - 1 for x in cols.split(",")] + col_list = [int(x) - 1 for x in cols.split(",")] return col_list else: return BED_DEFAULT_COLS -def default_printer( stream, exc, obj ): - print("%d: %s" % ( obj.linenum, obj.current_line ), file=stream) - print("\tError: %s" % ( str(exc) ), file=stream) +def default_printer(stream, exc, obj): + print("%d: %s" % (obj.linenum, obj.current_line), file=stream) + print("\tError: %s" % (str(exc)), file=stream) -def skipped( reader, filedesc="" ): +def skipped(reader, filedesc=""): first_line, line_contents, problem = reader.skipped_lines[0] - return 'Skipped %d invalid lines%s, 1st line #%d: "%s", problem: %s' % ( reader.skipped, filedesc, first_line, line_contents, problem ) + return 'Skipped %d invalid lines%s, 1st line #%d: "%s", problem: %s' % (reader.skipped, filedesc, first_line, line_contents, problem) diff --git a/lib/galaxy/tools/util/maf_utilities.py b/lib/galaxy/tools/util/maf_utilities.py index c50dc42747e6..1ee6cf01de23 100644 --- a/lib/galaxy/tools/util/maf_utilities.py +++ b/lib/galaxy/tools/util/maf_utilities.py @@ -19,54 +19,54 @@ import bx.intervals from six.moves import xrange -assert sys.version_info[:2] >= ( 2, 4 ) +assert sys.version_info[:2] >= (2, 4) log = logging.getLogger(__name__) -GAP_CHARS = [ '-' ] +GAP_CHARS = ['-'] SRC_SPLIT_CHAR = '.' -def src_split( src ): - fields = src.split( SRC_SPLIT_CHAR, 1 ) - spec = fields.pop( 0 ) +def src_split(src): + fields = src.split(SRC_SPLIT_CHAR, 1) + spec = fields.pop(0) if fields: - chrom = fields.pop( 0 ) + chrom = fields.pop(0) else: chrom = spec return spec, chrom -def src_merge( spec, chrom, contig=None ): - if None in [ spec, chrom ]: +def src_merge(spec, chrom, contig=None): + if None in [spec, chrom]: spec = chrom = spec or chrom - return bx.align.maf.src_merge( spec, chrom, contig ) + return bx.align.maf.src_merge(spec, chrom, contig) -def get_species_in_block( block ): +def get_species_in_block(block): species = [] for c in block.components: - spec, chrom = src_split( c.src ) + spec, chrom = src_split(c.src) if spec not in species: - species.append( spec ) + species.append(spec) return species -def tool_fail( msg="Unknown Error" ): +def tool_fail(msg="Unknown Error"): print("Fatal Error: %s" % msg, file=sys.stderr) sys.exit() -class TempFileHandler( object ): +class TempFileHandler(object): ''' Handles creating, opening, closing, and deleting of Temp files, with a maximum number of files open at one time. ''' - DEFAULT_MAX_OPEN_FILES = max( resource.getrlimit( resource.RLIMIT_NOFILE )[0] / 2, 1 ) + DEFAULT_MAX_OPEN_FILES = max(resource.getrlimit(resource.RLIMIT_NOFILE)[0] / 2, 1) - def __init__( self, max_open_files=None, **kwds ): + def __init__(self, max_open_files=None, **kwds): if max_open_files is None: max_open_files = self.DEFAULT_MAX_OPEN_FILES self.max_open_files = max_open_files @@ -74,216 +74,216 @@ def __init__( self, max_open_files=None, **kwds ): self.open_file_indexes = [] self.kwds = kwds - def get_open_tempfile( self, index=None, **kwds ): + def get_open_tempfile(self, index=None, **kwds): if index is not None and index in self.open_file_indexes: - self.open_file_indexes.remove( index ) + self.open_file_indexes.remove(index) else: if self.max_open_files: - while len( self.open_file_indexes ) >= self.max_open_files: - self.close( self.open_file_indexes[0] ) + while len(self.open_file_indexes) >= self.max_open_files: + self.close(self.open_file_indexes[0]) if index is None: - index = len( self.files ) - temp_kwds = dict( self.kwds ) - temp_kwds.update( kwds ) + index = len(self.files) + temp_kwds = dict(self.kwds) + temp_kwds.update(kwds) # Being able to use delete=True here, would simplify a bit, # but we support python2.4 in these tools while True: try: - tmp_file = tempfile.NamedTemporaryFile( **temp_kwds ) + tmp_file = tempfile.NamedTemporaryFile(**temp_kwds) filename = tmp_file.name break except OSError as e: if self.open_file_indexes and e.errno == EMFILE: - self.max_open_files = len( self.open_file_indexes ) - self.close( self.open_file_indexes[0] ) + self.max_open_files = len(self.open_file_indexes) + self.close(self.open_file_indexes[0]) else: raise e tmp_file.close() - self.files.append( open( filename, 'w+b' ) ) + self.files.append(open(filename, 'w+b')) else: while True: try: - self.files[ index ] = open( self.files[ index ].name, 'r+b' ) + self.files[index] = open(self.files[index].name, 'r+b') break except OSError as e: if self.open_file_indexes and e.errno == EMFILE: - self.max_open_files = len( self.open_file_indexes ) - self.close( self.open_file_indexes[0] ) + self.max_open_files = len(self.open_file_indexes) + self.close(self.open_file_indexes[0]) else: raise e - self.files[ index ].seek( 0, 2 ) - self.open_file_indexes.append( index ) - return index, self.files[ index ] + self.files[index].seek(0, 2) + self.open_file_indexes.append(index) + return index, self.files[index] - def close( self, index, delete=False ): + def close(self, index, delete=False): if index in self.open_file_indexes: - self.open_file_indexes.remove( index ) - rval = self.files[ index ].close() + self.open_file_indexes.remove(index) + rval = self.files[index].close() if delete: try: - os.unlink( self.files[ index ].name ) + os.unlink(self.files[index].name) except OSError: pass return rval - def flush( self, index ): + def flush(self, index): if index in self.open_file_indexes: - self.files[ index ].flush() + self.files[index].flush() - def __del__( self ): - for i in xrange( len( self.files ) ): - self.close( i, delete=True ) + def __del__(self): + for i in xrange(len(self.files)): + self.close(i, delete=True) # an object corresponding to a reference layered alignment -class RegionAlignment( object ): +class RegionAlignment(object): - DNA_COMPLEMENT = string.maketrans( "ACGTacgt", "TGCAtgca" ) + DNA_COMPLEMENT = string.maketrans("ACGTacgt", "TGCAtgca") MAX_SEQUENCE_SIZE = sys.maxsize # Maximum length of sequence allowed - def __init__( self, size, species=[], temp_file_handler=None ): - assert size <= self.MAX_SEQUENCE_SIZE, "Maximum length allowed for an individual sequence has been exceeded (%i > %i)." % ( size, self.MAX_SEQUENCE_SIZE ) + def __init__(self, size, species=[], temp_file_handler=None): + assert size <= self.MAX_SEQUENCE_SIZE, "Maximum length allowed for an individual sequence has been exceeded (%i > %i)." % (size, self.MAX_SEQUENCE_SIZE) self.size = size if not temp_file_handler: temp_file_handler = TempFileHandler() self.temp_file_handler = temp_file_handler self.sequences = {} - if not isinstance( species, list ): + if not isinstance(species, list): species = [species] for spec in species: - self.add_species( spec ) + self.add_species(spec) # add a species to the alignment - def add_species( self, species ): + def add_species(self, species): # make temporary sequence files file_index, fh = self.temp_file_handler.get_open_tempfile() self.sequences[species] = file_index - fh.write( "-" * self.size ) + fh.write("-" * self.size) # returns the names for species found in alignment, skipping names as requested - def get_species_names( self, skip=[] ): - if not isinstance( skip, list ): + def get_species_names(self, skip=[]): + if not isinstance(skip, list): skip = [skip] names = list(self.sequences.keys()) for name in skip: try: - names.remove( name ) + names.remove(name) except: pass return names # returns the sequence for a species - def get_sequence( self, species ): - file_index, fh = self.temp_file_handler.get_open_tempfile( self.sequences[species] ) - fh.seek( 0 ) + def get_sequence(self, species): + file_index, fh = self.temp_file_handler.get_open_tempfile(self.sequences[species]) + fh.seek(0) return fh.read() # returns the reverse complement of the sequence for a species - def get_sequence_reverse_complement( self, species ): - complement = [base for base in self.get_sequence( species ).translate( self.DNA_COMPLEMENT )] + def get_sequence_reverse_complement(self, species): + complement = [base for base in self.get_sequence(species).translate(self.DNA_COMPLEMENT)] complement.reverse() - return "".join( complement ) + return "".join(complement) # sets a position for a species - def set_position( self, index, species, base ): - if len( base ) != 1: - raise Exception( "A genomic position can only have a length of 1." ) - return self.set_range( index, species, base ) + def set_position(self, index, species, base): + if len(base) != 1: + raise Exception("A genomic position can only have a length of 1.") + return self.set_range(index, species, base) # sets a range for a species - def set_range( self, index, species, bases ): + def set_range(self, index, species, bases): if index >= self.size or index < 0: - raise Exception( "Your index (%i) is out of range (0 - %i)." % ( index, self.size - 1 ) ) - if len( bases ) == 0: - raise Exception( "A set of genomic positions can only have a positive length." ) + raise Exception("Your index (%i) is out of range (0 - %i)." % (index, self.size - 1)) + if len(bases) == 0: + raise Exception("A set of genomic positions can only have a positive length.") if species not in self.sequences.keys(): - self.add_species( species ) - file_index, fh = self.temp_file_handler.get_open_tempfile( self.sequences[species] ) - fh.seek( index ) - fh.write( bases ) + self.add_species(species) + file_index, fh = self.temp_file_handler.get_open_tempfile(self.sequences[species]) + fh.seek(index) + fh.write(bases) # Flush temp file of specified species, or all species - def flush( self, species=None ): + def flush(self, species=None): if species is None: species = self.sequences.keys() - elif not isinstance( species, list ): + elif not isinstance(species, list): species = [species] for spec in species: - self.temp_file_handler.flush( self.sequences[spec] ) + self.temp_file_handler.flush(self.sequences[spec]) -class GenomicRegionAlignment( RegionAlignment ): +class GenomicRegionAlignment(RegionAlignment): - def __init__( self, start, end, species=[], temp_file_handler=None ): - RegionAlignment.__init__( self, end - start, species, temp_file_handler=temp_file_handler ) + def __init__(self, start, end, species=[], temp_file_handler=None): + RegionAlignment.__init__(self, end - start, species, temp_file_handler=temp_file_handler) self.start = start self.end = end -class SplicedAlignment( object ): +class SplicedAlignment(object): - DNA_COMPLEMENT = string.maketrans( "ACGTacgt", "TGCAtgca" ) + DNA_COMPLEMENT = string.maketrans("ACGTacgt", "TGCAtgca") - def __init__( self, exon_starts, exon_ends, species=[], temp_file_handler=None ): - if not isinstance( exon_starts, list ): + def __init__(self, exon_starts, exon_ends, species=[], temp_file_handler=None): + if not isinstance(exon_starts, list): exon_starts = [exon_starts] - if not isinstance( exon_ends, list ): + if not isinstance(exon_ends, list): exon_ends = [exon_ends] - assert len( exon_starts ) == len( exon_ends ), "The number of starts does not match the number of sizes." + assert len(exon_starts) == len(exon_ends), "The number of starts does not match the number of sizes." self.exons = [] if not temp_file_handler: temp_file_handler = TempFileHandler() self.temp_file_handler = temp_file_handler - for i in range( len( exon_starts ) ): - self.exons.append( GenomicRegionAlignment( exon_starts[i], exon_ends[i], species, temp_file_handler=temp_file_handler ) ) + for i in range(len(exon_starts)): + self.exons.append(GenomicRegionAlignment(exon_starts[i], exon_ends[i], species, temp_file_handler=temp_file_handler)) # returns the names for species found in alignment, skipping names as requested - def get_species_names( self, skip=[] ): - if not isinstance( skip, list ): + def get_species_names(self, skip=[]): + if not isinstance(skip, list): skip = [skip] names = [] for exon in self.exons: - for name in exon.get_species_names( skip=skip ): + for name in exon.get_species_names(skip=skip): if name not in names: - names.append( name ) + names.append(name) return names # returns the sequence for a species - def get_sequence( self, species ): + def get_sequence(self, species): index, fh = self.temp_file_handler.get_open_tempfile() for exon in self.exons: if species in exon.get_species_names(): - seq = exon.get_sequence( species ) + seq = exon.get_sequence(species) # we need to refetch fh here, since exon.get_sequence( species ) uses a tempfile # and if max==1, it would close fh - index, fh = self.temp_file_handler.get_open_tempfile( index ) - fh.write( seq ) + index, fh = self.temp_file_handler.get_open_tempfile(index) + fh.write(seq) else: - fh.write( "-" * exon.size ) - fh.seek( 0 ) + fh.write("-" * exon.size) + fh.seek(0) rval = fh.read() - self.temp_file_handler.close( index, delete=True ) + self.temp_file_handler.close(index, delete=True) return rval # returns the reverse complement of the sequence for a species - def get_sequence_reverse_complement( self, species ): - complement = [base for base in self.get_sequence( species ).translate( self.DNA_COMPLEMENT )] + def get_sequence_reverse_complement(self, species): + complement = [base for base in self.get_sequence(species).translate(self.DNA_COMPLEMENT)] complement.reverse() - return "".join( complement ) + return "".join(complement) # Start and end of coding region @property - def start( self ): + def start(self): return self.exons[0].start @property - def end( self ): + def end(self): return self.exons[-1].end # Open a MAF index using a UID -def maf_index_by_uid( maf_uid, index_location_file ): - for line in open( index_location_file ): +def maf_index_by_uid(maf_uid, index_location_file): + for line in open(index_location_file): try: # read each line, if not enough fields, go to next line if line[0:1] == "#": @@ -291,30 +291,30 @@ def maf_index_by_uid( maf_uid, index_location_file ): fields = line.split('\t') if maf_uid == fields[1]: try: - maf_files = fields[4].replace( "\n", "" ).replace( "\r", "" ).split( "," ) - return bx.align.maf.MultiIndexed( maf_files, keep_open=True, parse_e_rows=False ) + maf_files = fields[4].replace("\n", "").replace("\r", "").split(",") + return bx.align.maf.MultiIndexed(maf_files, keep_open=True, parse_e_rows=False) except Exception as e: - raise Exception( 'MAF UID (%s) found, but configuration appears to be malformed: %s' % ( maf_uid, e ) ) + raise Exception('MAF UID (%s) found, but configuration appears to be malformed: %s' % (maf_uid, e)) except: pass return None # return ( index, temp_index_filename ) for user maf, if available, or build one and return it, return None when no tempfile is created -def open_or_build_maf_index( maf_file, index_filename, species=None ): +def open_or_build_maf_index(maf_file, index_filename, species=None): try: - return ( bx.align.maf.Indexed( maf_file, index_filename=index_filename, keep_open=True, parse_e_rows=False ), None ) + return (bx.align.maf.Indexed(maf_file, index_filename=index_filename, keep_open=True, parse_e_rows=False), None) except: - return build_maf_index( maf_file, species=species ) + return build_maf_index(maf_file, species=species) -def build_maf_index_species_chromosomes( filename, index_species=None ): +def build_maf_index_species_chromosomes(filename, index_species=None): species = [] species_chromosomes = {} indexes = bx.interval_index_file.Indexes() blocks = 0 try: - maf_reader = bx.align.maf.Reader( open( filename ) ) + maf_reader = bx.align.maf.Reader(open(filename)) while True: pos = maf_reader.file.tell() block = next(maf_reader) @@ -325,45 +325,45 @@ def build_maf_index_species_chromosomes( filename, index_species=None ): spec = c.src chrom = None if "." in spec: - spec, chrom = spec.split( ".", 1 ) + spec, chrom = spec.split(".", 1) if spec not in species: - species.append( spec ) + species.append(spec) species_chromosomes[spec] = [] if chrom and chrom not in species_chromosomes[spec]: - species_chromosomes[spec].append( chrom ) + species_chromosomes[spec].append(chrom) if index_species is None or spec in index_species: forward_strand_start = c.forward_strand_start forward_strand_end = c.forward_strand_end try: - forward_strand_start = int( forward_strand_start ) - forward_strand_end = int( forward_strand_end ) + forward_strand_start = int(forward_strand_start) + forward_strand_end = int(forward_strand_end) except ValueError: continue # start and end are not integers, can't add component to index, goto next component # this likely only occurs when parse_e_rows is True? # could a species exist as only e rows? should the if forward_strand_end > forward_strand_start: # require positive length; i.e. certain lines have start = end = 0 and cannot be indexed - indexes.add( c.src, forward_strand_start, forward_strand_end, pos, max=c.src_size ) + indexes.add(c.src, forward_strand_start, forward_strand_end, pos, max=c.src_size) except Exception as e: # most likely a bad MAF - log.debug( 'Building MAF index on %s failed: %s' % ( filename, e ) ) - return ( None, [], {}, 0 ) - return ( indexes, species, species_chromosomes, blocks ) + log.debug('Building MAF index on %s failed: %s' % (filename, e)) + return (None, [], {}, 0) + return (indexes, species, species_chromosomes, blocks) # builds and returns ( index, index_filename ) for specified maf_file -def build_maf_index( maf_file, species=None ): - indexes, found_species, species_chromosomes, blocks = build_maf_index_species_chromosomes( maf_file, species ) +def build_maf_index(maf_file, species=None): + indexes, found_species, species_chromosomes, blocks = build_maf_index_species_chromosomes(maf_file, species) if indexes is not None: fd, index_filename = tempfile.mkstemp() - out = os.fdopen( fd, 'w' ) - indexes.write( out ) + out = os.fdopen(fd, 'w') + indexes.write(out) out.close() - return ( bx.align.maf.Indexed( maf_file, index_filename=index_filename, keep_open=True, parse_e_rows=False ), index_filename ) - return ( None, None ) + return (bx.align.maf.Indexed(maf_file, index_filename=index_filename, keep_open=True, parse_e_rows=False), index_filename) + return (None, None) -def component_overlaps_region( c, region ): +def component_overlaps_region(c, region): if c is None: return False start, end = c.get_forward_strand_start(), c.get_forward_strand_end() @@ -372,7 +372,7 @@ def component_overlaps_region( c, region ): return True -def chop_block_by_region( block, src, region, species=None, mincols=0 ): +def chop_block_by_region(block, src, region, species=None, mincols=0): # This chopping method was designed to maintain consistency with how start/end padding gaps have been working in Galaxy thus far: # behavior as seen when forcing blocks to be '+' relative to src sequence (ref) and using block.slice_by_component( ref, slice_start, slice_end ) # whether-or-not this is the 'correct' behavior is questionable, but this will at least maintain consistency @@ -381,82 +381,82 @@ def chop_block_by_region( block, src, region, species=None, mincols=0 ): slice_end = 0 # min for the max() old_score = block.score # save old score for later use # We no longer assume only one occurance of src per block, so we need to check them all - for c in iter_components_by_src( block, src ): - if component_overlaps_region( c, region ): + for c in iter_components_by_src(block, src): + if component_overlaps_region(c, region): if c.text is not None: rev_strand = False if c.strand == "-": # We want our coord_to_col coordinates to be returned from positive stranded component rev_strand = True c = c.reverse_complement() - start = max( region.start, c.start ) - end = min( region.end, c.end ) - start = c.coord_to_col( start ) - end = c.coord_to_col( end ) + start = max(region.start, c.start) + end = min(region.end, c.end) + start = c.coord_to_col(start) + end = c.coord_to_col(end) if rev_strand: # need to orient slice coordinates to the original block direction slice_len = end - start - end = len( c.text ) - start + end = len(c.text) - start start = end - slice_len - slice_start = min( start, slice_start ) - slice_end = max( end, slice_end ) + slice_start = min(start, slice_start) + slice_end = max(end, slice_end) if slice_start < slice_end: - block = block.slice( slice_start, slice_end ) + block = block.slice(slice_start, slice_end) if block.text_size > mincols: # restore old score, may not be accurate, but it is better than 0 for everything? block.score = old_score if species is not None: - block = block.limit_to_species( species ) + block = block.limit_to_species(species) block.remove_all_gap_columns() return block return None -def orient_block_by_region( block, src, region, force_strand=None ): +def orient_block_by_region(block, src, region, force_strand=None): # loop through components matching src, # make sure each of these components overlap region # cache strand for each of overlaping regions # if force_strand / region.strand not in strand cache, reverse complement # we could have 2 sequences with same src, overlapping region, on different strands, this would cause no reverse_complementing - strands = [ c.strand for c in iter_components_by_src( block, src ) if component_overlaps_region( c, region ) ] - if strands and ( force_strand is None and region.strand not in strands ) or ( force_strand is not None and force_strand not in strands ): + strands = [c.strand for c in iter_components_by_src(block, src) if component_overlaps_region(c, region)] + if strands and (force_strand is None and region.strand not in strands) or (force_strand is not None and force_strand not in strands): block = block.reverse_complement() return block -def get_oriented_chopped_blocks_for_region( index, src, region, species=None, mincols=0, force_strand=None ): - for block, idx, offset in get_oriented_chopped_blocks_with_index_offset_for_region( index, src, region, species, mincols, force_strand ): +def get_oriented_chopped_blocks_for_region(index, src, region, species=None, mincols=0, force_strand=None): + for block, idx, offset in get_oriented_chopped_blocks_with_index_offset_for_region(index, src, region, species, mincols, force_strand): yield block -def get_oriented_chopped_blocks_with_index_offset_for_region( index, src, region, species=None, mincols=0, force_strand=None ): - for block, idx, offset in get_chopped_blocks_with_index_offset_for_region( index, src, region, species, mincols ): - yield orient_block_by_region( block, src, region, force_strand ), idx, offset +def get_oriented_chopped_blocks_with_index_offset_for_region(index, src, region, species=None, mincols=0, force_strand=None): + for block, idx, offset in get_chopped_blocks_with_index_offset_for_region(index, src, region, species, mincols): + yield orient_block_by_region(block, src, region, force_strand), idx, offset # split a block with multiple occurances of src into one block per src -def iter_blocks_split_by_src( block, src ): - for src_c in iter_components_by_src( block, src ): - new_block = bx.align.Alignment( score=block.score, attributes=deepcopy( block.attributes ) ) +def iter_blocks_split_by_src(block, src): + for src_c in iter_components_by_src(block, src): + new_block = bx.align.Alignment(score=block.score, attributes=deepcopy(block.attributes)) new_block.text_size = block.text_size for c in block.components: if c == src_c or c.src != src: - new_block.add_component( deepcopy( c ) ) # components have reference to alignment, dont want to loose reference to original alignment block in original components + new_block.add_component(deepcopy(c)) # components have reference to alignment, dont want to loose reference to original alignment block in original components yield new_block # split a block into multiple blocks with all combinations of a species appearing only once per block -def iter_blocks_split_by_species( block, species=None ): - def __split_components_by_species( components_by_species, new_block ): +def iter_blocks_split_by_species(block, species=None): + def __split_components_by_species(components_by_species, new_block): if components_by_species: # more species with components to add to this block - components_by_species = deepcopy( components_by_species ) - spec_comps = components_by_species.pop( 0 ) + components_by_species = deepcopy(components_by_species) + spec_comps = components_by_species.pop(0) for c in spec_comps: - newer_block = deepcopy( new_block ) - newer_block.add_component( deepcopy( c ) ) - for value in __split_components_by_species( components_by_species, newer_block ): + newer_block = deepcopy(new_block) + newer_block.add_component(deepcopy(c)) + for value in __split_components_by_species(components_by_species, newer_block): yield value else: # no more components to add, yield this block @@ -467,137 +467,137 @@ def __split_components_by_species( components_by_species, new_block ): if not species: species = [] for c in block.components: - spec, chrom = src_split( c.src ) + spec, chrom = src_split(c.src) if spec not in spec_dict: - spec_dict[ spec ] = [] - species.append( spec ) - spec_dict[ spec ].append( c ) + spec_dict[spec] = [] + species.append(spec) + spec_dict[spec].append(c) else: for spec in species: - spec_dict[ spec ] = [] - for c in iter_components_by_src_start( block, spec ): - spec_dict[ spec ].append( c ) + spec_dict[spec] = [] + for c in iter_components_by_src_start(block, spec): + spec_dict[spec].append(c) - empty_block = bx.align.Alignment( score=block.score, attributes=deepcopy( block.attributes ) ) # should we copy attributes? + empty_block = bx.align.Alignment(score=block.score, attributes=deepcopy(block.attributes)) # should we copy attributes? empty_block.text_size = block.text_size # call recursive function to split into each combo of spec/blocks - for value in __split_components_by_species( list(spec_dict.values()), empty_block ): - sort_block_components_by_block( value, block ) # restore original component order + for value in __split_components_by_species(list(spec_dict.values()), empty_block): + sort_block_components_by_block(value, block) # restore original component order yield value # generator yielding only chopped and valid blocks for a specified region -def get_chopped_blocks_for_region( index, src, region, species=None, mincols=0 ): - for block, idx, offset in get_chopped_blocks_with_index_offset_for_region( index, src, region, species, mincols ): +def get_chopped_blocks_for_region(index, src, region, species=None, mincols=0): + for block, idx, offset in get_chopped_blocks_with_index_offset_for_region(index, src, region, species, mincols): yield block -def get_chopped_blocks_with_index_offset_for_region( index, src, region, species=None, mincols=0 ): - for block, idx, offset in index.get_as_iterator_with_index_and_offset( src, region.start, region.end ): - block = chop_block_by_region( block, src, region, species, mincols ) +def get_chopped_blocks_with_index_offset_for_region(index, src, region, species=None, mincols=0): + for block, idx, offset in index.get_as_iterator_with_index_and_offset(src, region.start, region.end): + block = chop_block_by_region(block, src, region, species, mincols) if block is not None: yield block, idx, offset # returns a filled region alignment for specified regions -def get_region_alignment( index, primary_species, chrom, start, end, strand='+', species=None, mincols=0, overwrite_with_gaps=True, temp_file_handler=None ): +def get_region_alignment(index, primary_species, chrom, start, end, strand='+', species=None, mincols=0, overwrite_with_gaps=True, temp_file_handler=None): if species is not None: - alignment = RegionAlignment( end - start, species, temp_file_handler=temp_file_handler ) + alignment = RegionAlignment(end - start, species, temp_file_handler=temp_file_handler) else: - alignment = RegionAlignment( end - start, primary_species, temp_file_handler=temp_file_handler ) - return fill_region_alignment( alignment, index, primary_species, chrom, start, end, strand, species, mincols, overwrite_with_gaps ) + alignment = RegionAlignment(end - start, primary_species, temp_file_handler=temp_file_handler) + return fill_region_alignment(alignment, index, primary_species, chrom, start, end, strand, species, mincols, overwrite_with_gaps) # reduces a block to only positions exisiting in the src provided -def reduce_block_by_primary_genome( block, species, chromosome, region_start ): +def reduce_block_by_primary_genome(block, species, chromosome, region_start): # returns ( startIndex, {species:texts} # where texts' contents are reduced to only positions existing in the primary genome - src = "%s.%s" % ( species, chromosome ) - ref = block.get_component_by_src( src ) + src = "%s.%s" % (species, chromosome) + ref = block.get_component_by_src(src) start_offset = ref.start - region_start species_texts = {} for c in block.components: - species_texts[ c.src.split( '.' )[0] ] = list( c.text ) + species_texts[c.src.split('.')[0]] = list(c.text) # remove locations which are gaps in the primary species, starting from the downstream end - for i in range( len( species_texts[ species ] ) - 1, -1, -1 ): - if species_texts[ species ][i] == '-': + for i in range(len(species_texts[species]) - 1, -1, -1): + if species_texts[species][i] == '-': for text in species_texts.values(): - text.pop( i ) + text.pop(i) for spec, text in species_texts.items(): - species_texts[spec] = ''.join( text ) - return ( start_offset, species_texts ) + species_texts[spec] = ''.join(text) + return (start_offset, species_texts) # fills a region alignment -def fill_region_alignment( alignment, index, primary_species, chrom, start, end, strand='+', species=None, mincols=0, overwrite_with_gaps=True ): - region = bx.intervals.Interval( start, end ) +def fill_region_alignment(alignment, index, primary_species, chrom, start, end, strand='+', species=None, mincols=0, overwrite_with_gaps=True): + region = bx.intervals.Interval(start, end) region.chrom = chrom region.strand = strand - primary_src = "%s.%s" % ( primary_species, chrom ) + primary_src = "%s.%s" % (primary_species, chrom) # Order blocks overlaping this position by score, lowest first blocks = [] - for block, idx, offset in index.get_as_iterator_with_index_and_offset( primary_src, start, end ): - score = float( block.score ) - for i in range( 0, len( blocks ) ): + for block, idx, offset in index.get_as_iterator_with_index_and_offset(primary_src, start, end): + score = float(block.score) + for i in range(0, len(blocks)): if score < blocks[i][0]: - blocks.insert( i, ( score, idx, offset ) ) + blocks.insert(i, (score, idx, offset)) break else: - blocks.append( ( score, idx, offset ) ) + blocks.append((score, idx, offset)) # gap_chars_tuple = tuple( GAP_CHARS ) - gap_chars_str = ''.join( GAP_CHARS ) + gap_chars_str = ''.join(GAP_CHARS) # Loop through ordered blocks and layer by increasing score for block_dict in blocks: - for block in iter_blocks_split_by_species( block_dict[1].get_at_offset( block_dict[2] ) ): # need to handle each occurance of sequence in block seperately - if component_overlaps_region( block.get_component_by_src( primary_src ), region ): - block = chop_block_by_region( block, primary_src, region, species, mincols ) # chop block - block = orient_block_by_region( block, primary_src, region ) # orient block - start_offset, species_texts = reduce_block_by_primary_genome( block, primary_species, chrom, start ) + for block in iter_blocks_split_by_species(block_dict[1].get_at_offset(block_dict[2])): # need to handle each occurance of sequence in block seperately + if component_overlaps_region(block.get_component_by_src(primary_src), region): + block = chop_block_by_region(block, primary_src, region, species, mincols) # chop block + block = orient_block_by_region(block, primary_src, region) # orient block + start_offset, species_texts = reduce_block_by_primary_genome(block, primary_species, chrom, start) for spec, text in species_texts.items(): # we should trim gaps from both sides, since these are not positions in this species genome (sequence) - text = text.rstrip( gap_chars_str ) + text = text.rstrip(gap_chars_str) gap_offset = 0 # while text.startswith( gap_chars_tuple ): - while True in [ text.startswith( gap_char ) for gap_char in GAP_CHARS ]: # python2.4 doesn't accept a tuple for .startswith() + while True in [text.startswith(gap_char) for gap_char in GAP_CHARS]: # python2.4 doesn't accept a tuple for .startswith() gap_offset += 1 text = text[1:] if not text: break if text: if overwrite_with_gaps: - alignment.set_range( start_offset + gap_offset, spec, text ) + alignment.set_range(start_offset + gap_offset, spec, text) else: - for i, char in enumerate( text ): + for i, char in enumerate(text): if char not in GAP_CHARS: - alignment.set_position( start_offset + gap_offset + i, spec, char ) + alignment.set_position(start_offset + gap_offset + i, spec, char) return alignment # returns a filled spliced region alignment for specified region with start and end lists -def get_spliced_region_alignment( index, primary_species, chrom, starts, ends, strand='+', species=None, mincols=0, overwrite_with_gaps=True, temp_file_handler=None ): +def get_spliced_region_alignment(index, primary_species, chrom, starts, ends, strand='+', species=None, mincols=0, overwrite_with_gaps=True, temp_file_handler=None): # create spliced alignment object if species is not None: - alignment = SplicedAlignment( starts, ends, species, temp_file_handler=temp_file_handler ) + alignment = SplicedAlignment(starts, ends, species, temp_file_handler=temp_file_handler) else: - alignment = SplicedAlignment( starts, ends, [primary_species], temp_file_handler=temp_file_handler ) + alignment = SplicedAlignment(starts, ends, [primary_species], temp_file_handler=temp_file_handler) for exon in alignment.exons: - fill_region_alignment( exon, index, primary_species, chrom, exon.start, exon.end, strand, species, mincols, overwrite_with_gaps ) + fill_region_alignment(exon, index, primary_species, chrom, exon.start, exon.end, strand, species, mincols, overwrite_with_gaps) return alignment # loop through string array, only return non-commented lines -def line_enumerator( lines, comment_start='#' ): +def line_enumerator(lines, comment_start='#'): i = 0 for line in lines: - if not line.startswith( comment_start ): + if not line.startswith(comment_start): i += 1 - yield ( i, line ) + yield (i, line) # read a GeneBed file, return list of starts, ends, raw fields -def get_starts_ends_fields_from_gene_bed( line ): +def get_starts_ends_fields_from_gene_bed(line): # Starts and ends for exons starts = [] ends = [] @@ -605,137 +605,137 @@ def get_starts_ends_fields_from_gene_bed( line ): fields = line.split() # Requires atleast 12 BED columns if len(fields) < 12: - raise Exception( "Not a proper 12 column BED line (%s)." % line ) - tx_start = int( fields[1] ) + raise Exception("Not a proper 12 column BED line (%s)." % line) + tx_start = int(fields[1]) strand = fields[5] if strand != '-': strand = '+' # Default strand is + - cds_start = int( fields[6] ) - cds_end = int( fields[7] ) + cds_start = int(fields[6]) + cds_end = int(fields[7]) # Calculate and store starts and ends of coding exons region_start, region_end = cds_start, cds_end - exon_starts = list(map( int, fields[11].rstrip( ',\n' ).split( ',' ) )) + exon_starts = list(map(int, fields[11].rstrip(',\n').split(','))) exon_starts = [x + tx_start for x in exon_starts] - exon_ends = list(map( int, fields[10].rstrip( ',' ).split( ',' ) )) - exon_ends = [x + y for x, y in zip( exon_starts, exon_ends )] - for start, end in zip( exon_starts, exon_ends ): - start = max( start, region_start ) - end = min( end, region_end ) + exon_ends = list(map(int, fields[10].rstrip(',').split(','))) + exon_ends = [x + y for x, y in zip(exon_starts, exon_ends)] + for start, end in zip(exon_starts, exon_ends): + start = max(start, region_start) + end = min(end, region_end) if start < end: - starts.append( start ) - ends.append( end ) - return ( starts, ends, fields ) + starts.append(start) + ends.append(end) + return (starts, ends, fields) -def iter_components_by_src( block, src ): +def iter_components_by_src(block, src): for c in block.components: if c.src == src: yield c -def get_components_by_src( block, src ): - return [ value for value in iter_components_by_src( block, src ) ] +def get_components_by_src(block, src): + return [value for value in iter_components_by_src(block, src)] -def iter_components_by_src_start( block, src ): +def iter_components_by_src_start(block, src): for c in block.components: - if c.src.startswith( src ): + if c.src.startswith(src): yield c -def get_components_by_src_start( block, src ): - return [ value for value in iter_components_by_src_start( block, src ) ] +def get_components_by_src_start(block, src): + return [value for value in iter_components_by_src_start(block, src)] -def sort_block_components_by_block( block1, block2 ): +def sort_block_components_by_block(block1, block2): # orders the components in block1 by the index of the component in block2 # block1 must be a subset of block2 # occurs in-place - return block1.components.sort( cmp=lambda x, y: block2.components.index( x ) - block2.components.index( y ) ) + return block1.components.sort(cmp=lambda x, y: block2.components.index(x) - block2.components.index(y)) -def get_species_in_maf( maf_filename ): +def get_species_in_maf(maf_filename): species = [] - for block in bx.align.maf.Reader( open( maf_filename ) ): - for spec in get_species_in_block( block ): + for block in bx.align.maf.Reader(open(maf_filename)): + for spec in get_species_in_block(block): if spec not in species: - species.append( spec ) + species.append(spec) return species -def parse_species_option( species ): +def parse_species_option(species): if species: - species = species.split( ',' ) + species = species.split(',') if 'None' not in species: return species return None # provided species was '', None, or had 'None' in it -def remove_temp_index_file( index_filename ): +def remove_temp_index_file(index_filename): try: - os.unlink( index_filename ) + os.unlink(index_filename) except: pass # Below are methods to deal with FASTA files -def get_fasta_header( component, attributes={}, suffix=None ): - header = ">%s(%s):%i-%i|" % ( component.src, component.strand, component.get_forward_strand_start(), component.get_forward_strand_end() ) +def get_fasta_header(component, attributes={}, suffix=None): + header = ">%s(%s):%i-%i|" % (component.src, component.strand, component.get_forward_strand_start(), component.get_forward_strand_end()) for key, value in attributes.items(): - header = "%s%s=%s|" % ( header, key, value ) + header = "%s%s=%s|" % (header, key, value) if suffix: - header = "%s%s" % ( header, suffix ) + header = "%s%s" % (header, suffix) else: - header = "%s%s" % ( header, src_split( component.src )[ 0 ] ) + header = "%s%s" % (header, src_split(component.src)[0]) return header -def get_attributes_from_fasta_header( header ): +def get_attributes_from_fasta_header(header): if not header: return {} attributes = {} - header = header.lstrip( '>' ) + header = header.lstrip('>') header = header.strip() - fields = header.split( '|' ) + fields = header.split('|') try: region = fields[0] - region = region.split( '(', 1 ) - temp = region[0].split( '.', 1 ) + region = region.split('(', 1) + temp = region[0].split('.', 1) attributes['species'] = temp[0] - if len( temp ) == 2: + if len(temp) == 2: attributes['chrom'] = temp[1] else: attributes['chrom'] = temp[0] - region = region[1].split( ')', 1 ) + region = region[1].split(')', 1) attributes['strand'] = region[0] - region = region[1].lstrip( ':' ).split( '-' ) - attributes['start'] = int( region[0] ) - attributes['end'] = int( region[1] ) + region = region[1].lstrip(':').split('-') + attributes['start'] = int(region[0]) + attributes['end'] = int(region[1]) except: # fields 0 is not a region coordinate pass - if len( fields ) > 2: - for i in range( 1, len( fields ) - 1 ): - prop = fields[i].split( '=', 1 ) - if len( prop ) == 2: - attributes[ prop[0] ] = prop[1] - if len( fields ) > 1: + if len(fields) > 2: + for i in range(1, len(fields) - 1): + prop = fields[i].split('=', 1) + if len(prop) == 2: + attributes[prop[0]] = prop[1] + if len(fields) > 1: attributes['__suffix__'] = fields[-1] return attributes -def iter_fasta_alignment( filename ): +def iter_fasta_alignment(filename): class fastaComponent: - def __init__( self, species, text="" ): + def __init__(self, species, text=""): self.species = species self.text = text - def extend( self, text ): - self.text = self.text + text.replace( '\n', '' ).replace( '\r', '' ).strip() + def extend(self, text): + self.text = self.text + text.replace('\n', '').replace('\r', '').strip() # yields a list of fastaComponents for a FASTA file - f = open( filename, 'rb' ) + f = open(filename, 'rb') components = [] # cur_component = None while True: @@ -749,8 +749,8 @@ def extend( self, text ): if components: yield components components = [] - elif line.startswith( '>' ): - attributes = get_attributes_from_fasta_header( line ) - components.append( fastaComponent( attributes['species'] ) ) + elif line.startswith('>'): + attributes = get_attributes_from_fasta_header(line) + components.append(fastaComponent(attributes['species'])) elif components: - components[-1].extend( line ) + components[-1].extend(line) diff --git a/lib/galaxy/tools/verify/__init__.py b/lib/galaxy/tools/verify/__init__.py index ac29b46bbfde..82796d4b2493 100644 --- a/lib/galaxy/tools/verify/__init__.py +++ b/lib/galaxy/tools/verify/__init__.py @@ -43,8 +43,8 @@ def verify( verify_assertions(output_content, attributes["assert_list"]) except AssertionError as err: errmsg = '%s different than expected\n' % (item_label) - errmsg += str( err ) - raise AssertionError( errmsg ) + errmsg += str(err) + raise AssertionError(errmsg) # Verify checksum attributes... # works with older Galaxy style md5= or cwltest @@ -63,8 +63,8 @@ def verify( _verify_checksum(output_content, expected_checksum_type, expected_checksum) except AssertionError as err: errmsg = '%s different than expected\n' % (item_label) - errmsg += str( err ) - raise AssertionError( errmsg ) + errmsg += str(err) + raise AssertionError(errmsg) if filename is not None: local_name = get_filename(filename) @@ -77,7 +77,7 @@ def verify( ofn = os.path.join(keep_outputs_dir, os.path.basename(local_name)) log.debug('keep_outputs_dir: %s, ofn: %s', keep_outputs_dir, ofn) try: - shutil.copy( temp_name, ofn ) + shutil.copy(temp_name, ofn) except Exception as exc: error_log_msg = 'Could not save output file %s to %s: ' % (temp_name, ofn) error_log_msg += str(exc) @@ -94,31 +94,31 @@ def verify( if compare == 'diff': files_diff(local_name, temp_name, attributes=attributes) elif compare == 're_match': - files_re_match( local_name, temp_name, attributes=attributes ) + files_re_match(local_name, temp_name, attributes=attributes) elif compare == 're_match_multiline': - files_re_match_multiline( local_name, temp_name, attributes=attributes ) + files_re_match_multiline(local_name, temp_name, attributes=attributes) elif compare == 'sim_size': delta = attributes.get('delta', '100') s1 = len(output_content) s2 = os.path.getsize(local_name) if abs(s1 - s2) > int(delta): - raise AssertionError( 'Files %s=%db but %s=%db - compare by size (delta=%s) failed' % (temp_name, s1, local_name, s2, delta) ) + raise AssertionError('Files %s=%db but %s=%db - compare by size (delta=%s) failed' % (temp_name, s1, local_name, s2, delta)) elif compare == "contains": - files_contains( local_name, temp_name, attributes=attributes ) + files_contains(local_name, temp_name, attributes=attributes) else: - raise Exception( 'Unimplemented Compare type: %s' % compare ) + raise Exception('Unimplemented Compare type: %s' % compare) if verify_extra_files: extra_files = attributes.get('extra_files', None) if extra_files: verify_extra_files(extra_files) except AssertionError as err: - errmsg = '%s different than expected, difference (using %s):\n' % ( item_label, compare ) - errmsg += "( %s v. %s )\n" % ( local_name, temp_name ) - errmsg += str( err ) - raise AssertionError( errmsg ) + errmsg = '%s different than expected, difference (using %s):\n' % (item_label, compare) + errmsg += "( %s v. %s )\n" % (local_name, temp_name) + errmsg += str(err) + raise AssertionError(errmsg) finally: if 'GALAXY_TEST_NO_CLEANUP' not in os.environ: - os.remove( temp_name ) + os.remove(temp_name) def make_temp_fname(fname=None): @@ -129,14 +129,14 @@ def make_temp_fname(fname=None): def _bam_to_sam(local_name, temp_name): - temp_local = tempfile.NamedTemporaryFile( suffix='.sam', prefix='local_bam_converted_to_sam_' ) - fd, temp_temp = tempfile.mkstemp( suffix='.sam', prefix='history_bam_converted_to_sam_' ) - os.close( fd ) - command = 'samtools view -h -o "%s" "%s"' % ( temp_local.name, local_name ) - check_command( command, 'Converting local (test-data) bam to sam' ) - command = 'samtools view -h -o "%s" "%s"' % ( temp_temp, temp_name ) - check_command( command, 'Converting history bam to sam ' ) - os.remove( temp_name ) + temp_local = tempfile.NamedTemporaryFile(suffix='.sam', prefix='local_bam_converted_to_sam_') + fd, temp_temp = tempfile.mkstemp(suffix='.sam', prefix='history_bam_converted_to_sam_') + os.close(fd) + command = 'samtools view -h -o "%s" "%s"' % (temp_local.name, local_name) + check_command(command, 'Converting local (test-data) bam to sam') + command = 'samtools view -h -o "%s" "%s"' % (temp_temp, temp_name) + check_command(command, 'Converting history bam to sam ') + os.remove(temp_name) return temp_local, temp_temp @@ -145,7 +145,7 @@ def _verify_checksum(data, checksum_type, expected_checksum_value): raise Exception("Unimplemented hash algorithm [%s] encountered." % checksum_type) h = hashlib.new(checksum_type) - h.update( data ) + h.update(data) actual_checksum_value = h.hexdigest() if expected_checksum_value != actual_checksum_value: template = "Output checksum [%s] does not match expected [%s] (using hash algorithm %s)." @@ -156,7 +156,7 @@ def _verify_checksum(data, checksum_type, expected_checksum_value): def check_command(command, description): """Verify a command runs with an exit code of 0.""" # TODO: also collect ``which samtools`` and ``samtools --version`` - p = subprocess.Popen( args=command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True ) + p = subprocess.Popen(args=command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) (stdout, stderr) = p.communicate() if p.returncode: template = description @@ -167,37 +167,37 @@ def check_command(command, description): def files_diff(file1, file2, attributes=None): """Check the contents of 2 files for differences.""" - def get_lines_diff( diff ): + def get_lines_diff(diff): count = 0 for line in diff: - if ( line.startswith( '+' ) and not line.startswith( '+++' ) ) or ( line.startswith( '-' ) and not line.startswith( '---' ) ): + if (line.startswith('+') and not line.startswith('+++')) or (line.startswith('-') and not line.startswith('---')): count += 1 return count - if not filecmp.cmp( file1, file2 ): + if not filecmp.cmp(file1, file2): files_differ = False if attributes is None: attributes = {} decompress = attributes.get("decompress", None) if not decompress: - local_file = open( file1, 'U' ).readlines() - history_data = open( file2, 'U' ).readlines() + local_file = open(file1, 'U').readlines() + history_data = open(file2, 'U').readlines() else: - local_file = get_fileobj( file1, 'U' ).readlines() - history_data = get_fileobj( file2, 'U' ).readlines() - if attributes.get( 'sort', False ): + local_file = get_fileobj(file1, 'U').readlines() + history_data = get_fileobj(file2, 'U').readlines() + if attributes.get('sort', False): history_data.sort() # Why even bother with the check loop below, why not just use the diff output? This seems wasteful. - if len( local_file ) == len( history_data ): - for i in range( len( history_data ) ): - if local_file[i].rstrip( '\r\n' ) != history_data[i].rstrip( '\r\n' ): + if len(local_file) == len(history_data): + for i in range(len(history_data)): + if local_file[i].rstrip('\r\n') != history_data[i].rstrip('\r\n'): files_differ = True break else: files_differ = True if files_differ: - allowed_diff_count = int(attributes.get( 'lines_diff', 0 )) - diff = list( difflib.unified_diff( local_file, history_data, "local_file", "history_data" ) ) - diff_lines = get_lines_diff( diff ) + allowed_diff_count = int(attributes.get('lines_diff', 0)) + diff = list(difflib.unified_diff(local_file, history_data, "local_file", "history_data")) + diff_lines = get_lines_diff(diff) if diff_lines > allowed_diff_count: if 'GALAXY_TEST_RAW_DIFF' in os.environ: diff_slice = diff @@ -211,20 +211,20 @@ def get_lines_diff( diff ): # PDF forgiveness can probably be handled better by not special casing by __extension__ here # and instead using lines_diff or a regular expression matching # or by creating and using a specialized pdf comparison function - if file1.endswith( '.pdf' ) or file2.endswith( '.pdf' ): + if file1.endswith('.pdf') or file2.endswith('.pdf'): # PDF files contain creation dates, modification dates, ids and descriptions that change with each # new file, so we need to handle these differences. As long as the rest of the PDF file does # not differ we're ok. - valid_diff_strs = [ 'description', 'createdate', 'creationdate', 'moddate', 'id', 'producer', 'creator' ] + valid_diff_strs = ['description', 'createdate', 'creationdate', 'moddate', 'id', 'producer', 'creator'] valid_diff = False invalid_diff_lines = 0 for line in diff_slice: # Make sure to lower case strings before checking. line = line.lower() # Diff lines will always start with a + or - character, but handle special cases: '--- local_file \n', '+++ history_data \n' - if ( line.startswith( '+' ) or line.startswith( '-' ) ) and line.find( 'local_file' ) < 0 and line.find( 'history_data' ) < 0: + if (line.startswith('+') or line.startswith('-')) and line.find('local_file') < 0 and line.find('history_data') < 0: for vdf in valid_diff_strs: - if line.find( vdf ) < 0: + if line.find(vdf) < 0: valid_diff = False else: valid_diff = True @@ -236,61 +236,61 @@ def get_lines_diff( diff ): if invalid_diff_lines > allowed_diff_count: # Print out diff_slice so we can see what failed log.info("###### diff_slice ######") - raise AssertionError( "".join( diff_slice ) ) + raise AssertionError("".join(diff_slice)) else: log.info('## files diff on %s and %s lines_diff=%d, found diff = %d' % (file1, file2, allowed_diff_count, diff_lines)) for line in diff_slice: for char in line: - if ord( char ) > 128: - raise AssertionError( "Binary data detected, not displaying diff" ) - raise AssertionError( "".join( diff_slice ) ) + if ord(char) > 128: + raise AssertionError("Binary data detected, not displaying diff") + raise AssertionError("".join(diff_slice)) def files_re_match(file1, file2, attributes=None): """Check the contents of 2 files for differences using re.match.""" - local_file = open( file1, 'U' ).readlines() # regex file - history_data = open( file2, 'U' ).readlines() - assert len( local_file ) == len( history_data ), 'Data File and Regular Expression File contain a different number of lines (%s != %s)\nHistory Data (first 40 lines):\n%s' % ( len( local_file ), len( history_data ), ''.join( history_data[:40] ) ) + local_file = open(file1, 'U').readlines() # regex file + history_data = open(file2, 'U').readlines() + assert len(local_file) == len(history_data), 'Data File and Regular Expression File contain a different number of lines (%s != %s)\nHistory Data (first 40 lines):\n%s' % (len(local_file), len(history_data), ''.join(history_data[:40])) if attributes is None: attributes = {} - if attributes.get( 'sort', False ): + if attributes.get('sort', False): history_data.sort() - lines_diff = int(attributes.get( 'lines_diff', 0 )) + lines_diff = int(attributes.get('lines_diff', 0)) line_diff_count = 0 diffs = [] - for i in range( len( history_data ) ): - if not re.match( local_file[i].rstrip( '\r\n' ), history_data[i].rstrip( '\r\n' ) ): + for i in range(len(history_data)): + if not re.match(local_file[i].rstrip('\r\n'), history_data[i].rstrip('\r\n')): line_diff_count += 1 - diffs.append( 'Regular Expression: %s\nData file : %s' % ( local_file[i].rstrip( '\r\n' ), history_data[i].rstrip( '\r\n' ) ) ) + diffs.append('Regular Expression: %s\nData file : %s' % (local_file[i].rstrip('\r\n'), history_data[i].rstrip('\r\n'))) if line_diff_count > lines_diff: - raise AssertionError( "Regular expression did not match data file (allowed variants=%i):\n%s" % ( lines_diff, "".join( diffs ) ) ) + raise AssertionError("Regular expression did not match data file (allowed variants=%i):\n%s" % (lines_diff, "".join(diffs))) def files_re_match_multiline(file1, file2, attributes=None): """Check the contents of 2 files for differences using re.match in multiline mode.""" - local_file = open( file1, 'U' ).read() # regex file + local_file = open(file1, 'U').read() # regex file if attributes is None: attributes = {} - if attributes.get( 'sort', False ): - history_data = open( file2, 'U' ).readlines() + if attributes.get('sort', False): + history_data = open(file2, 'U').readlines() history_data.sort() - history_data = ''.join( history_data ) + history_data = ''.join(history_data) else: - history_data = open( file2, 'U' ).read() + history_data = open(file2, 'U').read() # lines_diff not applicable to multiline matching - assert re.match( local_file, history_data, re.MULTILINE ), "Multiline Regular expression did not match data file" + assert re.match(local_file, history_data, re.MULTILINE), "Multiline Regular expression did not match data file" def files_contains(file1, file2, attributes=None): """Check the contents of file2 for substrings found in file1, on a per-line basis.""" - local_file = open( file1, 'U' ).readlines() # regex file + local_file = open(file1, 'U').readlines() # regex file # TODO: allow forcing ordering of contains - history_data = open( file2, 'U' ).read() - lines_diff = int( attributes.get( 'lines_diff', 0 ) ) + history_data = open(file2, 'U').read() + lines_diff = int(attributes.get('lines_diff', 0)) line_diff_count = 0 while local_file: - contains = local_file.pop( 0 ).rstrip( '\n\r' ) + contains = local_file.pop(0).rstrip('\n\r') if contains not in history_data: line_diff_count += 1 if line_diff_count > lines_diff: - raise AssertionError( "Failed to find '%s' in history data. (lines_diff=%i):\n" % ( contains, lines_diff ) ) + raise AssertionError("Failed to find '%s' in history data. (lines_diff=%i):\n" % (contains, lines_diff)) diff --git a/lib/galaxy/tools/verify/asserts/__init__.py b/lib/galaxy/tools/verify/asserts/__init__.py index 81fddebdbf18..626a4dd7309e 100644 --- a/lib/galaxy/tools/verify/asserts/__init__.py +++ b/lib/galaxy/tools/verify/asserts/__init__.py @@ -2,7 +2,7 @@ import logging import sys -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) assertion_module_names = ['text', 'tabular', 'xml'] diff --git a/lib/galaxy/tools/wrappers.py b/lib/galaxy/tools/wrappers.py index 0fa3ab4d8f19..1ff41a5eb298 100644 --- a/lib/galaxy/tools/wrappers.py +++ b/lib/galaxy/tools/wrappers.py @@ -10,13 +10,13 @@ from galaxy.util.none_like import NoneDataset from galaxy.util.object_wrapper import wrap_with_safe_string -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) # Fields in .log files corresponding to paths, must have one of the following # field names and all such fields are assumed to be paths. This is to allow # remote ComputeEnvironments (such as one used by Pulsar) determine what values to # rewrite or transfer... -PATH_ATTRIBUTES = [ "path" ] +PATH_ATTRIBUTES = ["path"] # ... by default though - don't rewrite anything (if no ComputeEnviornment @@ -25,86 +25,88 @@ def DEFAULT_PATH_REWRITER(x): return x -class ToolParameterValueWrapper( object ): +class ToolParameterValueWrapper(object): """ Base class for object that Wraps a Tool Parameter and Value. """ - def __bool__( self ): - return bool( self.value ) + def __bool__(self): + return bool(self.value) __nonzero__ = __bool__ - def get_display_text( self, quote=True ): + def get_display_text(self, quote=True): """ Returns a string containing the value that would be displayed to the user in the tool interface. When quote is True (default), the string is escaped for e.g. command-line usage. """ - rval = self.input.value_to_display_text( self.value ) or '' + rval = self.input.value_to_display_text(self.value) or '' if quote: - return shlex_quote( rval ) + return shlex_quote(rval) return rval -class RawObjectWrapper( ToolParameterValueWrapper ): +class RawObjectWrapper(ToolParameterValueWrapper): """ Wraps an object so that __str__ returns module_name:class_name. """ - def __init__( self, obj ): + + def __init__(self, obj): self.obj = obj - def __bool__( self ): - return bool( self.obj ) # FIXME: would it be safe/backwards compatible to rename .obj to .value, so that we can just inherit this method? + def __bool__(self): + return bool(self.obj) # FIXME: would it be safe/backwards compatible to rename .obj to .value, so that we can just inherit this method? __nonzero__ = __bool__ - def __str__( self ): + def __str__(self): try: return "%s:%s" % (self.obj.__module__, self.obj.__class__.__name__) except: # Most likely None, which lacks __module__. - return str( self.obj ) + return str(self.obj) - def __getattr__( self, key ): - return getattr( self.obj, key ) + def __getattr__(self, key): + return getattr(self.obj, key) -class InputValueWrapper( ToolParameterValueWrapper ): +class InputValueWrapper(ToolParameterValueWrapper): """ Wraps an input so that __str__ gives the "param_dict" representation. """ - def __init__( self, input, value, other_values={} ): + + def __init__(self, input, value, other_values={}): self.input = input self.value = value self._other_values = other_values - def __eq__( self, other ): - if isinstance( other, string_types ): - return str( self ) == other - elif isinstance( other, int ): - return int( self ) == other - elif isinstance( other, float ): - return float( self ) == other + def __eq__(self, other): + if isinstance(other, string_types): + return str(self) == other + elif isinstance(other, int): + return int(self) == other + elif isinstance(other, float): + return float(self) == other else: - return super( InputValueWrapper, self ) == other + return super(InputValueWrapper, self) == other - def __ne__( self, other ): + def __ne__(self, other): return not self == other - def __str__( self ): - to_param_dict_string = self.input.to_param_dict_string( self.value, self._other_values ) - if isinstance( to_param_dict_string, list ): - return ','.join( to_param_dict_string ) + def __str__(self): + to_param_dict_string = self.input.to_param_dict_string(self.value, self._other_values) + if isinstance(to_param_dict_string, list): + return ','.join(to_param_dict_string) else: return to_param_dict_string - def __iter__( self ): - to_param_dict_string = self.input.to_param_dict_string( self.value, self._other_values ) - if not isinstance( to_param_dict_string, list ): - return iter( [ to_param_dict_string ] ) + def __iter__(self): + to_param_dict_string = self.input.to_param_dict_string(self.value, self._other_values) + if not isinstance(to_param_dict_string, list): + return iter([to_param_dict_string]) else: - return iter( to_param_dict_string ) + return iter(to_param_dict_string) - def __getattr__( self, key ): - return getattr( self.value, key ) + def __getattr__(self, key): + return getattr(self.value, key) def __int__(self): return int(str(self)) @@ -113,7 +115,7 @@ def __float__(self): return float(str(self)) -class SelectToolParameterWrapper( ToolParameterValueWrapper ): +class SelectToolParameterWrapper(ToolParameterValueWrapper): """ Wraps a SelectTooParameter so that __str__ returns the selected value, but all other attributes are accessible. @@ -124,52 +126,53 @@ class SelectToolParameterFieldWrapper: Provide access to any field by name or index for this particular value. Only applicable for dynamic_options selects, which have more than simple 'options' defined (name, value, selected). """ - def __init__( self, input, value, other_values, path_rewriter ): + + def __init__(self, input, value, other_values, path_rewriter): self._input = input self._value = value self._other_values = other_values self._fields = {} self._path_rewriter = path_rewriter - def __getattr__( self, name ): + def __getattr__(self, name): if name not in self._fields: - self._fields[ name ] = self._input.options.get_field_by_name_for_value( name, self._value, None, self._other_values ) - values = map( str, self._fields[ name ] ) + self._fields[name] = self._input.options.get_field_by_name_for_value(name, self._value, None, self._other_values) + values = map(str, self._fields[name]) if name in PATH_ATTRIBUTES: # If we infer this is a path, rewrite it if needed. - values = map( self._path_rewriter, values ) - return self._input.separator.join( values ) + values = map(self._path_rewriter, values) + return self._input.separator.join(values) - def __init__( self, input, value, other_values={}, path_rewriter=None ): + def __init__(self, input, value, other_values={}, path_rewriter=None): self.input = input self.value = value - self.input.value_label = input.value_to_display_text( value ) + self.input.value_label = input.value_to_display_text(value) self._other_values = other_values self._path_rewriter = path_rewriter or DEFAULT_PATH_REWRITER - self.fields = self.SelectToolParameterFieldWrapper( input, value, other_values, self._path_rewriter ) + self.fields = self.SelectToolParameterFieldWrapper(input, value, other_values, self._path_rewriter) - def __eq__( self, other ): - if isinstance( other, string_types ): - return str( self ) == other + def __eq__(self, other): + if isinstance(other, string_types): + return str(self) == other else: - return super( SelectToolParameterWrapper, self ) == other + return super(SelectToolParameterWrapper, self) == other - def __ne__( self, other ): + def __ne__(self, other): return not self == other - def __str__( self ): + def __str__(self): # Assuming value is never a path - otherwise would need to pass # along following argument value_map=self._path_rewriter. - return self.input.to_param_dict_string( self.value, other_values=self._other_values ) + return self.input.to_param_dict_string(self.value, other_values=self._other_values) - def __add__( self, x ): - return '%s%s' % ( self, x ) + def __add__(self, x): + return '%s%s' % (self, x) - def __getattr__( self, key ): - return getattr( self.input, key ) + def __getattr__(self, key): + return getattr(self.input, key) -class DatasetFilenameWrapper( ToolParameterValueWrapper ): +class DatasetFilenameWrapper(ToolParameterValueWrapper): """ Wraps a dataset so that __str__ returns the filename, but all other attributes are accessible. @@ -181,71 +184,72 @@ class MetadataWrapper: according to the metadata spec. Methods implemented to match behavior of a Metadata Collection. """ - def __init__( self, metadata ): + + def __init__(self, metadata): self.metadata = metadata - def __getattr__( self, name ): - rval = self.metadata.get( name, None ) + def __getattr__(self, name): + rval = self.metadata.get(name, None) if name in self.metadata.spec: if rval is None: rval = self.metadata.spec[name].no_value - rval = self.metadata.spec[ name ].param.to_safe_string( rval ) + rval = self.metadata.spec[name].param.to_safe_string(rval) # Store this value, so we don't need to recalculate if needed # again - setattr( self, name, rval ) + setattr(self, name, rval) else: # escape string value of non-defined metadata value - rval = wrap_with_safe_string( rval ) + rval = wrap_with_safe_string(rval) return rval - def __bool__( self ): + def __bool__(self): return self.metadata.__nonzero__() __nonzero__ = __bool__ - def __iter__( self ): + def __iter__(self): return self.metadata.__iter__() - def get( self, key, default=None ): + def get(self, key, default=None): try: - return getattr( self, key ) + return getattr(self, key) except: return default - def items( self ): - return iter( ( k, self.get( k ) ) for k, v in self.metadata.items() ) + def items(self): + return iter((k, self.get(k)) for k, v in self.metadata.items()) - def __init__( self, dataset, datatypes_registry=None, tool=None, name=None, dataset_path=None, identifier=None ): + def __init__(self, dataset, datatypes_registry=None, tool=None, name=None, dataset_path=None, identifier=None): if not dataset: try: # TODO: allow this to work when working with grouping ext = tool.inputs[name].extensions[0] except: ext = 'data' - self.dataset = wrap_with_safe_string( NoneDataset( datatypes_registry=datatypes_registry, ext=ext ), no_wrap_classes=ToolParameterValueWrapper ) + self.dataset = wrap_with_safe_string(NoneDataset(datatypes_registry=datatypes_registry, ext=ext), no_wrap_classes=ToolParameterValueWrapper) else: # Tool wrappers should not normally be accessing .dataset directly, # so we will wrap it and keep the original around for file paths # Should we name this .value to maintain consistency with most other ToolParameterValueWrapper? self.unsanitized = dataset - self.dataset = wrap_with_safe_string( dataset, no_wrap_classes=ToolParameterValueWrapper ) - self.metadata = self.MetadataWrapper( dataset.metadata ) + self.dataset = wrap_with_safe_string(dataset, no_wrap_classes=ToolParameterValueWrapper) + self.metadata = self.MetadataWrapper(dataset.metadata) self.datatypes_registry = datatypes_registry - self.false_path = getattr( dataset_path, "false_path", None ) - self.false_extra_files_path = getattr( dataset_path, "false_extra_files_path", None ) + self.false_path = getattr(dataset_path, "false_path", None) + self.false_extra_files_path = getattr(dataset_path, "false_extra_files_path", None) self._element_identifier = identifier @property - def element_identifier( self ): + def element_identifier(self): identifier = self._element_identifier if identifier is None: identifier = self.name return identifier @property - def is_collection( self ): + def is_collection(self): return False - def is_of_type( self, *exts ): + def is_of_type(self, *exts): datatypes = [] for e in exts: datatype = self.datatypes_registry.get_datatype_by_extension(e) @@ -253,15 +257,15 @@ def is_of_type( self, *exts ): datatypes.append(datatype) else: log.warning("Datatype class not found for extension '%s', which is used as parameter of 'is_of_type()' method" % (e)) - return self.dataset.datatype.matches_any( datatypes ) + return self.dataset.datatype.matches_any(datatypes) - def __str__( self ): + def __str__(self): if self.false_path is not None: return self.false_path else: return self.unsanitized.file_name - def __getattr__( self, key ): + def __getattr__(self, key): if self.false_path is not None and key == 'file_name': # Path to dataset was rewritten for this job. return self.false_path @@ -286,22 +290,22 @@ def __getattr__( self, key ): # path like DiskObjectStore. raise else: - return getattr( self.dataset, key ) + return getattr(self.dataset, key) - def __bool__( self ): - return bool( self.dataset ) + def __bool__(self): + return bool(self.dataset) __nonzero__ = __bool__ class HasDatasets: - def _dataset_wrapper( self, dataset, dataset_paths, **kwargs ): + def _dataset_wrapper(self, dataset, dataset_paths, **kwargs): wrapper_kwds = kwargs.copy() if dataset: real_path = dataset.file_name if real_path in dataset_paths: - wrapper_kwds[ "dataset_path" ] = dataset_paths[ real_path ] - return DatasetFilenameWrapper( dataset, **wrapper_kwds ) + wrapper_kwds["dataset_path"] = dataset_paths[real_path] + return DatasetFilenameWrapper(dataset, **wrapper_kwds) def paths_as_file(self, sep="\n"): handle, filepath = tempfile.mkstemp(prefix="gx_file_list", dir=self.job_working_directory) @@ -311,44 +315,45 @@ def paths_as_file(self, sep="\n"): return filepath -class DatasetListWrapper( list, ToolParameterValueWrapper, HasDatasets ): +class DatasetListWrapper(list, ToolParameterValueWrapper, HasDatasets): """ """ - def __init__( self, job_working_directory, datasets, dataset_paths=[], **kwargs ): + + def __init__(self, job_working_directory, datasets, dataset_paths=[], **kwargs): if not isinstance(datasets, list): datasets = [datasets] - def to_wrapper( dataset ): + def to_wrapper(dataset): if hasattr(dataset, "dataset_instance"): element = dataset dataset = element.dataset_instance kwargs["identifier"] = element.element_identifier - return self._dataset_wrapper( dataset, dataset_paths, **kwargs ) + return self._dataset_wrapper(dataset, dataset_paths, **kwargs) - list.__init__( self, map( to_wrapper, datasets ) ) + list.__init__(self, map(to_wrapper, datasets)) self.job_working_directory = job_working_directory @staticmethod - def to_dataset_instances( dataset_instance_sources ): + def to_dataset_instances(dataset_instance_sources): dataset_instances = [] - if not isinstance( dataset_instance_sources, list ): - dataset_instance_sources = [ dataset_instance_sources ] + if not isinstance(dataset_instance_sources, list): + dataset_instance_sources = [dataset_instance_sources] for dataset_instance_source in dataset_instance_sources: if dataset_instance_source is None: - dataset_instances.append( dataset_instance_source ) + dataset_instances.append(dataset_instance_source) elif dataset_instance_source.history_content_type == "dataset": - dataset_instances.append( dataset_instance_source ) + dataset_instances.append(dataset_instance_source) else: - dataset_instances.extend( dataset_instance_source.collection.dataset_elements ) + dataset_instances.extend(dataset_instance_source.collection.dataset_elements) return dataset_instances - def __str__( self ): - return ','.join( map( str, self ) ) + def __str__(self): + return ','.join(map(str, self)) -class DatasetCollectionWrapper( ToolParameterValueWrapper, HasDatasets ): +class DatasetCollectionWrapper(ToolParameterValueWrapper, HasDatasets): - def __init__( self, job_working_directory, has_collection, dataset_paths=[], **kwargs ): + def __init__(self, job_working_directory, has_collection, dataset_paths=[], **kwargs): super(DatasetCollectionWrapper, self).__init__() self.job_working_directory = job_working_directory @@ -358,11 +363,11 @@ def __init__( self, job_working_directory, has_collection, dataset_paths=[], **k else: self.__input_supplied = True - if hasattr( has_collection, "name" ): + if hasattr(has_collection, "name"): # It is a HistoryDatasetCollectionAssociation collection = has_collection.collection self.name = has_collection.name - elif hasattr( has_collection, "child_collection" ): + elif hasattr(has_collection, "child_collection"): # It is a DatasetCollectionElement instance referencing another collection collection = has_collection.child_collection self.name = has_collection.element_identifier @@ -379,49 +384,49 @@ def __init__( self, job_working_directory, has_collection, dataset_paths=[], **k element_identifier = dataset_collection_element.element_identifier if dataset_collection_element.is_collection: - element_wrapper = DatasetCollectionWrapper(job_working_directory, dataset_collection_element, dataset_paths, **kwargs ) + element_wrapper = DatasetCollectionWrapper(job_working_directory, dataset_collection_element, dataset_paths, **kwargs) else: - element_wrapper = self._dataset_wrapper( element_object, dataset_paths, identifier=element_identifier, **kwargs) + element_wrapper = self._dataset_wrapper(element_object, dataset_paths, identifier=element_identifier, **kwargs) element_instances[element_identifier] = element_wrapper - element_instance_list.append( element_wrapper ) + element_instance_list.append(element_wrapper) self.__element_instances = element_instances self.__element_instance_list = element_instance_list - def keys( self ): + def keys(self): if not self.__input_supplied: return [] return self.__element_instances.keys() @property - def is_collection( self ): + def is_collection(self): return True @property - def is_input_supplied( self ): + def is_input_supplied(self): return self.__input_supplied - def __getitem__( self, key ): + def __getitem__(self, key): if not self.__input_supplied: return None - if isinstance( key, int ): - return self.__element_instance_list[ key ] + if isinstance(key, int): + return self.__element_instance_list[key] else: - return self.__element_instances[ key ] + return self.__element_instances[key] - def __getattr__( self, key ): + def __getattr__(self, key): if not self.__input_supplied: return None - return self.__element_instances[ key ] + return self.__element_instances[key] - def __iter__( self ): + def __iter__(self): if not self.__input_supplied: return [].__iter__() return self.__element_instance_list.__iter__() - def __bool__( self ): + def __bool__(self): # Fail `#if $param` checks in cheetah is optional input # not specified or if resulting collection is empty. - return self.__input_supplied and bool( self.__element_instance_list ) + return self.__input_supplied and bool(self.__element_instance_list) __nonzero__ = __bool__ diff --git a/lib/galaxy/tools/xsd/galaxy.xsd b/lib/galaxy/tools/xsd/galaxy.xsd index 3d7bc3ef7a16..d32b0f60e5ee 100644 --- a/lib/galaxy/tools/xsd/galaxy.xsd +++ b/lib/galaxy/tools/xsd/galaxy.xsd @@ -3559,6 +3559,30 @@ the ```` documentation. + + + + + + + + + + @@ -3693,6 +3717,12 @@ The valid values for format can be found in This sets the data type of the output file to be the same format as that of a tool input dataset. + + + Sets the source of element identifier to the specified input. +This only applies to collections that are mapped over a non-collection input and that have equivalent structures. + + This copies the metadata information @@ -3867,7 +3897,12 @@ More information can be found on Planemo's documentation for [multiple output files](https://planemo.readthedocs.io/en/latest/writing_advanced.html#multiple-output-files). ]]> - + + + Indicate that dataset filenames should simply be read from the provided metadata file (e.g. galaxy.json). If this is set - pattern and sort must not be set. + + + Regular expression used to find filenames and parse dynamic properties. @@ -3915,7 +3950,12 @@ Galaxy, including: ]]> - + + + Indicate that dataset filenames should simply be read from the provided metadata file (e.g. galaxy.json). If this is set - pattern and sort must not be set. + + + Regular expression used to find filenames and parse dynamic properties. diff --git a/lib/galaxy/tours/__init__.py b/lib/galaxy/tours/__init__.py index f1027b2db632..0511aa29e830 100644 --- a/lib/galaxy/tours/__init__.py +++ b/lib/galaxy/tours/__init__.py @@ -8,7 +8,7 @@ from galaxy import util -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) def tour_loader(contents_dict): @@ -29,7 +29,7 @@ def tour_loader(contents_dict): class ToursRegistry(object): def __init__(self, tour_directories): - self.tour_directories = util.config_directories_from_setting( tour_directories ) + self.tour_directories = util.config_directories_from_setting(tour_directories) self.load_tours() def tours_by_id_with_description(self): diff --git a/lib/galaxy/util/__init__.py b/lib/galaxy/util/__init__.py index 94183cc60ceb..4f0f16fdbae4 100644 --- a/lib/galaxy/util/__init__.py +++ b/lib/galaxy/util/__init__.py @@ -64,11 +64,11 @@ bz2_magic = 'BZh' DEFAULT_ENCODING = os.environ.get('GALAXY_DEFAULT_ENCODING', 'utf-8') NULL_CHAR = '\000' -BINARY_CHARS = [ NULL_CHAR ] +BINARY_CHARS = [NULL_CHAR] FILENAME_VALID_CHARS = '.,^_-()[]0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' -def remove_protocol_from_url( url ): +def remove_protocol_from_url(url): """ Supplied URL may be null, if not ensure http:// or https:// etc... is stripped off. """ @@ -76,14 +76,14 @@ def remove_protocol_from_url( url ): return url # We have a URL - if url.find( '://' ) > 0: - new_url = url.split( '://' )[1] + if url.find('://') > 0: + new_url = url.split('://')[1] else: new_url = url - return new_url.rstrip( '/' ) + return new_url.rstrip('/') -def is_binary( value, binary_chars=None ): +def is_binary(value, binary_chars=None): """ File is binary if it contains a null-byte by default (e.g. behavior of grep, etc.). This may fail for utf-16 files, but so would ASCII encoding. @@ -102,7 +102,7 @@ def is_binary( value, binary_chars=None ): return False -def is_uuid( value ): +def is_uuid(value): """ This method returns True if value is a UUID, otherwise False. >>> is_uuid( "123e4567-e89b-12d3-a456-426655440000" ) @@ -110,14 +110,14 @@ def is_uuid( value ): >>> is_uuid( "0x3242340298902834" ) False """ - uuid_re = re.compile( "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" ) - if re.match( uuid_re, str( value ) ): + uuid_re = re.compile("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}") + if re.match(uuid_re, str(value)): return True else: return False -def directory_hash_id( id ): +def directory_hash_id(id): """ >>> directory_hash_id( 100 ) @@ -129,25 +129,25 @@ def directory_hash_id( id ): >>> directory_hash_id("135ee48a-4f51-470c-ae2f-ce8bd78799e6") ['1', '3', '5'] """ - s = str( id ) - l = len( s ) + s = str(id) + l = len(s) # Shortcut -- ids 0-999 go under ../000/ if l < 4: - return [ "000" ] + return ["000"] if not is_uuid(s): # Pad with zeros until a multiple of three - padded = ( ( 3 - len( s ) % 3 ) * "0" ) + s + padded = ((3 - len(s) % 3) * "0") + s # Drop the last three digits -- 1000 files per directory padded = padded[:-3] # Break into chunks of three - return [ padded[ i * 3:(i + 1 ) * 3 ] for i in range( len( padded ) // 3 ) ] + return [padded[i * 3:(i + 1) * 3] for i in range(len(padded) // 3)] else: # assume it is a UUID return list(iter(s[0:3])) -def get_charset_from_http_headers( headers, default=None ): - rval = headers.get('content-type', None ) +def get_charset_from_http_headers(headers, default=None): + rval = headers.get('content-type', None) if rval and 'charset=' in rval: rval = rval.split('charset=')[-1].split(';')[0].strip() if rval: @@ -181,7 +181,7 @@ def file_iter(fname, sep=None): yield line.split(sep) -def file_reader( fp, chunk_size=CHUNK_SIZE ): +def file_reader(fp, chunk_size=CHUNK_SIZE): """This generator yields the open fileobject in chunks (default 64k). Closes the file at the end""" while 1: data = fp.read(chunk_size) @@ -203,15 +203,15 @@ def unique_id(KEY_SIZE=128): return md5(random_bits).hexdigest() -def parse_xml( fname ): +def parse_xml(fname): """Returns a parsed xml tree""" # handle deprecation warning for XMLParsing a file with DOCTYPE - class DoctypeSafeCallbackTarget( ElementTree.TreeBuilder ): - def doctype( *args ): + class DoctypeSafeCallbackTarget(ElementTree.TreeBuilder): + def doctype(*args): pass tree = ElementTree.ElementTree() try: - root = tree.parse( fname, parser=ElementTree.XMLParser( target=DoctypeSafeCallbackTarget() ) ) + root = tree.parse(fname, parser=ElementTree.XMLParser(target=DoctypeSafeCallbackTarget())) except ParseError: log.exception("Error parsing file %s", fname) raise @@ -224,161 +224,161 @@ def parse_xml_string(xml_string): return tree -def xml_to_string( elem, pretty=False ): +def xml_to_string(elem, pretty=False): """Returns a string from an xml tree""" if pretty: - elem = pretty_print_xml( elem ) + elem = pretty_print_xml(elem) try: - return ElementTree.tostring( elem ) + return ElementTree.tostring(elem) except TypeError as e: # we assume this is a comment - if hasattr( elem, 'text' ): - return "\n" % ( elem.text ) + if hasattr(elem, 'text'): + return "\n" % (elem.text) else: raise e -def xml_element_compare( elem1, elem2 ): - if not isinstance( elem1, dict ): - elem1 = xml_element_to_dict( elem1 ) - if not isinstance( elem2, dict ): - elem2 = xml_element_to_dict( elem2 ) +def xml_element_compare(elem1, elem2): + if not isinstance(elem1, dict): + elem1 = xml_element_to_dict(elem1) + if not isinstance(elem2, dict): + elem2 = xml_element_to_dict(elem2) return elem1 == elem2 -def xml_element_list_compare( elem_list1, elem_list2 ): - return [ xml_element_to_dict( elem ) for elem in elem_list1 ] == [ xml_element_to_dict( elem ) for elem in elem_list2 ] +def xml_element_list_compare(elem_list1, elem_list2): + return [xml_element_to_dict(elem) for elem in elem_list1] == [xml_element_to_dict(elem) for elem in elem_list2] -def xml_element_to_dict( elem ): +def xml_element_to_dict(elem): rval = {} if elem.attrib: - rval[ elem.tag ] = {} + rval[elem.tag] = {} else: - rval[ elem.tag ] = None + rval[elem.tag] = None - sub_elems = list( elem ) + sub_elems = list(elem) if sub_elems: sub_elem_dict = dict() - for sub_sub_elem_dict in map( xml_element_to_dict, sub_elems ): + for sub_sub_elem_dict in map(xml_element_to_dict, sub_elems): for key, value in iteritems(sub_sub_elem_dict): if key not in sub_elem_dict: - sub_elem_dict[ key ] = [] - sub_elem_dict[ key ].append( value ) + sub_elem_dict[key] = [] + sub_elem_dict[key].append(value) for key, value in iteritems(sub_elem_dict): - if len( value ) == 1: - rval[ elem.tag ][ key ] = value[0] + if len(value) == 1: + rval[elem.tag][key] = value[0] else: - rval[ elem.tag ][ key ] = value + rval[elem.tag][key] = value if elem.attrib: for key, value in iteritems(elem.attrib): - rval[ elem.tag ][ "@%s" % key ] = value + rval[elem.tag]["@%s" % key] = value if elem.text: text = elem.text.strip() if text and sub_elems or elem.attrib: - rval[ elem.tag ][ '#text' ] = text + rval[elem.tag]['#text'] = text else: - rval[ elem.tag ] = text + rval[elem.tag] = text return rval -def pretty_print_xml( elem, level=0 ): +def pretty_print_xml(elem, level=0): pad = ' ' i = "\n" + level * pad - if len( elem ): + if len(elem): if not elem.text or not elem.text.strip(): elem.text = i + pad + pad if not elem.tail or not elem.tail.strip(): elem.tail = i for e in elem: - pretty_print_xml( e, level + 1 ) + pretty_print_xml(e, level + 1) if not elem.tail or not elem.tail.strip(): elem.tail = i else: - if level and ( not elem.tail or not elem.tail.strip() ): + if level and (not elem.tail or not elem.tail.strip()): elem.tail = i + pad return elem -def get_file_size( value, default=None ): +def get_file_size(value, default=None): try: # try built-in - return os.path.getsize( value ) + return os.path.getsize(value) except: try: # try built-in one name attribute - return os.path.getsize( value.name ) + return os.path.getsize(value.name) except: try: # try tell() of end of object offset = value.tell() - value.seek( 0, 2 ) + value.seek(0, 2) rval = value.tell() - value.seek( offset ) + value.seek(offset) return rval except: # return default value return default -def shrink_stream_by_size( value, size, join_by="..", left_larger=True, beginning_on_size_error=False, end_on_size_error=False ): +def shrink_stream_by_size(value, size, join_by="..", left_larger=True, beginning_on_size_error=False, end_on_size_error=False): rval = '' - if get_file_size( value ) > size: + if get_file_size(value) > size: start = value.tell() - len_join_by = len( join_by ) + len_join_by = len(join_by) min_size = len_join_by + 2 if size < min_size: if beginning_on_size_error: - rval = value.read( size ) - value.seek( start ) + rval = value.read(size) + value.seek(start) return rval elif end_on_size_error: - value.seek( -size, 2 ) - rval = value.read( size ) - value.seek( start ) + value.seek(-size, 2) + rval = value.read(size) + value.seek(start) return rval - raise ValueError( 'With the provided join_by value (%s), the minimum size value is %i.' % ( join_by, min_size ) ) - left_index = right_index = int( ( size - len_join_by ) / 2 ) + raise ValueError('With the provided join_by value (%s), the minimum size value is %i.' % (join_by, min_size)) + left_index = right_index = int((size - len_join_by) / 2) if left_index + right_index + len_join_by < size: if left_larger: left_index += 1 else: right_index += 1 - rval = value.read( left_index ) + join_by - value.seek( -right_index, 2 ) - rval += value.read( right_index ) + rval = value.read(left_index) + join_by + value.seek(-right_index, 2) + rval += value.read(right_index) else: while True: - data = value.read( CHUNK_SIZE ) + data = value.read(CHUNK_SIZE) if not data: break rval += data return rval -def shrink_string_by_size( value, size, join_by="..", left_larger=True, beginning_on_size_error=False, end_on_size_error=False ): - if len( value ) > size: - len_join_by = len( join_by ) +def shrink_string_by_size(value, size, join_by="..", left_larger=True, beginning_on_size_error=False, end_on_size_error=False): + if len(value) > size: + len_join_by = len(join_by) min_size = len_join_by + 2 if size < min_size: if beginning_on_size_error: return value[:size] elif end_on_size_error: return value[-size:] - raise ValueError( 'With the provided join_by value (%s), the minimum size value is %i.' % ( join_by, min_size ) ) - left_index = right_index = int( ( size - len_join_by ) / 2 ) + raise ValueError('With the provided join_by value (%s), the minimum size value is %i.' % (join_by, min_size)) + left_index = right_index = int((size - len_join_by) / 2) if left_index + right_index + len_join_by < size: if left_larger: left_index += 1 else: right_index += 1 - value = "%s%s%s" % ( value[:left_index], join_by, value[-right_index:] ) + value = "%s%s%s" % (value[:left_index], join_by, value[-right_index:]) return value -def pretty_print_time_interval( time=False, precise=False ): +def pretty_print_time_interval(time=False, precise=False): """ Get a datetime object or a int() Epoch timestamp and return a pretty string like 'an hour ago', 'Yesterday', '3 months ago', @@ -386,16 +386,16 @@ def pretty_print_time_interval( time=False, precise=False ): credit: http://stackoverflow.com/questions/1551382/user-friendly-time-format-in-python """ now = datetime.now() - if type( time ) is int: - diff = now - datetime.fromtimestamp( time ) - elif isinstance( time, datetime ): + if type(time) is int: + diff = now - datetime.fromtimestamp(time) + elif isinstance(time, datetime): diff = now - time - elif isinstance( time, string_types ): + elif isinstance(time, string_types): try: - time = datetime.strptime( time, "%Y-%m-%dT%H:%M:%S.%f" ) + time = datetime.strptime(time, "%Y-%m-%dT%H:%M:%S.%f") except ValueError: # MySQL may not support microseconds precision - time = datetime.strptime( time, "%Y-%m-%dT%H:%M:%S" ) + time = datetime.strptime(time, "%Y-%m-%dT%H:%M:%S") diff = now - time else: diff = now - now @@ -407,27 +407,27 @@ def pretty_print_time_interval( time=False, precise=False ): if precise: if day_diff == 0: - if second_diff < 10: - return "just now" - if second_diff < 60: - return str(second_diff) + " seconds ago" - if second_diff < 120: - return "a minute ago" - if second_diff < 3600: - return str(second_diff / 60) + " minutes ago" - if second_diff < 7200: - return "an hour ago" - if second_diff < 86400: - return str(second_diff / 3600) + " hours ago" + if second_diff < 10: + return "just now" + if second_diff < 60: + return str(second_diff) + " seconds ago" + if second_diff < 120: + return "a minute ago" + if second_diff < 3600: + return str(second_diff / 60) + " minutes ago" + if second_diff < 7200: + return "an hour ago" + if second_diff < 86400: + return str(second_diff / 3600) + " hours ago" if day_diff == 1: return "yesterday" if day_diff < 7: - return str( day_diff ) + " days ago" + return str(day_diff) + " days ago" if day_diff < 31: - return str( day_diff / 7 ) + " weeks ago" + return str(day_diff / 7) + " weeks ago" if day_diff < 365: - return str( day_diff / 30 ) + " months ago" - return str( day_diff / 365 ) + " years ago" + return str(day_diff / 30) + " months ago" + return str(day_diff / 365) + " years ago" else: if day_diff == 0: return "today" @@ -452,22 +452,22 @@ def pretty_print_json(json_data, is_json_string=False): valid_chars = set(string.ascii_letters + string.digits + " -=_.()/+*^,:?!") # characters that are allowed but need to be escaped -mapped_chars = { '>': '__gt__', - '<': '__lt__', - "'": '__sq__', - '"': '__dq__', - '[': '__ob__', - ']': '__cb__', - '{': '__oc__', - '}': '__cc__', - '@': '__at__', - '\n': '__cn__', - '\r': '__cr__', - '\t': '__tc__', - '#': '__pd__'} - - -def restore_text( text, character_map=mapped_chars ): +mapped_chars = {'>': '__gt__', + '<': '__lt__', + "'": '__sq__', + '"': '__dq__', + '[': '__ob__', + ']': '__cb__', + '{': '__oc__', + '}': '__cc__', + '@': '__at__', + '\n': '__cn__', + '\r': '__cr__', + '\t': '__tc__', + '#': '__pd__'} + + +def restore_text(text, character_map=mapped_chars): """Restores sanitized text""" if not text: return text @@ -476,19 +476,19 @@ def restore_text( text, character_map=mapped_chars ): return text -def sanitize_text( text, valid_characters=valid_chars, character_map=mapped_chars, invalid_character='X' ): +def sanitize_text(text, valid_characters=valid_chars, character_map=mapped_chars, invalid_character='X'): """ Restricts the characters that are allowed in text; accepts both strings and lists of strings; non-string entities will be cast to strings. """ - if isinstance( text, list ): - return [ sanitize_text( x, valid_characters=valid_characters, character_map=character_map, invalid_character=invalid_character ) for x in text ] - if not isinstance( text, string_types ): - text = smart_str( text ) - return _sanitize_text_helper( text, valid_characters=valid_characters, character_map=character_map ) + if isinstance(text, list): + return [sanitize_text(x, valid_characters=valid_characters, character_map=character_map, invalid_character=invalid_character) for x in text] + if not isinstance(text, string_types): + text = smart_str(text) + return _sanitize_text_helper(text, valid_characters=valid_characters, character_map=character_map) -def _sanitize_text_helper( text, valid_characters=valid_chars, character_map=mapped_chars, invalid_character='X' ): +def _sanitize_text_helper(text, valid_characters=valid_chars, character_map=mapped_chars, invalid_character='X'): """Restricts the characters that are allowed in a string""" out = [] @@ -496,41 +496,41 @@ def _sanitize_text_helper( text, valid_characters=valid_chars, character_map=map if c in valid_characters: out.append(c) elif c in character_map: - out.append( character_map[c] ) + out.append(character_map[c]) else: - out.append( invalid_character ) # makes debugging easier + out.append(invalid_character) # makes debugging easier return ''.join(out) -def sanitize_lists_to_string( values, valid_characters=valid_chars, character_map=mapped_chars, invalid_character='X' ): - if isinstance( values, list ): +def sanitize_lists_to_string(values, valid_characters=valid_chars, character_map=mapped_chars, invalid_character='X'): + if isinstance(values, list): rval = [] for value in values: - rval.append( sanitize_lists_to_string( value, - valid_characters=valid_characters, - character_map=character_map, - invalid_character=invalid_character ) ) - values = ",".join( rval ) + rval.append(sanitize_lists_to_string(value, + valid_characters=valid_characters, + character_map=character_map, + invalid_character=invalid_character)) + values = ",".join(rval) else: - values = sanitize_text( values, valid_characters=valid_characters, character_map=character_map, invalid_character=invalid_character ) + values = sanitize_text(values, valid_characters=valid_characters, character_map=character_map, invalid_character=invalid_character) return values -def sanitize_param( value, valid_characters=valid_chars, character_map=mapped_chars, invalid_character='X' ): +def sanitize_param(value, valid_characters=valid_chars, character_map=mapped_chars, invalid_character='X'): """Clean incoming parameters (strings or lists)""" - if isinstance( value, string_types ): - return sanitize_text( value, valid_characters=valid_characters, character_map=character_map, invalid_character=invalid_character ) - elif isinstance( value, list ): - return [ sanitize_text( x, valid_characters=valid_characters, character_map=character_map, invalid_character=invalid_character ) for x in value ] + if isinstance(value, string_types): + return sanitize_text(value, valid_characters=valid_characters, character_map=character_map, invalid_character=invalid_character) + elif isinstance(value, list): + return [sanitize_text(x, valid_characters=valid_characters, character_map=character_map, invalid_character=invalid_character) for x in value] else: - raise Exception('Unknown parameter type (%s)' % ( type( value ) )) + raise Exception('Unknown parameter type (%s)' % (type(value))) -valid_filename_chars = set( string.ascii_letters + string.digits + '_.' ) -invalid_filenames = [ '', '.', '..' ] +valid_filename_chars = set(string.ascii_letters + string.digits + '_.') +invalid_filenames = ['', '.', '..'] -def sanitize_for_filename( text, default=None ): +def sanitize_for_filename(text, default=None): """ Restricts the characters that are allowed in a filename portion; Returns default value or a unique id string if result is not a valid name. Method is overly aggressive to minimize possible complications, but a maximum length is not considered. @@ -538,18 +538,18 @@ def sanitize_for_filename( text, default=None ): out = [] for c in text: if c in valid_filename_chars: - out.append( c ) + out.append(c) else: - out.append( '_' ) - out = ''.join( out ) + out.append('_') + out = ''.join(out) if out in invalid_filenames: if default is None: - return sanitize_for_filename( str( unique_id() ) ) + return sanitize_for_filename(str(unique_id())) return default return out -def mask_password_from_url( url ): +def mask_password_from_url(url): """ Masks out passwords from connection urls like the database connection in galaxy.ini @@ -573,7 +573,7 @@ def mask_password_from_url( url ): return url -def ready_name_for_url( raw_name ): +def ready_name_for_url(raw_name): u""" General method to convert a string (i.e. object name) to a URL-ready slug. @@ -586,9 +586,9 @@ def ready_name_for_url( raw_name ): """ # Replace whitespace with '-' - slug_base = re.sub( "\s+", "-", raw_name ) + slug_base = re.sub("\s+", "-", raw_name) # Remove all non-alphanumeric characters. - slug_base = re.sub( "[^a-zA-Z0-9\-]", "", slug_base ) + slug_base = re.sub("[^a-zA-Z0-9\-]", "", slug_base) # Remove trailing '-'. if slug_base.endswith('-'): slug_base = slug_base[:-1] @@ -599,7 +599,7 @@ def which(file): # http://stackoverflow.com/questions/5226958/which-equivalent-function-in-python for path in os.environ["PATH"].split(":"): if os.path.exists(path + "/" + file): - return path + "/" + file + return path + "/" + file return None @@ -619,7 +619,7 @@ def safe_makedirs(path): raise -def in_directory( file, directory, local_path_module=os.path ): +def in_directory(file, directory, local_path_module=os.path): """ Return true, if the common prefix of both is equal to directory e.g. /a/b/c/d.rst and directory is /a/b, the common prefix is /a/b @@ -631,7 +631,7 @@ def in_directory( file, directory, local_path_module=os.path ): return local_path_module.commonprefix([file, directory]) == directory -def merge_sorted_iterables( operator, *iterables ): +def merge_sorted_iterables(operator, *iterables): """ >>> operator = lambda x: x @@ -642,20 +642,20 @@ def merge_sorted_iterables( operator, *iterables ): >>> list( merge_sorted_iterables( operator, [1, 4, 5], [2], [3] ) ) [1, 2, 3, 4, 5] """ - first_iterable = iterables[ 0 ] - if len( iterables ) == 1: + first_iterable = iterables[0] + if len(iterables) == 1: for el in first_iterable: yield el else: for el in __merge_two_sorted_iterables( operator, - iter( first_iterable ), - merge_sorted_iterables( operator, *iterables[ 1: ] ) + iter(first_iterable), + merge_sorted_iterables(operator, *iterables[1:]) ): yield el -def __merge_two_sorted_iterables( operator, iterable1, iterable2 ): +def __merge_two_sorted_iterables(operator, iterable1, iterable2): unset = object() continue_merge = True next_1 = unset @@ -663,10 +663,10 @@ def __merge_two_sorted_iterables( operator, iterable1, iterable2 ): while continue_merge: try: if next_1 is unset: - next_1 = next( iterable1 ) + next_1 = next(iterable1) if next_2 is unset: - next_2 = next( iterable2 ) - if operator( next_2 ) < operator( next_1 ): + next_2 = next(iterable2) + if operator(next_2) < operator(next_1): yield next_2 next_2 = unset else: @@ -684,7 +684,7 @@ def __merge_two_sorted_iterables( operator, iterable1, iterable2 ): yield el -class Params( object ): +class Params(object): """ Stores and 'sanitizes' parameters. Alphanumeric characters and the non-alphanumeric ones that are deemed safe are let to pass through (see L{valid_chars}). @@ -710,7 +710,7 @@ class Params( object ): # is NEVER_SANITIZE required now that sanitizing for tool parameters can be controlled on a per parameter basis and occurs via InputValueWrappers? NEVER_SANITIZE = ['file_data', 'url_paste', 'URL', 'filesystem_paths'] - def __init__( self, params, sanitize=True ): + def __init__(self, params, sanitize=True): if sanitize: for key, value in params.items(): # sanitize check both ungrouped and grouped parameters by @@ -718,12 +718,12 @@ def __init__( self, params, sanitize=True ): # changed to not require this and NEVER_SANITIZE should be # removed. if (value is not None and - key not in self.NEVER_SANITIZE and - True not in [ key.endswith( "|%s" % nonsanitize_parameter ) for - nonsanitize_parameter in self.NEVER_SANITIZE ]): - self.__dict__[ key ] = sanitize_param( value ) + key not in self.NEVER_SANITIZE and + True not in [key.endswith("|%s" % nonsanitize_parameter) for + nonsanitize_parameter in self.NEVER_SANITIZE]): + self.__dict__[key] = sanitize_param(value) else: - self.__dict__[ key ] = value + self.__dict__[key] = value else: self.__dict__.update(params) @@ -735,9 +735,9 @@ def flatten(self): for key, value in self.__dict__.items(): if isinstance(value, list): for v in value: - flat.append( (key, v) ) + flat.append((key, v)) else: - flat.append( (key, value) ) + flat.append((key, value)) return flat def __getattr__(self, name): @@ -760,17 +760,17 @@ def update(self, values): self.__dict__.update(values) -def rst_to_html( s ): +def rst_to_html(s): """Convert a blob of reStructuredText to HTML""" - log = logging.getLogger( "docutils" ) + log = logging.getLogger("docutils") if docutils_core is None: raise Exception("Attempted to use rst_to_html but docutils unavailable.") - class FakeStream( object ): - def write( self, str ): - if len( str ) > 0 and not str.isspace(): - log.warning( str ) + class FakeStream(object): + def write(self, str): + if len(str) > 0 and not str.isspace(): + log.warning(str) settings_overrides = { "embed_stylesheet": False, @@ -780,9 +780,9 @@ def write( self, str ): # number of sections in help content. } - return unicodify( docutils_core.publish_string( s, + return unicodify(docutils_core.publish_string(s, writer=docutils_html4css1.Writer(), - settings_overrides=settings_overrides ) ) + settings_overrides=settings_overrides)) def xml_text(root, name=None): @@ -820,14 +820,14 @@ def asbool(obj): return bool(obj) -def string_as_bool( string ): - if str( string ).lower() in ( 'true', 'yes', 'on', '1' ): +def string_as_bool(string): + if str(string).lower() in ('true', 'yes', 'on', '1'): return True else: return False -def string_as_bool_or_none( string ): +def string_as_bool_or_none(string): """ Returns True, None or False based on the argument: True if passed True, 'True', 'Yes', or 'On' @@ -837,8 +837,8 @@ def string_as_bool_or_none( string ): Note: string comparison is case-insensitive so lowecase versions of those function equivalently. """ - string = str( string ).lower() - if string in ( 'true', 'yes', 'on' ): + string = str(string).lower() + if string in ('true', 'yes', 'on'): return True elif string == 'none': return None @@ -846,22 +846,22 @@ def string_as_bool_or_none( string ): return False -def listify( item, do_strip=False ): +def listify(item, do_strip=False): """ Make a single item a single item list, or return a list if passed a list. Passing a None returns an empty list. """ if not item: return [] - elif isinstance( item, list ): + elif isinstance(item, list): return item - elif isinstance( item, string_types ) and item.count( ',' ): + elif isinstance(item, string_types) and item.count(','): if do_strip: - return [token.strip() for token in item.split( ',' )] + return [token.strip() for token in item.split(',')] else: - return item.split( ',' ) + return item.split(',') else: - return [ item ] + return [item] def commaify(amount): @@ -935,54 +935,54 @@ def smart_str(s, encoding=DEFAULT_ENCODING, strings_only=False, errors='strict') return s -def object_to_string( obj ): - return binascii.hexlify( obj ) +def object_to_string(obj): + return binascii.hexlify(obj) -def string_to_object( s ): - return binascii.unhexlify( s ) +def string_to_object(s): + return binascii.unhexlify(s) -class ParamsWithSpecs( collections.defaultdict ): +class ParamsWithSpecs(collections.defaultdict): """ """ - def __init__( self, specs=None, params=None ): + def __init__(self, specs=None, params=None): self.specs = specs or dict() self.params = params or dict() for name, value in self.params.items(): if name not in self.specs: - self._param_unknown_error( name ) - if 'map' in self.specs[ name ]: + self._param_unknown_error(name) + if 'map' in self.specs[name]: try: - self.params[ name ] = self.specs[ name ][ 'map' ]( value ) + self.params[name] = self.specs[name]['map'](value) except Exception: - self._param_map_error( name, value ) - if 'valid' in self.specs[ name ]: - if not self.specs[ name ][ 'valid' ]( value ): - self._param_vaildation_error( name, value ) + self._param_map_error(name, value) + if 'valid' in self.specs[name]: + if not self.specs[name]['valid'](value): + self._param_vaildation_error(name, value) - self.update( self.params ) + self.update(self.params) - def __missing__( self, name ): - return self.specs[ name ][ 'default' ] + def __missing__(self, name): + return self.specs[name]['default'] - def __getattr__( self, name ): - return self[ name ] + def __getattr__(self, name): + return self[name] - def _param_unknown_error( self, name ): + def _param_unknown_error(self, name): raise NotImplementedError() - def _param_map_error( self, name, value ): + def _param_map_error(self, name, value): raise NotImplementedError() - def _param_vaildation_error( self, name, value ): + def _param_vaildation_error(self, name, value): raise NotImplementedError() -def compare_urls( url1, url2, compare_scheme=True, compare_hostname=True, compare_path=True ): - url1 = urlparse.urlparse( url1 ) - url2 = urlparse.urlparse( url2 ) +def compare_urls(url1, url2, compare_scheme=True, compare_hostname=True, compare_path=True): + url1 = urlparse.urlparse(url1) + url2 = urlparse.urlparse(url2) if compare_scheme and url1.scheme and url2.scheme and url1.scheme != url2.scheme: return False if compare_hostname and url1.hostname and url2.hostname and url1.hostname != url2.hostname: @@ -994,7 +994,7 @@ def compare_urls( url1, url2, compare_scheme=True, compare_hostname=True, compar def read_dbnames(filename): """ Read build names from file """ - class DBNames( list ): + class DBNames(list): default_value = "?" default_name = "unspecified (?)" db_names = DBNames() @@ -1004,7 +1004,7 @@ class DBNames( list ): name_to_db_base = {} if filename is None: # Should only be happening with the galaxy.tools.parameters.basic:GenomeBuildParameter docstring unit test - filename = os.path.join( 'tool-data', 'shared', 'ucsc', 'builds.txt.sample' ) + filename = os.path.join('tool-data', 'shared', 'ucsc', 'builds.txt.sample') for line in open(filename): try: if line[0:1] == "#": @@ -1037,20 +1037,20 @@ class DBNames( list ): ucsc_builds[db_base].sort() ucsc_builds[db_base].reverse() ucsc_builds[db_base] = [(build, name) for _, build, name in ucsc_builds[db_base]] - db_names = DBNames( db_names + ucsc_builds[db_base] ) - if len( db_names ) > 1 and len( man_builds ) > 0: - db_names.append( ( db_names.default_value, '----- Additional Species Are Below -----' ) ) + db_names = DBNames(db_names + ucsc_builds[db_base]) + if len(db_names) > 1 and len(man_builds) > 0: + db_names.append((db_names.default_value, '----- Additional Species Are Below -----')) man_builds.sort() man_builds = [(build, name) for name, build in man_builds] - db_names = DBNames( db_names + man_builds ) + db_names = DBNames(db_names + man_builds) except Exception as e: - log.error( "ERROR: Unable to read builds file: %s", e ) + log.error("ERROR: Unable to read builds file: %s", e) if len(db_names) < 1: - db_names = DBNames( [( db_names.default_value, db_names.default_name )] ) + db_names = DBNames([(db_names.default_value, db_names.default_name)]) return db_names -def read_build_sites( filename, check_builds=True ): +def read_build_sites(filename, check_builds=True): """ read db names to ucsc mappings from file, this file should probably be merged with the one above """ build_sites = [] try: @@ -1066,51 +1066,51 @@ def read_build_sites( filename, check_builds=True ): site_dict = {'name': site_name, 'url': site, 'builds': site_builds} else: site_dict = {'name': site_name, 'url': site} - build_sites.append( site_dict ) + build_sites.append(site_dict) except: continue except: - log.error( "ERROR: Unable to read builds for site file %s", filename ) + log.error("ERROR: Unable to read builds for site file %s", filename) return build_sites -def relativize_symlinks( path, start=None, followlinks=False): - for root, dirs, files in os.walk( path, followlinks=followlinks ): +def relativize_symlinks(path, start=None, followlinks=False): + for root, dirs, files in os.walk(path, followlinks=followlinks): rel_start = None for file_name in files: - symlink_file_name = os.path.join( root, file_name ) - if os.path.islink( symlink_file_name ): - symlink_target = os.readlink( symlink_file_name ) + symlink_file_name = os.path.join(root, file_name) + if os.path.islink(symlink_file_name): + symlink_target = os.readlink(symlink_file_name) if rel_start is None: if start is None: rel_start = root else: rel_start = start - rel_path = relpath( symlink_target, rel_start ) - os.remove( symlink_file_name ) - os.symlink( rel_path, symlink_file_name ) + rel_path = relpath(symlink_target, rel_start) + os.remove(symlink_file_name) + os.symlink(rel_path, symlink_file_name) -def stringify_dictionary_keys( in_dict ): +def stringify_dictionary_keys(in_dict): # returns a new dictionary # changes unicode keys into strings, only works on top level (does not recurse) # unicode keys are not valid for expansion into keyword arguments on method calls out_dict = {} for key, value in iteritems(in_dict): - out_dict[ str( key ) ] = value + out_dict[str(key)] = value return out_dict -def recursively_stringify_dictionary_keys( d ): +def recursively_stringify_dictionary_keys(d): if isinstance(d, dict): - return dict([(k.encode( DEFAULT_ENCODING ), recursively_stringify_dictionary_keys(v)) for k, v in iteritems(d)]) + return dict([(k.encode(DEFAULT_ENCODING), recursively_stringify_dictionary_keys(v)) for k, v in iteritems(d)]) elif isinstance(d, list): return [recursively_stringify_dictionary_keys(x) for x in d] else: return d -def mkstemp_ln( src, prefix='mkstemp_ln_' ): +def mkstemp_ln(src, prefix='mkstemp_ln_'): """ From tempfile._mkstemp_inner, generate a hard link in the same dir with a random name. Created so we can persist the underlying file of a @@ -1122,7 +1122,7 @@ def mkstemp_ln( src, prefix='mkstemp_ln_' ): name = next(names) file = os.path.join(dir, prefix + name) try: - os.link( src, file ) + os.link(src, file) return (os.path.abspath(file)) except OSError as e: if e.errno == errno.EEXIST: @@ -1131,41 +1131,41 @@ def mkstemp_ln( src, prefix='mkstemp_ln_' ): raise IOError(errno.EEXIST, "No usable temporary file name found") -def umask_fix_perms( path, umask, unmasked_perms, gid=None ): +def umask_fix_perms(path, umask, unmasked_perms, gid=None): """ umask-friendly permissions fixing """ perms = unmasked_perms & ~umask try: - st = os.stat( path ) + st = os.stat(path) except OSError as e: - log.exception( 'Unable to set permissions or group on %s', path ) + log.exception('Unable to set permissions or group on %s', path) return # fix modes - if stat.S_IMODE( st.st_mode ) != perms: + if stat.S_IMODE(st.st_mode) != perms: try: - os.chmod( path, perms ) + os.chmod(path, perms) except Exception as e: - log.warning( 'Unable to honor umask (%s) for %s, tried to set: %s but mode remains %s, error was: %s' % ( oct( umask ), - path, - oct( perms ), - oct( stat.S_IMODE( st.st_mode ) ), - e ) ) + log.warning('Unable to honor umask (%s) for %s, tried to set: %s but mode remains %s, error was: %s' % (oct(umask), + path, + oct(perms), + oct(stat.S_IMODE(st.st_mode)), + e)) # fix group if gid is not None and st.st_gid != gid: try: - os.chown( path, -1, gid ) + os.chown(path, -1, gid) except Exception as e: try: - desired_group = grp.getgrgid( gid ) - current_group = grp.getgrgid( st.st_gid ) + desired_group = grp.getgrgid(gid) + current_group = grp.getgrgid(st.st_gid) except: desired_group = gid current_group = st.st_gid - log.warning( 'Unable to honor primary group (%s) for %s, group remains %s, error was: %s' % ( desired_group, - path, - current_group, - e ) ) + log.warning('Unable to honor primary group (%s) for %s, group remains %s, error was: %s' % (desired_group, + path, + current_group, + e)) def docstring_trim(docstring): @@ -1208,12 +1208,12 @@ def nice_size(size): >>> nice_size(100000000) '95.4 MB' """ - words = [ 'bytes', 'KB', 'MB', 'GB', 'TB' ] + words = ['bytes', 'KB', 'MB', 'GB', 'TB'] prefix = '' try: - size = float( size ) + size = float(size) if size < 0: - size = abs( size ) + size = abs(size) prefix = '-' except: return '??? bytes' @@ -1222,39 +1222,39 @@ def nice_size(size): if step > size: size = size / float(1024 ** ind) if word == 'bytes': # No decimals for bytes - return "%s%d bytes" % ( prefix, size ) - return "%s%.1f %s" % ( prefix, size, word ) + return "%s%d bytes" % (prefix, size) + return "%s%.1f %s" % (prefix, size, word) return '??? bytes' -def size_to_bytes( size ): +def size_to_bytes(size): """ Returns a number of bytes if given a reasonably formatted string with the size """ # Assume input in bytes if we can convert directly to an int try: - return int( size ) + return int(size) except: pass # Otherwise it must have non-numeric characters - size_re = re.compile( '([\d\.]+)\s*([tgmk]b?|b|bytes?)$' ) - size_match = re.match( size_re, size.lower() ) + size_re = re.compile('([\d\.]+)\s*([tgmk]b?|b|bytes?)$') + size_match = re.match(size_re, size.lower()) assert size_match is not None - size = float( size_match.group(1) ) + size = float(size_match.group(1)) multiple = size_match.group(2) - if multiple.startswith( 't' ): - return int( size * 1024 ** 4 ) - elif multiple.startswith( 'g' ): - return int( size * 1024 ** 3 ) - elif multiple.startswith( 'm' ): - return int( size * 1024 ** 2 ) - elif multiple.startswith( 'k' ): - return int( size * 1024 ) - elif multiple.startswith( 'b' ): - return int( size ) - - -def send_mail( frm, to, subject, body, config, html=None ): + if multiple.startswith('t'): + return int(size * 1024 ** 4) + elif multiple.startswith('g'): + return int(size * 1024 ** 3) + elif multiple.startswith('m'): + return int(size * 1024 ** 2) + elif multiple.startswith('k'): + return int(size * 1024) + elif multiple.startswith('b'): + return int(size) + + +def send_mail(frm, to, subject, body, config, html=None): """ Sends an email. @@ -1278,86 +1278,86 @@ def send_mail( frm, to, subject, body, config, html=None ): provided will convert the message to a MIMEMultipart. (Default 'None') """ - to = listify( to ) + to = listify(to) if html: msg = email_mime_multipart.MIMEMultipart('alternative') else: - msg = email_mime_text.MIMEText( body.encode( 'ascii', 'replace' ) ) + msg = email_mime_text.MIMEText(body.encode('ascii', 'replace')) - msg[ 'To' ] = ', '.join( to ) - msg[ 'From' ] = frm - msg[ 'Subject' ] = subject + msg['To'] = ', '.join(to) + msg['From'] = frm + msg['Subject'] = subject if config.smtp_server is None: - log.error( "Mail is not configured for this Galaxy instance." ) - log.info( msg ) + log.error("Mail is not configured for this Galaxy instance.") + log.info(msg) return if html: - mp_text = email_mime_text.MIMEText( body.encode( 'ascii', 'replace' ), 'plain' ) - mp_html = email_mime_text.MIMEText( html.encode( 'ascii', 'replace' ), 'html' ) + mp_text = email_mime_text.MIMEText(body.encode('ascii', 'replace'), 'plain') + mp_html = email_mime_text.MIMEText(html.encode('ascii', 'replace'), 'html') msg.attach(mp_text) msg.attach(mp_html) - smtp_ssl = asbool( getattr(config, 'smtp_ssl', False ) ) + smtp_ssl = asbool(getattr(config, 'smtp_ssl', False)) if smtp_ssl: s = smtplib.SMTP_SSL() else: s = smtplib.SMTP() - s.connect( config.smtp_server ) + s.connect(config.smtp_server) if not smtp_ssl: try: s.starttls() - log.debug( 'Initiated SSL/TLS connection to SMTP server: %s' % config.smtp_server ) + log.debug('Initiated SSL/TLS connection to SMTP server: %s' % config.smtp_server) except RuntimeError as e: - log.warning( 'SSL/TLS support is not available to your Python interpreter: %s' % e ) + log.warning('SSL/TLS support is not available to your Python interpreter: %s' % e) except smtplib.SMTPHeloError as e: - log.error( "The server didn't reply properly to the HELO greeting: %s" % e ) + log.error("The server didn't reply properly to the HELO greeting: %s" % e) s.close() raise except smtplib.SMTPException as e: - log.warning( 'The server does not support the STARTTLS extension: %s' % e ) + log.warning('The server does not support the STARTTLS extension: %s' % e) if config.smtp_username and config.smtp_password: try: - s.login( config.smtp_username, config.smtp_password ) + s.login(config.smtp_username, config.smtp_password) except smtplib.SMTPHeloError as e: - log.error( "The server didn't reply properly to the HELO greeting: %s" % e ) + log.error("The server didn't reply properly to the HELO greeting: %s" % e) s.close() raise except smtplib.SMTPAuthenticationError as e: - log.error( "The server didn't accept the username/password combination: %s" % e ) + log.error("The server didn't accept the username/password combination: %s" % e) s.close() raise except smtplib.SMTPException as e: - log.error( "No suitable authentication method was found: %s" % e ) + log.error("No suitable authentication method was found: %s" % e) s.close() raise - s.sendmail( frm, to, msg.as_string() ) + s.sendmail(frm, to, msg.as_string()) s.quit() -def force_symlink( source, link_name ): +def force_symlink(source, link_name): try: - os.symlink( source, link_name ) + os.symlink(source, link_name) except OSError as e: if e.errno == errno.EEXIST: - os.remove( link_name ) - os.symlink( source, link_name ) + os.remove(link_name) + os.symlink(source, link_name) else: raise e -def move_merge( source, target ): +def move_merge(source, target): # when using shutil and moving a directory, if the target exists, # then the directory is placed inside of it # if the target doesn't exist, then the target is made into the directory # this makes it so that the target is always the target, and if it exists, # the source contents are moved into the target - if os.path.isdir( source ) and os.path.exists( target ) and os.path.isdir( target ): - for name in os.listdir( source ): - move_merge( os.path.join( source, name ), os.path.join( target, name ) ) + if os.path.isdir(source) and os.path.exists(target) and os.path.isdir(target): + for name in os.listdir(source): + move_merge(os.path.join(source, name), os.path.join(target, name)) else: - return shutil.move( source, target ) + return shutil.move(source, target) def safe_str_cmp(a, b): @@ -1378,7 +1378,7 @@ def galaxy_directory(): return os.path.abspath(galaxy_root_path) -def config_directories_from_setting( directories_setting, galaxy_root=galaxy_root_path ): +def config_directories_from_setting(directories_setting, galaxy_root=galaxy_root_path): """ Parse the ``directories_setting`` into a list of relative or absolute filesystem paths that will be searched to discover plugins. @@ -1396,14 +1396,14 @@ def config_directories_from_setting( directories_setting, galaxy_root=galaxy_roo if not directories_setting: return directories - for directory in listify( directories_setting ): + for directory in listify(directories_setting): directory = directory.strip() - if not directory.startswith( '/' ): - directory = os.path.join( galaxy_root, directory ) - if not os.path.exists( directory ): - log.warning( 'directory not found: %s', directory ) + if not directory.startswith('/'): + directory = os.path.join(galaxy_root, directory) + if not os.path.exists(directory): + log.warning('directory not found: %s', directory) continue - directories.append( directory ) + directories.append(directory) return directories @@ -1425,7 +1425,7 @@ def parse_int(value, min_val=None, max_val=None, default=None, allow_none=False) raise -def parse_non_hex_float( s ): +def parse_non_hex_float(s): """ Parse string `s` into a float but throw a `ValueError` if the string is in the otherwise acceptable format `\d+e\d+` (e.g. 40000000000000e5.) @@ -1444,48 +1444,48 @@ def parse_non_hex_float( s ): ... ValueError: could not convert string to float: 40000000000000e5 """ - f = float( s ) + f = float(s) # successfully parsed as float if here - check for format in original string - if 'e' in s and not ( '+' in s or '-' in s ): - raise ValueError( 'could not convert string to float: ' + s ) + if 'e' in s and not ('+' in s or '-' in s): + raise ValueError('could not convert string to float: ' + s) return f -def build_url( base_url, port=80, scheme='http', pathspec=None, params=None, doseq=False ): +def build_url(base_url, port=80, scheme='http', pathspec=None, params=None, doseq=False): if params is None: params = dict() if pathspec is None: pathspec = [] - parsed_url = urlparse.urlparse( base_url ) + parsed_url = urlparse.urlparse(base_url) if scheme != 'http': parsed_url.scheme = scheme if port != 80: - url = '%s://%s:%d/%s' % ( parsed_url.scheme, parsed_url.netloc.rstrip( '/' ), int( port ), parsed_url.path ) + url = '%s://%s:%d/%s' % (parsed_url.scheme, parsed_url.netloc.rstrip('/'), int(port), parsed_url.path) else: - url = '%s://%s/%s' % ( parsed_url.scheme, parsed_url.netloc.rstrip( '/' ), parsed_url.path.lstrip( '/' ) ) - if len( pathspec ) > 0: - url = '%s/%s' % ( url.rstrip( '/' ), '/'.join( pathspec ) ) + url = '%s://%s/%s' % (parsed_url.scheme, parsed_url.netloc.rstrip('/'), parsed_url.path.lstrip('/')) + if len(pathspec) > 0: + url = '%s/%s' % (url.rstrip('/'), '/'.join(pathspec)) if parsed_url.query: - for query_parameter in parsed_url.query.split( '&' ): - key, value = query_parameter.split( '=' ) - params[ key ] = value + for query_parameter in parsed_url.query.split('&'): + key, value = query_parameter.split('=') + params[key] = value if params: - url += '?%s' % urlparse.urlencode( params, doseq=doseq ) + url += '?%s' % urlparse.urlencode(params, doseq=doseq) return url -def url_get( base_url, password_mgr=None, pathspec=None, params=None ): +def url_get(base_url, password_mgr=None, pathspec=None, params=None): """Make contact with the uri provided and return any contents.""" # Uses system proxy settings if they exist. proxy = urlrequest.ProxyHandler() if password_mgr is not None: - auth = urlrequest.HTTPDigestAuthHandler( password_mgr ) - urlopener = urlrequest.build_opener( proxy, auth ) + auth = urlrequest.HTTPDigestAuthHandler(password_mgr) + urlopener = urlrequest.build_opener(proxy, auth) else: - urlopener = urlrequest.build_opener( proxy ) - urlrequest.install_opener( urlopener ) - full_url = build_url( base_url, pathspec=pathspec, params=params ) - response = urlopener.open( full_url ) + urlopener = urlrequest.build_opener(proxy) + urlrequest.install_opener(urlopener) + full_url = build_url(base_url, pathspec=pathspec, params=params) + response = urlopener.open(full_url) content = response.read() response.close() return content diff --git a/lib/galaxy/util/aliaspickler.py b/lib/galaxy/util/aliaspickler.py index 0cbac922e82f..20f4afd82a13 100644 --- a/lib/galaxy/util/aliaspickler.py +++ b/lib/galaxy/util/aliaspickler.py @@ -3,29 +3,29 @@ from six.moves import cStringIO as StringIO -class AliasUnpickler( pickle.Unpickler ): - def __init__( self, aliases, *args, **kw): - pickle.Unpickler.__init__( self, *args, **kw ) +class AliasUnpickler(pickle.Unpickler): + def __init__(self, aliases, *args, **kw): + pickle.Unpickler.__init__(self, *args, **kw) self.aliases = aliases - def find_class( self, module, name ): + def find_class(self, module, name): module, name = self.aliases.get((module, name), (module, name)) - return pickle.Unpickler.find_class( self, module, name ) + return pickle.Unpickler.find_class(self, module, name) -class AliasPickleModule( object ): - def __init__( self, aliases ): +class AliasPickleModule(object): + def __init__(self, aliases): self.aliases = aliases - def dump( self, obj, fileobj, protocol=0): - return pickle.dump( obj, fileobj, protocol ) + def dump(self, obj, fileobj, protocol=0): + return pickle.dump(obj, fileobj, protocol) - def dumps( self, obj, protocol=0 ): - return pickle.dumps( obj, protocol ) + def dumps(self, obj, protocol=0): + return pickle.dumps(obj, protocol) - def load( self, fileobj ): - return AliasUnpickler( self.aliases, fileobj ).load() + def load(self, fileobj): + return AliasUnpickler(self.aliases, fileobj).load() - def loads( self, string ): - fileobj = StringIO( string ) - return AliasUnpickler( self.aliases, fileobj ).load() + def loads(self, string): + fileobj = StringIO(string) + return AliasUnpickler(self.aliases, fileobj).load() diff --git a/lib/galaxy/util/biostar.py b/lib/galaxy/util/biostar.py index 278e9491cf45..c140aa6e6bc9 100644 --- a/lib/galaxy/util/biostar.py +++ b/lib/galaxy/util/biostar.py @@ -16,7 +16,7 @@ from . import smart_str -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) _punct_re = re.compile(r'[\t !"#$%&\'()*\-/<=>?@\[\\\]^_`{|},.]+') @@ -30,24 +30,24 @@ } BIOSTAR_ACTIONS = { - None: { 'url': lambda x: '', 'uses_payload': False }, - 'new_post': { 'url': lambda x: 'p/new/external/post/', 'uses_payload': True, 'hmac_values': {'content': 'digest'} }, - 'show_tags': { 'url': lambda x: 't/%s/' % ( "+".join( ( x.get( 'tag_val' ) or DEFAULT_GALAXY_TAG ).split( ',' ) ) ), 'uses_payload': False }, - 'log_out': { 'url': lambda x: 'site/logout/', 'uses_payload': False } + None: {'url': lambda x: '', 'uses_payload': False}, + 'new_post': {'url': lambda x: 'p/new/external/post/', 'uses_payload': True, 'hmac_values': {'content': 'digest'}}, + 'show_tags': {'url': lambda x: 't/%s/' % ("+".join((x.get('tag_val') or DEFAULT_GALAXY_TAG).split(','))), 'uses_payload': False}, + 'log_out': {'url': lambda x: 'site/logout/', 'uses_payload': False} } DEFAULT_BIOSTAR_COOKIE_AGE = 1 -def biostar_enabled( app ): - return bool( app.config.biostar_url ) +def biostar_enabled(app): + return bool(app.config.biostar_url) # Slugifying from Armin Ronacher (http://flask.pocoo.org/snippets/5/) def slugify(text, delim=u'-'): """Generates an slightly worse ASCII-only slug.""" - if not isinstance( text, text_type ): - text = text_type( text ) + if not isinstance(text, text_type): + text = text_type(text) result = [] for word in _punct_re.split(text.lower()): word = normalize('NFKD', word).encode('ascii', 'ignore') @@ -56,126 +56,139 @@ def slugify(text, delim=u'-'): return text_type(delim.join(result)) -def get_biostar_url( app, payload=None, biostar_action=None ): +def get_biostar_url(app, payload=None, biostar_action=None): # Ensure biostar integration is enabled - if not biostar_enabled( app ): - raise Exception( "Biostar integration is not enabled" ) + if not biostar_enabled(app): + raise Exception("Biostar integration is not enabled") if biostar_action not in BIOSTAR_ACTIONS: - raise Exception( "Invalid action specified (%s)." % ( biostar_action ) ) - biostar_action = BIOSTAR_ACTIONS[ biostar_action ] + raise Exception("Invalid action specified (%s)." % (biostar_action)) + biostar_action = BIOSTAR_ACTIONS[biostar_action] # Start building up the payload payload = payload or {} - payload = dict( DEFAULT_PAYLOAD, **payload ) - payload[ 'name' ] = app.config.biostar_key_name - for hmac_value_name, hmac_parameter_name in biostar_action.get( 'hmac_values', {} ).items(): + payload = dict(DEFAULT_PAYLOAD, **payload) + payload['name'] = app.config.biostar_key_name + for hmac_value_name, hmac_parameter_name in biostar_action.get('hmac_values', {}).items(): # Biostar requires ascii only on HMAC'd things - payload[ hmac_value_name ] = smart_str( payload.get( hmac_value_name, '' ), encoding='ascii', errors='replace' ) - payload[ hmac_parameter_name ] = hmac.new( app.config.biostar_key, payload[ hmac_value_name ] ).hexdigest() + payload[hmac_value_name] = smart_str(payload.get(hmac_value_name, ''), encoding='ascii', errors='replace') + payload[hmac_parameter_name] = hmac.new(app.config.biostar_key, payload[hmac_value_name]).hexdigest() # generate url, can parse payload info - url = str( urlparse.urljoin( app.config.biostar_url, biostar_action.get( 'url' )( payload ) ) ) - if not biostar_action.get( 'uses_payload' ): + url = str(urlparse.urljoin(app.config.biostar_url, biostar_action.get('url')(payload))) + if not biostar_action.get('uses_payload'): payload = {} - url = url_for( url ) + url = url_for(url) return url, payload -def tag_for_tool( tool ): +def tag_for_tool(tool): """ Generate a reasonable biostar tag for a tool. """ # Biostar can now handle tags with spaces, do we want to generate tags differently now? - return slugify( tool.name, delim='-' ) + return slugify(tool.name, delim='-') -def populate_tag_payload( payload=None, tool=None ): +def populate_tag_payload(payload=None, tool=None): if payload is None: payload = {} if DEFAULT_GALAXY_TAG: - tag_val = [ DEFAULT_GALAXY_TAG ] + tag_val = [DEFAULT_GALAXY_TAG] else: tag_val = [] if tool: - tag_val.append( tag_for_tool( tool ) ) - payload[ 'tag_val' ] = ','.join( tag_val ) + tag_val.append(tag_for_tool(tool)) + payload['tag_val'] = ','.join(tag_val) return payload -def populate_tool_payload( payload=None, tool=None ): - payload = populate_tag_payload( payload=payload, tool=tool ) - payload[ 'title' ] = 'Need help with "%s" tool' % ( tool.name ) +def populate_tool_payload(payload=None, tool=None): + payload = populate_tag_payload(payload=payload, tool=tool) + payload['title'] = 'Need help with "%s" tool' % (tool.name) tool_url = None if tool.tool_shed_repository: - tool_url = tool.tool_shed_repository.get_sharable_url( tool.app ) + tool_url = tool.tool_shed_repository.get_sharable_url(tool.app) if tool_url: - tool_url = '
    ToolShed URL: %s' % ( tool_url, tool_url ) + tool_url = '
    ToolShed URL: %s' % (tool_url, tool_url) if not tool_url: tool_url = '' - payload[ 'content' ] = '

    Tool name: %s
    Tool version: %s
    Tool ID: %s%s


    ' % ( tool.name, tool.version, tool.id, tool_url ) + payload['content'] = '

    Tool name: %s
    Tool version: %s
    Tool ID: %s%s


    ' % (tool.name, tool.version, tool.id, tool_url) return payload -def determine_cookie_domain( galaxy_hostname, biostar_hostname ): +def determine_cookie_domain(galaxy_hostname, biostar_hostname): if galaxy_hostname == biostar_hostname: return galaxy_hostname - sub_biostar_hostname = biostar_hostname.split( '.', 1 )[-1] + sub_biostar_hostname = biostar_hostname.split('.', 1)[-1] if sub_biostar_hostname == galaxy_hostname: return galaxy_hostname - sub_galaxy_hostname = galaxy_hostname.split( '.', 1 )[-1] + sub_galaxy_hostname = galaxy_hostname.split('.', 1)[-1] if sub_biostar_hostname == sub_galaxy_hostname: return sub_galaxy_hostname return galaxy_hostname -def create_cookie( trans, key_name, key, email, age=DEFAULT_BIOSTAR_COOKIE_AGE, override_never_authenticate=False ): +def create_cookie(trans, key_name, key, email, age=DEFAULT_BIOSTAR_COOKIE_AGE, override_never_authenticate=False): if trans.app.config.biostar_never_authenticate and not override_never_authenticate: - log.debug( 'A BioStar link was clicked, but never authenticate has been enabled, so we will not create the login cookie.' ) + log.debug('A BioStar link was clicked, but never authenticate has been enabled, so we will not create the login cookie.') return - digest = hmac.new( key, email ).hexdigest() + digest = hmac.new(key, email).hexdigest() value = "%s:%s" % (email, digest) - trans.set_cookie( value, name=key_name, path='/', age=age, version='1' ) + trans.set_cookie(value, name=key_name, path='/', age=age, version='1') # We need to explicitly set the domain here, in order to allow for biostar in a subdomain to work - galaxy_hostname = urlparse.urlsplit( url_for( '/', qualified=True ) ).hostname - biostar_hostname = urlparse.urlsplit( trans.app.config.biostar_url ).hostname - trans.response.cookies[ key_name ][ 'domain' ] = determine_cookie_domain( galaxy_hostname, biostar_hostname ) + galaxy_hostname = urlparse.urlsplit(url_for('/', qualified=True)).hostname + biostar_hostname = urlparse.urlsplit(trans.app.config.biostar_url).hostname + trans.response.cookies[key_name]['domain'] = determine_cookie_domain(galaxy_hostname, biostar_hostname) -def delete_cookie( trans, key_name ): +def delete_cookie(trans, key_name): # Set expiration of Cookie to time in past, to cause browser to delete if key_name in trans.request.cookies: - create_cookie( trans, trans.app.config.biostar_key_name, '', '', age=-90, override_never_authenticate=True ) + create_cookie(trans, trans.app.config.biostar_key_name, '', '', age=-90, override_never_authenticate=True) -def biostar_logged_in( trans ): - if biostar_enabled( trans.app ): +def biostar_logged_in(trans): + if biostar_enabled(trans.app): if trans.app.config.biostar_key_name in trans.request.cookies: return True return False -def biostar_logout( trans ): - if biostar_enabled( trans.app ): - delete_cookie( trans, trans.app.config.biostar_key_name ) - return get_biostar_url( trans.app, biostar_action='log_out' )[0] +def biostar_logout(trans): + if biostar_enabled(trans.app): + delete_cookie(trans, trans.app.config.biostar_key_name) + return get_biostar_url(trans.app, biostar_action='log_out')[0] return None -class BiostarErrorReporter( ErrorReporter ): - def _send_report( self, user, email=None, message=None, **kwd ): - assert biostar_enabled( self.app ), ValueError( "Biostar is not configured for this galaxy instance" ) - assert self.app.config.biostar_enable_bug_reports, ValueError( "Biostar is not configured to allow bug reporting for this galaxy instance" ) - assert self._can_access_dataset( user ), Exception( "You are not allowed to access this dataset." ) +class BiostarErrorReporter(ErrorReporter): + def _send_report(self, user, email=None, message=None, **kwd): + assert biostar_enabled(self.app), ValueError("Biostar is not configured for this galaxy instance") + assert self.app.config.biostar_enable_bug_reports, ValueError("Biostar is not configured to allow bug reporting for this galaxy instance") + assert self._can_access_dataset(user), Exception("You are not allowed to access this dataset.") tool_version_select_field, tools, tool = \ - self.app.toolbox.get_tool_components( self.tool_id, tool_version=None, get_loaded_tools_by_lineage=False, set_selected=True ) - payload = { 'title': 'Bug report on "%s" tool' % ( tool.name ), 'content': self.report.replace( '\n', '
    ' ).replace( '\r', '' ), 'tag_val': slugify( 'bug report' ) } + self.app.toolbox.get_tool_components(self.tool_id, tool_version=None, get_loaded_tools_by_lineage=False, set_selected=True) + + # Strip out unwanted HTML characters + html_remove = ['', '', '', '', '\n', '\r'] + report = self.html_report + for tag in html_remove: + report = report.replace(tag, '') + # Must lstrip spaces or it isn't recognised as HTML + report = report.lstrip() + + payload = { + 'title': 'Bug report on "%s" tool' % (tool.name), + 'content': report, + 'tag_val': slugify('bug report') + } # Get footer for email from here - payload2 = populate_tool_payload( tool=tool ) + payload2 = populate_tool_payload(tool=tool) if 'content' in payload2: - payload[ 'content' ] = "%s
    %s" % ( payload['content'], payload2['content'] ) + payload['content'] = "%s
    %s" % (payload['content'], payload2['content']) if 'tag_val' in payload2: - payload[ 'tag_val' ] = ','.join( [ payload2[ 'tag_val' ], payload[ 'tag_val' ] ] ) + payload['tag_val'] = ','.join([payload2['tag_val'], payload['tag_val']]) if 'action' not in payload: - payload[ 'action' ] = 1 # Automatically post bug reports to biostar + payload['action'] = 1 # Automatically post bug reports to biostar return payload diff --git a/lib/galaxy/util/bunch.py b/lib/galaxy/util/bunch.py index 2b1e49e0635c..d353686a2ce5 100644 --- a/lib/galaxy/util/bunch.py +++ b/lib/galaxy/util/bunch.py @@ -1,10 +1,11 @@ -class Bunch( object ): +class Bunch(object): """ http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52308 Often we want to just collect a bunch of stuff together, naming each item of the bunch; a dictionary's OK for that, but a small do-nothing class is even handier, and prettier to use. """ + def __init__(self, **kwds): self.__dict__.update(kwds) diff --git a/lib/galaxy/util/checkers.py b/lib/galaxy/util/checkers.py index 85110e67e130..ca22ad97220e 100644 --- a/lib/galaxy/util/checkers.py +++ b/lib/galaxy/util/checkers.py @@ -11,22 +11,24 @@ HTML_CHECK_LINES = 100 -def check_html( file_path, chunk=None ): +def check_html(file_path, chunk=None): if chunk is None: - temp = open( file_path, "U" ) + temp = open(file_path, "U") + elif hasattr(chunk, "splitlines"): + temp = chunk.splitlines() else: temp = chunk - regexp1 = re.compile( "]*HREF[^>]+>", re.I ) - regexp2 = re.compile( "]*>", re.I ) - regexp3 = re.compile( "]*>", re.I ) - regexp4 = re.compile( "]*>", re.I ) - regexp5 = re.compile( "]*>", re.I ) + regexp1 = re.compile("]*HREF[^>]+>", re.I) + regexp2 = re.compile("]*>", re.I) + regexp3 = re.compile("]*>", re.I) + regexp4 = re.compile("]*>", re.I) + regexp5 = re.compile("]*>", re.I) lineno = 0 # TODO: Potentially reading huge lines into string here, this should be # reworked. for line in temp: lineno += 1 - matches = regexp1.search( line ) or regexp2.search( line ) or regexp3.search( line ) or regexp4.search( line ) or regexp5.search( line ) + matches = regexp1.search(line) or regexp2.search(line) or regexp3.search(line) or regexp4.search(line) or regexp5.search(line) if matches: if chunk is None: temp.close() @@ -38,91 +40,99 @@ def check_html( file_path, chunk=None ): return False -def check_binary( name, file_path=True ): +def check_binary(name, file_path=True): # Handles files if file_path is True or text if file_path is False is_binary = False if file_path: - temp = open( name, "U" ) + temp = open(name, "U") else: - temp = StringIO( name ) + temp = StringIO(name) try: - for char in temp.read( 100 ): - if util.is_binary( char ): + for char in temp.read(100): + if util.is_binary(char): is_binary = True break finally: - temp.close( ) + temp.close() return is_binary -def check_gzip( file_path ): +def check_gzip(file_path, check_content=True): # This method returns a tuple of booleans representing ( is_gzipped, is_valid ) # Make sure we have a gzipped file try: - temp = open( file_path, "U" ) - magic_check = temp.read( 2 ) + temp = open(file_path, "U") + magic_check = temp.read(2) temp.close() if magic_check != util.gzip_magic: - return ( False, False ) + return (False, False) except: - return ( False, False ) + return (False, False) # We support some binary data types, so check if the compressed binary file is valid # If the file is Bam, it should already have been detected as such, so we'll just check # for sff format. try: - header = gzip.open( file_path ).read(4) + header = gzip.open(file_path).read(4) if header == b'.sff': - return ( True, True ) + return (True, True) except: - return( False, False ) + return(False, False) + + if not check_content: + return (True, True) + CHUNK_SIZE = 2 ** 15 # 32Kb - gzipped_file = gzip.GzipFile( file_path, mode='rb' ) - chunk = gzipped_file.read( CHUNK_SIZE ) + gzipped_file = gzip.GzipFile(file_path, mode='rb') + chunk = gzipped_file.read(CHUNK_SIZE) gzipped_file.close() # See if we have a compressed HTML file - if check_html( file_path, chunk=chunk ): - return ( True, False ) - return ( True, True ) + if check_html(file_path, chunk=chunk): + return (True, False) + return (True, True) -def check_bz2( file_path ): +def check_bz2(file_path, check_content=True): try: - temp = open( file_path, "U" ) - magic_check = temp.read( 3 ) + temp = open(file_path, "U") + magic_check = temp.read(3) temp.close() if magic_check != util.bz2_magic: - return ( False, False ) + return (False, False) except: - return( False, False ) + return(False, False) + + if not check_content: + return (True, True) + CHUNK_SIZE = 2 ** 15 # reKb - bzipped_file = bz2.BZ2File( file_path, mode='rb' ) - chunk = bzipped_file.read( CHUNK_SIZE ) + bzipped_file = bz2.BZ2File(file_path, mode='rb') + chunk = bzipped_file.read(CHUNK_SIZE) bzipped_file.close() # See if we have a compressed HTML file - if check_html( file_path, chunk=chunk ): - return ( True, False ) - return ( True, True ) + if check_html(file_path, chunk=chunk): + return (True, False) + return (True, True) -def check_zip( file_path ): - if zipfile.is_zipfile( file_path ): +def check_zip(file_path): + if zipfile.is_zipfile(file_path): return True return False -def is_bz2( file_path ): - is_bz2, is_valid = check_bz2( file_path ) +def is_bz2(file_path): + is_bz2, is_valid = check_bz2(file_path, check_content=False) return is_bz2 -def is_gzip( file_path ): - is_gzipped, is_valid = check_gzip( file_path ) +def is_gzip(file_path): + is_gzipped, is_valid = check_gzip(file_path, check_content=False) return is_gzipped -def check_image( file_path ): +def check_image(file_path): """ Simple wrapper around image_type to yield a True/False verdict """ - if image_type( file_path ): + if image_type(file_path): return True return False diff --git a/lib/galaxy/util/dbkeys.py b/lib/galaxy/util/dbkeys.py index 321bbe152235..87fc5b0e848b 100644 --- a/lib/galaxy/util/dbkeys.py +++ b/lib/galaxy/util/dbkeys.py @@ -11,21 +11,21 @@ from galaxy.util.object_wrapper import sanitize_lists_to_string -class GenomeBuilds( object ): +class GenomeBuilds(object): default_value = "?" default_name = "unspecified (?)" - def __init__( self, app, data_table_name="__dbkeys__", load_old_style=True ): + def __init__(self, app, data_table_name="__dbkeys__", load_old_style=True): self._app = app self._data_table_name = data_table_name self._static_chrom_info_path = app.config.len_file_path # A dbkey can be listed multiple times, but with different names, so we can't use dictionaries for lookups if load_old_style: - self._static_dbkeys = list( read_dbnames( app.config.builds_file_path ) ) + self._static_dbkeys = list(read_dbnames(app.config.builds_file_path)) else: self._static_dbkeys = [] - def get_genome_build_names( self, trans=None ): + def get_genome_build_names(self, trans=None): # FIXME: how to deal with key duplicates? rval = [] # load user custom genome builds @@ -36,37 +36,37 @@ def get_genome_build_names( self, trans=None ): # It does allow one-off, history specific dbkeys to be created by a user. But we are not filtering, # so a len file will be listed twice (as the build name and again as dataset name), # if custom dbkey creation/conversion occurred within the current history. - datasets = trans.sa_session.query( self._app.model.HistoryDatasetAssociation ) \ - .filter_by( deleted=False, history_id=trans.history.id, extension="len" ) + datasets = trans.sa_session.query(self._app.model.HistoryDatasetAssociation) \ + .filter_by(deleted=False, history_id=trans.history.id, extension="len") for dataset in datasets: - rval.append( ( dataset.dbkey, "%s (%s) [History]" % ( dataset.name, dataset.dbkey ) ) ) + rval.append((dataset.dbkey, "%s (%s) [History]" % (dataset.name, dataset.dbkey))) user = trans.user - if user and hasattr( user, 'preferences' ) and 'dbkeys' in user.preferences: - user_keys = loads( user.preferences['dbkeys'] ) + if user and hasattr(user, 'preferences') and 'dbkeys' in user.preferences: + user_keys = loads(user.preferences['dbkeys']) for key, chrom_dict in user_keys.items(): - rval.append( ( key, "%s (%s) [Custom]" % ( chrom_dict['name'], key ) ) ) + rval.append((key, "%s (%s) [Custom]" % (chrom_dict['name'], key))) # Load old builds.txt static keys - rval.extend( self._static_dbkeys ) + rval.extend(self._static_dbkeys) # load dbkeys from dbkey data table - dbkey_table = self._app.tool_data_tables.get( self._data_table_name, None ) + dbkey_table = self._app.tool_data_tables.get(self._data_table_name, None) if dbkey_table is not None: for field_dict in dbkey_table.get_named_fields_list(): - rval.append( ( field_dict[ 'value' ], field_dict[ 'name' ] ) ) + rval.append((field_dict['value'], field_dict['name'])) return rval - def get_chrom_info( self, dbkey, trans=None, custom_build_hack_get_len_from_fasta_conversion=True ): + def get_chrom_info(self, dbkey, trans=None, custom_build_hack_get_len_from_fasta_conversion=True): # FIXME: flag to turn off custom_build_hack_get_len_from_fasta_conversion should not be required chrom_info = None db_dataset = None # Collect chromInfo from custom builds if trans: - db_dataset = trans.db_dataset_for( dbkey ) + db_dataset = trans.db_dataset_for(dbkey) if db_dataset: chrom_info = db_dataset.file_name else: # Do Custom Build handling - if trans.user and ( 'dbkeys' in trans.user.preferences ) and ( dbkey in loads( trans.user.preferences[ 'dbkeys' ] ) ): - custom_build_dict = loads( trans.user.preferences[ 'dbkeys' ] )[ dbkey ] + if trans.user and ('dbkeys' in trans.user.preferences) and (dbkey in loads(trans.user.preferences['dbkeys'])): + custom_build_dict = loads(trans.user.preferences['dbkeys'])[dbkey] # HACK: the attempt to get chrom_info below will trigger the # fasta-to-len converter if the dataset is not available or, # which will in turn create a recursive loop when @@ -75,20 +75,20 @@ def get_chrom_info( self, dbkey, trans=None, custom_build_hack_get_len_from_fast # fasta-to-len converter. if 'fasta' in custom_build_dict and custom_build_hack_get_len_from_fasta_conversion: # Build is defined by fasta; get len file, which is obtained from converting fasta. - build_fasta_dataset = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( custom_build_dict[ 'fasta' ] ) - chrom_info = build_fasta_dataset.get_converted_dataset( trans, 'len' ).file_name + build_fasta_dataset = trans.sa_session.query(trans.app.model.HistoryDatasetAssociation).get(custom_build_dict['fasta']) + chrom_info = build_fasta_dataset.get_converted_dataset(trans, 'len').file_name elif 'len' in custom_build_dict: # Build is defined by len file, so use it. - chrom_info = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( custom_build_dict[ 'len' ] ).file_name + chrom_info = trans.sa_session.query(trans.app.model.HistoryDatasetAssociation).get(custom_build_dict['len']).file_name # Check Data table if not chrom_info: - dbkey_table = self._app.tool_data_tables.get( self._data_table_name, None ) + dbkey_table = self._app.tool_data_tables.get(self._data_table_name, None) if dbkey_table is not None: - chrom_info = dbkey_table.get_entry( 'value', dbkey, 'len_path', default=None ) + chrom_info = dbkey_table.get_entry('value', dbkey, 'len_path', default=None) # use configured server len path if not chrom_info: # Default to built-in build. # Since we are using an unverified dbkey, we will sanitize the dbkey before use - chrom_info = os.path.join( self._static_chrom_info_path, "%s.len" % sanitize_lists_to_string( dbkey ) ) - chrom_info = os.path.abspath( chrom_info ) - return ( chrom_info, db_dataset ) + chrom_info = os.path.join(self._static_chrom_info_path, "%s.len" % sanitize_lists_to_string(dbkey)) + chrom_info = os.path.abspath(chrom_info) + return (chrom_info, db_dataset) diff --git a/lib/galaxy/util/dictifiable.py b/lib/galaxy/util/dictifiable.py index 29051e887f0d..0046466474b7 100644 --- a/lib/galaxy/util/dictifiable.py +++ b/lib/galaxy/util/dictifiable.py @@ -7,7 +7,7 @@ class Dictifiable: when for sharing objects across boundaries, such as the API, tool scripts, and JavaScript code. """ - def to_dict( self, view='collection', value_mapper=None ): + def to_dict(self, view='collection', value_mapper=None): """ Return item dictionary. """ @@ -15,17 +15,17 @@ def to_dict( self, view='collection', value_mapper=None ): if not value_mapper: value_mapper = {} - def get_value( key, item ): + def get_value(key, item): """ Recursive helper function to get item values. """ # FIXME: why use exception here? Why not look for key in value_mapper # first and then default to to_dict? try: - return item.to_dict( view=view, value_mapper=value_mapper ) + return item.to_dict(view=view, value_mapper=value_mapper) except: if key in value_mapper: - return value_mapper.get( key )( item ) + return value_mapper.get(key)(item) if type(item) == datetime.datetime: return item.isoformat() elif type(item) == uuid.UUID: @@ -44,19 +44,19 @@ def get_value( key, item ): # Fill item dict with visible keys. try: - visible_keys = self.__getattribute__( 'dict_' + view + '_visible_keys' ) + visible_keys = self.__getattribute__('dict_' + view + '_visible_keys') except AttributeError: - raise Exception( 'Unknown Dictifiable view: %s' % view ) + raise Exception('Unknown Dictifiable view: %s' % view) for key in visible_keys: try: - item = self.__getattribute__( key ) - if isinstance( item, list ): - rval[ key ] = [] + item = self.__getattribute__(key) + if isinstance(item, list): + rval[key] = [] for i in item: - rval[ key ].append( get_value( key, i ) ) + rval[key].append(get_value(key, i)) else: - rval[ key ] = get_value( key, item ) + rval[key] = get_value(key, item) except AttributeError: - rval[ key ] = None + rval[key] = None return rval diff --git a/lib/galaxy/util/expressions.py b/lib/galaxy/util/expressions.py index 2b106a87fd5f..4ccba3a2ceaf 100644 --- a/lib/galaxy/util/expressions.py +++ b/lib/galaxy/util/expressions.py @@ -9,8 +9,8 @@ from itertools import chain -class ExpressionContext( MutableMapping ): - def __init__( self, dict, parent=None ): +class ExpressionContext(MutableMapping): + def __init__(self, dict, parent=None): """ Create a new expression context that looks for values in the container object 'dict', and falls back to 'parent' @@ -30,27 +30,27 @@ def __iter__(self): def __len__(self): return len(self.dict) + len(self.parent or []) - def __getitem__( self, key ): + def __getitem__(self, key): if key in self.dict: return self.dict[key] if self.parent is not None and key in self.parent: return self.parent[key] - raise KeyError( key ) + raise KeyError(key) - def __setitem__( self, key, value ): + def __setitem__(self, key, value): self.dict[key] = value - def __contains__( self, key ): + def __contains__(self, key): if key in self.dict: return True if self.parent is not None and key in self.parent: return True return False - def __str__( self ): - return str( self.dict ) + def __str__(self): + return str(self.dict) - def __nonzero__( self ): + def __nonzero__(self): if not self.dict and not self.parent: return False return True diff --git a/lib/galaxy/util/handlers.py b/lib/galaxy/util/handlers.py index dc01b07e6222..e494f9df5ed5 100644 --- a/lib/galaxy/util/handlers.py +++ b/lib/galaxy/util/handlers.py @@ -8,7 +8,7 @@ import os import random -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) class ConfiguresHandlers: @@ -25,7 +25,7 @@ def _init_handlers(self, config_element): self.handlers[handler_id] = (handler_id,) self._parse_handler(handler_id, handler) if handler.get('tags', None) is not None: - for tag in [ x.strip() for x in handler.get('tags').split(',') ]: + for tag in [x.strip() for x in handler.get('tags').split(',')]: if tag in self.handlers: self.handlers[tag].append(handler_id) else: diff --git a/lib/galaxy/util/hash_util.py b/lib/galaxy/util/hash_util.py index 63efe64d100a..5709e306bbef 100644 --- a/lib/galaxy/util/hash_util.py +++ b/lib/galaxy/util/hash_util.py @@ -6,7 +6,7 @@ import hmac import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) sha1 = hashlib.sha1 sha = sha1 @@ -24,24 +24,24 @@ def md5_hash_file(path): return hasher.hexdigest() -def new_secure_hash( text_type=None ): +def new_secure_hash(text_type=None): """ Returns either a sha1 hash object (if called with no arguments), or a hexdigest of the sha1 hash of the argument `text_type`. """ if text_type: - return sha1( text_type ).hexdigest() + return sha1(text_type).hexdigest() else: return sha1() -def hmac_new( key, value ): - return hmac.new( key, value, sha ).hexdigest() +def hmac_new(key, value): + return hmac.new(key, value, sha).hexdigest() -def is_hashable( value ): +def is_hashable(value): try: - hash( value ) + hash(value) except: return False return True diff --git a/lib/galaxy/util/heartbeat.py b/lib/galaxy/util/heartbeat.py index 9026e011fdaf..9bd8b3f7fc20 100644 --- a/lib/galaxy/util/heartbeat.py +++ b/lib/galaxy/util/heartbeat.py @@ -23,18 +23,19 @@ def get_current_thread_object_dict(): # Acquire the lock and then union the contents of 'active' and 'limbo' # threads into the return value. threading._active_limbo_lock.acquire() - rval.update( threading._active ) - rval.update( threading._limbo ) + rval.update(threading._active) + rval.update(threading._limbo) threading._active_limbo_lock.release() return rval -class Heartbeat( threading.Thread ): +class Heartbeat(threading.Thread): """ Thread that periodically dumps the state of all threads to a file """ - def __init__( self, config, name="Heartbeat Thread", period=20, fname="heartbeat.log" ): - threading.Thread.__init__( self, name=name ) + + def __init__(self, config, name="Heartbeat Thread", period=20, fname="heartbeat.log"): + threading.Thread.__init__(self, name=name) self.config = config self.should_stop = False self.period = period @@ -43,11 +44,11 @@ def __init__( self, config, name="Heartbeat Thread", period=20, fname="heartbeat self.fname_nonsleeping = None self.file_nonsleeping = None self.pid = None - self.nonsleeping_heartbeats = { } + self.nonsleeping_heartbeats = {} # Event to wait on when sleeping, allows us to interrupt for shutdown self.wait_event = threading.Event() - def run( self ): + def run(self): self.pid = os.getpid() self.fname = self.fname.format( server_name=self.config.server_name, @@ -61,52 +62,52 @@ def run( self ): while not self.should_stop: if self.period > 0: self.dump() - self.wait_event.wait( wait ) + self.wait_event.wait(wait) - def open_logs( self ): + def open_logs(self): if self.file is None or self.file.closed: - self.file = open( self.fname, "a" ) - self.file_nonsleeping = open( self.fname_nonsleeping, "a" ) - self.file.write( "Heartbeat for pid %d thread started at %s\n\n" % ( self.pid, time.asctime() ) ) - self.file_nonsleeping.write( "Non-Sleeping-threads for pid %d thread started at %s\n\n" % ( self.pid, time.asctime() ) ) + self.file = open(self.fname, "a") + self.file_nonsleeping = open(self.fname_nonsleeping, "a") + self.file.write("Heartbeat for pid %d thread started at %s\n\n" % (self.pid, time.asctime())) + self.file_nonsleeping.write("Non-Sleeping-threads for pid %d thread started at %s\n\n" % (self.pid, time.asctime())) - def close_logs( self ): + def close_logs(self): if self.file is not None and not self.file.closed: - self.file.write( "Heartbeat for pid %d thread stopped at %s\n\n" % ( self.pid, time.asctime() ) ) - self.file_nonsleeping.write( "Non-Sleeping-threads for pid %d thread stopped at %s\n\n" % ( self.pid, time.asctime() ) ) + self.file.write("Heartbeat for pid %d thread stopped at %s\n\n" % (self.pid, time.asctime())) + self.file_nonsleeping.write("Non-Sleeping-threads for pid %d thread stopped at %s\n\n" % (self.pid, time.asctime())) self.file.close() self.file_nonsleeping.close() - def dump( self ): + def dump(self): self.open_logs() try: # Print separator with timestamp - self.file.write( "Traceback dump for all threads at %s:\n\n" % time.asctime() ) + self.file.write("Traceback dump for all threads at %s:\n\n" % time.asctime()) # Print the thread states threads = get_current_thread_object_dict() for thread_id, frame in iteritems(sys._current_frames()): if thread_id in threads: - object = repr( threads[thread_id] ) + object = repr(threads[thread_id]) else: object = "" - self.file.write( "Thread %s, %s:\n\n" % ( thread_id, object ) ) - traceback.print_stack( frame, file=self.file ) - self.file.write( "\n" ) - self.file.write( "End dump\n\n" ) + self.file.write("Thread %s, %s:\n\n" % (thread_id, object)) + traceback.print_stack(frame, file=self.file) + self.file.write("\n") + self.file.write("End dump\n\n") self.file.flush() self.print_nonsleeping(threads) except: - self.file.write( "Caught exception attempting to dump thread states:" ) - traceback.print_exc( None, self.file ) - self.file.write( "\n" ) + self.file.write("Caught exception attempting to dump thread states:") + traceback.print_exc(None, self.file) + self.file.write("\n") - def shutdown( self ): + def shutdown(self): self.should_stop = True self.wait_event.set() self.close_logs() self.join() - def thread_is_sleeping( self, last_stack_frame ): + def thread_is_sleeping(self, last_stack_frame): """ Returns True if the given stack-frame represents a known sleeper function (at least in python 2.5) @@ -138,7 +139,7 @@ def thread_is_sleeping( self, last_stack_frame ): # By default, assume the thread is not sleeping return False - def get_interesting_stack_frame( self, stack_frames ): + def get_interesting_stack_frame(self, stack_frames): """ Scans a given backtrace stack frames, returns a single quadraple of [filename, line, function-name, text] of @@ -153,17 +154,17 @@ def get_interesting_stack_frame( self, stack_frames ): idx = _filename.find("/lib/galaxy/") if idx != -1: relative_filename = _filename[idx:] - return ( relative_filename, _line, _funcname, _text ) + return (relative_filename, _line, _funcname, _text) # no "/lib/galaxy" code found, return the innermost frame return stack_frames[-1] - def print_nonsleeping( self, threads_object_dict ): - self.file_nonsleeping.write( "Non-Sleeping threads at %s:\n\n" % time.asctime() ) + def print_nonsleeping(self, threads_object_dict): + self.file_nonsleeping.write("Non-Sleeping threads at %s:\n\n" % time.asctime()) all_threads_are_sleeping = True threads = get_current_thread_object_dict() for thread_id, frame in iteritems(sys._current_frames()): if thread_id in threads: - object = repr( threads[thread_id] ) + object = repr(threads[thread_id]) else: object = "" tb = traceback.extract_stack(frame) @@ -179,14 +180,14 @@ def print_nonsleeping( self, threads_object_dict ): self.nonsleeping_heartbeats[thread_id] = 1 good_frame = self.get_interesting_stack_frame(tb) - self.file_nonsleeping.write( "Thread %s\t%s\tnon-sleeping for %d heartbeat(s)\n File %s:%d\n Function \"%s\"\n %s\n" % - ( thread_id, object, self.nonsleeping_heartbeats[thread_id], good_frame[0], good_frame[1], good_frame[2], good_frame[3] ) ) + self.file_nonsleeping.write("Thread %s\t%s\tnon-sleeping for %d heartbeat(s)\n File %s:%d\n Function \"%s\"\n %s\n" % + (thread_id, object, self.nonsleeping_heartbeats[thread_id], good_frame[0], good_frame[1], good_frame[2], good_frame[3])) all_threads_are_sleeping = False if all_threads_are_sleeping: - self.file_nonsleeping.write( "All threads are sleeping.\n" ) - self.file_nonsleeping.write( "\n" ) + self.file_nonsleeping.write("All threads are sleeping.\n") + self.file_nonsleeping.write("\n") self.file_nonsleeping.flush() - def dump_signal_handler( self, signum, frame ): + def dump_signal_handler(self, signum, frame): self.dump() diff --git a/lib/galaxy/util/image_util.py b/lib/galaxy/util/image_util.py index a54043bfc31b..3d150eac3fc1 100644 --- a/lib/galaxy/util/image_util.py +++ b/lib/galaxy/util/image_util.py @@ -13,11 +13,11 @@ log = logging.getLogger(__name__) -def image_type( filename ): +def image_type(filename): fmt = None if PIL is not None: try: - im = PIL.open( filename ) + im = PIL.open(filename) fmt = im.format im.close() except: @@ -25,24 +25,24 @@ def image_type( filename ): # exception we expect to happen frequently, so we're not logging pass if not fmt: - fmt = imghdr.what( filename ) + fmt = imghdr.what(filename) if fmt: return fmt.upper() else: return False -def check_image_type( filename, types ): - fmt = image_type( filename ) +def check_image_type(filename, types): + fmt = image_type(filename) if fmt in types: return True return False -def get_image_ext( file_path ): +def get_image_ext(file_path): # determine ext - fmt = image_type( file_path ) - if fmt in [ 'JPG', 'JPEG' ]: + fmt = image_type(file_path) + if fmt in ['JPG', 'JPEG']: return 'jpg' if fmt == 'PNG': return 'png' diff --git a/lib/galaxy/util/inflection.py b/lib/galaxy/util/inflection.py index 58c22d051b61..ff340a83d178 100644 --- a/lib/galaxy/util/inflection.py +++ b/lib/galaxy/util/inflection.py @@ -267,7 +267,7 @@ class Inflector: based on naming conventions like on Ruby on Rails. """ - def __init__( self, Inflector=English ): + def __init__(self, Inflector=English): assert callable(Inflector), "Inflector should be a callable obj" self.Inflector = Inflector() diff --git a/lib/galaxy/util/json.py b/lib/galaxy/util/json.py index 6bd11fc73fdf..80d3a66f1c19 100644 --- a/lib/galaxy/util/json.py +++ b/lib/galaxy/util/json.py @@ -10,36 +10,36 @@ from six import iteritems, string_types, text_type -__all__ = ( "safe_dumps", "json_fix", "validate_jsonrpc_request", "validate_jsonrpc_response", "jsonrpc_request", "jsonrpc_response" ) +__all__ = ("safe_dumps", "json_fix", "validate_jsonrpc_request", "validate_jsonrpc_response", "jsonrpc_request", "jsonrpc_response") -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) to_json_string = json.dumps from_json_string = json.loads -def json_fix( val ): - if isinstance( val, list ): - return [ json_fix( v ) for v in val ] - elif isinstance( val, dict ): - return dict( [ ( json_fix( k ), json_fix( v ) ) for ( k, v ) in iteritems(val) ] ) - elif isinstance( val, text_type ): - return val.encode( "utf8" ) +def json_fix(val): + if isinstance(val, list): + return [json_fix(v) for v in val] + elif isinstance(val, dict): + return dict([(json_fix(k), json_fix(v)) for (k, v) in iteritems(val)]) + elif isinstance(val, text_type): + return val.encode("utf8") else: return val -def swap_inf_nan( val ): +def swap_inf_nan(val): """ This takes an arbitrary object and preps it for jsonifying safely, templating Inf/NaN. """ if isinstance(val, string_types): # basestring first, because it's a sequence and would otherwise get caught below. return val - elif isinstance( val, collections.Sequence ): - return [ swap_inf_nan( v ) for v in val ] - elif isinstance( val, collections.Mapping ): - return dict( [ ( swap_inf_nan( k ), swap_inf_nan( v ) ) for ( k, v ) in iteritems(val) ] ) + elif isinstance(val, collections.Sequence): + return [swap_inf_nan(v) for v in val] + elif isinstance(val, collections.Mapping): + return dict([(swap_inf_nan(k), swap_inf_nan(v)) for (k, v) in iteritems(val)]) elif isinstance(val, float): if math.isnan(val): return "__NaN__" @@ -53,22 +53,22 @@ def swap_inf_nan( val ): return val -def safe_loads( arg ): +def safe_loads(arg): """ This is a wrapper around loads that returns the parsed value instead of raising a value error. It also avoids autoconversion of non-iterables i.e numeric and boolean values. """ try: - loaded = json.loads( arg ) - if loaded is not None and not isinstance( loaded, collections.Iterable ): + loaded = json.loads(arg) + if loaded is not None and not isinstance(loaded, collections.Iterable): loaded = arg - except ( TypeError, ValueError ): + except (TypeError, ValueError): loaded = arg return loaded -def safe_dumps( *args, **kwargs ): +def safe_dumps(*args, **kwargs): """ This is a wrapper around dumps that encodes Infinity and NaN values. It's a fairly rare case (which will be low in request volume). Basically, we tell @@ -76,25 +76,25 @@ def safe_dumps( *args, **kwargs ): re-encoding. """ try: - dumped = json.dumps( *args, allow_nan=False, **kwargs ) + dumped = json.dumps(*args, allow_nan=False, **kwargs) except ValueError: - obj = swap_inf_nan( copy.deepcopy( args[0] ) ) - dumped = json.dumps( obj, allow_nan=False, **kwargs ) - if kwargs.get( 'escape_closing_tags', True ): - return dumped.replace( '': '__gt__', - '<': '__lt__', - "'": '__sq__', - '"': '__dq__', - '[': '__ob__', - ']': '__cb__', - '{': '__oc__', - '}': '__cc__', - '\n': '__cn__', - '\r': '__cr__', - '\t': '__tc__', - '#': '__pd__'} +CHARACTER_MAP = {'>': '__gt__', + '<': '__lt__', + "'": '__sq__', + '"': '__dq__', + '[': '__ob__', + ']': '__cb__', + '{': '__oc__', + '}': '__cc__', + '\n': '__cn__', + '\r': '__cr__', + '\t': '__tc__', + '#': '__pd__'} INVALID_CHARACTER = "X" @@ -118,36 +118,36 @@ def cmp(x, y): return (x > y) - (x < y) -def sanitize_lists_to_string( values, valid_characters=VALID_CHARACTERS, character_map=CHARACTER_MAP, invalid_character=INVALID_CHARACTER ): - return _sanitize_lists_to_string( values, valid_characters=valid_characters, character_map=character_map, invalid_character=invalid_character ) +def sanitize_lists_to_string(values, valid_characters=VALID_CHARACTERS, character_map=CHARACTER_MAP, invalid_character=INVALID_CHARACTER): + return _sanitize_lists_to_string(values, valid_characters=valid_characters, character_map=character_map, invalid_character=invalid_character) -def wrap_with_safe_string( value, no_wrap_classes=None ): +def wrap_with_safe_string(value, no_wrap_classes=None): """ Recursively wrap values that should be wrapped. """ - def __do_wrap( value ): - if isinstance( value, SafeStringWrapper ): + def __do_wrap(value): + if isinstance(value, SafeStringWrapper): # Only ever wrap one-layer return value - if isinstance( value, collections.Callable ): + if isinstance(value, collections.Callable): safe_class = CallableSafeStringWrapper else: safe_class = SafeStringWrapper - if isinstance( value, no_wrap_classes ): + if isinstance(value, no_wrap_classes): return value - if isinstance( value, __DONT_WRAP_TYPES__ ): - return sanitize_lists_to_string( value, valid_characters=VALID_CHARACTERS, character_map=CHARACTER_MAP ) - if isinstance( value, __WRAP_NO_SUBCLASS__ ): - return safe_class( value, safe_string_wrapper_function=__do_wrap ) + if isinstance(value, __DONT_WRAP_TYPES__): + return sanitize_lists_to_string(value, valid_characters=VALID_CHARACTERS, character_map=CHARACTER_MAP) + if isinstance(value, __WRAP_NO_SUBCLASS__): + return safe_class(value, safe_string_wrapper_function=__do_wrap) for this_type in __WRAP_SEQUENCES__ + __WRAP_SETS__: - if isinstance( value, this_type ): - return this_type( map( __do_wrap, value ) ) + if isinstance(value, this_type): + return this_type(map(__do_wrap, value)) for this_type in __WRAP_MAPPINGS__: - if isinstance( value, this_type ): + if isinstance(value, this_type): # Wrap both key and value - return this_type( ( __do_wrap( x[0] ), __do_wrap( x[1] ) ) for x in value.items() ) + return this_type((__do_wrap(x[0]), __do_wrap(x[1])) for x in value.items()) # Create a dynamic class that joins SafeStringWrapper with the object being wrapped. # This allows e.g. isinstance to continue to work. try: @@ -156,51 +156,51 @@ def __do_wrap( value ): except: wrapped_class_name = value.__class__.__name__ wrapped_class = value.__class__ - value_mod = inspect.getmodule( value ) + value_mod = inspect.getmodule(value) if value_mod: - wrapped_class_name = "%s.%s" % ( value_mod.__name__, wrapped_class_name ) - wrapped_class_name = "SafeStringWrapper(%s:%s)" % ( wrapped_class_name, ",".join( sorted( map( str, no_wrap_classes ) ) ) ) - do_wrap_func_name = "__do_wrap_%s" % ( wrapped_class_name ) + wrapped_class_name = "%s.%s" % (value_mod.__name__, wrapped_class_name) + wrapped_class_name = "SafeStringWrapper(%s:%s)" % (wrapped_class_name, ",".join(sorted(map(str, no_wrap_classes)))) + do_wrap_func_name = "__do_wrap_%s" % (wrapped_class_name) do_wrap_func = __do_wrap global_dict = globals() if wrapped_class_name in global_dict: # Check to see if we have created a wrapper for this class yet, if so, reuse - wrapped_class = global_dict.get( wrapped_class_name ) - do_wrap_func = global_dict.get( do_wrap_func_name, __do_wrap ) + wrapped_class = global_dict.get(wrapped_class_name) + do_wrap_func = global_dict.get(do_wrap_func_name, __do_wrap) else: try: - wrapped_class = type( wrapped_class_name, ( safe_class, wrapped_class, ), {} ) + wrapped_class = type(wrapped_class_name, (safe_class, wrapped_class, ), {}) except TypeError as e: # Fail-safe for when a class cannot be dynamically subclassed. - log.warning( "Unable to create dynamic subclass for %s, %s: %s", type( value), value, e ) - wrapped_class = type( wrapped_class_name, ( safe_class, ), {} ) - if wrapped_class not in ( SafeStringWrapper, CallableSafeStringWrapper ): + log.warning("Unable to create dynamic subclass for %s, %s: %s", type(value), value, e) + wrapped_class = type(wrapped_class_name, (safe_class, ), {}) + if wrapped_class not in (SafeStringWrapper, CallableSafeStringWrapper): # Save this wrapper for reuse and pickling/copying - global_dict[ wrapped_class_name ] = wrapped_class + global_dict[wrapped_class_name] = wrapped_class do_wrap_func.__name__ = do_wrap_func_name - global_dict[ do_wrap_func_name ] = do_wrap_func + global_dict[do_wrap_func_name] = do_wrap_func - def pickle_safe_object( safe_object ): - return ( wrapped_class, ( safe_object.unsanitized, do_wrap_func, ) ) + def pickle_safe_object(safe_object): + return (wrapped_class, (safe_object.unsanitized, do_wrap_func, )) # Set pickle and copy properties - copy_reg.pickle( wrapped_class, pickle_safe_object, do_wrap_func ) - return wrapped_class( value, safe_string_wrapper_function=do_wrap_func ) + copy_reg.pickle(wrapped_class, pickle_safe_object, do_wrap_func) + return wrapped_class(value, safe_string_wrapper_function=do_wrap_func) # Determine classes not to wrap if no_wrap_classes: - if not isinstance( no_wrap_classes, ( tuple, list ) ): - no_wrap_classes = [ no_wrap_classes ] - no_wrap_classes = list( no_wrap_classes ) + list( __DONT_SANITIZE_TYPES__ ) + [ SafeStringWrapper ] + if not isinstance(no_wrap_classes, (tuple, list)): + no_wrap_classes = [no_wrap_classes] + no_wrap_classes = list(no_wrap_classes) + list(__DONT_SANITIZE_TYPES__) + [SafeStringWrapper] else: - no_wrap_classes = list( __DONT_SANITIZE_TYPES__ ) + [ SafeStringWrapper ] - no_wrap_classes = tuple( set( sorted( no_wrap_classes, key=str ) ) ) - return __do_wrap( value ) + no_wrap_classes = list(__DONT_SANITIZE_TYPES__) + [SafeStringWrapper] + no_wrap_classes = tuple(set(sorted(no_wrap_classes, key=str))) + return __do_wrap(value) # N.B. refer to e.g. https://docs.python.org/2/reference/datamodel.html for information on Python's Data Model. -class SafeStringWrapper( object ): +class SafeStringWrapper(object): """ Class that wraps and sanitizes any provided value's attributes that will attempt to be cast into a string. @@ -216,93 +216,93 @@ class SafeStringWrapper( object ): will still be sanitized, but not wrapped), and e.g. integers will have neither. """ __UNSANITIZED_ATTRIBUTE_NAME__ = 'unsanitized' - __NO_WRAP_NAMES__ = [ '__safe_string_wrapper_function__', '__class__', __UNSANITIZED_ATTRIBUTE_NAME__] + __NO_WRAP_NAMES__ = ['__safe_string_wrapper_function__', '__class__', __UNSANITIZED_ATTRIBUTE_NAME__] - def __new__( cls, *arg, **kwd ): + def __new__(cls, *arg, **kwd): # We need to define a __new__ since, we are subclassing from e.g. immutable str, which internally sets data # that will be used when other + this (this + other is handled by __add__) try: - return super( SafeStringWrapper, cls ).__new__( cls, sanitize_lists_to_string( arg[0], valid_characters=VALID_CHARACTERS, character_map=CHARACTER_MAP ) ) + return super(SafeStringWrapper, cls).__new__(cls, sanitize_lists_to_string(arg[0], valid_characters=VALID_CHARACTERS, character_map=CHARACTER_MAP)) except Exception as e: - log.warning( "Could not provide an argument to %s.__new__: %s; will try without arguments.", cls, e ) - return super( SafeStringWrapper, cls ).__new__( cls ) + log.warning("Could not provide an argument to %s.__new__: %s; will try without arguments.", cls, e) + return super(SafeStringWrapper, cls).__new__(cls) - def __init__( self, value, safe_string_wrapper_function=wrap_with_safe_string ): + def __init__(self, value, safe_string_wrapper_function=wrap_with_safe_string): self.unsanitized = value self.__safe_string_wrapper_function__ = safe_string_wrapper_function - def __str__( self ): - return sanitize_lists_to_string( self.unsanitized, valid_characters=VALID_CHARACTERS, character_map=CHARACTER_MAP ) + def __str__(self): + return sanitize_lists_to_string(self.unsanitized, valid_characters=VALID_CHARACTERS, character_map=CHARACTER_MAP) - def __repr__( self ): - return "%s object at %x on: %s" % ( sanitize_lists_to_string( self.__class__.__name__, valid_characters=VALID_CHARACTERS, character_map=CHARACTER_MAP ), id( self ), sanitize_lists_to_string( repr( self.unsanitized ), valid_characters=VALID_CHARACTERS, character_map=CHARACTER_MAP ) ) + def __repr__(self): + return "%s object at %x on: %s" % (sanitize_lists_to_string(self.__class__.__name__, valid_characters=VALID_CHARACTERS, character_map=CHARACTER_MAP), id(self), sanitize_lists_to_string(repr(self.unsanitized), valid_characters=VALID_CHARACTERS, character_map=CHARACTER_MAP)) - def __lt__( self, other ): - while isinstance( other, SafeStringWrapper ): + def __lt__(self, other): + while isinstance(other, SafeStringWrapper): other = other.unsanitized return self.unsanitized < other - def __le__( self, other ): - while isinstance( other, SafeStringWrapper ): + def __le__(self, other): + while isinstance(other, SafeStringWrapper): other = other.unsanitized return self.unsanitized <= other - def __eq__( self, other ): - while isinstance( other, SafeStringWrapper ): + def __eq__(self, other): + while isinstance(other, SafeStringWrapper): other = other.unsanitized return self.unsanitized == other - def __ne__( self, other ): - while isinstance( other, SafeStringWrapper ): + def __ne__(self, other): + while isinstance(other, SafeStringWrapper): other = other.unsanitized return self.unsanitized != other - def __gt__( self, other ): - while isinstance( other, SafeStringWrapper ): + def __gt__(self, other): + while isinstance(other, SafeStringWrapper): other = other.unsanitized return self.unsanitized > other - def __ge__( self, other ): - while isinstance( other, SafeStringWrapper ): + def __ge__(self, other): + while isinstance(other, SafeStringWrapper): other = other.unsanitized return self.unsanitized >= other - def __cmp__( self, other ): - while isinstance( other, SafeStringWrapper ): + def __cmp__(self, other): + while isinstance(other, SafeStringWrapper): other = other.unsanitized - return cmp( self.unsanitized, other ) + return cmp(self.unsanitized, other) # Do not implement __rcmp__, python 2.2 < 2.6 - def __hash__( self ): - return hash( self.unsanitized ) + def __hash__(self): + return hash(self.unsanitized) - def __bool__( self ): - return bool( self.unsanitized ) + def __bool__(self): + return bool(self.unsanitized) __nonzero__ = __bool__ # Do not implement __unicode__, we will rely on __str__ - def __getattr__( self, name ): + def __getattr__(self, name): if name in SafeStringWrapper.__NO_WRAP_NAMES__: # FIXME: is this ever reached? - return object.__getattribute__( self, name ) - return self.__safe_string_wrapper_function__( getattr( self.unsanitized, name ) ) + return object.__getattribute__(self, name) + return self.__safe_string_wrapper_function__(getattr(self.unsanitized, name)) - def __setattr__( self, name, value ): + def __setattr__(self, name, value): if name in SafeStringWrapper.__NO_WRAP_NAMES__: - return object.__setattr__( self, name, value ) - return setattr( self.unsanitized, name, value ) + return object.__setattr__(self, name, value) + return setattr(self.unsanitized, name, value) - def __delattr__( self, name ): + def __delattr__(self, name): if name in SafeStringWrapper.__NO_WRAP_NAMES__: - return object.__delattr__( self, name ) - return delattr( self.unsanitized, name ) + return object.__delattr__(self, name) + return delattr(self.unsanitized, name) - def __getattribute__( self, name ): + def __getattribute__(self, name): if name in SafeStringWrapper.__NO_WRAP_NAMES__: - return object.__getattribute__( self, name ) - return self.__safe_string_wrapper_function__( getattr( object.__getattribute__( self, 'unsanitized' ), name ) ) + return object.__getattribute__(self, name) + return self.__safe_string_wrapper_function__(getattr(object.__getattribute__(self, 'unsanitized'), name)) # Skip Descriptors @@ -317,179 +317,179 @@ def __getattribute__( self, name ): # We address __call__ as needed based upon unsanitized, through the use of a CallableSafeStringWrapper class - def __len__( self ): + def __len__(self): original_value = self.unsanitized - while isinstance( original_value, SafeStringWrapper ): + while isinstance(original_value, SafeStringWrapper): original_value = self.unsanitized - return len( self.unsanitized ) + return len(self.unsanitized) - def __getitem__( self, key ): - return self.__safe_string_wrapper_function__( self.unsanitized[ key ] ) + def __getitem__(self, key): + return self.__safe_string_wrapper_function__(self.unsanitized[key]) - def __setitem__( self, key, value ): - while isinstance( value, SafeStringWrapper ): + def __setitem__(self, key, value): + while isinstance(value, SafeStringWrapper): value = value.unsanitized - self.unsanitized[ key ] = value + self.unsanitized[key] = value - def __delitem__( self, key ): - del self.unsanitized[ key ] + def __delitem__(self, key): + del self.unsanitized[key] - def __iter__( self ): - return iter( map( self.__safe_string_wrapper_function__, iter( self.unsanitized ) ) ) + def __iter__(self): + return iter(map(self.__safe_string_wrapper_function__, iter(self.unsanitized))) # Do not implement __reversed__ - def __contains__( self, item ): + def __contains__(self, item): # FIXME: Do we need to consider if item is/isn't or does/doesn't contain SafeStringWrapper? # When considering e.g. nested lists/dicts/etc, this gets complicated - while isinstance( item, SafeStringWrapper ): + while isinstance(item, SafeStringWrapper): item = item.unsanitized return item in self.unsanitized # Not sure that we need these slice methods, but will provide anyway - def __getslice__( self, i, j ): - return self.__safe_string_wrapper_function__( self.unsanitized[ i:j ] ) + def __getslice__(self, i, j): + return self.__safe_string_wrapper_function__(self.unsanitized[i:j]) - def __setslice__( self, i, j, value ): - self.unsanitized[ i:j ] = value + def __setslice__(self, i, j, value): + self.unsanitized[i:j] = value - def __delslice__( self, i, j ): - del self.unsanitized[ i:j ] + def __delslice__(self, i, j): + del self.unsanitized[i:j] - def __add__( self, other ): - while isinstance( other, SafeStringWrapper ): + def __add__(self, other): + while isinstance(other, SafeStringWrapper): other = other.unsanitized - return self.__safe_string_wrapper_function__( self.unsanitized + other ) + return self.__safe_string_wrapper_function__(self.unsanitized + other) - def __sub__( self, other ): - while isinstance( other, SafeStringWrapper ): + def __sub__(self, other): + while isinstance(other, SafeStringWrapper): other = other.unsanitized - return self.__safe_string_wrapper_function__( self.unsanitized - other ) + return self.__safe_string_wrapper_function__(self.unsanitized - other) - def __mul__( self, other ): - while isinstance( other, SafeStringWrapper ): + def __mul__(self, other): + while isinstance(other, SafeStringWrapper): other = other.unsanitized - return self.__safe_string_wrapper_function__( self.unsanitized * other ) + return self.__safe_string_wrapper_function__(self.unsanitized * other) - def __floordiv__( self, other ): - while isinstance( other, SafeStringWrapper ): + def __floordiv__(self, other): + while isinstance(other, SafeStringWrapper): other = other.unsanitized - return self.__safe_string_wrapper_function__( self.unsanitized // other ) + return self.__safe_string_wrapper_function__(self.unsanitized // other) - def __mod__( self, other ): - while isinstance( other, SafeStringWrapper ): + def __mod__(self, other): + while isinstance(other, SafeStringWrapper): other = other.unsanitized - return self.__safe_string_wrapper_function__( self.unsanitized % other ) + return self.__safe_string_wrapper_function__(self.unsanitized % other) - def __divmod__( self, other ): - while isinstance( other, SafeStringWrapper ): + def __divmod__(self, other): + while isinstance(other, SafeStringWrapper): other = other.unsanitized - return self.__safe_string_wrapper_function__( divmod( self.unsanitized, other ) ) + return self.__safe_string_wrapper_function__(divmod(self.unsanitized, other)) - def __pow__( self, *other ): - while isinstance( other, SafeStringWrapper ): + def __pow__(self, *other): + while isinstance(other, SafeStringWrapper): other = other.unsanitized - return self.__safe_string_wrapper_function__( pow( self.unsanitized, *other ) ) + return self.__safe_string_wrapper_function__(pow(self.unsanitized, *other)) - def __lshift__( self, other ): - while isinstance( other, SafeStringWrapper ): + def __lshift__(self, other): + while isinstance(other, SafeStringWrapper): other = other.unsanitized - return self.__safe_string_wrapper_function__( self.unsanitized << other ) + return self.__safe_string_wrapper_function__(self.unsanitized << other) - def __rshift__( self, other ): - while isinstance( other, SafeStringWrapper ): + def __rshift__(self, other): + while isinstance(other, SafeStringWrapper): other = other.unsanitized - return self.__safe_string_wrapper_function__( self.unsanitized >> other ) + return self.__safe_string_wrapper_function__(self.unsanitized >> other) - def __and__( self, other ): - while isinstance( other, SafeStringWrapper ): + def __and__(self, other): + while isinstance(other, SafeStringWrapper): other = other.unsanitized - return self.__safe_string_wrapper_function__( self.unsanitized & other ) + return self.__safe_string_wrapper_function__(self.unsanitized & other) - def __xor__( self, other ): - while isinstance( other, SafeStringWrapper ): + def __xor__(self, other): + while isinstance(other, SafeStringWrapper): other = other.unsanitized - return self.__safe_string_wrapper_function__( self.unsanitized ^ other ) + return self.__safe_string_wrapper_function__(self.unsanitized ^ other) - def __or__( self, other ): - while isinstance( other, SafeStringWrapper ): + def __or__(self, other): + while isinstance(other, SafeStringWrapper): other = other.unsanitized - return self.__safe_string_wrapper_function__( self.unsanitized | other ) + return self.__safe_string_wrapper_function__(self.unsanitized | other) - def __div__( self, other ): - while isinstance( other, SafeStringWrapper ): + def __div__(self, other): + while isinstance(other, SafeStringWrapper): other = other.unsanitized - return self.__safe_string_wrapper_function__( self.unsanitized / other ) + return self.__safe_string_wrapper_function__(self.unsanitized / other) - def __truediv__( self, other ): - while isinstance( other, SafeStringWrapper ): + def __truediv__(self, other): + while isinstance(other, SafeStringWrapper): other = other.unsanitized - return self.__safe_string_wrapper_function__( self.unsanitized / other ) + return self.__safe_string_wrapper_function__(self.unsanitized / other) # The only reflected operand that we will define is __rpow__, due to coercion rules complications as per docs - def __rpow__( self, other ): - while isinstance( other, SafeStringWrapper ): + def __rpow__(self, other): + while isinstance(other, SafeStringWrapper): other = other.unsanitized - return self.__safe_string_wrapper_function__( pow( other, self.unsanitized ) ) + return self.__safe_string_wrapper_function__(pow(other, self.unsanitized)) # Do not implement in-place operands - def __neg__( self ): - return self.__safe_string_wrapper_function__( -self.unsanitized ) + def __neg__(self): + return self.__safe_string_wrapper_function__(-self.unsanitized) - def __pos__( self ): - return self.__safe_string_wrapper_function__( +self.unsanitized ) + def __pos__(self): + return self.__safe_string_wrapper_function__(+self.unsanitized) - def __abs__( self ): - return self.__safe_string_wrapper_function__( abs( self.unsanitized ) ) + def __abs__(self): + return self.__safe_string_wrapper_function__(abs(self.unsanitized)) - def __invert__( self ): - return self.__safe_string_wrapper_function__( ~self.unsanitized ) + def __invert__(self): + return self.__safe_string_wrapper_function__(~self.unsanitized) - def __complex__( self ): - return self.__safe_string_wrapper_function__( complex( self.unsanitized ) ) + def __complex__(self): + return self.__safe_string_wrapper_function__(complex(self.unsanitized)) - def __int__( self ): - return int( self.unsanitized ) + def __int__(self): + return int(self.unsanitized) - def __float__( self ): - return float( self.unsanitized ) + def __float__(self): + return float(self.unsanitized) - def __oct__( self ): - return oct( self.unsanitized ) + def __oct__(self): + return oct(self.unsanitized) - def __hex__( self ): - return hex( self.unsanitized ) + def __hex__(self): + return hex(self.unsanitized) - def __index__( self ): + def __index__(self): return self.unsanitized.index() - def __coerce__( self, other ): - while isinstance( other, SafeStringWrapper ): + def __coerce__(self, other): + while isinstance(other, SafeStringWrapper): other = other.unsanitized - return coerce( self.unsanitized, other ) + return coerce(self.unsanitized, other) - def __enter__( self ): + def __enter__(self): return self.unsanitized.__enter__() - def __exit__( self, *args ): - return self.unsanitized.__exit__( *args ) + def __exit__(self, *args): + return self.unsanitized.__exit__(*args) -class CallableSafeStringWrapper( SafeStringWrapper ): +class CallableSafeStringWrapper(SafeStringWrapper): - def __call__( self, *args, **kwds ): - return self.__safe_string_wrapper_function__( self.unsanitized( *args, **kwds ) ) + def __call__(self, *args, **kwds): + return self.__safe_string_wrapper_function__(self.unsanitized(*args, **kwds)) # Enable pickling/deepcopy -def pickle_SafeStringWrapper( safe_object ): - args = ( safe_object.unsanitized, ) +def pickle_SafeStringWrapper(safe_object): + args = (safe_object.unsanitized, ) cls = SafeStringWrapper - if isinstance( safe_object, CallableSafeStringWrapper ): + if isinstance(safe_object, CallableSafeStringWrapper): cls = CallableSafeStringWrapper - return ( cls, args ) + return (cls, args) -copy_reg.pickle( SafeStringWrapper, pickle_SafeStringWrapper, wrap_with_safe_string ) -copy_reg.pickle( CallableSafeStringWrapper, pickle_SafeStringWrapper, wrap_with_safe_string ) +copy_reg.pickle(SafeStringWrapper, pickle_SafeStringWrapper, wrap_with_safe_string) +copy_reg.pickle(CallableSafeStringWrapper, pickle_SafeStringWrapper, wrap_with_safe_string) diff --git a/lib/galaxy/util/odict.py b/lib/galaxy/util/odict.py index e33013a97f68..3f123d2ecf05 100644 --- a/lib/galaxy/util/odict.py +++ b/lib/galaxy/util/odict.py @@ -14,81 +14,82 @@ class odict(UserDict): added. Calling keys(), values(), items(), etc. will return results in this order. """ - def __init__( self, dict=None ): + + def __init__(self, dict=None): item = dict self._keys = [] if isinstance(item, dict_alias): - UserDict.__init__( self, item ) + UserDict.__init__(self, item) else: - UserDict.__init__( self, None ) + UserDict.__init__(self, None) if isinstance(item, list): for (key, value) in item: self[key] = value - def __delitem__( self, key ): - UserDict.__delitem__( self, key ) - self._keys.remove( key ) + def __delitem__(self, key): + UserDict.__delitem__(self, key) + self._keys.remove(key) - def __setitem__( self, key, item ): - UserDict.__setitem__( self, key, item ) + def __setitem__(self, key, item): + UserDict.__setitem__(self, key, item) if key not in self._keys: - self._keys.append( key ) + self._keys.append(key) - def clear( self ): - UserDict.clear( self ) + def clear(self): + UserDict.clear(self) self._keys = [] def copy(self): new = odict() - new.update( self ) + new.update(self) return new - def items( self ): - return zip( self._keys, self.values() ) + def items(self): + return zip(self._keys, self.values()) - def keys( self ): + def keys(self): return self._keys[:] - def popitem( self ): + def popitem(self): try: key = self._keys[-1] except IndexError: - raise KeyError( 'dictionary is empty' ) - val = self[ key ] - del self[ key ] - return ( key, val ) + raise KeyError('dictionary is empty') + val = self[key] + del self[key] + return (key, val) - def setdefault( self, key, failobj=None ): + def setdefault(self, key, failobj=None): if key not in self._keys: - self._keys.append( key ) - return UserDict.setdefault( self, key, failobj ) + self._keys.append(key) + return UserDict.setdefault(self, key, failobj) - def update( self, dict ): - for ( key, val ) in dict.items(): - self.__setitem__( key, val ) + def update(self, dict): + for (key, val) in dict.items(): + self.__setitem__(key, val) - def values( self ): - return map( self.get, self._keys ) + def values(self): + return map(self.get, self._keys) - def iterkeys( self ): - return iter( self._keys ) + def iterkeys(self): + return iter(self._keys) - def itervalues( self ): + def itervalues(self): for key in self._keys: - yield self.get( key ) + yield self.get(key) - def iteritems( self ): + def iteritems(self): for key in self._keys: - yield key, self.get( key ) + yield key, self.get(key) - def __iter__( self ): + def __iter__(self): for key in self._keys: yield key - def reverse( self ): + def reverse(self): self._keys.reverse() - def insert( self, index, key, item ): + def insert(self, index, key, item): if key not in self._keys: - self._keys.insert( index, key ) - UserDict.__setitem__( self, key, item ) + self._keys.insert(index, key) + UserDict.__setitem__(self, key, item) diff --git a/lib/galaxy/util/pastescript/loadwsgi.py b/lib/galaxy/util/pastescript/loadwsgi.py index ab31383bebc7..ed7f15cded3d 100644 --- a/lib/galaxy/util/pastescript/loadwsgi.py +++ b/lib/galaxy/util/pastescript/loadwsgi.py @@ -723,6 +723,7 @@ class FuncLoader(_Loader): Dot notation is supported in both the module and function name, e.g.: use = call:my.module.path:object.method """ + def __init__(self, spec): self.spec = spec if ':' not in spec: diff --git a/lib/galaxy/util/pastescript/serve.py b/lib/galaxy/util/pastescript/serve.py index 49fca8c6eab9..974501e29e0a 100644 --- a/lib/galaxy/util/pastescript/serve.py +++ b/lib/galaxy/util/pastescript/serve.py @@ -19,20 +19,21 @@ from __future__ import print_function import atexit -import ConfigParser import errno import logging import optparse import os import re +import signal import subprocess import sys import textwrap import threading import time - from logging.config import fileConfig +from six.moves import configparser + from .loadwsgi import loadapp, loadserver @@ -193,7 +194,7 @@ def run(self, args): self.simulate = getattr(self.options, 'simulate', False) # For #! situations: - if (os.environ.get('PASTE_CONFIG_FILE') and self.takes_config_file is not None): + if os.environ.get('PASTE_CONFIG_FILE') and self.takes_config_file is not None: take = self.takes_config_file filename = os.environ.get('PASTE_CONFIG_FILE') if take == 1: @@ -205,7 +206,7 @@ def run(self, args): "Value takes_config_file must be None, 1, or -1 (not %r)" % take) - if (os.environ.get('PASTE_DEFAULT_QUIET')): + if os.environ.get('PASTE_DEFAULT_QUIET'): self.verbose = 0 # Validate: @@ -315,7 +316,7 @@ def quote_first_command_arg(self, arg): that case, or on non-Windows systems or an executable with no spaces, it just leaves well enough alone. """ - if (sys.platform != 'win32' or ' ' not in arg): + if sys.platform != 'win32' or ' ' not in arg: # Problem does not apply: return arg try: @@ -351,7 +352,7 @@ def logging_file_config(self, config_file): ConfigParser defaults are specified for the special ``__file__`` and ``here`` variables, similar to PasteDeploy config loading. """ - parser = ConfigParser.ConfigParser() + parser = configparser.ConfigParser() parser.read([config_file]) if parser.has_section('loggers'): config_file = os.path.abspath(config_file) @@ -386,7 +387,7 @@ def run(self, args): jython = sys.platform.startswith('java') -class DaemonizeException( Exception ): +class DaemonizeException(Exception): pass @@ -499,7 +500,7 @@ def command(self): if not self.args: raise BadCommand('You must give a config file') app_spec = self.args[0] - if (len(self.args) > 1 and self.args[1] in self.possible_subcommands): + if len(self.args) > 1 and self.args[1] in self.possible_subcommands: cmd = self.args[1] restvars = self.args[2:] else: @@ -507,7 +508,7 @@ def command(self): restvars = self.args[1:] else: app_spec = "" - if (self.args and self.args[0] in self.possible_subcommands): + if self.args and self.args[0] in self.possible_subcommands: cmd = self.args[0] restvars = self.args[1:] else: @@ -637,7 +638,7 @@ def command(self): server = loadserver(server_spec, name=server_name, relative_to=base, global_conf=vars) - app = loadapp( app_spec, name=app_name, relative_to=base, global_conf=vars) + app = loadapp(app_spec, name=app_name, relative_to=base, global_conf=vars) if self.verbose > 0: if hasattr(os, 'getpid'): @@ -699,7 +700,7 @@ def daemonize(self): import resource # Resource usage information. maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1] - if (maxfd == resource.RLIM_INFINITY): + if maxfd == resource.RLIM_INFINITY: maxfd = MAXFD # Iterate through and close all file descriptors. for fd in range(0, maxfd): @@ -708,7 +709,7 @@ def daemonize(self): except OSError: # ERROR, fd wasn't open to begin with (ignored) pass - if (hasattr(os, "devnull")): + if hasattr(os, "devnull"): REDIRECT_TO = os.devnull else: REDIRECT_TO = "/dev/null" @@ -747,7 +748,6 @@ def stop_daemon(self): for j in range(10): if not live_pidfile(pid_file): break - import signal os.kill(pid, signal.SIGTERM) time.sleep(1) else: @@ -802,9 +802,7 @@ def restart_with_monitor(self, reloader=False): raise return 1 finally: - if (proc is not None and - hasattr(os, 'kill')): - import signal + if proc is not None and hasattr(os, 'kill'): try: os.kill(proc.pid, signal.SIGTERM) except (OSError, IOError): @@ -1006,10 +1004,6 @@ def _turn_sigterm_into_systemexit(): """ Attempts to turn a SIGTERM exception into a SystemExit exception. """ - try: - import signal - except ImportError: - return def handle_term(signo, frame): raise SystemExit @@ -1039,10 +1033,8 @@ def handle_term(signo, frame): def run(args=None): - if (not args and - len(sys.argv) >= 2 and - os.environ.get('_') and sys.argv[0] != os.environ['_'] and - os.environ['_'] == sys.argv[1]): + if (not args and len(sys.argv) >= 2 and os.environ.get('_') and + sys.argv[0] != os.environ['_'] and os.environ['_'] == sys.argv[1]): # probably it's an exe execution args = ['exe', os.environ['_']] + sys.argv[2:] if args is None: diff --git a/lib/galaxy/util/permutations.py b/lib/galaxy/util/permutations.py index d78e569d5b65..b153bc2f0dde 100644 --- a/lib/galaxy/util/permutations.py +++ b/lib/galaxy/util/permutations.py @@ -16,13 +16,13 @@ ) -class InputMatchedException( MessageException ): +class InputMatchedException(MessageException): """ Indicates problem matching inputs while building up inputs permutations. """ -def expand_multi_inputs( inputs, classifier, key_filter=None ): - key_filter = key_filter or ( lambda x: True ) +def expand_multi_inputs(inputs, classifier, key_filter=None): + key_filter = key_filter or (lambda x: True) single_inputs, matched_multi_inputs, multiplied_multi_inputs = __split_inputs( inputs, @@ -31,33 +31,33 @@ def expand_multi_inputs( inputs, classifier, key_filter=None ): ) # Build up every combination of inputs to be run together. - input_combos = __extend_with_matched_combos( single_inputs, matched_multi_inputs ) - input_combos = __extend_with_multiplied_combos( input_combos, multiplied_multi_inputs ) + input_combos = __extend_with_matched_combos(single_inputs, matched_multi_inputs) + input_combos = __extend_with_multiplied_combos(input_combos, multiplied_multi_inputs) return input_combos -def __split_inputs( inputs, classifier, key_filter ): - key_filter = key_filter or ( lambda x: True ) - input_keys = filter( key_filter, inputs ) +def __split_inputs(inputs, classifier, key_filter): + key_filter = key_filter or (lambda x: True) + input_keys = filter(key_filter, inputs) single_inputs = {} matched_multi_inputs = {} multiplied_multi_inputs = {} for input_key in input_keys: - input_type, expanded_val = classifier( input_key ) + input_type, expanded_val = classifier(input_key) if input_type == input_classification.SINGLE: - single_inputs[ input_key ] = expanded_val + single_inputs[input_key] = expanded_val elif input_type == input_classification.MATCHED: - matched_multi_inputs[ input_key ] = expanded_val + matched_multi_inputs[input_key] = expanded_val elif input_type == input_classification.MULTIPLIED: - multiplied_multi_inputs[ input_key ] = expanded_val + multiplied_multi_inputs[input_key] = expanded_val - return ( single_inputs, matched_multi_inputs, multiplied_multi_inputs ) + return (single_inputs, matched_multi_inputs, multiplied_multi_inputs) -def __extend_with_matched_combos( single_inputs, multi_inputs ): +def __extend_with_matched_combos(single_inputs, multi_inputs): """ {a => 1, b => 2} and {c => {3, 4}, d => {5, 6}} @@ -68,31 +68,31 @@ def __extend_with_matched_combos( single_inputs, multi_inputs ): """ - if len( multi_inputs ) == 0: - return [ single_inputs ] + if len(multi_inputs) == 0: + return [single_inputs] matched_multi_inputs = [] - first_multi_input_key = multi_inputs.keys()[ 0 ] + first_multi_input_key = multi_inputs.keys()[0] first_multi_value = multi_inputs.get(first_multi_input_key) for value in first_multi_value: - new_inputs = __copy_and_extend_inputs( single_inputs, first_multi_input_key, value ) - matched_multi_inputs.append( new_inputs ) + new_inputs = __copy_and_extend_inputs(single_inputs, first_multi_input_key, value) + matched_multi_inputs.append(new_inputs) for multi_input_key, multi_input_values in multi_inputs.iteritems(): if multi_input_key == first_multi_input_key: continue - if len( multi_input_values ) != len( first_multi_value ): + if len(multi_input_values) != len(first_multi_value): raise InputMatchedException() - for index, value in enumerate( multi_input_values ): - matched_multi_inputs[ index ][ multi_input_key ] = value + for index, value in enumerate(multi_input_values): + matched_multi_inputs[index][multi_input_key] = value return matched_multi_inputs -def __extend_with_multiplied_combos( input_combos, multi_inputs ): +def __extend_with_multiplied_combos(input_combos, multi_inputs): combos = input_combos for multi_input_key, multi_input_value in multi_inputs.iteritems(): @@ -100,15 +100,15 @@ def __extend_with_multiplied_combos( input_combos, multi_inputs ): for combo in combos: for input_value in multi_input_value: - iter_combo = __copy_and_extend_inputs( combo, multi_input_key, input_value ) - iter_combos.append( iter_combo ) + iter_combo = __copy_and_extend_inputs(combo, multi_input_key, input_value) + iter_combos.append(iter_combo) combos = iter_combos return combos -def __copy_and_extend_inputs( inputs, key, value ): - new_inputs = dict( inputs ) - new_inputs[ key ] = value +def __copy_and_extend_inputs(inputs, key, value): + new_inputs = dict(inputs) + new_inputs[key] = value return new_inputs diff --git a/lib/galaxy/util/plugin_config.py b/lib/galaxy/util/plugin_config.py index d9deab5bdd3a..133998f210f3 100644 --- a/lib/galaxy/util/plugin_config.py +++ b/lib/galaxy/util/plugin_config.py @@ -15,17 +15,17 @@ def plugins_dict(module, plugin_type_identifier): """ plugin_dict = {} - for plugin_module in submodules( module ): + for plugin_module in submodules(module): # FIXME: this is not how one is suppose to use __all__ why did you do # this past John? - for clazz in getattr( plugin_module, "__all__", [] ): + for clazz in getattr(plugin_module, "__all__", []): try: - clazz = getattr( plugin_module, clazz ) + clazz = getattr(plugin_module, clazz) except TypeError: clazz = clazz - plugin_type = getattr( clazz, plugin_type_identifier, None ) + plugin_type = getattr(clazz, plugin_type_identifier, None) if plugin_type: - plugin_dict[ plugin_type ] = clazz + plugin_dict[plugin_type] = clazz return plugin_dict @@ -43,17 +43,17 @@ def __load_plugins_from_element(plugins_dict, plugins_element, extra_kwds): for plugin_element in plugins_element: plugin_type = plugin_element.tag - plugin_kwds = dict( plugin_element.items() ) - plugin_kwds.update( extra_kwds ) + plugin_kwds = dict(plugin_element.items()) + plugin_kwds.update(extra_kwds) try: - plugin_klazz = plugins_dict[ plugin_type ] + plugin_klazz = plugins_dict[plugin_type] except KeyError: template = "Failed to find plugin of type [%s] in available plugin types %s" - message = template % ( plugin_type, str( plugins_dict.keys() ) ) - raise Exception( message ) + message = template % (plugin_type, str(plugins_dict.keys())) + raise Exception(message) - plugin = plugin_klazz( **plugin_kwds ) - plugins.append( plugin ) + plugin = plugin_klazz(**plugin_kwds) + plugins.append(plugin) return plugins @@ -62,20 +62,20 @@ def __load_plugins_from_dicts(plugins_dict, configs, extra_kwds): plugins = [] for config in configs: - plugin_type = config[ "type" ] + plugin_type = config["type"] plugin_kwds = config - plugin_kwds.update( extra_kwds ) - plugin = plugins_dict[ plugin_type ]( **plugin_kwds ) - plugins.append( plugin ) + plugin_kwds.update(extra_kwds) + plugin = plugins_dict[plugin_type](**plugin_kwds) + plugins.append(plugin) return plugins def plugin_source_from_path(path): - if path.endswith(".yaml") or path.endswith(".yml"): + if path.endswith(".yaml") or path.endswith(".yml") or path.endswith(".yaml.sample") or path.endswith(".yml.sample"): return ('dict', __read_yaml(path)) else: - return ('xml', ElementTree.parse( path ).getroot()) + return ('xml', ElementTree.parse(path).getroot()) def __read_yaml(path): diff --git a/lib/galaxy/util/properties.py b/lib/galaxy/util/properties.py index b5743dc0ef26..2ca160c27f75 100644 --- a/lib/galaxy/util/properties.py +++ b/lib/galaxy/util/properties.py @@ -6,14 +6,20 @@ import os.path import sys +import yaml + from six import iteritems from six.moves.configparser import ConfigParser +from galaxy.util import listify + -def find_config_file(default, old_default, explicit, cwd=None): +def find_config_file(default, old_defaults, explicit, cwd=None): + old_defaults = listify(old_defaults) if cwd is not None: default = os.path.join(cwd, default) - old_default = os.path.join(cwd, old_default) + for i in range(len(old_defaults)): + old_defaults[i] = os.path.join(cwd, old_defaults[i]) if explicit is not None: explicit = os.path.join(cwd, explicit) @@ -23,47 +29,72 @@ def find_config_file(default, old_default, explicit, cwd=None): else: raise Exception("Problem determining Galaxy's configuration - the specified configuration file cannot be found.") else: - if not os.path.exists( default ) and os.path.exists( old_default ): - config_file = old_default - elif os.path.exists( default ): + config_file = None + if os.path.exists(default): config_file = default - else: + if config_file is None: + for old_default in old_defaults: + if os.path.exists(old_default): + config_file = old_default + + if config_file is None: config_file = default + ".sample" + return config_file def load_app_properties( kwds={}, ini_file=None, - ini_section="app:main", + ini_section=None, + config_file=None, + config_section=None, config_prefix="GALAXY_CONFIG_" ): properties = kwds.copy() if kwds else {} - if ini_file: - defaults = { - 'here': os.path.dirname(os.path.abspath(ini_file)), - '__file__': os.path.abspath(ini_file) - } - parser = NicerConfigParser(ini_file, defaults=defaults) - parser.optionxform = str # Don't lower-case keys - with open(ini_file) as f: - parser.read_file(f) - - properties.update( dict( parser.items( ini_section ) ) ) + if config_file is None: + config_file = ini_file + config_section = ini_section + + if config_file: + if not config_file.endswith(".yml") and not config_file.endswith(".yml.sample"): + if config_section is None: + config_section = "app:main" + parser = nice_config_parser(config_file) + properties.update(dict(parser.items(config_section))) + else: + if config_section is None: + config_section = "galaxy" + + with open(config_file, "r") as f: + raw_properties = yaml.load(f) + properties = raw_properties[config_section] or {} override_prefix = "%sOVERRIDE_" % config_prefix for key in os.environ: - if key.startswith( override_prefix ): - config_key = key[ len( override_prefix ): ].lower() - properties[ config_key ] = os.environ[ key ] - elif key.startswith( config_prefix ): - config_key = key[ len( config_prefix ): ].lower() + if key.startswith(override_prefix): + config_key = key[len(override_prefix):].lower() + properties[config_key] = os.environ[key] + elif key.startswith(config_prefix): + config_key = key[len(config_prefix):].lower() if config_key not in properties: - properties[ config_key ] = os.environ[ key ] + properties[config_key] = os.environ[key] return properties +def nice_config_parser(path): + defaults = { + 'here': os.path.dirname(os.path.abspath(path)), + '__file__': os.path.abspath(path) + } + parser = NicerConfigParser(path, defaults=defaults) + parser.optionxform = str # Don't lower-case keys + with open(path) as f: + parser.read_file(f) + return parser + + class NicerConfigParser(ConfigParser): def __init__(self, filename, *args, **kw): diff --git a/lib/galaxy/util/sanitize_html.py b/lib/galaxy/util/sanitize_html.py index aeaa80f8c58e..bc94574bef24 100644 --- a/lib/galaxy/util/sanitize_html.py +++ b/lib/galaxy/util/sanitize_html.py @@ -27,17 +27,17 @@ _cp1252 = { unichr(128): unichr(8364), # euro sign unichr(130): unichr(8218), # single low-9 quotation mark - unichr(131): unichr( 402), # latin small letter f with hook + unichr(131): unichr(402), # latin small letter f with hook unichr(132): unichr(8222), # double low-9 quotation mark unichr(133): unichr(8230), # horizontal ellipsis unichr(134): unichr(8224), # dagger unichr(135): unichr(8225), # double dagger - unichr(136): unichr( 710), # modifier letter circumflex accent + unichr(136): unichr(710), # modifier letter circumflex accent unichr(137): unichr(8240), # per mille sign - unichr(138): unichr( 352), # latin capital letter s with caron + unichr(138): unichr(352), # latin capital letter s with caron unichr(139): unichr(8249), # single left-pointing angle quotation mark - unichr(140): unichr( 338), # latin capital ligature oe - unichr(142): unichr( 381), # latin capital letter z with caron + unichr(140): unichr(338), # latin capital ligature oe + unichr(142): unichr(381), # latin capital letter z with caron unichr(145): unichr(8216), # left single quotation mark unichr(146): unichr(8217), # right single quotation mark unichr(147): unichr(8220), # left double quotation mark @@ -45,13 +45,13 @@ unichr(149): unichr(8226), # bullet unichr(150): unichr(8211), # en dash unichr(151): unichr(8212), # em dash - unichr(152): unichr( 732), # small tilde + unichr(152): unichr(732), # small tilde unichr(153): unichr(8482), # trade mark sign - unichr(154): unichr( 353), # latin small letter s with caron + unichr(154): unichr(353), # latin small letter s with caron unichr(155): unichr(8250), # single right-pointing angle quotation mark - unichr(156): unichr( 339), # latin small ligature oe - unichr(158): unichr( 382), # latin small letter z with caron - unichr(159): unichr( 376)} # latin capital letter y with diaeresis + unichr(156): unichr(339), # latin small ligature oe + unichr(158): unichr(382), # latin small letter z with caron + unichr(159): unichr(376)} # latin capital letter y with diaeresis class _BaseHTMLProcessor(sgmllib.SGMLParser): @@ -316,9 +316,9 @@ class _HTMLSanitizer(_BaseHTMLProcessor): svg_attr_map = None svg_elem_map = None - acceptable_svg_properties = [ 'fill', 'fill-opacity', 'fill-rule', - 'stroke', 'stroke-width', 'stroke-linecap', 'stroke-linejoin', - 'stroke-opacity'] + acceptable_svg_properties = ['fill', 'fill-opacity', 'fill-rule', + 'stroke', 'stroke-width', 'stroke-linecap', 'stroke-linejoin', + 'stroke-opacity'] def reset(self): _BaseHTMLProcessor.reset(self) diff --git a/lib/galaxy/util/simplegraph.py b/lib/galaxy/util/simplegraph.py index 280eb5740860..40a4a3fb363d 100644 --- a/lib/galaxy/util/simplegraph.py +++ b/lib/galaxy/util/simplegraph.py @@ -6,11 +6,12 @@ from galaxy.util.odict import odict -class SimpleGraphNode( object ): +class SimpleGraphNode(object): """ Node representation. """ - def __init__( self, index, **data ): + + def __init__(self, index, **data): """ :param index: index of this node in some parent list :type index: int @@ -22,11 +23,12 @@ def __init__( self, index, **data ): self.data = data -class SimpleGraphEdge( object ): +class SimpleGraphEdge(object): """ Edge representation. """ - def __init__( self, source_index, target_index, **data ): + + def __init__(self, source_index, target_index, **data): """ :param source_index: index of the edge's source node in some parent list :type source_index: int @@ -40,7 +42,7 @@ def __init__( self, source_index, target_index, **data ): self.data = data -class SimpleGraph( object ): +class SimpleGraph(object): """ Each node is unique (by id) and stores its own index in the node list/odict. Each edge is represented as two indeces into the node list/odict. @@ -53,12 +55,13 @@ class SimpleGraph( object ): These graphs are not specifically directed but since source and targets on the edges are listed - it could easily be used that way. """ - def __init__( self, nodes=None, edges=None ): + + def __init__(self, nodes=None, edges=None): # use an odict so that edge indeces actually match the final node list indeces self.nodes = nodes or odict() self.edges = edges or [] - def add_node( self, node_id, **data ): + def add_node(self, node_id, **data): """ Adds a new node only if it doesn't already exist. :param node_id: some unique identifier @@ -68,13 +71,13 @@ def add_node( self, node_id, **data ): :returns: the new node """ if node_id in self.nodes: - return self.nodes[ node_id ] - node_index = len( self.nodes ) - new_node = SimpleGraphNode( node_index, **data ) - self.nodes[ node_id ] = new_node + return self.nodes[node_id] + node_index = len(self.nodes) + new_node = SimpleGraphNode(node_index, **data) + self.nodes[node_id] = new_node return new_node - def add_edge( self, source_id, target_id, **data ): + def add_edge(self, source_id, target_id, **data): """ Adds a new node only if it doesn't already exist. :param source_id: the id of the source node @@ -92,22 +95,22 @@ def add_edge( self, source_id, target_id, **data ): # adds target_id to source_id's edge list # adding source_id and/or target_id to nodes if not there already if source_id not in self.nodes: - self.add_node( source_id ) + self.add_node(source_id) if target_id not in self.nodes: - self.add_node( target_id ) - new_edge = SimpleGraphEdge( self.nodes[ source_id ].index, self.nodes[ target_id ].index, **data ) - self.edges.append( new_edge ) + self.add_node(target_id) + new_edge = SimpleGraphEdge(self.nodes[source_id].index, self.nodes[target_id].index, **data) + self.edges.append(new_edge) return new_edge - def gen_node_dicts( self ): + def gen_node_dicts(self): """ Returns a generator that yields node dictionaries in the form: { 'id': , 'data': } """ for node_id, node in self.nodes.items(): - yield { 'id': node_id, 'data': node.data } + yield {'id': node_id, 'data': node.data} - def gen_edge_dicts( self ): + def gen_edge_dicts(self): """ Returns a generator that yields node dictionaries in the form:: @@ -118,12 +121,12 @@ def gen_edge_dicts( self ): } """ for edge in self.edges: - yield { 'source': edge.source_index, 'target': edge.target_index, 'data': edge.data } + yield {'source': edge.source_index, 'target': edge.target_index, 'data': edge.data} - def as_dict( self ): + def as_dict(self): """ Returns a dictionary of the form:: { 'nodes': , 'edges': } """ - return { 'nodes': list( self.gen_node_dicts() ), 'edges': list( self.gen_edge_dicts() ) } + return {'nodes': list(self.gen_node_dicts()), 'edges': list(self.gen_edge_dicts())} diff --git a/lib/galaxy/util/sleeper.py b/lib/galaxy/util/sleeper.py index f8d8a450f120..a1f523d38577 100644 --- a/lib/galaxy/util/sleeper.py +++ b/lib/galaxy/util/sleeper.py @@ -1,21 +1,22 @@ import threading -class Sleeper( object ): +class Sleeper(object): """ Provides a 'sleep' method that sleeps for a number of seconds *unless* the notify method is called (from a different thread). """ - def __init__( self ): + + def __init__(self): self.condition = threading.Condition() - def sleep( self, seconds ): + def sleep(self, seconds): # Should this be in a try/finally block? -John self.condition.acquire() - self.condition.wait( seconds ) + self.condition.wait(seconds) self.condition.release() - def wake( self ): + def wake(self): # Should this be in a try/finally block? -John self.condition.acquire() self.condition.notify() diff --git a/lib/galaxy/util/sockets.py b/lib/galaxy/util/sockets.py index b7946ce46e6c..ffc246627e1d 100644 --- a/lib/galaxy/util/sockets.py +++ b/lib/galaxy/util/sockets.py @@ -33,7 +33,7 @@ def __unused_port_on_range(range): col = line.split() local_address = col[3] local_port = local_address.split(':')[1] - occupied_ports.add( int(local_port) ) + occupied_ports.add(int(local_port)) # Generate random free port number. while True: diff --git a/lib/galaxy/util/specs.py b/lib/galaxy/util/specs.py index a9d35d8a2614..87821b53f682 100644 --- a/lib/galaxy/util/specs.py +++ b/lib/galaxy/util/specs.py @@ -5,28 +5,28 @@ # Utility methods for specifing maps. -def to_str_or_none( value ): +def to_str_or_none(value): if value is None: return None else: - return str( value ) + return str(value) -def to_bool_or_none( value ): - return util.string_as_bool_or_none( value ) +def to_bool_or_none(value): + return util.string_as_bool_or_none(value) -def to_bool( value ): - return util.asbool( value ) +def to_bool(value): + return util.asbool(value) -def to_float_or_none( value ): +def to_float_or_none(value): if value is None: return None else: - return float( value ) + return float(value) # Utility methods for specifing valid... -def is_in( *args ): - return functools.partial( operator.contains, args ) +def is_in(*args): + return functools.partial(operator.contains, args) diff --git a/lib/galaxy/util/streamball.py b/lib/galaxy/util/streamball.py index 8445c222d9c7..54bcb6297f85 100644 --- a/lib/galaxy/util/streamball.py +++ b/lib/galaxy/util/streamball.py @@ -7,11 +7,11 @@ from galaxy.exceptions import ObjectNotFound -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class StreamBall( object ): - def __init__( self, mode, members=None ): +class StreamBall(object): + def __init__(self, mode, members=None): self.members = members if members is None: self.members = [] @@ -19,24 +19,24 @@ def __init__( self, mode, members=None ): self.wsgi_status = None self.wsgi_headeritems = None - def add( self, file, relpath, check_file=False): + def add(self, file, relpath, check_file=False): if check_file and len(file) > 0: if not os.path.isfile(file): raise ObjectNotFound else: - self.members.append( ( file, relpath ) ) + self.members.append((file, relpath)) else: - self.members.append( ( file, relpath ) ) + self.members.append((file, relpath)) - def stream( self, environ, start_response ): - response_write = start_response( self.wsgi_status, self.wsgi_headeritems ) + def stream(self, environ, start_response): + response_write = start_response(self.wsgi_status, self.wsgi_headeritems) class tarfileobj: - def write( self, *args, **kwargs ): - response_write( *args, **kwargs ) - tf = tarfile.open( mode=self.mode, fileobj=tarfileobj() ) + def write(self, *args, **kwargs): + response_write(*args, **kwargs) + tf = tarfile.open(mode=self.mode, fileobj=tarfileobj()) for (file, rel) in self.members: - tf.add( file, arcname=rel ) + tf.add(file, arcname=rel) tf.close() return [] @@ -47,13 +47,13 @@ def __init__(self, tmpf, tmpd): self._tmpd = tmpd def stream(self, environ, start_response): - response_write = start_response( self.wsgi_status, self.wsgi_headeritems ) - tmpfh = open( self._tmpf ) + response_write = start_response(self.wsgi_status, self.wsgi_headeritems) + tmpfh = open(self._tmpf) response_write(tmpfh.read()) tmpfh.close() try: - os.unlink( self._tmpf ) - os.rmdir( self._tmpd ) + os.unlink(self._tmpf) + os.rmdir(self._tmpd) except OSError: - log.exception( "Unable to remove temporary library download archive and directory" ) + log.exception("Unable to remove temporary library download archive and directory") return [] diff --git a/lib/galaxy/util/template.py b/lib/galaxy/util/template.py index ef62853a781c..4bb70f85c61a 100644 --- a/lib/galaxy/util/template.py +++ b/lib/galaxy/util/template.py @@ -2,7 +2,7 @@ from Cheetah.Template import Template -def fill_template( template_text, context=None, **kwargs ): +def fill_template(template_text, context=None, **kwargs): """Fill a cheetah template out for specified context. If template_text is None, an exception will be thrown, if context @@ -13,4 +13,4 @@ def fill_template( template_text, context=None, **kwargs ): raise TypeError("Template text specified as None to fill_template.") if not context: context = kwargs - return str( Template( source=template_text, searchList=[context] ) ) + return str(Template(source=template_text, searchList=[context])) diff --git a/lib/galaxy/util/topsort.py b/lib/galaxy/util/topsort.py index 306792dc0261..82de81eb26e8 100644 --- a/lib/galaxy/util/topsort.py +++ b/lib/galaxy/util/topsort.py @@ -78,10 +78,10 @@ def get_pairlist(self): for x in self.get_elements(): if x in succs: for y in succs[x]: - answer.append( (x, y) ) + answer.append((x, y)) else: # make sure x appears in topsort's output! - answer.append( (x, x) ) + answer.append((x, x)) return answer # return remaining elt -> list of predecessors map @@ -194,7 +194,7 @@ def topsort_levels(pairlist): levparents = [x for x in numpreds.keys() if numpreds[x] == 0] if not levparents: break - answer.append( levparents ) + answer.append(levparents) for levparent in levparents: del numpreds[levparent] if levparent in successors: @@ -205,6 +205,6 @@ def topsort_levels(pairlist): if numpreds: # Everything in num_parents has at least one child -> # there's a cycle. - raise CycleError( answer, numpreds, successors ) + raise CycleError(answer, numpreds, successors) return answer diff --git a/lib/galaxy/util/ucsc.py b/lib/galaxy/util/ucsc.py index 4869a6ec8701..1542542b95dd 100644 --- a/lib/galaxy/util/ucsc.py +++ b/lib/galaxy/util/ucsc.py @@ -3,30 +3,31 @@ """ -class UCSCLimitException( Exception ): +class UCSCLimitException(Exception): pass -class UCSCOutWrapper( object ): +class UCSCOutWrapper(object): """File-like object that throws an exception if it encounters the UCSC limit error lines""" - def __init__( self, other ): - self.other = iter( other ) + + def __init__(self, other): + self.other = iter(other) # Need one line of lookahead to be sure we are hitting the limit message self.lookahead = None - def __iter__( self ): + def __iter__(self): return self - def next( self ): + def next(self): if self.lookahead is None: line = self.other.next() else: line = self.lookahead self.lookahead = None - if line.startswith( "----------" ): + if line.startswith("----------"): next_line = self.other.next() - if next_line.startswith( "Reached output limit" ): - raise UCSCLimitException( next_line.strip() ) + if next_line.startswith("Reached output limit"): + raise UCSCLimitException(next_line.strip()) else: self.lookahead = next_line return line diff --git a/lib/galaxy/util/validation.py b/lib/galaxy/util/validation.py index 2b27d1e735ff..a851148c5e53 100644 --- a/lib/galaxy/util/validation.py +++ b/lib/galaxy/util/validation.py @@ -8,26 +8,26 @@ from galaxy.util.sanitize_html import sanitize_html -def validate_and_sanitize_basestring( key, val ): - if not isinstance( val, string_types ): - raise exceptions.RequestParameterInvalidException( '%s must be a string or unicode: %s' - % ( key, str( type( val ) ) ) ) - return sanitize_html( val, 'utf-8', 'text/html' ) +def validate_and_sanitize_basestring(key, val): + if not isinstance(val, string_types): + raise exceptions.RequestParameterInvalidException('%s must be a string or unicode: %s' + % (key, str(type(val)))) + return sanitize_html(val, 'utf-8', 'text/html') -def validate_and_sanitize_basestring_list( key, val ): +def validate_and_sanitize_basestring_list(key, val): try: - assert isinstance( val, list ) - return [ sanitize_html( t, 'utf-8', 'text/html' ) for t in val ] - except ( AssertionError, TypeError ): - raise exceptions.RequestParameterInvalidException( '%s must be a list of strings: %s' - % ( key, str( type( val ) ) ) ) + assert isinstance(val, list) + return [sanitize_html(t, 'utf-8', 'text/html') for t in val] + except (AssertionError, TypeError): + raise exceptions.RequestParameterInvalidException('%s must be a list of strings: %s' + % (key, str(type(val)))) -def validate_boolean( key, val ): - if not isinstance( val, bool ): - raise exceptions.RequestParameterInvalidException( '%s must be a boolean: %s' - % ( key, str( type( val ) ) ) ) +def validate_boolean(key, val): + if not isinstance(val, bool): + raise exceptions.RequestParameterInvalidException('%s must be a boolean: %s' + % (key, str(type(val)))) return val diff --git a/lib/galaxy/util/xml_macros.py b/lib/galaxy/util/xml_macros.py index b9a3eac249bb..cb433ed3405e 100644 --- a/lib/galaxy/util/xml_macros.py +++ b/lib/galaxy/util/xml_macros.py @@ -219,7 +219,7 @@ def _imported_macro_paths_from_el(macros_el): raw_import_path = macro_import_el.text tool_relative_import_path = \ os.path.basename(raw_import_path) # Sanitize this - imported_macro_paths.append( tool_relative_import_path ) + imported_macro_paths.append(tool_relative_import_path) return imported_macro_paths diff --git a/lib/galaxy/visualization/data_providers/basic.py b/lib/galaxy/visualization/data_providers/basic.py index c394eb6c9c92..456ffaa420ce 100644 --- a/lib/galaxy/visualization/data_providers/basic.py +++ b/lib/galaxy/visualization/data_providers/basic.py @@ -4,40 +4,40 @@ from galaxy.datatypes.tabular import Tabular -class BaseDataProvider( object ): +class BaseDataProvider(object): """ Base class for data providers. Data providers (a) read and package data from datasets; and (b) write subsets of data to new datasets. """ - def __init__( self, converted_dataset=None, original_dataset=None, dependencies=None, - error_max_vals="Only the first %i values are returned." ): + def __init__(self, converted_dataset=None, original_dataset=None, dependencies=None, + error_max_vals="Only the first %i values are returned."): """ Create basic data provider. """ self.converted_dataset = converted_dataset self.original_dataset = original_dataset self.dependencies = dependencies self.error_max_vals = error_max_vals - def has_data( self, **kwargs ): + def has_data(self, **kwargs): """ Returns true if dataset has data in the specified genome window, false otherwise. """ - raise Exception( "Unimplemented Function" ) + raise Exception("Unimplemented Function") - def get_iterator( self, **kwargs ): + def get_iterator(self, **kwargs): """ Returns an iterator that provides data in the region chrom:start-end """ - raise Exception( "Unimplemented Function" ) + raise Exception("Unimplemented Function") - def process_data( self, iterator, start_val=0, max_vals=None, **kwargs ): + def process_data(self, iterator, start_val=0, max_vals=None, **kwargs): """ Process data from an iterator to a format that can be provided to client. """ - raise Exception( "Unimplemented Function" ) + raise Exception("Unimplemented Function") - def get_data( self, chrom, start, end, start_val=0, max_vals=sys.maxint, **kwargs ): + def get_data(self, chrom, start, end, start_val=0, max_vals=sys.maxint, **kwargs): """ Returns data as specified by kwargs. start_val is the first element to return and max_vals indicates the number of values to return. @@ -45,151 +45,151 @@ def get_data( self, chrom, start, end, start_val=0, max_vals=sys.maxint, **kwarg Return value must be a dictionary with the following attributes: dataset_type, data """ - iterator = self.get_iterator( chrom, start, end ) - return self.process_data( iterator, start_val, max_vals, **kwargs ) + iterator = self.get_iterator(chrom, start, end) + return self.process_data(iterator, start_val, max_vals, **kwargs) - def write_data_to_file( self, filename, **kwargs ): + def write_data_to_file(self, filename, **kwargs): """ Write data in region defined by chrom, start, and end to a file. """ - raise Exception( "Unimplemented Function" ) + raise Exception("Unimplemented Function") -class ColumnDataProvider( BaseDataProvider ): +class ColumnDataProvider(BaseDataProvider): """ Data provider for columnar data """ MAX_LINES_RETURNED = 30000 - def __init__( self, original_dataset, max_lines_returned=MAX_LINES_RETURNED ): + def __init__(self, original_dataset, max_lines_returned=MAX_LINES_RETURNED): # Compatibility check. - if not isinstance( original_dataset.datatype, Tabular ): - raise Exception( "Data provider can only be used with tabular data" ) + if not isinstance(original_dataset.datatype, Tabular): + raise Exception("Data provider can only be used with tabular data") # Attribute init. self.original_dataset = original_dataset # allow throttling self.max_lines_returned = max_lines_returned - def get_data( self, columns=None, start_val=0, max_vals=None, skip_comments=True, **kwargs ): + def get_data(self, columns=None, start_val=0, max_vals=None, skip_comments=True, **kwargs): """ Returns data from specified columns in dataset. Format is list of lists where each list is a line of data. """ if not columns: - raise TypeError( 'parameter required: columns' ) + raise TypeError('parameter required: columns') # TODO: validate kwargs try: - max_vals = int( max_vals ) - max_vals = min([ max_vals, self.max_lines_returned ]) - except ( ValueError, TypeError ): + max_vals = int(max_vals) + max_vals = min([max_vals, self.max_lines_returned]) + except (ValueError, TypeError): max_vals = self.max_lines_returned try: - start_val = int( start_val ) - start_val = max([ start_val, 0 ]) - except ( ValueError, TypeError ): + start_val = int(start_val) + start_val = max([start_val, 0]) + except (ValueError, TypeError): start_val = 0 # skip comment lines (if any/avail) # pre: should have original_dataset and - if( skip_comments and + if(skip_comments and self.original_dataset.metadata.comment_lines and - start_val < self.original_dataset.metadata.comment_lines ): - start_val = int( self.original_dataset.metadata.comment_lines ) + start_val < self.original_dataset.metadata.comment_lines): + start_val = int(self.original_dataset.metadata.comment_lines) # columns is an array of ints for now (should handle column names later) - columns = loads( columns ) + columns = loads(columns) for column in columns: - assert( ( column < self.original_dataset.metadata.columns ) and - ( column >= 0 ) ), ( - "column index (%d) must be positive and less" % ( column ) + - " than the number of columns: %d" % ( self.original_dataset.metadata.columns ) ) + assert((column < self.original_dataset.metadata.columns) and + (column >= 0)), ( + "column index (%d) must be positive and less" % (column) + + " than the number of columns: %d" % (self.original_dataset.metadata.columns)) # print columns, start_val, max_vals, skip_comments, kwargs # set up the response, column lists response = {} - response[ 'data' ] = data = [ [] for column in columns ] - response[ 'meta' ] = meta = [{ + response['data'] = data = [[] for column in columns] + response['meta'] = meta = [{ 'min' : None, 'max' : None, 'count' : 0, 'sum' : 0 - } for column in columns ] + } for column in columns] - column_types = [ self.original_dataset.metadata.column_types[ column ] for column in columns ] + column_types = [self.original_dataset.metadata.column_types[column] for column in columns] # function for casting by column_types - def cast_val( val, type ): + def cast_val(val, type): """ Cast value based on type. Return None if can't be cast """ if type == 'int': try: - val = int( val ) + val = int(val) except: return None elif type == 'float': try: - val = float( val ) + val = float(val) except: return None return val returning_data = False - f = open( self.original_dataset.file_name ) + f = open(self.original_dataset.file_name) # TODO: add f.seek if given fptr in kwargs - for count, line in enumerate( f ): + for count, line in enumerate(f): # check line v. desired start, end if count < start_val: continue - if ( count - start_val ) >= max_vals: + if (count - start_val) >= max_vals: break returning_data = True fields = line.split() - fields_len = len( fields ) + fields_len = len(fields) # NOTE: this will return None/null for abberrant column values (including bad indeces) - for index, column in enumerate( columns ): + for index, column in enumerate(columns): column_val = None - column_type = column_types[ index ] + column_type = column_types[index] if column < fields_len: - column_val = cast_val( fields[ column ], column_type ) + column_val = cast_val(fields[column], column_type) if column_val is not None: # if numeric, maintain min, max, sum - if( column_type == 'float' or column_type == 'int' ): - if( ( meta[ index ][ 'min' ] is None ) or ( column_val < meta[ index ][ 'min' ] ) ): - meta[ index ][ 'min' ] = column_val + if(column_type == 'float' or column_type == 'int'): + if((meta[index]['min'] is None) or (column_val < meta[index]['min'])): + meta[index]['min'] = column_val - if( ( meta[ index ][ 'max' ] is None ) or ( column_val > meta[ index ][ 'max' ] ) ): - meta[ index ][ 'max' ] = column_val + if((meta[index]['max'] is None) or (column_val > meta[index]['max'])): + meta[index]['max'] = column_val - meta[ index ][ 'sum' ] += column_val + meta[index]['sum'] += column_val # maintain a count - for other stats - meta[ index ][ 'count' ] += 1 - data[ index ].append( column_val ) + meta[index]['count'] += 1 + data[index].append(column_val) - response[ 'endpoint' ] = dict( last_line=( count - 1 ), file_ptr=f.tell() ) + response['endpoint'] = dict(last_line=(count - 1), file_ptr=f.tell()) f.close() if not returning_data: return None - for index, meta in enumerate( response[ 'meta' ] ): - column_type = column_types[ index ] - count = meta[ 'count' ] + for index, meta in enumerate(response['meta']): + column_type = column_types[index] + count = meta['count'] - if( ( column_type == 'float' or column_type == 'int' ) and count ): - meta[ 'mean' ] = float( meta[ 'sum' ] ) / count + if((column_type == 'float' or column_type == 'int') and count): + meta['mean'] = float(meta['sum']) / count - sorted_data = sorted( response[ 'data' ][ index ] ) - middle_index = ( count / 2 ) - 1 + sorted_data = sorted(response['data'][index]) + middle_index = (count / 2) - 1 if count % 2 == 0: - meta[ 'median' ] = ( ( sorted_data[ middle_index ] + sorted_data[( middle_index + 1 )] ) / 2.0 ) + meta['median'] = ((sorted_data[middle_index] + sorted_data[(middle_index + 1)]) / 2.0) else: - meta[ 'median' ] = sorted_data[ middle_index ] + meta['median'] = sorted_data[middle_index] # ugh ... metadata_data_lines is not a reliable source; hafta have an EOF return response diff --git a/lib/galaxy/visualization/data_providers/cigar.py b/lib/galaxy/visualization/data_providers/cigar.py index 2ed6f2865073..b65072c77fbd 100644 --- a/lib/galaxy/visualization/data_providers/cigar.py +++ b/lib/galaxy/visualization/data_providers/cigar.py @@ -5,7 +5,7 @@ import operator -def get_ref_based_read_seq_and_cigar( read_seq, read_start, ref_seq, ref_seq_start, cigar ): +def get_ref_based_read_seq_and_cigar(read_seq, read_start, ref_seq, ref_seq_start, cigar): ''' Returns a ( new_read_seq, new_cigar ) that can be used with reference sequence to reconstruct the read. The new read sequence includes only @@ -33,49 +33,49 @@ def get_ref_based_read_seq_and_cigar( read_seq, read_start, ref_seq, ref_seq_sta # Transform Ms to =s and Xs using reference. new_op = '' total_count = 0 - while total_count < op_len and ref_seq_pos < len( ref_seq ): - match, count = _match_mismatch_counter( read_seq, read_pos, ref_seq, ref_seq_pos ) + while total_count < op_len and ref_seq_pos < len(ref_seq): + match, count = _match_mismatch_counter(read_seq, read_pos, ref_seq, ref_seq_pos) # Use min because count cannot exceed remainder of operation. - count = min( count, op_len - total_count ) + count = min(count, op_len - total_count) if match: new_op = '=' else: new_op = 'X' # Include mismatched bases in new read sequence. - new_read_seq += read_seq[ read_pos:read_pos + count ] - new_cigar += '%i%s' % ( count, new_op ) + new_read_seq += read_seq[read_pos:read_pos + count] + new_cigar += '%i%s' % (count, new_op) total_count += count read_pos += count ref_seq_pos += count # If end of read falls outside of ref_seq data, leave as M. if total_count < op_len: - new_cigar += '%iM' % ( op_len - total_count ) + new_cigar += '%iM' % (op_len - total_count) elif op == 1: # Insertion - new_cigar += '%i%s' % ( op_len, cigar_ops[ op ] ) + new_cigar += '%i%s' % (op_len, cigar_ops[op]) # Include insertion bases in new read sequence. - new_read_seq += read_seq[ read_pos:read_pos + op_len ] + new_read_seq += read_seq[read_pos:read_pos + op_len] read_pos += op_len - elif op in [ 2, 3, 6 ]: # Deletion, Skip, or Padding + elif op in [2, 3, 6]: # Deletion, Skip, or Padding ref_seq_pos += op_len - new_cigar += '%i%s' % ( op_len, cigar_ops[ op ] ) + new_cigar += '%i%s' % (op_len, cigar_ops[op]) elif op == 4: # Soft clipping read_pos += op_len - new_cigar += '%i%s' % ( op_len, cigar_ops[ op ] ) + new_cigar += '%i%s' % (op_len, cigar_ops[op]) elif op == 5: # Hard clipping - new_cigar += '%i%s' % ( op_len, cigar_ops[ op ] ) - elif op in [ 7, 8 ]: # Match or mismatch + new_cigar += '%i%s' % (op_len, cigar_ops[op]) + elif op in [7, 8]: # Match or mismatch if op == 8: # Include mismatched bases in new read sequence. - new_read_seq += read_seq[ read_pos:read_pos + op_len ] + new_read_seq += read_seq[read_pos:read_pos + op_len] read_pos += op_len ref_seq_pos += op_len - new_cigar += '%i%s' % ( op_len, cigar_ops[ op ] ) + new_cigar += '%i%s' % (op_len, cigar_ops[op]) - return ( new_read_seq, new_cigar ) + return (new_read_seq, new_cigar) -def _match_mismatch_counter( s1, p1, s2, p2 ): +def _match_mismatch_counter(s1, p1, s2, p2): ''' Count consecutive matches/mismatches between strings s1 and s2 starting at p1 and p2, respectively. @@ -83,7 +83,7 @@ def _match_mismatch_counter( s1, p1, s2, p2 ): # Do initial comparison to determine whether to count matches or # mismatches. - if s1[ p1 ] == s2[ p2 ]: + if s1[p1] == s2[p2]: cmp_fn = operator.eq match = True else: @@ -96,7 +96,7 @@ def _match_mismatch_counter( s1, p1, s2, p2 ): p2 += 1 # Count matches/mismatches. - while p1 < len( s1 ) and p2 < len( s2 ) and cmp_fn( s1[ p1 ], s2[ p2 ] ): + while p1 < len(s1) and p2 < len(s2) and cmp_fn(s1[p1], s2[p2]): count += 1 p1 += 1 p2 += 1 diff --git a/lib/galaxy/visualization/data_providers/genome.py b/lib/galaxy/visualization/data_providers/genome.py index c96b9f296786..460765eb3d57 100644 --- a/lib/galaxy/visualization/data_providers/genome.py +++ b/lib/galaxy/visualization/data_providers/genome.py @@ -36,59 +36,59 @@ def float_nan(n): return float(n) -def get_bounds( reads, start_pos_index, end_pos_index ): +def get_bounds(reads, start_pos_index, end_pos_index): ''' Returns the minimum and maximum position for a set of reads. ''' max_low = sys.maxint max_high = -sys.maxint for read in reads: - if read[ start_pos_index ] < max_low: - max_low = read[ start_pos_index ] - if read[ end_pos_index ] > max_high: - max_high = read[ end_pos_index ] + if read[start_pos_index] < max_low: + max_low = read[start_pos_index] + if read[end_pos_index] > max_high: + max_high = read[end_pos_index] return max_low, max_high -def _convert_between_ucsc_and_ensemble_naming( chrom ): +def _convert_between_ucsc_and_ensemble_naming(chrom): ''' Convert between UCSC chromosome ('chr1') naming conventions and Ensembl naming conventions ('1') ''' - if chrom.startswith( 'chr' ): + if chrom.startswith('chr'): # Convert from UCSC to Ensembl - return chrom[ 3: ] + return chrom[3:] else: # Convert from Ensembl to UCSC return 'chr' + chrom -def _chrom_naming_matches( chrom1, chrom2 ): - return ( chrom1.startswith( 'chr' ) and chrom2.startswith( 'chr' ) ) or ( not chrom1.startswith( 'chr' ) and not chrom2.startswith( 'chr' ) ) +def _chrom_naming_matches(chrom1, chrom2): + return (chrom1.startswith('chr') and chrom2.startswith('chr')) or (not chrom1.startswith('chr') and not chrom2.startswith('chr')) -class FeatureLocationIndexDataProvider( BaseDataProvider ): +class FeatureLocationIndexDataProvider(BaseDataProvider): """ Reads/writes/queries feature location index (FLI) datasets. """ - def __init__( self, converted_dataset ): + def __init__(self, converted_dataset): self.converted_dataset = converted_dataset - def get_data( self, query ): + def get_data(self, query): # Init. - textloc_file = open( self.converted_dataset.file_name, 'r' ) - line_len = int( textloc_file.readline() ) - file_len = os.path.getsize( self.converted_dataset.file_name ) + textloc_file = open(self.converted_dataset.file_name, 'r') + line_len = int(textloc_file.readline()) + file_len = os.path.getsize(self.converted_dataset.file_name) query = query.lower() # Find query in file using binary search. low = 0 high = file_len / line_len while low < high: - mid = ( low + high ) // 2 + mid = (low + high) // 2 position = mid * line_len - textloc_file.seek( position ) + textloc_file.seek(position) # Compare line with query and update low, high. line = textloc_file.readline() @@ -100,23 +100,23 @@ def get_data( self, query ): # Need to move back one line because last line read may be included in # results. position = low * line_len - textloc_file.seek( position ) + textloc_file.seek(position) # At right point in file, generate hits. result = [] while True: line = textloc_file.readline() - if not line.startswith( query ): + if not line.startswith(query): break - if line[ -1: ] == '\n': - line = line[ :-1 ] - result.append( line.split()[1:] ) + if line[-1:] == '\n': + line = line[:-1] + result.append(line.split()[1:]) textloc_file.close() return result -class GenomeDataProvider( BaseDataProvider ): +class GenomeDataProvider(BaseDataProvider): """ Base class for genome data providers. All genome providers use BED coordinate format (0-based, half-open coordinates) for both queries and returned data. @@ -133,51 +133,51 @@ class GenomeDataProvider( BaseDataProvider ): """ col_name_data_attr_mapping = {} - def __init__( self, converted_dataset=None, original_dataset=None, dependencies=None, - error_max_vals="Only the first %i %s in this region are displayed." ): - super( GenomeDataProvider, self ).__init__( converted_dataset=converted_dataset, - original_dataset=original_dataset, - dependencies=dependencies, - error_max_vals=error_max_vals ) + def __init__(self, converted_dataset=None, original_dataset=None, dependencies=None, + error_max_vals="Only the first %i %s in this region are displayed."): + super(GenomeDataProvider, self).__init__(converted_dataset=converted_dataset, + original_dataset=original_dataset, + dependencies=dependencies, + error_max_vals=error_max_vals) - def write_data_to_file( self, regions, filename ): + def write_data_to_file(self, regions, filename): """ Write data in region defined by chrom, start, and end to a file. """ - raise Exception( "Unimplemented Function" ) + raise Exception("Unimplemented Function") - def valid_chroms( self ): + def valid_chroms(self): """ Returns chroms/contigs that the dataset contains """ return None # by default - def has_data( self, chrom, start, end, **kwargs ): + def has_data(self, chrom, start, end, **kwargs): """ Returns true if dataset has data in the specified genome window, false otherwise. """ - raise Exception( "Unimplemented Function" ) + raise Exception("Unimplemented Function") - def open_data_file( self ): + def open_data_file(self): """ Open data file for reading data. """ - raise Exception( "Unimplemented Function" ) + raise Exception("Unimplemented Function") - def get_iterator( self, data_file, chrom, start, end, **kwargs ): + def get_iterator(self, data_file, chrom, start, end, **kwargs): """ Returns an iterator that provides data in the region chrom:start-end """ - raise Exception( "Unimplemented Function" ) + raise Exception("Unimplemented Function") - def process_data( self, iterator, start_val=0, max_vals=None, **kwargs ): + def process_data(self, iterator, start_val=0, max_vals=None, **kwargs): """ Process data from an iterator to a format that can be provided to client. """ - raise Exception( "Unimplemented Function" ) + raise Exception("Unimplemented Function") - def get_data( self, chrom=None, low=None, high=None, start_val=0, max_vals=sys.maxint, **kwargs ): + def get_data(self, chrom=None, low=None, high=None, start_val=0, max_vals=sys.maxint, **kwargs): """ Returns data in region defined by chrom, start, and end. start_val and max_vals are used to denote the data to return: start_val is the first element to @@ -186,10 +186,10 @@ def get_data( self, chrom=None, low=None, high=None, start_val=0, max_vals=sys.m Return value must be a dictionary with the following attributes: dataset_type, data """ - start, end = int( low ), int( high ) + start, end = int(low), int(high) data_file = self.open_data_file() - iterator = self.get_iterator( data_file, chrom, start, end, **kwargs ) - data = self.process_data( iterator, start_val, max_vals, start=start, end=end, **kwargs ) + iterator = self.get_iterator(data_file, chrom, start, end, **kwargs) + data = self.process_data(iterator, start_val, max_vals, start=start, end=end, **kwargs) try: data_file.close() except AttributeError: @@ -200,15 +200,15 @@ def get_data( self, chrom=None, low=None, high=None, start_val=0, max_vals=sys.m return data - def get_genome_data( self, chroms_info, **kwargs ): + def get_genome_data(self, chroms_info, **kwargs): """ Returns data for complete genome. """ genome_data = [] - for chrom_info in chroms_info[ 'chrom_info' ]: - chrom = chrom_info[ 'chrom' ] - chrom_len = chrom_info[ 'len' ] - chrom_data = self.get_data( chrom, 0, chrom_len, **kwargs ) + for chrom_info in chroms_info['chrom_info']: + chrom = chrom_info['chrom'] + chrom_len = chrom_info['len'] + chrom_data = self.get_data(chrom, 0, chrom_len, **kwargs) # FIXME: data providers probably should never return None. # Some data providers return None when there's no data, so # create a dummy dict if necessary. @@ -216,15 +216,15 @@ def get_genome_data( self, chroms_info, **kwargs ): chrom_data = { 'data': None } - chrom_data[ 'region' ] = "%s:%i-%i" % ( chrom, 0, chrom_len ) - genome_data.append( chrom_data ) + chrom_data['region'] = "%s:%i-%i" % (chrom, 0, chrom_len) + genome_data.append(chrom_data) return { 'data': genome_data, 'dataset_type': self.dataset_type } - def get_filters( self ): + def get_filters(self): """ Returns filters for provider's data. Return value is a list of filters; each filter is a dictionary with the keys 'name', 'index', 'type'. @@ -236,7 +236,7 @@ def get_filters( self ): column_names = self.original_dataset.datatype.column_names except AttributeError: try: - column_names = range( self.original_dataset.metadata.columns ) + column_names = range(self.original_dataset.metadata.columns) except: # Give up return [] @@ -252,20 +252,20 @@ def get_filters( self ): for viz_col_index in self.original_dataset.metadata.viz_filter_cols: # Some columns are optional, so can't assume that a filter # column is in dataset. - if viz_col_index >= len( column_names ): + if viz_col_index >= len(column_names): continue - col_name = column_names[ viz_col_index ] + col_name = column_names[viz_col_index] # Make sure that column has a mapped index. If not, do not add filter. try: - attrs = self.col_name_data_attr_mapping[ col_name ] + attrs = self.col_name_data_attr_mapping[col_name] except KeyError: continue - filters.append( { 'name': attrs[ 'name' ], - 'type': column_types[viz_col_index], - 'index': attrs[ 'index' ] } ) + filters.append({'name': attrs['name'], + 'type': column_types[viz_col_index], + 'index': attrs['index']}) return filters - def get_default_max_vals( self ): + def get_default_max_vals(self): return 5000 # @@ -274,20 +274,20 @@ def get_default_max_vals( self ): class FilterableMixin: - def get_filters( self ): + def get_filters(self): """ Returns a dataset's filters. """ # is_ functions taken from Tabular.set_meta - def is_int( column_text ): + def is_int(column_text): try: - int( column_text ) + int(column_text) return True except: return False - def is_float( column_text ): + def is_float(column_text): try: - float( column_text ) + float(column_text) return True except: if column_text.strip().lower() == 'na': @@ -303,51 +303,50 @@ def is_float( column_text ): filters = [] # HACK: first 8 fields are for drawing, so start filter column index at 9. filter_col = 8 - if isinstance( self.original_dataset.datatype, Gff ): + if isinstance(self.original_dataset.datatype, Gff): # Can filter by score and GTF attributes. - filters = [ { 'name': 'Score', - 'type': 'number', - 'index': filter_col, - 'tool_id': 'Filter1', - 'tool_exp_name': 'c6' } ] + filters = [{'name': 'Score', + 'type': 'number', + 'index': filter_col, + 'tool_id': 'Filter1', + 'tool_exp_name': 'c6'}] filter_col += 1 - if isinstance( self.original_dataset.datatype, Gtf ): + if isinstance(self.original_dataset.datatype, Gtf): # Create filters based on dataset metadata. for name, a_type in self.original_dataset.metadata.attribute_types.items(): - if a_type in [ 'int', 'float' ]: + if a_type in ['int', 'float']: filters.append( - { 'name': name, - 'type': 'number', - 'index': filter_col, - 'tool_id': 'gff_filter_by_attribute', - 'tool_exp_name': name } ) + {'name': name, + 'type': 'number', + 'index': filter_col, + 'tool_id': 'gff_filter_by_attribute', + 'tool_exp_name': name}) filter_col += 1 - elif isinstance( self.original_dataset.datatype, Bed ): + elif isinstance(self.original_dataset.datatype, Bed): # Can filter by score column only. - filters = [ { 'name': 'Score', - 'type': 'number', - 'index': filter_col, - 'tool_id': 'Filter1', - 'tool_exp_name': 'c5' - } ] + filters = [{'name': 'Score', + 'type': 'number', + 'index': filter_col, + 'tool_id': 'Filter1', + 'tool_exp_name': 'c5'}] return filters -class TabixDataProvider( FilterableMixin, GenomeDataProvider ): +class TabixDataProvider(FilterableMixin, GenomeDataProvider): dataset_type = 'tabix' """ Tabix index data provider for the Galaxy track browser. """ - col_name_data_attr_mapping = { 4: { 'index': 4, 'name': 'Score' } } + col_name_data_attr_mapping = {4: {'index': 4, 'name': 'Score'}} - def open_data_file( self ): + def open_data_file(self): return ctabix.Tabixfile(self.dependencies['bgzip'].file_name, index=self.converted_dataset.file_name) - def get_iterator( self, data_file, chrom, start, end, **kwargs ): + def get_iterator(self, data_file, chrom, start, end, **kwargs): # chrom must be a string, start/end integers. # in previous versions of pysam, unicode was accepted for chrom, but not in 8.4 chrom = str(chrom) @@ -356,26 +355,26 @@ def get_iterator( self, data_file, chrom, start, end, **kwargs ): if end >= (2 << 29): end = (2 << 29 - 1) # Tabix-enforced maximum # Get iterator using either naming scheme. - iterator = iter( [] ) + iterator = iter([]) if chrom in data_file.contigs: iterator = data_file.fetch(reference=chrom, start=start, end=end) else: # Try alternative naming scheme. - chrom = _convert_between_ucsc_and_ensemble_naming( chrom ) + chrom = _convert_between_ucsc_and_ensemble_naming(chrom) if chrom in data_file.contigs: iterator = data_file.fetch(reference=chrom, start=start, end=end) return iterator - def write_data_to_file( self, regions, filename ): - out = open( filename, "w" ) + def write_data_to_file(self, regions, filename): + out = open(filename, "w") data_file = self.open_data_file() for region in regions: # Write data in region. - iterator = self.get_iterator( data_file, region.chrom, region.start, region.end ) + iterator = self.get_iterator(data_file, region.chrom, region.start, region.end) for line in iterator: - out.write( "%s\n" % line ) + out.write("%s\n" % line) # TODO: once Pysam is updated and Tabixfile has a close() method, # data_file.close() @@ -387,7 +386,7 @@ def write_data_to_file( self, regions, filename ): # -class IntervalDataProvider( GenomeDataProvider ): +class IntervalDataProvider(GenomeDataProvider): dataset_type = 'interval_index' """ @@ -396,10 +395,10 @@ class IntervalDataProvider( GenomeDataProvider ): Payload format: [ uid (offset), start, end, name, strand, thick_start, thick_end, blocks ] """ - def get_iterator( self, data_file, chrom, start, end, **kwargs ): - raise Exception( "Unimplemented Function" ) + def get_iterator(self, data_file, chrom, start, end, **kwargs): + raise Exception("Unimplemented Function") - def process_data( self, iterator, start_val=0, max_vals=None, **kwargs ): + def process_data(self, iterator, start_val=0, max_vals=None, **kwargs): """ Provides """ @@ -408,8 +407,8 @@ def process_data( self, iterator, start_val=0, max_vals=None, **kwargs ): # # First three entries are mandatory, others are optional. # - filter_cols = loads( kwargs.get( "filter_cols", "[]" ) ) - no_detail = ( "no_detail" in kwargs ) + filter_cols = loads(kwargs.get("filter_cols", "[]")) + no_detail = ("no_detail" in kwargs) rval = [] message = None @@ -419,49 +418,49 @@ def col_fn(col): start_col = self.original_dataset.metadata.startCol - 1 end_col = self.original_dataset.metadata.endCol - 1 - strand_col = col_fn( self.original_dataset.metadata.strandCol ) - name_col = col_fn( self.original_dataset.metadata.nameCol ) - for count, line in enumerate( iterator ): + strand_col = col_fn(self.original_dataset.metadata.strandCol) + name_col = col_fn(self.original_dataset.metadata.nameCol) + for count, line in enumerate(iterator): if count < start_val: continue if max_vals and count - start_val >= max_vals: - message = self.error_max_vals % ( max_vals, "features" ) + message = self.error_max_vals % (max_vals, "features") break feature = line.split() length = len(feature) # Unique id is just a hash of the line - payload = [ hash(line), int( feature[start_col] ), int( feature[end_col] ) ] + payload = [hash(line), int(feature[start_col]), int(feature[end_col])] if no_detail: - rval.append( payload ) + rval.append(payload) continue # Name, strand. if name_col: - payload.append( feature[name_col] ) + payload.append(feature[name_col]) if strand_col: # Put empty name as placeholder. if not name_col: - payload.append( "" ) - payload.append( feature[strand_col] ) + payload.append("") + payload.append(feature[strand_col]) # Score (filter data) if length >= 5 and filter_cols and filter_cols[0] == "Score": try: - payload.append( float( feature[4] ) ) + payload.append(float(feature[4])) except: - payload.append( feature[4] ) + payload.append(feature[4]) - rval.append( payload ) + rval.append(payload) - return { 'data': rval, 'message': message } + return {'data': rval, 'message': message} - def write_data_to_file( self, regions, filename ): - raise Exception( "Unimplemented Function" ) + def write_data_to_file(self, regions, filename): + raise Exception("Unimplemented Function") -class IntervalTabixDataProvider( TabixDataProvider, IntervalDataProvider ): +class IntervalTabixDataProvider(TabixDataProvider, IntervalDataProvider): """ Provides data from a BED file indexed via tabix. """ @@ -472,7 +471,7 @@ class IntervalTabixDataProvider( TabixDataProvider, IntervalDataProvider ): # -- BED data providers -- # -class BedDataProvider( GenomeDataProvider ): +class BedDataProvider(GenomeDataProvider): """ Processes BED data from native format to payload format. @@ -481,10 +480,10 @@ class BedDataProvider( GenomeDataProvider ): dataset_type = 'interval_index' - def get_iterator( self, data_file, chrom, start, end, **kwargs ): - raise Exception( "Unimplemented Method" ) + def get_iterator(self, data_file, chrom, start, end, **kwargs): + raise Exception("Unimplemented Method") - def process_data( self, iterator, start_val=0, max_vals=None, **kwargs ): + def process_data(self, iterator, start_val=0, max_vals=None, **kwargs): """ Provides """ @@ -494,15 +493,15 @@ def process_data( self, iterator, start_val=0, max_vals=None, **kwargs ): # # First three entries are mandatory, others are optional. # - filter_cols = loads( kwargs.get( "filter_cols", "[]" ) ) - no_detail = ( "no_detail" in kwargs ) + filter_cols = loads(kwargs.get("filter_cols", "[]")) + no_detail = ("no_detail" in kwargs) rval = [] message = None - for count, line in enumerate( iterator ): + for count, line in enumerate(iterator): if count < start_val: continue if max_vals and count - start_val >= max_vals: - message = self.error_max_vals % ( max_vals, "features" ) + message = self.error_max_vals % (max_vals, "features") break # TODO: can we use column metadata to fill out payload? # TODO: use function to set payload data @@ -510,10 +509,10 @@ def process_data( self, iterator, start_val=0, max_vals=None, **kwargs ): feature = line.split() length = len(feature) # Unique id is just a hash of the line - payload = [ hash(line), int(feature[1]), int(feature[2]) ] + payload = [hash(line), int(feature[1]), int(feature[2])] if no_detail: - rval.append( payload ) + rval.append(payload) continue # Name, strand, thick start, thick end. @@ -527,29 +526,29 @@ def process_data( self, iterator, start_val=0, max_vals=None, **kwargs ): # Blocks. if length >= 12: - block_sizes = [ int(n) for n in feature[10].split(',') if n != ''] - block_starts = [ int(n) for n in feature[11].split(',') if n != '' ] - blocks = zip( block_sizes, block_starts ) - payload.append( [ ( int(feature[1]) + block[1], int(feature[1]) + block[1] + block[0] ) for block in blocks ] ) + block_sizes = [int(n) for n in feature[10].split(',') if n != ''] + block_starts = [int(n) for n in feature[11].split(',') if n != ''] + blocks = zip(block_sizes, block_starts) + payload.append([(int(feature[1]) + block[1], int(feature[1]) + block[1] + block[0]) for block in blocks]) # Score (filter data) if length >= 5 and filter_cols and filter_cols[0] == "Score": # If dataset doesn't have name/strand/thick start/thick end/blocks, # add placeholders. There should be 8 entries if all attributes # are present. - payload.extend( [ None for i in range( 8 - len( payload ) ) ] ) + payload.extend([None for i in range(8 - len(payload))]) try: - payload.append( float( feature[4] ) ) + payload.append(float(feature[4])) except: - payload.append( feature[4] ) + payload.append(feature[4]) - rval.append( payload ) + rval.append(payload) - return { 'data': rval, 'dataset_type': self.dataset_type, 'message': message } + return {'data': rval, 'dataset_type': self.dataset_type, 'message': message} - def write_data_to_file( self, regions, filename ): - out = open( filename, "w" ) + def write_data_to_file(self, regions, filename): + out = open(filename, "w") for region in regions: # Write data in region. @@ -557,22 +556,22 @@ def write_data_to_file( self, regions, filename ): start = region.start end = region.end data_file = self.open_data_file() - iterator = self.get_iterator( data_file, chrom, start, end ) + iterator = self.get_iterator(data_file, chrom, start, end) for line in iterator: - out.write( "%s\n" % line ) + out.write("%s\n" % line) data_file.close() out.close() -class BedTabixDataProvider( TabixDataProvider, BedDataProvider ): +class BedTabixDataProvider(TabixDataProvider, BedDataProvider): """ Provides data from a BED file indexed via tabix. """ pass -class RawBedDataProvider( BedDataProvider ): +class RawBedDataProvider(BedDataProvider): """ Provide data from BED file. @@ -580,26 +579,26 @@ class RawBedDataProvider( BedDataProvider ): for large datasets. """ - def get_iterator( self, data_file, chrom=None, start=None, end=None, **kwargs ): + def get_iterator(self, data_file, chrom=None, start=None, end=None, **kwargs): # Read first line in order to match chrom naming format. line = data_file.readline() dataset_chrom = line.split()[0] - if not _chrom_naming_matches( chrom, dataset_chrom ): - chrom = _convert_between_ucsc_and_ensemble_naming( chrom ) + if not _chrom_naming_matches(chrom, dataset_chrom): + chrom = _convert_between_ucsc_and_ensemble_naming(chrom) # Undo read. - data_file.seek( 0 ) + data_file.seek(0) def line_filter_iter(): - for line in open( self.original_dataset.file_name ): - if line.startswith( "track" ) or line.startswith( "browser" ): + for line in open(self.original_dataset.file_name): + if line.startswith("track") or line.startswith("browser"): continue feature = line.split() feature_chrom = feature[0] - feature_start = int( feature[1] ) - feature_end = int( feature[2] ) - if ( chrom is not None and feature_chrom != chrom ) \ - or ( start is not None and feature_start > end ) \ - or ( end is not None and feature_end < start ): + feature_start = int(feature[1]) + feature_end = int(feature[2]) + if (chrom is not None and feature_chrom != chrom) \ + or (start is not None and feature_start > end) \ + or (end is not None and feature_end < start): continue yield line @@ -610,7 +609,7 @@ def line_filter_iter(): # -class VcfDataProvider( GenomeDataProvider ): +class VcfDataProvider(GenomeDataProvider): """ Abstract class that processes VCF data from native format to payload format. @@ -627,11 +626,11 @@ class VcfDataProvider( GenomeDataProvider ): 8-end: allele counts for each alternative """ - col_name_data_attr_mapping = { 'Qual': { 'index': 6, 'name': 'Qual' } } + col_name_data_attr_mapping = {'Qual': {'index': 6, 'name': 'Qual'}} dataset_type = 'variant' - def process_data( self, iterator, start_val=0, max_vals=None, **kwargs ): + def process_data(self, iterator, start_val=0, max_vals=None, **kwargs): """ Returns a dict with the following attributes:: @@ -647,7 +646,7 @@ def process_data( self, iterator, start_val=0, max_vals=None, **kwargs ): data = [] message = None - def get_mapping( ref, alt ): + def get_mapping(ref, alt): """ Returns ( offset, new_seq, cigar ) tuple that defines mapping of alt to ref. Cigar format is an array of [ op_index, length ] pairs @@ -656,43 +655,43 @@ def get_mapping( ref, alt ): cig_ops = "MIDNSHP=X" - ref_len = len( ref ) - alt_len = len( alt ) + ref_len = len(ref) + alt_len = len(alt) # Substitutions? if ref_len == alt_len: - return 0, alt, [ [ cig_ops.find( "M" ), ref_len ] ] + return 0, alt, [[cig_ops.find("M"), ref_len]] # Deletions? - alt_in_ref_index = ref.find( alt ) + alt_in_ref_index = ref.find(alt) if alt_in_ref_index != -1: - return alt_in_ref_index, ref[ alt_in_ref_index + 1: ], [ [ cig_ops.find( "D" ), ref_len - alt_len ] ] + return alt_in_ref_index, ref[alt_in_ref_index + 1:], [[cig_ops.find("D"), ref_len - alt_len]] # Insertions? - ref_in_alt_index = alt.find( ref ) + ref_in_alt_index = alt.find(ref) if ref_in_alt_index != -1: - return ref_in_alt_index, alt[ ref_in_alt_index + 1: ], [ [ cig_ops.find( "I" ), alt_len - ref_len ] ] + return ref_in_alt_index, alt[ref_in_alt_index + 1:], [[cig_ops.find("I"), alt_len - ref_len]] # Pack data. - genotype_re = re.compile( '/|\|' ) - for count, line in enumerate( iterator ): + genotype_re = re.compile('/|\|') + for count, line in enumerate(iterator): if count < start_val: continue if max_vals and count - start_val >= max_vals: - message = self.error_max_vals % ( max_vals, "features" ) + message = self.error_max_vals % (max_vals, "features") break # Split line and aggregate data. feature = line.split() - pos, c_id, ref, alt, qual, c_filter, info = feature[ 1:8 ] + pos, c_id, ref, alt, qual, c_filter, info = feature[1:8] # Format and samples data are optional. samples_data = [] - if len( feature ) > 8: - samples_data = feature[ 9: ] + if len(feature) > 8: + samples_data = feature[9:] # VCF is 1-based but provided position is 0-based. - pos = int( pos ) - 1 + pos = int(pos) - 1 # FIXME: OK to skip? if alt == '.': @@ -700,7 +699,7 @@ def get_mapping( ref, alt ): continue # Set up array to track allele counts. - allele_counts = [ 0 for i in range( alt.count( ',' ) + 1 ) ] + allele_counts = [0 for i in range(alt.count(',') + 1)] sample_gts = [] if samples_data: @@ -708,20 +707,20 @@ def get_mapping( ref, alt ): alleles_seen = {} has_alleles = False - for i, sample in enumerate( samples_data ): + for i, sample in enumerate(samples_data): # Parse and count alleles. - genotype = sample.split( ':' )[ 0 ] + genotype = sample.split(':')[0] has_alleles = False alleles_seen.clear() - for allele in genotype_re.split( genotype ): + for allele in genotype_re.split(genotype): try: # This may throw a ValueError if allele is missing. - allele = int( allele ) + allele = int(allele) # Only count allele if it hasn't been seen yet. if allele != 0 and allele not in alleles_seen: - allele_counts[ allele - 1 ] += 1 - alleles_seen[ allele ] = True + allele_counts[allele - 1] += 1 + alleles_seen[allele] = True has_alleles = True except ValueError: pass @@ -730,11 +729,11 @@ def get_mapping( ref, alt ): if not has_alleles: genotype = '' - sample_gts.append( genotype ) + sample_gts.append(genotype) else: # No samples, so set allele count and sample genotype manually. - allele_counts = [ 1 ] - sample_gts = [ '1/1' ] + allele_counts = [1] + sample_gts = ['1/1'] # Add locus data. locus_data = [ @@ -745,26 +744,26 @@ def get_mapping( ref, alt ): alt, qual, c_filter, - ','.join( sample_gts ) + ','.join(sample_gts) ] - locus_data.extend( allele_counts ) - data.append( locus_data ) + locus_data.extend(allele_counts) + data.append(locus_data) - return { 'data': data, 'message': message } + return {'data': data, 'message': message} - def write_data_to_file( self, regions, filename ): - out = open( filename, "w" ) + def write_data_to_file(self, regions, filename): + out = open(filename, "w") data_file = self.open_data_file() for region in regions: # Write data in region. - iterator = self.get_iterator( data_file, region.chrom, region.start, region.end ) + iterator = self.get_iterator(data_file, region.chrom, region.start, region.end) for line in iterator: - out.write( "%s\n" % line ) + out.write("%s\n" % line) out.close() -class VcfTabixDataProvider( TabixDataProvider, VcfDataProvider ): +class VcfTabixDataProvider(TabixDataProvider, VcfDataProvider): """ Provides data from a VCF file indexed via tabix. """ @@ -772,7 +771,7 @@ class VcfTabixDataProvider( TabixDataProvider, VcfDataProvider ): dataset_type = 'variant' -class RawVcfDataProvider( VcfDataProvider ): +class RawVcfDataProvider(VcfDataProvider): """ Provide data from VCF file. @@ -780,10 +779,10 @@ class RawVcfDataProvider( VcfDataProvider ): for large datasets. """ - def open_data_file( self ): - return open( self.original_dataset.file_name ) + def open_data_file(self): + return open(self.original_dataset.file_name) - def get_iterator( self, data_file, chrom, start, end, **kwargs ): + def get_iterator(self, data_file, chrom, start, end, **kwargs): # Skip comments. line = None for line in data_file: @@ -791,37 +790,37 @@ def get_iterator( self, data_file, chrom, start, end, **kwargs ): break # If last line is a comment, there are no data lines. - if line.startswith( "#" ): + if line.startswith("#"): return [] # Match chrom naming format. if line: dataset_chrom = line.split()[0] - if not _chrom_naming_matches( chrom, dataset_chrom ): - chrom = _convert_between_ucsc_and_ensemble_naming( chrom ) + if not _chrom_naming_matches(chrom, dataset_chrom): + chrom = _convert_between_ucsc_and_ensemble_naming(chrom) - def line_in_region( vcf_line, chrom, start, end ): + def line_in_region(vcf_line, chrom, start, end): """ Returns true if line is in region. """ - variant_chrom, variant_start = vcf_line.split()[ 0:2 ] + variant_chrom, variant_start = vcf_line.split()[0:2] # VCF format is 1-based. - variant_start = int( variant_start ) - 1 + variant_start = int(variant_start) - 1 return variant_chrom == chrom and variant_start >= start and variant_start <= end def line_filter_iter(): """ Yields lines in data that are in region chrom:start-end """ # Yield data line read above. - if line_in_region( line, chrom, start, end ): + if line_in_region(line, chrom, start, end): yield line # Search for and yield other data lines. for data_line in data_file: - if line_in_region( data_line, chrom, start, end ): + if line_in_region(data_line, chrom, start, end): yield data_line return line_filter_iter() -class BamDataProvider( GenomeDataProvider, FilterableMixin ): +class BamDataProvider(GenomeDataProvider, FilterableMixin): """ Provides access to intervals from a sorted indexed BAM file. Coordinate data is reported in BED format: 0-based, half-open. @@ -829,30 +828,29 @@ class BamDataProvider( GenomeDataProvider, FilterableMixin ): dataset_type = 'bai' - def get_filters( self ): + def get_filters(self): """ Returns filters for dataset. """ # HACK: first 7 fields are for drawing, so start filter column index at 7. filter_col = 7 filters = [] - filters.append( { 'name': 'Mapping Quality', + filters.append({'name': 'Mapping Quality', 'type': 'number', - 'index': filter_col } - ) + 'index': filter_col}) return filters - def write_data_to_file( self, regions, filename ): + def write_data_to_file(self, regions, filename): """ Write reads in regions to file. """ # Open current BAM file using index. - bamfile = pysam.AlignmentFile( self.original_dataset.file_name, mode='rb', - index_filename=self.converted_dataset.file_name ) + bamfile = pysam.AlignmentFile(self.original_dataset.file_name, mode='rb', + index_filename=self.converted_dataset.file_name) # TODO: write headers as well? - new_bamfile = pysam.AlignmentFile( filename, template=bamfile, mode='wb' ) + new_bamfile = pysam.AlignmentFile(filename, template=bamfile, mode='wb') for region in regions: # Write data from region. @@ -864,26 +862,26 @@ def write_data_to_file( self, regions, filename ): data = bamfile.fetch(start=start, end=end, reference=chrom) except ValueError: # Try alternative chrom naming. - chrom = _convert_between_ucsc_and_ensemble_naming( chrom ) + chrom = _convert_between_ucsc_and_ensemble_naming(chrom) try: - data = bamfile.fetch( start=start, end=end, reference=chrom ) + data = bamfile.fetch(start=start, end=end, reference=chrom) except ValueError: return None # Write reads in region. - for i, read in enumerate( data ): - new_bamfile.write( read ) + for i, read in enumerate(data): + new_bamfile.write(read) # Cleanup. new_bamfile.close() bamfile.close() - def open_data_file( self ): + def open_data_file(self): # Attempt to open the BAM file with index - return pysam.AlignmentFile( self.original_dataset.file_name, mode='rb', - index_filename=self.converted_dataset.file_name ) + return pysam.AlignmentFile(self.original_dataset.file_name, mode='rb', + index_filename=self.converted_dataset.file_name) - def get_iterator( self, data_file, chrom, start, end, **kwargs ): + def get_iterator(self, data_file, chrom, start, end, **kwargs): """ Returns an iterator that provides data in the region chrom:start-end """ @@ -893,18 +891,18 @@ def get_iterator( self, data_file, chrom, start, end, **kwargs ): start = int(start) end = int(end) try: - data = data_file.fetch( start=start, end=end, reference=chrom ) + data = data_file.fetch(start=start, end=end, reference=chrom) except ValueError: # Try alternative chrom naming. - chrom = _convert_between_ucsc_and_ensemble_naming( chrom ) + chrom = _convert_between_ucsc_and_ensemble_naming(chrom) try: - data = data_file.fetch( start=start, end=end, reference=chrom ) + data = data_file.fetch(start=start, end=end, reference=chrom) except ValueError: return None return data - def process_data( self, iterator, start_val=0, max_vals=None, ref_seq=None, - iterator_type='nth', mean_depth=None, start=0, end=0, **kwargs ): + def process_data(self, iterator, start_val=0, max_vals=None, ref_seq=None, + iterator_type='nth', mean_depth=None, start=0, end=0, **kwargs): """ Returns a dict with the following attributes:: @@ -930,22 +928,22 @@ def process_data( self, iterator, start_val=0, max_vals=None, ref_seq=None, """ # No iterator indicates no reads. if iterator is None: - return { 'data': [], 'message': None } + return {'data': [], 'message': None} # # Helper functions. # - def decode_strand( read_flag, mask ): + def decode_strand(read_flag, mask): """ Decode strand from read flag. """ - strand_flag = ( read_flag & mask == 0 ) + strand_flag = (read_flag & mask == 0) if strand_flag: return "+" else: return "-" - def _random_read_iterator( read_iterator, threshold ): + def _random_read_iterator(read_iterator, threshold): """ An iterator that returns a random stream of reads from the read_iterator as well as corresponding pairs for returned reads. @@ -953,31 +951,31 @@ def _random_read_iterator( read_iterator, threshold ): to return. """ for e in read_iterator: - if e.qname in paired_pending or random.uniform( 0, 1 ) <= threshold: + if e.qname in paired_pending or random.uniform(0, 1) <= threshold: yield e - def _nth_read_iterator( read_iterator, threshold ): + def _nth_read_iterator(read_iterator, threshold): """ An iterator that returns every nth read. """ # Convert threshold to N for stepping through iterator. - n = int( 1 / threshold ) - return itertools.islice( read_iterator, None, None, n ) + n = int(1 / threshold) + return itertools.islice(read_iterator, None, None, n) # -- Choose iterator. -- # Calculate threshold for non-sequential iterators based on mean_depth and read length. try: - first_read = next( iterator ) + first_read = next(iterator) except StopIteration: # no reads. - return { 'data': [], 'message': None, 'max_low': start, 'max_high': start } + return {'data': [], 'message': None, 'max_low': start, 'max_high': start} - read_len = len( first_read.seq ) - num_reads = max( ( end - start ) * mean_depth / float( read_len ), 1 ) - threshold = float( max_vals ) / num_reads - iterator = itertools.chain( iter( [ first_read ] ), iterator ) + read_len = len(first_read.seq) + num_reads = max((end - start) * mean_depth / float(read_len), 1) + threshold = float(max_vals) / num_reads + iterator = itertools.chain(iter([first_read]), iterator) # Use specified iterator type, save for when threshold is >= 1. # A threshold of >= 1 indicates all reads are to be returned, so no @@ -985,9 +983,9 @@ def _nth_read_iterator( read_iterator, threshold ): if iterator_type == 'sequential' or threshold >= 1: read_iterator = iterator elif iterator_type == 'random': - read_iterator = _random_read_iterator( iterator, threshold ) + read_iterator = _random_read_iterator(iterator, threshold) elif iterator_type == 'nth': - read_iterator = _nth_read_iterator( iterator, threshold ) + read_iterator = _nth_read_iterator(iterator, threshold) # # Encode reads as list of lists. @@ -1000,21 +998,21 @@ def _nth_read_iterator( read_iterator, threshold ): for read in read_iterator: if count < start_val: continue - if ( count - start_val - unmapped ) >= max_vals: - message = self.error_max_vals % ( max_vals, "reads" ) + if (count - start_val - unmapped) >= max_vals: + message = self.error_max_vals % (max_vals, "reads") break # If not mapped, skip read. - is_mapped = ( read.flag & 0x0004 == 0 ) + is_mapped = (read.flag & 0x0004 == 0) if not is_mapped: unmapped += 1 continue qname = read.qname seq = read.seq - strand = decode_strand( read.flag, 0x0010 ) + strand = decode_strand(read.flag, 0x0010) if read.cigar is not None: - read_len = sum( [cig[1] for cig in read.cigar] ) # Use cigar to determine length + read_len = sum([cig[1] for cig in read.cigar]) # Use cigar to determine length else: read_len = len(seq) # If no cigar, just use sequence length @@ -1022,24 +1020,23 @@ def _nth_read_iterator( read_iterator, threshold ): if qname in paired_pending: # Found pair. pair = paired_pending[qname] - results.append( [ hash( "%i_%s" % ( pair['start'], qname ) ), - pair['start'], - read.pos + read_len, - qname, - [ pair['start'], pair['end'], pair['cigar'], pair['strand'], pair['seq'] ], - [ read.pos, read.pos + read_len, read.cigar, strand, seq ], - None, [ pair['mapq'], read.mapq ] - ] ) + results.append([hash("%i_%s" % (pair['start'], qname)), + pair['start'], + read.pos + read_len, + qname, + [pair['start'], pair['end'], pair['cigar'], pair['strand'], pair['seq']], + [read.pos, read.pos + read_len, read.cigar, strand, seq], + None, [pair['mapq'], read.mapq]]) del paired_pending[qname] else: # Insert first of pair. - paired_pending[qname] = { 'start': read.pos, 'end': read.pos + read_len, 'seq': seq, 'mate_start': read.mpos, - 'rlen': read_len, 'strand': strand, 'cigar': read.cigar, 'mapq': read.mapq } + paired_pending[qname] = {'start': read.pos, 'end': read.pos + read_len, 'seq': seq, 'mate_start': read.mpos, + 'rlen': read_len, 'strand': strand, 'cigar': read.cigar, 'mapq': read.mapq} count += 1 else: - results.append( [ hash( "%i_%s" % ( read.pos, qname ) ), + results.append([hash("%i_%s" % (read.pos, qname)), read.pos, read.pos + read_len, qname, - read.cigar, strand, read.seq, read.mapq ] ) + read.cigar, strand, read.seq, read.mapq]) count += 1 # Take care of reads whose mates are out of range. @@ -1050,43 +1047,43 @@ def _nth_read_iterator( read_iterator, threshold ): read_end = read['end'] # Make read_1 start=end so that length is 0 b/c we don't know # read length. - r1 = [ read['mate_start'], read['mate_start'] ] - r2 = [ read['start'], read['end'], read['cigar'], read['strand'], read['seq'] ] + r1 = [read['mate_start'], read['mate_start']] + r2 = [read['start'], read['end'], read['cigar'], read['strand'], read['seq']] else: # Mate is after read. read_start = read['start'] # Make read_2 start=end so that length is 0 b/c we don't know # read length. Hence, end of read is start of read_2. read_end = read['mate_start'] - r1 = [ read['start'], read['end'], read['cigar'], read['strand'], read['seq'] ] - r2 = [ read['mate_start'], read['mate_start'] ] + r1 = [read['start'], read['end'], read['cigar'], read['strand'], read['seq']] + r2 = [read['mate_start'], read['mate_start']] - results.append( [ hash( "%i_%s" % ( read_start, qname ) ), read_start, read_end, qname, r1, r2, [read[ 'mapq' ], 125] ] ) + results.append([hash("%i_%s" % (read_start, qname)), read_start, read_end, qname, r1, r2, [read['mapq'], 125]]) # Clean up. TODO: is this needed? If so, we'll need a cleanup function after processing the data. # bamfile.close() - def compress_seq_and_cigar( read, start_field, cigar_field, seq_field ): + def compress_seq_and_cigar(read, start_field, cigar_field, seq_field): ''' Use reference-based compression to compress read sequence and cigar. ''' - read_seq, read_cigar = get_ref_based_read_seq_and_cigar( read[ seq_field ].upper(), - read[ start_field ], - ref_seq.sequence, - ref_seq.start, - read[ cigar_field ] ) - read[ seq_field ] = read_seq - read[ cigar_field ] = read_cigar - - def convert_cigar( read, start_field, cigar_field, seq_field ): + read_seq, read_cigar = get_ref_based_read_seq_and_cigar(read[seq_field].upper(), + read[start_field], + ref_seq.sequence, + ref_seq.start, + read[cigar_field]) + read[seq_field] = read_seq + read[cigar_field] = read_cigar + + def convert_cigar(read, start_field, cigar_field, seq_field): ''' Convert read cigar from pysam format to string format. ''' cigar_ops = 'MIDNSHP=X' read_cigar = '' - for op_tuple in read[ cigar_field ]: - read_cigar += '%i%s' % ( op_tuple[1], cigar_ops[ op_tuple[0] ] ) - read[ cigar_field ] = read_cigar + for op_tuple in read[cigar_field]: + read_cigar += '%i%s' % (op_tuple[1], cigar_ops[op_tuple[0]]) + read[cigar_field] = read_cigar # Choose method for processing reads. Use reference-based compression # if possible. Otherwise, convert cigar. @@ -1099,30 +1096,30 @@ def convert_cigar( read, start_field, cigar_field, seq_field ): # Process reads. for read in results: - if isinstance( read[ 5 ], list ): + if isinstance(read[5], list): # Paired-end read. - if len( read[4] ) > 2: - process_read( read[4], 0, 2, 4 ) - if len( read[5] ) > 2: - process_read( read[5], 0, 2, 4 ) + if len(read[4]) > 2: + process_read(read[4], 0, 2, 4) + if len(read[5]) > 2: + process_read(read[5], 0, 2, 4) else: # Single-end read. - process_read( read, 1, 4, 6) + process_read(read, 1, 4, 6) - max_low, max_high = get_bounds( results, 1, 2 ) + max_low, max_high = get_bounds(results, 1, 2) - return { 'data': results, 'message': message, 'max_low': max_low, 'max_high': max_high } + return {'data': results, 'message': message, 'max_low': max_low, 'max_high': max_high} -class SamDataProvider( BamDataProvider ): +class SamDataProvider(BamDataProvider): dataset_type = 'bai' - def __init__( self, converted_dataset=None, original_dataset=None, dependencies=None ): + def __init__(self, converted_dataset=None, original_dataset=None, dependencies=None): """ Create SamDataProvider. """ - super( SamDataProvider, self ).__init__( converted_dataset=converted_dataset, - original_dataset=original_dataset, - dependencies=dependencies ) + super(SamDataProvider, self).__init__(converted_dataset=converted_dataset, + original_dataset=original_dataset, + dependencies=dependencies) # To use BamDataProvider, original dataset must be BAM and # converted dataset must be BAI. Use BAI from BAM metadata. @@ -1131,33 +1128,33 @@ def __init__( self, converted_dataset=None, original_dataset=None, dependencies= self.converted_dataset = converted_dataset.metadata.bam_index -class BBIDataProvider( GenomeDataProvider ): +class BBIDataProvider(GenomeDataProvider): """ BBI data provider for the Galaxy track browser. """ dataset_type = 'bigwig' - def valid_chroms( self ): + def valid_chroms(self): # No way to return this info as of now return None - def has_data( self, chrom ): + def has_data(self, chrom): f, bbi = self._get_dataset() - all_dat = bbi.query( chrom, 0, 2147483647, 1 ) or \ - bbi.query( _convert_between_ucsc_and_ensemble_naming( chrom ), 0, 2147483647, 1 ) + all_dat = bbi.query(chrom, 0, 2147483647, 1) or \ + bbi.query(_convert_between_ucsc_and_ensemble_naming(chrom), 0, 2147483647, 1) f.close() return all_dat is not None - def get_data( self, chrom, start, end, start_val=0, max_vals=None, num_samples=1000, **kwargs ): - start = int( start ) - end = int( end ) + def get_data(self, chrom, start, end, start_val=0, max_vals=None, num_samples=1000, **kwargs): + start = int(start) + end = int(end) # Helper function for getting summary data regardless of chromosome # naming convention. - def _summarize_bbi( bbi, chrom, start, end, num_points ): - return bbi.summarize( chrom, start, end, num_points ) or \ - bbi.summarize( _convert_between_ucsc_and_ensemble_naming( chrom ), start, end, num_points ) + def _summarize_bbi(bbi, chrom, start, end, num_points): + return bbi.summarize(chrom, start, end, num_points) or \ + bbi.summarize(_convert_between_ucsc_and_ensemble_naming(chrom), start, end, num_points) # Bigwig can be a standalone bigwig file, in which case we use # original_dataset, or coming from wig->bigwig conversion in @@ -1168,7 +1165,7 @@ def _summarize_bbi( bbi, chrom, start, end, num_points ): # start:endbut no reduced data. This is currently used by client # to determine the default range. if 'stats' in kwargs: - summary = _summarize_bbi( bbi, chrom, start, end, 1 ) + summary = _summarize_bbi(bbi, chrom, start, end, 1) f.close() min_val = 0 @@ -1182,16 +1179,16 @@ def _summarize_bbi( bbi, chrom, start, end, num_points ): # Compute $\mu \pm 2\sigma$ to provide an estimate for upper and lower # bounds that contain ~95% of the data. mean = summary.sum_data[0] / valid_count - var = max( summary.sum_squares[0] - mean, 0 ) # Prevent variance underflow. + var = max(summary.sum_squares[0] - mean, 0) # Prevent variance underflow. if valid_count > 1: var /= valid_count - 1 - sd = math.sqrt( var ) + sd = math.sqrt(var) min_val = summary.min_val[0] max_val = summary.max_val[0] - return dict( data=dict( min=min_val, max=max_val, mean=mean, sd=sd ) ) + return dict(data=dict(min=min_val, max=max_val, mean=mean, sd=sd)) - def summarize_region( bbi, chrom, start, end, num_points ): + def summarize_region(bbi, chrom, start, end, num_points): ''' Returns results from summarizing a region using num_points. NOTE: num_points cannot be greater than end - start or BBI @@ -1202,7 +1199,7 @@ def summarize_region( bbi, chrom, start, end, num_points ): # Get summary; this samples at intervals of length # (end - start)/num_points -- i.e. drops any fractional component # of interval length. - summary = _summarize_bbi( bbi, chrom, start, end, num_points ) + summary = _summarize_bbi(bbi, chrom, start, end, num_points) if summary: # mean = summary.sum_data / summary.valid_count @@ -1214,14 +1211,14 @@ def summarize_region( bbi, chrom, start, end, num_points ): pos = start step_size = (end - start) / num_points - for i in range( num_points ): - result.append( (pos, float_nan( summary.sum_data[i] / summary.valid_count[i] ) ) ) + for i in range(num_points): + result.append((pos, float_nan(summary.sum_data[i] / summary.valid_count[i]))) pos += step_size return result # Approach is different depending on region size. - num_samples = int( num_samples ) + num_samples = int(num_samples) if end - start < num_samples: # Get values for individual bases in region, including start and end. # To do this, need to increase end to next base and request number of points. @@ -1244,14 +1241,14 @@ def summarize_region( bbi, chrom, start, end, num_points ): # Start with N samples. num_points = num_samples - step_size = ( end - start ) / num_points + step_size = (end - start) / num_points # Add additional points to sample in the remainder not covered by # the initial N samples. remainder_start = start + step_size * num_points - additional_points = ( end - remainder_start ) / step_size + additional_points = (end - remainder_start) / step_size num_points += additional_points - result = summarize_region( bbi, chrom, start, end, num_points ) + result = summarize_region(bbi, chrom, start, end, num_points) # Cleanup and return. f.close() @@ -1261,77 +1258,78 @@ def summarize_region( bbi, chrom, start, end, num_points ): } -class BigBedDataProvider( BBIDataProvider ): - def _get_dataset( self ): +class BigBedDataProvider(BBIDataProvider): + def _get_dataset(self): # Nothing converts to bigBed so we don't consider converted dataset - f = open( self.original_dataset.file_name ) + f = open(self.original_dataset.file_name) return f, BigBedFile(file=f) -class BigWigDataProvider ( BBIDataProvider ): +class BigWigDataProvider (BBIDataProvider): """ Provides data from BigWig files; position data is reported in 1-based coordinate system, i.e. wiggle format. """ - def _get_dataset( self ): + + def _get_dataset(self): if self.converted_dataset is not None: - f = open( self.converted_dataset.file_name ) + f = open(self.converted_dataset.file_name) else: - f = open( self.original_dataset.file_name ) + f = open(self.original_dataset.file_name) return f, BigWigFile(file=f) -class IntervalIndexDataProvider( FilterableMixin, GenomeDataProvider ): +class IntervalIndexDataProvider(FilterableMixin, GenomeDataProvider): """ Interval index files used for GFF, Pileup files. """ - col_name_data_attr_mapping = { 4: { 'index': 4, 'name': 'Score' } } + col_name_data_attr_mapping = {4: {'index': 4, 'name': 'Score'}} dataset_type = 'interval_index' - def write_data_to_file( self, regions, filename ): - source = open( self.original_dataset.file_name ) - index = Indexes( self.converted_dataset.file_name ) - out = open( filename, 'w' ) + def write_data_to_file(self, regions, filename): + source = open(self.original_dataset.file_name) + index = Indexes(self.converted_dataset.file_name) + out = open(filename, 'w') for region in regions: # Write data from region. chrom = region.chrom start = region.start end = region.end - for start, end, offset in index.find( chrom, start, end ): - source.seek( offset ) + for start, end, offset in index.find(chrom, start, end): + source.seek(offset) # HACK: write differently depending on original dataset format. - if self.original_dataset.ext not in [ 'gff', 'gff3', 'gtf' ]: + if self.original_dataset.ext not in ['gff', 'gff3', 'gtf']: line = source.readline() - out.write( line ) + out.write(line) else: - reader = GFFReaderWrapper( source, fix_strand=True ) + reader = GFFReaderWrapper(source, fix_strand=True) feature = reader.next() for interval in feature.intervals: - out.write( '\t'.join( interval.fields ) + '\n' ) + out.write('\t'.join(interval.fields) + '\n') source.close() out.close() - def open_data_file( self ): - return Indexes( self.converted_dataset.file_name ) + def open_data_file(self): + return Indexes(self.converted_dataset.file_name) - def get_iterator( self, data_file, chrom, start, end, **kwargs ): + def get_iterator(self, data_file, chrom, start, end, **kwargs): """ Returns an iterator for data in data_file in chrom:start-end """ if chrom not in data_file.indexes: # Try alternative naming. - chrom = _convert_between_ucsc_and_ensemble_naming( chrom ) + chrom = _convert_between_ucsc_and_ensemble_naming(chrom) return data_file.find(chrom, start, end) - def process_data( self, iterator, start_val=0, max_vals=None, **kwargs ): + def process_data(self, iterator, start_val=0, max_vals=None, **kwargs): results = [] message = None - source = open( self.original_dataset.file_name ) + source = open(self.original_dataset.file_name) # # Build data to return. Payload format is: @@ -1340,30 +1338,30 @@ def process_data( self, iterator, start_val=0, max_vals=None, **kwargs ): # # First three entries are mandatory, others are optional. # - filter_cols = loads( kwargs.get( "filter_cols", "[]" ) ) - no_detail = ( "no_detail" in kwargs ) - for count, val in enumerate( iterator ): + filter_cols = loads(kwargs.get("filter_cols", "[]")) + no_detail = ("no_detail" in kwargs) + for count, val in enumerate(iterator): offset = val[2] if count < start_val: continue if count - start_val >= max_vals: - message = self.error_max_vals % ( max_vals, "features" ) + message = self.error_max_vals % (max_vals, "features") break - source.seek( offset ) + source.seek(offset) # TODO: can we use column metadata to fill out payload? # GFF dataset. - reader = GFFReaderWrapper( source, fix_strand=True ) + reader = GFFReaderWrapper(source, fix_strand=True) feature = reader.next() - payload = package_gff_feature( feature, no_detail, filter_cols ) - payload.insert( 0, offset ) + payload = package_gff_feature(feature, no_detail, filter_cols) + payload.insert(0, offset) - results.append( payload ) + results.append(payload) - return { 'data': results, 'message': message } + return {'data': results, 'message': message} -class RawGFFDataProvider( GenomeDataProvider ): +class RawGFFDataProvider(GenomeDataProvider): """ Provide data from GFF file that has not been indexed. @@ -1373,119 +1371,119 @@ class RawGFFDataProvider( GenomeDataProvider ): dataset_type = 'interval_index' - def get_iterator( self, data_file, chrom, start, end, **kwargs ): + def get_iterator(self, data_file, chrom, start, end, **kwargs): """ Returns an iterator that provides data in the region chrom:start-end as well as a file offset. """ - source = open( self.original_dataset.file_name ) + source = open(self.original_dataset.file_name) # Read first line in order to match chrom naming format. line = source.readline() # If line empty, assume file is empty and return empty iterator. - if len( line ) == 0: + if len(line) == 0: return iter([]) # Determine chromosome naming format. dataset_chrom = line.split()[0] - if not _chrom_naming_matches( chrom, dataset_chrom ): - chrom = _convert_between_ucsc_and_ensemble_naming( chrom ) + if not _chrom_naming_matches(chrom, dataset_chrom): + chrom = _convert_between_ucsc_and_ensemble_naming(chrom) # Undo read. - source.seek( 0 ) + source.seek(0) def features_in_region_iter(): offset = 0 - for feature in GFFReaderWrapper( source, fix_strand=True ): + for feature in GFFReaderWrapper(source, fix_strand=True): # Only provide features that are in region. - feature_start, feature_end = convert_gff_coords_to_bed( [ feature.start, feature.end ] ) + feature_start, feature_end = convert_gff_coords_to_bed([feature.start, feature.end]) if feature.chrom == chrom and feature_end > start and feature_start < end: yield feature, offset offset += feature.raw_size return features_in_region_iter() - def process_data( self, iterator, start_val=0, max_vals=None, **kwargs ): + def process_data(self, iterator, start_val=0, max_vals=None, **kwargs): """ Process data from an iterator to a format that can be provided to client. """ - filter_cols = loads( kwargs.get( "filter_cols", "[]" ) ) - no_detail = ( "no_detail" in kwargs ) + filter_cols = loads(kwargs.get("filter_cols", "[]")) + no_detail = ("no_detail" in kwargs) results = [] message = None - for count, ( feature, offset ) in enumerate( iterator ): + for count, (feature, offset) in enumerate(iterator): if count < start_val: continue if count - start_val >= max_vals: - message = self.error_max_vals % ( max_vals, "reads" ) + message = self.error_max_vals % (max_vals, "reads") break - payload = package_gff_feature( feature, no_detail=no_detail, filter_cols=filter_cols ) - payload.insert( 0, offset ) - results.append( payload ) + payload = package_gff_feature(feature, no_detail=no_detail, filter_cols=filter_cols) + payload.insert(0, offset) + results.append(payload) - return { 'data': results, 'dataset_type': self.dataset_type, 'message': message } + return {'data': results, 'dataset_type': self.dataset_type, 'message': message} -class GtfTabixDataProvider( TabixDataProvider ): +class GtfTabixDataProvider(TabixDataProvider): """ Returns data from GTF datasets that are indexed via tabix. """ - def process_data( self, iterator, start_val=0, max_vals=None, **kwargs ): + def process_data(self, iterator, start_val=0, max_vals=None, **kwargs): # Loop through lines and group by transcript_id; each group is a feature. # TODO: extend this code or use code in gff_util to process GFF/3 as well # and then create a generic GFFDataProvider that can be used with both # raw and tabix datasets. features = {} - for count, line in enumerate( iterator ): - line_attrs = parse_gff_attributes( line.split('\t')[8] ) - transcript_id = line_attrs[ 'transcript_id' ] + for count, line in enumerate(iterator): + line_attrs = parse_gff_attributes(line.split('\t')[8]) + transcript_id = line_attrs['transcript_id'] if transcript_id in features: - feature = features[ transcript_id ] + feature = features[transcript_id] else: feature = [] - features[ transcript_id ] = feature - feature.append( GFFInterval( None, line.split( '\t') ) ) + features[transcript_id] = feature + feature.append(GFFInterval(None, line.split('\t'))) # Process data. - filter_cols = loads( kwargs.get( "filter_cols", "[]" ) ) - no_detail = ( "no_detail" in kwargs ) + filter_cols = loads(kwargs.get("filter_cols", "[]")) + no_detail = ("no_detail" in kwargs) results = [] message = None - for count, intervals in enumerate( features.values() ): + for count, intervals in enumerate(features.values()): if count < start_val: continue if count - start_val >= max_vals: - message = self.error_max_vals % ( max_vals, "reads" ) + message = self.error_max_vals % (max_vals, "reads") break - feature = GFFFeature( None, intervals=intervals ) - payload = package_gff_feature( feature, no_detail=no_detail, filter_cols=filter_cols ) - payload.insert( 0, feature.intervals[ 0 ].attributes[ 'transcript_id' ] ) - results.append( payload ) + feature = GFFFeature(None, intervals=intervals) + payload = package_gff_feature(feature, no_detail=no_detail, filter_cols=filter_cols) + payload.insert(0, feature.intervals[0].attributes['transcript_id']) + results.append(payload) - return { 'data': results, 'message': message } + return {'data': results, 'message': message} # # -- ENCODE Peak data providers. # -class ENCODEPeakDataProvider( GenomeDataProvider ): +class ENCODEPeakDataProvider(GenomeDataProvider): """ Abstract class that processes ENCODEPeak data from native format to payload format. Payload format: [ uid (offset), start, end, name, strand, thick_start, thick_end, blocks ] """ - def get_iterator( self, data_file, chrom, start, end, **kwargs ): - raise Exception( "Unimplemented Method" ) + def get_iterator(self, data_file, chrom, start, end, **kwargs): + raise Exception("Unimplemented Method") - def process_data( self, iterator, start_val=0, max_vals=None, **kwargs ): + def process_data(self, iterator, start_val=0, max_vals=None, **kwargs): """ Provides """ @@ -1500,14 +1498,14 @@ def process_data( self, iterator, start_val=0, max_vals=None, **kwargs ): # # First three entries are mandatory, others are optional. # - no_detail = ( "no_detail" in kwargs ) + no_detail = ("no_detail" in kwargs) rval = [] message = None - for count, line in enumerate( iterator ): + for count, line in enumerate(iterator): if count < start_val: continue if max_vals and count - start_val >= max_vals: - message = self.error_max_vals % ( max_vals, "features" ) + message = self.error_max_vals % (max_vals, "features") break feature = line.split() @@ -1515,73 +1513,73 @@ def process_data( self, iterator, start_val=0, max_vals=None, **kwargs ): # Feature initialization. payload = [ # GUID is just a hash of the line - hash( line ), + hash(line), # Add start, end. - int( feature[1] ), - int( feature[2] ) + int(feature[1]), + int(feature[2]) ] if no_detail: - rval.append( payload ) + rval.append(payload) continue # Extend with additional data. - payload.extend( [ + payload.extend([ # Add name, strand. feature[3], feature[5], # Thick start, end are feature start, end for now. - int( feature[1] ), - int( feature[2] ), + int(feature[1]), + int(feature[2]), # No blocks. None, # Filtering data: Score, signalValue, pValue, qValue. - float( feature[4] ), - float( feature[6] ), - float( feature[7] ), - float( feature[8] ) - ] ) + float(feature[4]), + float(feature[6]), + float(feature[7]), + float(feature[8]) + ]) - rval.append( payload ) + rval.append(payload) - return { 'data': rval, 'message': message } + return {'data': rval, 'message': message} -class ENCODEPeakTabixDataProvider( TabixDataProvider, ENCODEPeakDataProvider ): +class ENCODEPeakTabixDataProvider(TabixDataProvider, ENCODEPeakDataProvider): """ Provides data from an ENCODEPeak dataset indexed via tabix. """ - def get_filters( self ): + def get_filters(self): """ Returns filters for dataset. """ # HACK: first 8 fields are for drawing, so start filter column index at 9. filter_col = 8 filters = [] - filters.append( { 'name': 'Score', - 'type': 'number', - 'index': filter_col, - 'tool_id': 'Filter1', - 'tool_exp_name': 'c6' } ) + filters.append({'name': 'Score', + 'type': 'number', + 'index': filter_col, + 'tool_id': 'Filter1', + 'tool_exp_name': 'c6'}) filter_col += 1 - filters.append( { 'name': 'Signal Value', - 'type': 'number', - 'index': filter_col, - 'tool_id': 'Filter1', - 'tool_exp_name': 'c7' } ) + filters.append({'name': 'Signal Value', + 'type': 'number', + 'index': filter_col, + 'tool_id': 'Filter1', + 'tool_exp_name': 'c7'}) filter_col += 1 - filters.append( { 'name': 'pValue', + filters.append({'name': 'pValue', 'type': 'number', - 'index': filter_col, - 'tool_id': 'Filter1', - 'tool_exp_name': 'c8' } ) + 'index': filter_col, + 'tool_id': 'Filter1', + 'tool_exp_name': 'c8'}) filter_col += 1 - filters.append( { 'name': 'qValue', + filters.append({'name': 'qValue', 'type': 'number', - 'index': filter_col, - 'tool_id': 'Filter1', - 'tool_exp_name': 'c9' } ) + 'index': filter_col, + 'tool_id': 'Filter1', + 'tool_exp_name': 'c9'}) return filters # @@ -1589,93 +1587,92 @@ def get_filters( self ): # -class ChromatinInteractionsDataProvider( GenomeDataProvider ): - def process_data( self, iterator, start_val=0, max_vals=None, **kwargs ): +class ChromatinInteractionsDataProvider(GenomeDataProvider): + def process_data(self, iterator, start_val=0, max_vals=None, **kwargs): """ Provides """ rval = [] message = None - for count, line in enumerate( iterator ): + for count, line in enumerate(iterator): if count < start_val: continue if max_vals and count - start_val >= max_vals: - message = self.error_max_vals % ( max_vals, "interactions" ) + message = self.error_max_vals % (max_vals, "interactions") break feature = line.split() - s1 = int( feature[1] ) - e1 = int( feature[2] ) + s1 = int(feature[1]) + e1 = int(feature[2]) c = feature[3] - s2 = int( feature[4] ) - e2 = int( feature[5] ) - v = float( feature[6] ) + s2 = int(feature[4]) + e2 = int(feature[5]) + v = float(feature[6]) # Feature initialization. payload = [ # GUID is just a hash of the line - hash( line ), + hash(line), # Add start1, end1, chr2, start2, end2, value. s1, e1, c, s2, e2, v ] - rval.append( payload ) + rval.append(payload) - return { 'data': rval, 'message': message } + return {'data': rval, 'message': message} - def get_default_max_vals( self ): + def get_default_max_vals(self): return 100000 -class ChromatinInteractionsTabixDataProvider( TabixDataProvider, ChromatinInteractionsDataProvider ): - def get_iterator( self, data_file, chrom, start=0, end=sys.maxint, interchromosomal=False, **kwargs ): +class ChromatinInteractionsTabixDataProvider(TabixDataProvider, ChromatinInteractionsDataProvider): + def get_iterator(self, data_file, chrom, start=0, end=sys.maxint, interchromosomal=False, **kwargs): """ """ # Modify start as needed to get earlier interactions with start region. - span = int( end ) - int( start ) - filter_start = max( 0, int( start ) - span - span / 2 ) + span = int(end) - int(start) + filter_start = max(0, int(start) - span - span / 2) - def filter( iter ): + def filter(iter): for line in iter: feature = line.split() - s1 = int( feature[1] ) - e1 = int( feature[2] ) + s1 = int(feature[1]) + e1 = int(feature[2]) c = feature[3] - s2 = int( feature[4] ) - e2 = int( feature[5] ) + s2 = int(feature[4]) + e2 = int(feature[5]) # Check for intrachromosal interactions. - if ( ( s1 + s2 ) / 2 <= end ) and ( ( e1 + e2 ) / 2 >= start ) and ( c == chrom ): + if ((s1 + s2) / 2 <= end) and ((e1 + e2) / 2 >= start) and (c == chrom): yield line # Check for interchromosal interactions. if interchromosomal and c != chrom: yield line - return filter( TabixDataProvider.get_iterator( self, data_file, chrom, filter_start, end ) ) + return filter(TabixDataProvider.get_iterator(self, data_file, chrom, filter_start, end)) # # -- Helper methods. -- # -def package_gff_feature( feature, no_detail=False, filter_cols=[] ): +def package_gff_feature(feature, no_detail=False, filter_cols=[]): """ Package a GFF feature in an array for data providers. """ - feature = convert_gff_coords_to_bed( feature ) + feature = convert_gff_coords_to_bed(feature) # No detail means only start, end. if no_detail: - return [ feature.start, feature.end ] + return [feature.start, feature.end] # Return full feature. - payload = [ feature.start, - feature.end, - feature.name(), - feature.strand, - # No notion of thick start, end in GFF, so make everything - # thick. - feature.start, - feature.end - ] + payload = [feature.start, + feature.end, + feature.name(), + feature.strand, + # No notion of thick start, end in GFF, so make everything + # thick. + feature.start, + feature.end] # HACK: ignore interval with name 'transcript' from feature. # Cufflinks puts this interval in each of its transcripts, @@ -1686,32 +1683,32 @@ def package_gff_feature( feature, no_detail=False, filter_cols=[] ): if feature.intervals[0].fields[2] == 'transcript': feature_intervals = feature.intervals[1:] # Add blocks. - block_sizes = [ (interval.end - interval.start ) for interval in feature_intervals ] - block_starts = [ ( interval.start - feature.start ) for interval in feature_intervals ] - blocks = zip( block_sizes, block_starts ) - payload.append( [ ( feature.start + block[1], feature.start + block[1] + block[0] ) for block in blocks ] ) + block_sizes = [(interval.end - interval.start) for interval in feature_intervals] + block_starts = [(interval.start - feature.start) for interval in feature_intervals] + blocks = zip(block_sizes, block_starts) + payload.append([(feature.start + block[1], feature.start + block[1] + block[0]) for block in blocks]) # Add filter data to payload. for col in filter_cols: if col == "Score": if feature.score == 'nan': - payload.append( feature.score ) + payload.append(feature.score) else: try: - f = float( feature.score ) - payload.append( f ) + f = float(feature.score) + payload.append(f) except: - payload.append( feature.score ) + payload.append(feature.score) elif col in feature.attributes: if feature.attributes[col] == 'nan': - payload.append( feature.attributes[col] ) + payload.append(feature.attributes[col]) else: try: - f = float( feature.attributes[col] ) - payload.append( f ) + f = float(feature.attributes[col]) + payload.append(f) except: - payload.append( feature.attributes[col] ) + payload.append(feature.attributes[col]) else: # Dummy value. - payload.append( 0 ) + payload.append(0) return payload diff --git a/lib/galaxy/visualization/data_providers/phyloviz/__init__.py b/lib/galaxy/visualization/data_providers/phyloviz/__init__.py index c84807810b50..d7644ff23fc1 100644 --- a/lib/galaxy/visualization/data_providers/phyloviz/__init__.py +++ b/lib/galaxy/visualization/data_providers/phyloviz/__init__.py @@ -7,14 +7,14 @@ from galaxy.visualization.data_providers.phyloviz.phyloxmlparser import Phyloxml_Parser -class PhylovizDataProvider( BaseDataProvider ): +class PhylovizDataProvider(BaseDataProvider): dataset_type = "phylo" - def __init__( self, original_dataset=None ): - super( PhylovizDataProvider, self ).__init__( original_dataset=original_dataset ) + def __init__(self, original_dataset=None): + super(PhylovizDataProvider, self).__init__(original_dataset=original_dataset) - def get_data( self, tree_index=0 ): + def get_data(self, tree_index=0): """ Returns trees. Trees are actually an array of JsonDicts. It's usually one tree, except in the case of Nexus @@ -24,21 +24,21 @@ def get_data( self, tree_index=0 ): file_name = self.original_dataset.file_name parseMsg = None jsonDicts = [] - rval = { 'dataset_type': self.dataset_type } + rval = {'dataset_type': self.dataset_type} if file_ext == "nhx": # parses newick files newickParser = Newick_Parser() - jsonDicts, parseMsg = newickParser.parseFile( file_name ) + jsonDicts, parseMsg = newickParser.parseFile(file_name) elif file_ext == "phyloxml": # parses phyloXML files phyloxmlParser = Phyloxml_Parser() - jsonDicts, parseMsg = phyloxmlParser.parseFile( file_name ) + jsonDicts, parseMsg = phyloxmlParser.parseFile(file_name) elif file_ext == "nex": # parses nexus files nexusParser = Nexus_Parser() - jsonDicts, parseMsg = nexusParser.parseFile( file_name ) - jsonDicts = jsonDicts[ int( tree_index ) ] + jsonDicts, parseMsg = nexusParser.parseFile(file_name) + jsonDicts = jsonDicts[int(tree_index)] rval["trees"] = parseMsg - rval[ "data" ] = jsonDicts - rval[ "msg"] = parseMsg + rval["data"] = jsonDicts + rval["msg"] = parseMsg return rval diff --git a/lib/galaxy/visualization/data_providers/phyloviz/baseparser.py b/lib/galaxy/visualization/data_providers/phyloviz/baseparser.py index 78767d7241d9..f55728a568ca 100644 --- a/lib/galaxy/visualization/data_providers/phyloviz/baseparser.py +++ b/lib/galaxy/visualization/data_providers/phyloviz/baseparser.py @@ -3,6 +3,7 @@ class Node(object): """Node class of PhyloTree, which represents a CLAUDE in a phylogenetic tree""" + def __init__(self, nodeName, **kwargs): """Creates a node and adds in the typical annotations""" self.name, self.id = nodeName, kwargs.get("id", 0) @@ -43,7 +44,7 @@ def addChildrenToJson(self, jsonDict): """Needs a special method to addChildren, such that the key does not appear in the Jsondict when the children is empty this requirement is due to the layout algorithm used by d3 layout for hiding subtree """ if len(self.children) > 0: - children = [ node.toJson() for node in self.children] + children = [node.toJson() for node in self.children] jsonDict["children"] = children return jsonDict diff --git a/lib/galaxy/visualization/data_providers/phyloviz/newickparser.py b/lib/galaxy/visualization/data_providers/phyloviz/newickparser.py index fe17cfc53aa6..c3d2a9d1086e 100644 --- a/lib/galaxy/visualization/data_providers/phyloviz/newickparser.py +++ b/lib/galaxy/visualization/data_providers/phyloviz/newickparser.py @@ -147,7 +147,7 @@ def parseNode(self, string, depth): if match: indexOfNextSymbol = match.start() stringRepOfInternalNode = stringRightOfBracket[:indexOfNextSymbol] - internalNodes = self._makeNodesFromString( stringRepOfInternalNode, depth) + internalNodes = self._makeNodesFromString(stringRepOfInternalNode, depth) if len(internalNodes) > 0: InternalNode = internalNodes[0] lenOfPreceedingInternalNodeString = len(stringRepOfInternalNode) @@ -155,11 +155,11 @@ def parseNode(self, string, depth): InternalNode = self._makeNodesFromString(string[j + 1:], depth)[0] lenOfPreceedingInternalNodeString = len(string) - j if InternalNode is None: # creating a generic node if it is unnamed - InternalNode = self.phyloTree.makeNode( "", depth=depth, isInternal=True ) # "internal-" + str(depth) + InternalNode = self.phyloTree.makeNode("", depth=depth, isInternal=True) # "internal-" + str(depth) lenOfPreceedingInternalNodeString = 0 # recussive call to make the internal claude - childSubString = string[ i + 1 : j ] + childSubString = string[i + 1 : j] InternalNode.addChildNode(self.parseNode(childSubString, depth + 1)) nodes.append(InternalNode) # we append the internal node later to preserve order diff --git a/lib/galaxy/visualization/data_providers/phyloviz/nexusparser.py b/lib/galaxy/visualization/data_providers/phyloviz/nexusparser.py index f18c2d295fe5..8ef67f649d4c 100644 --- a/lib/galaxy/visualization/data_providers/phyloviz/nexusparser.py +++ b/lib/galaxy/visualization/data_providers/phyloviz/nexusparser.py @@ -21,7 +21,7 @@ def parseNexus(self, filename): Nexus can store multiple trees """ - with open( filename, "rt") as nex_file: + with open(filename, "rt") as nex_file: nexlines = nex_file.readlines() rowCount = 0 @@ -75,7 +75,7 @@ def parseNexus(self, filename): self.phyloTrees.append(currPhyloTree) treeIndex = len(self.phyloTrees) - 1 - treeNames.append( (treeName, treeIndex) ) # appending name of tree, and its index + treeNames.append((treeName, treeIndex)) # appending name of tree, and its index continue return self.phyloTrees, treeNames diff --git a/lib/galaxy/visualization/data_providers/phyloviz/phyloxmlparser.py b/lib/galaxy/visualization/data_providers/phyloviz/phyloxmlparser.py index 5b2a6a97128c..817cca6a5665 100644 --- a/lib/galaxy/visualization/data_providers/phyloviz/phyloxmlparser.py +++ b/lib/galaxy/visualization/data_providers/phyloviz/phyloxmlparser.py @@ -64,7 +64,7 @@ def parseNode(self, node, depth): return currentNode - def _makeLeafNode(self, leafNode, depth=0 ): + def _makeLeafNode(self, leafNode, depth=0): """Makes leaf nodes by calling Phylotree methods""" node = {} for child in leafNode: diff --git a/lib/galaxy/visualization/data_providers/registry.py b/lib/galaxy/visualization/data_providers/registry.py index 4fd655fb6f2d..519dd3b2f9ec 100644 --- a/lib/galaxy/visualization/data_providers/registry.py +++ b/lib/galaxy/visualization/data_providers/registry.py @@ -10,12 +10,12 @@ from galaxy.datatypes.data import Newick, Nexus -class DataProviderRegistry( object ): +class DataProviderRegistry(object): """ Registry for data providers that enables listing and lookup. """ - def __init__( self ): + def __init__(self): # Mapping from dataset type name to a class that can fetch data from a file of that # type. First key is converted dataset type; if result is another dict, second key # is original dataset type. @@ -38,7 +38,7 @@ def __init__( self ): "column_with_stats": ColumnDataProvider } - def get_data_provider( self, trans, name=None, source='data', raw=False, original_dataset=None ): + def get_data_provider(self, trans, name=None, source='data', raw=False, original_dataset=None): """ Returns data provider matching parameter values. For standalone data sources, source parameter is ignored. @@ -47,42 +47,42 @@ def get_data_provider( self, trans, name=None, source='data', raw=False, origina data_provider = None if raw: # Working with raw data. - if isinstance( original_dataset.datatype, Gff ): + if isinstance(original_dataset.datatype, Gff): data_provider_class = genome.RawGFFDataProvider - elif isinstance( original_dataset.datatype, Bed ): + elif isinstance(original_dataset.datatype, Bed): data_provider_class = genome.RawBedDataProvider - elif isinstance( original_dataset.datatype, Vcf ): + elif isinstance(original_dataset.datatype, Vcf): data_provider_class = genome.RawVcfDataProvider - elif isinstance( original_dataset.datatype, Tabular ): + elif isinstance(original_dataset.datatype, Tabular): data_provider_class = ColumnDataProvider - elif isinstance( original_dataset.datatype, ( Nexus, Newick, Phyloxml ) ): + elif isinstance(original_dataset.datatype, (Nexus, Newick, Phyloxml)): data_provider_class = PhylovizDataProvider - data_provider = data_provider_class( original_dataset=original_dataset ) + data_provider = data_provider_class(original_dataset=original_dataset) else: # Working with converted or standalone dataset. if name: # Provider requested by name; get from mappings. - value = self.dataset_type_name_to_data_provider[ name ] - if isinstance( value, dict ): + value = self.dataset_type_name_to_data_provider[name] + if isinstance(value, dict): # Get converter by dataset extension; if there is no data provider, # get the default. - data_provider_class = value.get( original_dataset.datatype.__class__, value.get( "default" ) ) + data_provider_class = value.get(original_dataset.datatype.__class__, value.get("default")) else: data_provider_class = value # If name is the same as original dataset's type, dataset is standalone. # Otherwise, a converted dataset is being used. if name == original_dataset.ext: - data_provider = data_provider_class( original_dataset=original_dataset ) + data_provider = data_provider_class(original_dataset=original_dataset) else: - converted_dataset = original_dataset.get_converted_dataset( trans, name ) - deps = original_dataset.get_converted_dataset_deps( trans, name ) - data_provider = data_provider_class( original_dataset=original_dataset, - converted_dataset=converted_dataset, - dependencies=deps ) + converted_dataset = original_dataset.get_converted_dataset(trans, name) + deps = original_dataset.get_converted_dataset_deps(trans, name) + data_provider = data_provider_class(original_dataset=original_dataset, + converted_dataset=converted_dataset, + dependencies=deps) elif original_dataset: # No name, so look up a provider name from datatype's information. @@ -94,18 +94,18 @@ def get_data_provider( self, trans, name=None, source='data', raw=False, origina # Get data provider mapping and data provider. data_provider_mapping = original_dataset.datatype.data_sources if 'data_standalone' in data_provider_mapping: - data_provider = self.get_data_provider( trans, - name=data_provider_mapping[ 'data_standalone' ], - original_dataset=original_dataset ) + data_provider = self.get_data_provider(trans, + name=data_provider_mapping['data_standalone'], + original_dataset=original_dataset) else: - source_list = data_provider_mapping[ source ] - if isinstance( source_list, string_types ): - source_list = [ source_list ] + source_list = data_provider_mapping[source] + if isinstance(source_list, string_types): + source_list = [source_list] # Find a valid data provider in the source list. for source in source_list: try: - data_provider = self.get_data_provider( trans, name=source, original_dataset=original_dataset ) + data_provider = self.get_data_provider(trans, name=source, original_dataset=original_dataset) break except NoConverterException: pass diff --git a/lib/galaxy/visualization/genomes.py b/lib/galaxy/visualization/genomes.py index 06eb5270ed2c..b9165346470e 100644 --- a/lib/galaxy/visualization/genomes.py +++ b/lib/galaxy/visualization/genomes.py @@ -8,7 +8,7 @@ from galaxy.util.bunch import Bunch -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) # FIXME: copied from tracks.py # Message strings returned to browser @@ -24,36 +24,36 @@ ) -def decode_dbkey( dbkey ): +def decode_dbkey(dbkey): """ Decodes dbkey and returns tuple ( username, dbkey )""" if ':' in dbkey: - return dbkey.split( ':' ) + return dbkey.split(':') else: return None, dbkey -class GenomeRegion( object ): +class GenomeRegion(object): """ A genomic region on an individual chromosome. """ - def __init__( self, chrom=None, start=0, end=0, sequence=None ): + def __init__(self, chrom=None, start=0, end=0, sequence=None): self.chrom = chrom - self.start = int( start ) - self.end = int( end ) + self.start = int(start) + self.end = int(end) self.sequence = sequence - def __str__( self ): - return self.chrom + ":" + str( self.start ) + "-" + str( self.end ) + def __str__(self): + return self.chrom + ":" + str(self.start) + "-" + str(self.end) @staticmethod - def from_dict( obj_dict ): - return GenomeRegion( chrom=obj_dict[ 'chrom' ], - start=obj_dict[ 'start' ], - end=obj_dict[ 'end' ] ) + def from_dict(obj_dict): + return GenomeRegion(chrom=obj_dict['chrom'], + start=obj_dict['start'], + end=obj_dict['end']) @staticmethod - def from_str( obj_str ): + def from_str(obj_str): # check for gene region gene_region = obj_str.split(':') @@ -71,17 +71,18 @@ def from_str( obj_str ): return GenomeRegion() -class Genome( object ): +class Genome(object): """ Encapsulates information about a known genome/dbkey. """ - def __init__( self, key, description, len_file=None, twobit_file=None ): + + def __init__(self, key, description, len_file=None, twobit_file=None): self.key = key self.description = description self.len_file = len_file self.twobit_file = twobit_file - def to_dict( self, num=None, chrom=None, low=None ): + def to_dict(self, num=None, chrom=None, low=None): """ Returns representation of self as a dictionary. """ @@ -93,18 +94,18 @@ def check_int(s): return s def split_by_number(s): - return [ check_int(c) for c in re.split('([0-9]+)', s) ] + return [check_int(c) for c in re.split('([0-9]+)', s)] # # Parameter check, setting. # if num: - num = int( num ) + num = int(num) else: num = sys.maxsize # just a big number if low: - low = int( low ) + low = int(low) if low < 0: low = 0 else: @@ -116,7 +117,7 @@ def split_by_number(s): # (b) whether there are previous, next chroms; # (c) index of start chrom. # - len_file_enumerate = enumerate( open( self.len_file ) ) + len_file_enumerate = enumerate(open(self.len_file)) chroms = {} prev_chroms = False start_index = 0 @@ -129,11 +130,11 @@ def split_by_number(s): continue name, len = line.split("\t") if found: - chroms[ name ] = int( len ) + chroms[name] = int(len) count += 1 elif name == chrom: # Found starting chrom. - chroms[ name ] = int( len ) + chroms[name] = int(len) count += 1 found = True start_index = line_num @@ -143,8 +144,8 @@ def split_by_number(s): break else: # Use low to start list. - high = low + int( num ) - prev_chroms = ( low != 0 ) + high = low + int(num) + prev_chroms = (low != 0) start_index = low # Read chrom data from len file. @@ -158,7 +159,7 @@ def split_by_number(s): # LEN files have format: # fields = line.split("\t") - chroms[ fields[0] ] = int( fields[1] ) + chroms[fields[0]] = int(fields[1]) # Set flag to indicate whether there are more chroms after list. next_chroms = False @@ -169,7 +170,7 @@ def split_by_number(s): # No more chroms to read. pass - to_sort = [{ 'chrom': chrm, 'len': length } for chrm, length in chroms.items()] + to_sort = [{'chrom': chrm, 'len': length} for chrm, length in chroms.items()] to_sort.sort(key=lambda _: split_by_number(_['chrom'])) return { 'id': self.key, @@ -181,71 +182,71 @@ def split_by_number(s): } -class Genomes( object ): +class Genomes(object): """ Provides information about available genome data and methods for manipulating that data. """ - def __init__( self, app ): + def __init__(self, app): self.app = app # Create list of genomes from app.genome_builds self.genomes = {} # Store internal versions of data tables for twobit and __dbkey__ - self._table_versions = { 'twobit': None, '__dbkeys__': None } + self._table_versions = {'twobit': None, '__dbkeys__': None} self.reload_genomes() - def reload_genomes( self ): + def reload_genomes(self): self.genomes = {} # Store table versions for later for table_name in self._table_versions.keys(): - table = self.app.tool_data_tables.get( table_name, None ) + table = self.app.tool_data_tables.get(table_name, None) if table is not None: - self._table_versions[ table_name ] = table._loaded_content_version + self._table_versions[table_name] = table._loaded_content_version - twobit_table = self.app.tool_data_tables.get( 'twobit', None ) + twobit_table = self.app.tool_data_tables.get('twobit', None) twobit_fields = {} if twobit_table is None: # Add genome data (twobit files) to genomes, directly from twobit.loc try: - for line in open( os.path.join( self.app.config.tool_data_path, "twobit.loc" ) ): + for line in open(os.path.join(self.app.config.tool_data_path, "twobit.loc")): if line.startswith("#"): continue val = line.split() - if len( val ) == 2: + if len(val) == 2: key, path = val - twobit_fields[ key ] = path + twobit_fields[key] = path except IOError: # Thrown if twobit.loc does not exist. - log.exception( "Error reading twobit.loc" ) + log.exception("Error reading twobit.loc") for key, description in self.app.genome_builds.get_genome_build_names(): - self.genomes[ key ] = Genome( key, description ) + self.genomes[key] = Genome(key, description) # Add len files to genomes. - self.genomes[ key ].len_file = self.app.genome_builds.get_chrom_info( key )[0] - if self.genomes[ key ].len_file: - if not os.path.exists( self.genomes[ key ].len_file ): - self.genomes[ key ].len_file = None + self.genomes[key].len_file = self.app.genome_builds.get_chrom_info(key)[0] + if self.genomes[key].len_file: + if not os.path.exists(self.genomes[key].len_file): + self.genomes[key].len_file = None # Add genome data (twobit files) to genomes. if twobit_table is not None: - self.genomes[ key ].twobit_file = twobit_table.get_entry( 'value', key, 'path', default=None ) + self.genomes[key].twobit_file = twobit_table.get_entry('value', key, 'path', default=None) elif key in twobit_fields: - self.genomes[ key ].twobit_file = twobit_fields[ key ] + self.genomes[key].twobit_file = twobit_fields[key] - def check_and_reload( self ): + def check_and_reload(self): # Check if tables have been modified, if so reload for table_name, table_version in self._table_versions.items(): - table = self.app.tool_data_tables.get( table_name, None ) - if table is not None and not table.is_current_version( table_version ): + table = self.app.tool_data_tables.get(table_name, None) + if table is not None and not table.is_current_version(table_version): return self.reload_genomes() - def get_build( self, dbkey ): + def get_build(self, dbkey): """ Returns build for the given key. """ self.check_and_reload() rval = None if dbkey in self.genomes: - rval = self.genomes[ dbkey ] + rval = self.genomes[dbkey] return rval - def get_dbkeys( self, trans, chrom_info=False, **kwd ): + def get_dbkeys(self, trans, chrom_info=False, **kwd): """ Returns all known dbkeys. If chrom_info is True, only dbkeys with chromosome lengths are returned. """ self.check_and_reload() @@ -256,8 +257,8 @@ def get_dbkeys( self, trans, chrom_info=False, **kwd ): user = trans.get_user() if user: if 'dbkeys' in user.preferences: - user_keys_dict = loads( user.preferences[ 'dbkeys' ] ) - dbkeys.extend( [ (attributes[ 'name' ], key ) for key, attributes in user_keys_dict.items() ] ) + user_keys_dict = loads(user.preferences['dbkeys']) + dbkeys.extend([(attributes['name'], key) for key, attributes in user_keys_dict.items()]) # Add app keys to dbkeys. @@ -269,20 +270,20 @@ def filter_fn(b): def filter_fn(b): return True - dbkeys.extend( [ ( genome.description, genome.key ) for key, genome in self.genomes.items() if filter_fn( genome ) ] ) + dbkeys.extend([(genome.description, genome.key) for key, genome in self.genomes.items() if filter_fn(genome)]) return dbkeys - def chroms( self, trans, dbkey=None, num=None, chrom=None, low=None ): + def chroms(self, trans, dbkey=None, num=None, chrom=None, low=None): """ Returns a naturally sorted list of chroms/contigs for a given dbkey. Use either chrom or low to specify the starting chrom in the return list. """ self.check_and_reload() # If there is no dbkey owner, default to current user. - dbkey_owner, dbkey = decode_dbkey( dbkey ) + dbkey_owner, dbkey = decode_dbkey(dbkey) if dbkey_owner: - dbkey_user = trans.sa_session.query( trans.app.model.User ).filter_by( username=dbkey_owner ).first() + dbkey_user = trans.sa_session.query(trans.app.model.User).filter_by(username=dbkey_owner).first() else: dbkey_user = trans.user @@ -294,80 +295,80 @@ def chroms( self, trans, dbkey=None, num=None, chrom=None, low=None ): # Look first in user's custom builds. if dbkey_user and 'dbkeys' in dbkey_user.preferences: - user_keys = loads( dbkey_user.preferences['dbkeys'] ) + user_keys = loads(dbkey_user.preferences['dbkeys']) if dbkey in user_keys: - dbkey_attributes = user_keys[ dbkey ] - dbkey_name = dbkey_attributes[ 'name' ] + dbkey_attributes = user_keys[dbkey] + dbkey_name = dbkey_attributes['name'] # If there's a fasta for genome, convert to 2bit for later use. if 'fasta' in dbkey_attributes: - build_fasta = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( dbkey_attributes[ 'fasta' ] ) - len_file = build_fasta.get_converted_dataset( trans, 'len' ).file_name - build_fasta.get_converted_dataset( trans, 'twobit' ) + build_fasta = trans.sa_session.query(trans.app.model.HistoryDatasetAssociation).get(dbkey_attributes['fasta']) + len_file = build_fasta.get_converted_dataset(trans, 'len').file_name + build_fasta.get_converted_dataset(trans, 'twobit') # HACK: set twobit_file to True rather than a file name because # get_converted_dataset returns null during conversion even though # there will eventually be a twobit file available for genome. twobit_file = True # Backwards compatibility: look for len file directly. elif 'len' in dbkey_attributes: - len_file = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( user_keys[ dbkey ][ 'len' ] ).file_name + len_file = trans.sa_session.query(trans.app.model.HistoryDatasetAssociation).get(user_keys[dbkey]['len']).file_name if len_file: - genome = Genome( dbkey, dbkey_name, len_file=len_file, twobit_file=twobit_file ) + genome = Genome(dbkey, dbkey_name, len_file=len_file, twobit_file=twobit_file) # Look in history and system builds. if not genome: # Look in history for chromosome len file. - len_ds = trans.db_dataset_for( dbkey ) + len_ds = trans.db_dataset_for(dbkey) if len_ds: - genome = Genome( dbkey, dbkey_name, len_file=len_ds.file_name ) + genome = Genome(dbkey, dbkey_name, len_file=len_ds.file_name) # Look in system builds. elif dbkey in self.genomes: - genome = self.genomes[ dbkey ] + genome = self.genomes[dbkey] # Set up return value or log exception if genome not found for key. rval = None if genome: - rval = genome.to_dict( num=num, chrom=chrom, low=low ) + rval = genome.to_dict(num=num, chrom=chrom, low=low) else: - log.exception( 'genome not found for key %s', dbkey ) + log.exception('genome not found for key %s', dbkey) return rval - def has_reference_data( self, dbkey, dbkey_owner=None ): + def has_reference_data(self, dbkey, dbkey_owner=None): """ Returns true if there is reference data for the specified dbkey. If dbkey is custom, dbkey_owner is needed to determine if there is reference data. """ self.check_and_reload() # Look for key in built-in builds. - if dbkey in self.genomes and self.genomes[ dbkey ].twobit_file: + if dbkey in self.genomes and self.genomes[dbkey].twobit_file: # There is built-in reference data. return True # Look for key in owner's custom builds. if dbkey_owner and 'dbkeys' in dbkey_owner.preferences: - user_keys = loads( dbkey_owner.preferences[ 'dbkeys' ] ) + user_keys = loads(dbkey_owner.preferences['dbkeys']) if dbkey in user_keys: - dbkey_attributes = user_keys[ dbkey ] + dbkey_attributes = user_keys[dbkey] if 'fasta' in dbkey_attributes: # Fasta + converted datasets can provide reference data. return True return False - def reference( self, trans, dbkey, chrom, low, high ): + def reference(self, trans, dbkey, chrom, low, high): """ Return reference data for a build. """ self.check_and_reload() # If there is no dbkey owner, default to current user. - dbkey_owner, dbkey = decode_dbkey( dbkey ) + dbkey_owner, dbkey = decode_dbkey(dbkey) if dbkey_owner: - dbkey_user = trans.sa_session.query( trans.app.model.User ).filter_by( username=dbkey_owner ).first() + dbkey_user = trans.sa_session.query(trans.app.model.User).filter_by(username=dbkey_owner).first() else: dbkey_user = trans.user - if not self.has_reference_data( dbkey, dbkey_user ): + if not self.has_reference_data(dbkey, dbkey_user): return None # @@ -378,21 +379,21 @@ def reference( self, trans, dbkey, chrom, low, high ): # Built-in twobit. twobit_file_name = self.genomes[dbkey].twobit_file else: - user_keys = loads( dbkey_user.preferences['dbkeys'] ) - dbkey_attributes = user_keys[ dbkey ] - fasta_dataset = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( dbkey_attributes[ 'fasta' ] ) - msg = fasta_dataset.convert_dataset( trans, 'twobit' ) + user_keys = loads(dbkey_user.preferences['dbkeys']) + dbkey_attributes = user_keys[dbkey] + fasta_dataset = trans.sa_session.query(trans.app.model.HistoryDatasetAssociation).get(dbkey_attributes['fasta']) + msg = fasta_dataset.convert_dataset(trans, 'twobit') if msg: return msg else: - twobit_dataset = fasta_dataset.get_converted_dataset( trans, 'twobit' ) + twobit_dataset = fasta_dataset.get_converted_dataset(trans, 'twobit') twobit_file_name = twobit_dataset.file_name # Read and return reference data. try: - twobit = TwoBitFile( open( twobit_file_name ) ) + twobit = TwoBitFile(open(twobit_file_name)) if chrom in twobit: - seq_data = twobit[chrom].get( int(low), int(high) ) - return GenomeRegion( chrom=chrom, start=low, end=high, sequence=seq_data ) + seq_data = twobit[chrom].get(int(low), int(high)) + return GenomeRegion(chrom=chrom, start=low, end=high, sequence=seq_data) except IOError: return None diff --git a/lib/galaxy/visualization/plugins/config_parser.py b/lib/galaxy/visualization/plugins/config_parser.py index 8898a4be7440..1c32035411fa 100644 --- a/lib/galaxy/visualization/plugins/config_parser.py +++ b/lib/galaxy/visualization/plugins/config_parser.py @@ -4,10 +4,10 @@ from galaxy import util import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class ParsingException( ValueError ): +class ParsingException(ValueError): """ An exception class for errors that occur during parsing of the visualizations framework configuration XML file. @@ -15,7 +15,7 @@ class ParsingException( ValueError ): pass -class VisualizationsConfigParser( object ): +class VisualizationsConfigParser(object): """ Class that parses a visualizations configuration XML file. @@ -30,24 +30,24 @@ class VisualizationsConfigParser( object ): #: what are the allowed 'entry_point_type' for entry_point elements ALLOWED_ENTRY_POINT_TYPES = ['mako', 'html', 'script'] #: what are the allowed href targets when clicking on a visualization anchor - VALID_RENDER_TARGETS = [ 'galaxy_main', '_top', '_blank' ] + VALID_RENDER_TARGETS = ['galaxy_main', '_top', '_blank'] - def __init__( self ): + def __init__(self): # what parsers should be used for sub-components self.data_source_parser = DataSourceParser() self.param_parser = ParamParser() self.param_modifier_parser = ParamModifierParser() - def parse_file( self, xml_filepath ): + def parse_file(self, xml_filepath): """ Parse the given XML file for visualizations data. :returns: visualization config dictionary """ - xml_tree = util.parse_xml( xml_filepath ) - visualization = self.parse_visualization( xml_tree.getroot() ) + xml_tree = util.parse_xml(xml_filepath) + visualization = self.parse_visualization(xml_tree.getroot()) return visualization - def parse_visualization( self, xml_tree ): + def parse_visualization(self, xml_tree): """ Parse the template, name, and any data_sources and params from the given `xml_tree` for a visualization. @@ -56,109 +56,109 @@ def parse_visualization( self, xml_tree ): # main tag specifies plugin type (visualization or # interactive_enviornment). - returned[ 'plugin_type' ] = xml_tree.tag + returned['plugin_type'] = xml_tree.tag # a text display name for end user links - returned[ 'name' ] = xml_tree.attrib.get( 'name', None ) - if not returned[ 'name' ]: - raise ParsingException( 'visualization needs a name attribute' ) + returned['name'] = xml_tree.attrib.get('name', None) + if not returned['name']: + raise ParsingException('visualization needs a name attribute') # allow manually turning off a vis by checking for a disabled property if 'disabled' in xml_tree.attrib: - log.info( 'Visualizations plugin disabled: %s. Skipping...', returned[ 'name' ] ) + log.info('Visualizations plugin disabled: %s. Skipping...', returned['name']) return None # record the embeddable flag - defaults to false # this is a design by contract promise that the visualization can be rendered inside another page # often by rendering only a DOM fragment. Since this is an advanced feature that requires a bit more # work from the creator's side - it defaults to False - returned[ 'embeddable' ] = False + returned['embeddable'] = False if 'embeddable' in xml_tree.attrib: - returned[ 'embeddable' ] = xml_tree.attrib.get( 'embeddable', False ) == 'true' + returned['embeddable'] = xml_tree.attrib.get('embeddable', False) == 'true' # a (for now) text description of what the visualization does - description = xml_tree.find( 'description' ) - returned[ 'description' ] = description.text.strip() if description is not None else None + description = xml_tree.find('description') + returned['description'] = description.text.strip() if description is not None else None # data_sources are the kinds of objects/data associated with the visualization # e.g. views on HDAs can use this to find out what visualizations are applicable to them data_sources = [] - data_sources_confs = xml_tree.find( 'data_sources' ) - for data_source_conf in data_sources_confs.findall( 'data_source' ): - data_source = self.data_source_parser.parse( data_source_conf ) + data_sources_confs = xml_tree.find('data_sources') + for data_source_conf in data_sources_confs.findall('data_source'): + data_source = self.data_source_parser.parse(data_source_conf) if data_source: - data_sources.append( data_source ) + data_sources.append(data_source) # data_sources are not required if not data_sources: - raise ParsingException( 'No valid data_sources for visualization' ) - returned[ 'data_sources' ] = data_sources + raise ParsingException('No valid data_sources for visualization') + returned['data_sources'] = data_sources # TODO: this is effectively required due to param_confs.findall( 'param' ) # parameters spell out how to convert query string params into resources and data # that will be parsed, fetched, etc. and passed to the template # list or dict? ordered or not? params = {} - param_confs = xml_tree.find( 'params' ) - param_elements = param_confs.findall( 'param' ) if param_confs is not None else [] + param_confs = xml_tree.find('params') + param_elements = param_confs.findall('param') if param_confs is not None else [] for param_conf in param_elements: - param = self.param_parser.parse( param_conf ) + param = self.param_parser.parse(param_conf) if param: - params[ param_conf.text ] = param + params[param_conf.text] = param # params are not required if params: - returned[ 'params' ] = params + returned['params'] = params # param modifiers provide extra information for other params (e.g. hda_ldda='hda' -> dataset_id is an hda id) # store these modifiers in a 2-level dictionary { target_param: { param_modifier_key: { param_mod_data } # ugh - wish we didn't need these param_modifiers = {} - param_modifier_elements = param_confs.findall( 'param_modifier' ) if param_confs is not None else [] + param_modifier_elements = param_confs.findall('param_modifier') if param_confs is not None else [] for param_modifier_conf in param_modifier_elements: - param_modifier = self.param_modifier_parser.parse( param_modifier_conf ) + param_modifier = self.param_modifier_parser.parse(param_modifier_conf) # param modifiers map accrd. to the params they modify (for faster lookup) - target_param = param_modifier_conf.get( 'modifies' ) + target_param = param_modifier_conf.get('modifies') param_modifier_key = param_modifier_conf.text if param_modifier and target_param in params: # multiple params can modify a single, other param, # so store in a sub-dict, initializing if this is the first if target_param not in param_modifiers: - param_modifiers[ target_param ] = {} - param_modifiers[ target_param ][ param_modifier_key ] = param_modifier + param_modifiers[target_param] = {} + param_modifiers[target_param][param_modifier_key] = param_modifier # not required if param_modifiers: - returned[ 'param_modifiers' ] = param_modifiers + returned['param_modifiers'] = param_modifiers # entry_point: how will this plugin render/load? mako, script tag, or static html file? - returned[ 'entry_point' ] = self.parse_entry_point( xml_tree ) + returned['entry_point'] = self.parse_entry_point(xml_tree) # link_text: the string to use for the text of any links/anchors to this visualization - link_text = xml_tree.find( 'link_text' ) + link_text = xml_tree.find('link_text') if link_text is not None and link_text.text: - returned[ 'link_text' ] = link_text + returned['link_text'] = link_text # render_target: where in the browser to open the rendered visualization # defaults to: galaxy_main - render_target = xml_tree.find( 'render_target' ) - if( ( render_target is not None and render_target.text ) and - ( render_target.text in self.VALID_RENDER_TARGETS ) ): - returned[ 'render_target' ] = render_target.text + render_target = xml_tree.find('render_target') + if((render_target is not None and render_target.text) and + (render_target.text in self.VALID_RENDER_TARGETS)): + returned['render_target'] = render_target.text else: - returned[ 'render_target' ] = 'galaxy_main' + returned['render_target'] = 'galaxy_main' # consider unifying the above into its own element and parsing method return returned - def parse_entry_point( self, xml_tree ): + def parse_entry_point(self, xml_tree): """ Parse the config file for an appropriate entry point: a mako template, a script tag, or an html file, returning as dictionary with: `type`, `file`, and `attr`ibutes of the element. """ # (older) mako-only syntax: the template to use in rendering the visualization - template = xml_tree.find( 'template' ) + template = xml_tree.find('template') if template is not None and template.text: - log.info( 'template syntax is deprecated: use entry_point instead' ) + log.info('template syntax is deprecated: use entry_point instead') return { 'type' : 'mako', 'file' : template.text, @@ -166,15 +166,15 @@ def parse_entry_point( self, xml_tree ): } # need one of the two: (the deprecated) template or entry_point - entry_point = xml_tree.find( 'entry_point' ) + entry_point = xml_tree.find('entry_point') if entry_point is None: - raise ParsingException( 'template or entry_point required' ) + raise ParsingException('template or entry_point required') # parse by returning a sub-object and simply copying any attributes unused here entry_point_attrib = entry_point.attrib.copy() - entry_point_type = entry_point_attrib.pop( 'entry_point_type', 'mako' ) + entry_point_type = entry_point_attrib.pop('entry_point_type', 'mako') if entry_point_type not in self.ALLOWED_ENTRY_POINT_TYPES: - raise ParsingException( 'Unknown entry_point type: ' + entry_point_type ) + raise ParsingException('Unknown entry_point type: ' + entry_point_type) return { 'type' : entry_point_type, 'file' : entry_point.text, @@ -183,7 +183,7 @@ def parse_entry_point( self, xml_tree ): # ------------------------------------------------------------------- -class DataSourceParser( object ): +class DataSourceParser(object): """ Component class of VisualizationsConfigParser that parses data_source elements within visualization elements. @@ -206,31 +206,31 @@ class DataSourceParser( object ): 'datatype' ] - def parse( self, xml_tree ): + def parse(self, xml_tree): """ Return a visualization data_source dictionary parsed from the given XML element. """ returned = {} # model_class (required, only one) - look up and convert model_class to actual galaxy model class - model_class = self.parse_model_class( xml_tree.find( 'model_class' ) ) + model_class = self.parse_model_class(xml_tree.find('model_class')) if not model_class: - raise ParsingException( 'data_source needs a model class' ) - returned[ 'model_class' ] = model_class + raise ParsingException('data_source needs a model class') + returned['model_class'] = model_class # tests (optional, 0 or more) - data for boolean test: 'is the visualization usable by this object?' # when no tests are given, default to isinstance( object, model_class ) - returned[ 'tests' ] = self.parse_tests( xml_tree.findall( 'test' ) ) + returned['tests'] = self.parse_tests(xml_tree.findall('test')) # to_params (optional, 0 or more) - tells the registry to set certain params based on the model_clas, tests - returned[ 'to_params' ] = {} - to_params = self.parse_to_params( xml_tree.findall( 'to_param' ) ) + returned['to_params'] = {} + to_params = self.parse_to_params(xml_tree.findall('to_param')) if to_params: - returned[ 'to_params' ] = to_params + returned['to_params'] = to_params return returned - def parse_model_class( self, xml_tree ): + def parse_model_class(self, xml_tree): """ Convert xml model_class element to a galaxy model class (or None if model class is not found). @@ -239,34 +239,34 @@ def parse_model_class( self, xml_tree ): The model_class string must be in ALLOWED_MODEL_CLASSES. """ if xml_tree is None or not xml_tree.text: - raise ParsingException( 'data_source entry requires a model_class' ) + raise ParsingException('data_source entry requires a model_class') if xml_tree.text not in self.ALLOWED_MODEL_CLASSES: # log.debug( 'available data_source model_classes: %s' %( str( self.ALLOWED_MODEL_CLASSES ) ) ) - raise ParsingException( 'Invalid data_source model_class: %s' % ( xml_tree.text ) ) + raise ParsingException('Invalid data_source model_class: %s' % (xml_tree.text)) # look up the model from the model module returning an empty data_source if not found - model_class = getattr( galaxy.model, xml_tree.text, None ) + model_class = getattr(galaxy.model, xml_tree.text, None) return model_class - def _build_getattr_lambda( self, attr_name_list ): + def _build_getattr_lambda(self, attr_name_list): """ Recursively builds a compound lambda function of getattr's from the attribute names given in `attr_name_list`. """ - if len( attr_name_list ) == 0: + if len(attr_name_list) == 0: # identity - if list is empty, return object itself return lambda o: o next_attr_name = attr_name_list[-1] - if len( attr_name_list ) == 1: + if len(attr_name_list) == 1: # recursive base case - return lambda o: getattr( o, next_attr_name ) + return lambda o: getattr(o, next_attr_name) # recursive case - return lambda o: getattr( self._build_getattr_lambda( attr_name_list[:-1] )( o ), next_attr_name ) + return lambda o: getattr(self._build_getattr_lambda(attr_name_list[:-1])(o), next_attr_name) - def parse_tests( self, xml_tree_list ): + def parse_tests(self, xml_tree_list): """ Returns a list of test dictionaries that the registry can use against a given object to determine if the visualization can be @@ -280,52 +280,52 @@ def parse_tests( self, xml_tree_list ): return tests for test_elem in xml_tree_list: - test_type = test_elem.get( 'type', 'eq' ) + test_type = test_elem.get('type', 'eq') test_result = test_elem.text.strip() if test_elem.text else None if not test_type or not test_result: - log.warning( 'Skipping test. Needs both type attribute and text node to be parsed: ' + - '%s, %s' % ( test_type, test_elem.text ) ) + log.warning('Skipping test. Needs both type attribute and text node to be parsed: ' + + '%s, %s' % (test_type, test_elem.text)) continue test_result = test_result.strip() # test_attr can be a dot separated chain of object attributes (e.g. dataset.datatype) - convert to list # TODO: too dangerous - constrain these to some allowed list # TODO: does this err if no test_attr - it should... - test_attr = test_elem.get( 'test_attr' ) - test_attr = test_attr.split( self.ATTRIBUTE_SPLIT_CHAR ) if isinstance( test_attr, string_types ) else [] + test_attr = test_elem.get('test_attr') + test_attr = test_attr.split(self.ATTRIBUTE_SPLIT_CHAR) if isinstance(test_attr, string_types) else [] # log.debug( 'test_type: %s, test_attr: %s, test_result: %s', test_type, test_attr, test_result ) # build a lambda function that gets the desired attribute to test - getter = self._build_getattr_lambda( test_attr ) + getter = self._build_getattr_lambda(test_attr) # result type should tell the registry how to convert the result before the test - test_result_type = test_elem.get( 'result_type', 'string' ) + test_result_type = test_elem.get('result_type', 'string') # test functions should be sent an object to test, and the parsed result expected from the test if test_type == 'isinstance': # is test_attr attribute an instance of result # TODO: wish we could take this further but it would mean passing in the datatypes_registry def test_fn(o, result): - return isinstance( getter( o ), result ) + return isinstance(getter(o), result) elif test_type == 'has_dataprovider': # does the object itself have a datatype attr and does that datatype have the given dataprovider def test_fn(o, result): - return (hasattr( getter( o ), 'has_dataprovider' ) and - getter( o ).has_dataprovider( result ) ) + return (hasattr(getter(o), 'has_dataprovider') and + getter(o).has_dataprovider(result)) elif test_type == 'has_attribute': # does the object itself have attr in 'result' (no equivalence checking) def test_fn(o, result): - return hasattr( getter( o ), result ) + return hasattr(getter(o), result) elif test_type == 'not_eq': def test_fn(o, result): - return str( getter( o ) ) != result + return str(getter(o)) != result else: # default to simple (string) equilavance (coercing the test_attr to a string) def test_fn(o, result): - return str( getter( o ) ) == result + return str(getter(o)) == result tests.append({ 'type' : test_type, @@ -336,7 +336,7 @@ def test_fn(o, result): return tests - def parse_to_params( self, xml_tree_list ): + def parse_to_params(self, xml_tree_list): """ Given a list of `to_param` elements, returns a dictionary that allows the registry to convert the data_source into one or more appropriate @@ -350,36 +350,36 @@ def parse_to_params( self, xml_tree_list ): # param_name required param_name = element.text if not param_name: - raise ParsingException( 'to_param requires text (the param name)' ) + raise ParsingException('to_param requires text (the param name)') param = {} # assign is a shortcut param_attr that assigns a value to a param (as text) - assign = element.get( 'assign' ) + assign = element.get('assign') if assign is not None: - param[ 'assign' ] = assign + param['assign'] = assign # param_attr is the attribute of the object (that the visualization will be applied to) # that should be converted into a query param (e.g. param_attr="id" -> dataset_id) # TODO:?? use the build attr getter here? # simple (1 lvl) attrs for now - param_attr = element.get( 'param_attr' ) + param_attr = element.get('param_attr') if param_attr is not None: - param[ 'param_attr' ] = param_attr + param['param_attr'] = param_attr # element must have either param_attr or assign? what about no params (the object itself) if not param_attr and not assign: - raise ParsingException( 'to_param requires either assign or param_attr attributes: %s', param_name ) + raise ParsingException('to_param requires either assign or param_attr attributes: %s', param_name) # TODO: consider making the to_param name an attribute (param="hda_ldda") and the text what would # be used for the conversion - this would allow CDATA values to be passed # if param: - to_param_dict[ param_name ] = param + to_param_dict[param_name] = param return to_param_dict -class ParamParser( object ): +class ParamParser(object): """ Component class of VisualizationsConfigParser that parses param elements within visualization elements. @@ -389,7 +389,7 @@ class ParamParser( object ): """ DEFAULT_PARAM_TYPE = 'str' - def parse( self, xml_tree ): + def parse(self, xml_tree): """ Parse a visualization parameter from the given `xml_tree`. """ @@ -398,51 +398,51 @@ def parse( self, xml_tree ): # don't store key, just check it param_key = xml_tree.text if not param_key: - raise ParsingException( 'Param entry requires text' ) + raise ParsingException('Param entry requires text') - returned[ 'type' ] = self.parse_param_type( xml_tree ) + returned['type'] = self.parse_param_type(xml_tree) # is the parameter required in the template and, # if not, what is the default value? - required = xml_tree.get( 'required' ) == "true" - returned[ 'required' ] = required + required = xml_tree.get('required') == "true" + returned['required'] = required if not required: # default defaults to None default = None if 'default' in xml_tree.attrib: - default = xml_tree.get( 'default' ) + default = xml_tree.get('default') # convert default based on param_type here - returned[ 'default' ] = default + returned['default'] = default # does the param have to be within a list of certain values # NOTE: the interpretation of this list is deferred till parsing and based on param type # e.g. it could be 'val in constrain_to', or 'constrain_to is min, max for number', etc. # TODO: currently unused - constrain_to = xml_tree.get( 'constrain_to' ) + constrain_to = xml_tree.get('constrain_to') if constrain_to: - returned[ 'constrain_to' ] = constrain_to.split( ',' ) + returned['constrain_to'] = constrain_to.split(',') # is the param a comma-separated-value list? - returned[ 'csv' ] = xml_tree.get( 'csv' ) == "true" + returned['csv'] = xml_tree.get('csv') == "true" # remap keys in the params/query string to the var names used in the template - var_name_in_template = xml_tree.get( 'var_name_in_template' ) + var_name_in_template = xml_tree.get('var_name_in_template') if var_name_in_template: - returned[ 'var_name_in_template' ] = var_name_in_template + returned['var_name_in_template'] = var_name_in_template return returned - def parse_param_type( self, xml_tree ): + def parse_param_type(self, xml_tree): """ Parse a param type from the given `xml_tree`. """ # default to string as param_type - param_type = xml_tree.get( 'type' ) or self.DEFAULT_PARAM_TYPE + param_type = xml_tree.get('type') or self.DEFAULT_PARAM_TYPE # TODO: set parsers and validaters, convert here return param_type -class ParamModifierParser( ParamParser ): +class ParamModifierParser(ParamParser): """ Component class of VisualizationsConfigParser that parses param_modifier elements within visualization elements. @@ -452,10 +452,11 @@ class ParamModifierParser( ParamParser ): (normal) param (e.g. 'hda_ldda' can equal 'hda' or 'ldda' and control whether a visualizations 'dataset_id' param is for an HDA or LDDA). """ - def parse( self, element ): + + def parse(self, element): # modifies is required - modifies = element.get( 'modifies' ) + modifies = element.get('modifies') if not modifies: - raise ParsingException( 'param_modifier entry requires a target param key (attribute "modifies")' ) - returned = super( ParamModifierParser, self).parse( element ) + raise ParsingException('param_modifier entry requires a target param key (attribute "modifies")') + returned = super(ParamModifierParser, self).parse(element) return returned diff --git a/lib/galaxy/visualization/plugins/plugin.py b/lib/galaxy/visualization/plugins/plugin.py index 2406605516b1..50e97cbbcd41 100644 --- a/lib/galaxy/visualization/plugins/plugin.py +++ b/lib/galaxy/visualization/plugins/plugin.py @@ -16,7 +16,7 @@ from galaxy.visualization.plugins import utils import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) # ============================================================================= @@ -25,12 +25,12 @@ # allow config to override static/template settings # allow config detection in alternate places: galaxy-visualization.xml # ============================================================================= -class ServesStaticPluginMixin( object ): +class ServesStaticPluginMixin(object): """ An object that serves static files from the server. """ - def _set_up_static_plugin( self, **kwargs ): + def _set_up_static_plugin(self, **kwargs): """ Detect and set up static paths and urls if needed. """ @@ -42,21 +42,21 @@ def _set_up_static_plugin( self, **kwargs ): self.serves_static = True return self.serves_static - def _is_static_plugin( self ): + def _is_static_plugin(self): """ Detect whether this plugin should serve static resources. """ - return os.path.isdir( self._build_static_path() ) + return os.path.isdir(self._build_static_path()) - def _build_static_path( self ): - return os.path.join( self.path, 'static' ) + def _build_static_path(self): + return os.path.join(self.path, 'static') - def _build_static_url( self ): - return '/'.join([ self.base_url, 'static' ]) + def _build_static_url(self): + return '/'.join([self.base_url, 'static']) # ============================================================================= -class ServesTemplatesPluginMixin( object ): +class ServesTemplatesPluginMixin(object): """ An object that renders (mako) template files from the server. """ @@ -66,41 +66,41 @@ class ServesTemplatesPluginMixin( object ): #: default encoding of plugin templates DEFAULT_TEMPLATE_ENCODING = 'utf-8' - def _set_up_template_plugin( self, template_cache_dir, additional_template_paths=None, **kwargs ): + def _set_up_template_plugin(self, template_cache_dir, additional_template_paths=None, **kwargs): """ Detect and set up template paths if the plugin serves templates. """ self.serves_templates = False if self._is_template_plugin(): self.template_path = self._build_template_path() - self.template_lookup = self._build_template_lookup( template_cache_dir, - additional_template_paths=additional_template_paths ) + self.template_lookup = self._build_template_lookup(template_cache_dir, + additional_template_paths=additional_template_paths) self.serves_templates = True return self.serves_templates - def _is_template_plugin( self ): - return os.path.isdir( self._build_template_path() ) + def _is_template_plugin(self): + return os.path.isdir(self._build_template_path()) - def _build_template_path( self ): - return os.path.join( self.path, 'templates' ) + def _build_template_path(self): + return os.path.join(self.path, 'templates') - def _build_template_lookup( self, template_cache_dir, additional_template_paths=None, - collection_size=DEFAULT_TEMPLATE_COLLECTION_SIZE, output_encoding=DEFAULT_TEMPLATE_ENCODING ): + def _build_template_lookup(self, template_cache_dir, additional_template_paths=None, + collection_size=DEFAULT_TEMPLATE_COLLECTION_SIZE, output_encoding=DEFAULT_TEMPLATE_ENCODING): """ Build a mako template filename lookup for the plugin. """ template_lookup_paths = self.template_path if additional_template_paths: - template_lookup_paths = [ template_lookup_paths ] + additional_template_paths + template_lookup_paths = [template_lookup_paths] + additional_template_paths return mako.lookup.TemplateLookup( directories=template_lookup_paths, module_directory=template_cache_dir, collection_size=collection_size, - output_encoding=output_encoding ) + output_encoding=output_encoding) # ============================================================================= -class VisualizationPlugin( pluginframework.Plugin, ServesStaticPluginMixin, ServesTemplatesPluginMixin ): +class VisualizationPlugin(pluginframework.Plugin, ServesStaticPluginMixin, ServesTemplatesPluginMixin): """ A plugin that instantiates resources, serves static files, and uses mako templates to render web pages. @@ -109,58 +109,58 @@ class VisualizationPlugin( pluginframework.Plugin, ServesStaticPluginMixin, Serv # config[ 'entry_point' ][ 'type' ] == 'mako' # TODO: concept/name collision between plugin config and visualization config - def __init__( self, app, path, name, config, context=None, **kwargs ): - super( VisualizationPlugin, self ).__init__( app, path, name, config, context=None, **kwargs ) + def __init__(self, app, path, name, config, context=None, **kwargs): + super(VisualizationPlugin, self).__init__(app, path, name, config, context=None, **kwargs) context = context or {} self.config = config - base_url = context.get( 'base_url', '' ) - self.base_url = '/'.join([ base_url, self.name ]) if base_url else self.name + base_url = context.get('base_url', '') + self.base_url = '/'.join([base_url, self.name]) if base_url else self.name self._set_up_static_plugin() - template_cache_dir = context.get( 'template_cache_dir', None ) - additional_template_paths = context.get( 'additional_template_paths', [] ) - self._set_up_template_plugin( template_cache_dir, additional_template_paths=additional_template_paths ) + template_cache_dir = context.get('template_cache_dir', None) + additional_template_paths = context.get('additional_template_paths', []) + self._set_up_template_plugin(template_cache_dir, additional_template_paths=additional_template_paths) - self.resource_parser = resource_parser.ResourceParser( app ) + self.resource_parser = resource_parser.ResourceParser(app) - def render( self, trans=None, embedded=None, **kwargs ): + def render(self, trans=None, embedded=None, **kwargs): """ Render and return the text of the non-saved plugin webpage/fragment. """ # not saved - no existing config config = {} # set up render vars based on plugin.config and kwargs - render_vars = self._build_render_vars( config, trans=trans, **kwargs ) - return self._render( render_vars, trans=trans, embedded=embedded ) + render_vars = self._build_render_vars(config, trans=trans, **kwargs) + return self._render(render_vars, trans=trans, embedded=embedded) - def render_saved( self, visualization, trans=None, embedded=None, **kwargs ): + def render_saved(self, visualization, trans=None, embedded=None, **kwargs): """ Render and return the text of the plugin webpage/fragment using the config/data of a saved visualization. """ - config = self._get_saved_visualization_config( visualization, **kwargs ) + config = self._get_saved_visualization_config(visualization, **kwargs) # pass the saved visualization config for parsing into render vars - render_vars = self._build_render_vars( config, trans=trans, **kwargs ) + render_vars = self._build_render_vars(config, trans=trans, **kwargs) # update any values that were loaded from the saved Visualization - render_vars.update( dict( + render_vars.update(dict( title=visualization.latest_revision.title, saved_visualization=visualization, - visualization_id=trans.security.encode_id( visualization.id ), + visualization_id=trans.security.encode_id(visualization.id), )) - return self._render( render_vars, trans=trans, embedded=embedded ) + return self._render(render_vars, trans=trans, embedded=embedded) - def _get_saved_visualization_config( self, visualization, revision=None, **kwargs ): + def _get_saved_visualization_config(self, visualization, revision=None, **kwargs): """ Return the config of a saved visualization and revision. If no revision given, default to latest revision. """ # TODO: allow loading a specific revision - should be part of UsesVisualization - return copy.copy( visualization.latest_revision.config ) + return copy.copy(visualization.latest_revision.config) # ---- non-public - def _build_render_vars( self, config, trans=None, **kwargs ): + def _build_render_vars(self, config, trans=None, **kwargs): """ Build all the variables that will be passed into the renderer. """ @@ -168,72 +168,72 @@ def _build_render_vars( self, config, trans=None, **kwargs ): # Meta variables passed to the template/renderer to describe the visualization being rendered. render_vars.update( visualization_name=self.name, - visualization_display_name=self.config[ 'name' ], - title=kwargs.get( 'title', None ), + visualization_display_name=self.config['name'], + title=kwargs.get('title', None), saved_visualization=None, visualization_id=None, # NOTE: passing *unparsed* kwargs as query query=kwargs, ) # config based on existing or kwargs - config = self._build_config( config, trans=trans, **kwargs ) - render_vars[ 'config' ] = config + config = self._build_config(config, trans=trans, **kwargs) + render_vars['config'] = config # further parse config to resources (models, etc.) used in template based on registry config - resources = self._config_to_resources( trans, config ) - render_vars.update( resources ) + resources = self._config_to_resources(trans, config) + render_vars.update(resources) return render_vars - def _build_config( self, config, trans=None, **kwargs ): + def _build_config(self, config, trans=None, **kwargs): """ Build the configuration for this new/saved visualization by combining any existing config and the kwargs (gen. from the url query). """ # first, pull from any existing config if config: - config = copy.copy( config ) + config = copy.copy(config) else: config = {} # then, overwrite with keys/values from kwargs (gen. a query string) - config_from_kwargs = self._kwargs_to_config( trans, kwargs ) - config.update( config_from_kwargs ) + config_from_kwargs = self._kwargs_to_config(trans, kwargs) + config.update(config_from_kwargs) # to object format for easier querying - config = utils.OpenObject( **config ) + config = utils.OpenObject(**config) return config # TODO: the difference between config & resources is unclear in this section - is it needed? - def _kwargs_to_config( self, trans, kwargs ): + def _kwargs_to_config(self, trans, kwargs): """ Given a kwargs dict (gen. a query string dict from a controller action), parse and return any key/value pairs found in the plugin's `params` section. """ - expected_params = self.config.get( 'params', {} ) - config = self.resource_parser.parse_config( trans, expected_params, kwargs ) + expected_params = self.config.get('params', {}) + config = self.resource_parser.parse_config(trans, expected_params, kwargs) return config - def _config_to_resources( self, trans, config ): + def _config_to_resources(self, trans, config): """ Instantiate/deserialize the resources (HDAs, LDDAs, etc.) given in a visualization config into models/variables a visualization renderer can use. """ - expected_params = self.config.get( 'params', {} ) - param_modifiers = self.config.get( 'param_modifiers', {} ) - resources = self.resource_parser.parse_parameter_dictionary( trans, expected_params, config, param_modifiers ) + expected_params = self.config.get('params', {}) + param_modifiers = self.config.get('param_modifiers', {}) + resources = self.resource_parser.parse_parameter_dictionary(trans, expected_params, config, param_modifiers) return resources - def _render( self, render_vars, trans=None, embedded=None, **kwargs ): + def _render(self, render_vars, trans=None, embedded=None, **kwargs): """ Render the visualization via Mako and the plugin's template file. """ - render_vars[ 'embedded' ] = self._parse_embedded( embedded ) + render_vars['embedded'] = self._parse_embedded(embedded) # NOTE: (mako specific) vars is a dictionary for shared data in the template # this feels hacky to me but it's what mako recommends: # http://docs.makotemplates.org/en/latest/runtime.html - render_vars.update( vars={} ) - template_filename = self.config[ 'entry_point' ][ 'file' ] - return trans.fill_template( template_filename, template_lookup=self.template_lookup, **render_vars ) + render_vars.update(vars={}) + template_filename = self.config['entry_point']['file'] + return trans.fill_template(template_filename, template_lookup=self.template_lookup, **render_vars) - def _parse_embedded( self, embedded ): + def _parse_embedded(self, embedded): """ Parse information on dimensions, readonly, etc. from the embedded query val. """ @@ -242,56 +242,56 @@ def _parse_embedded( self, embedded ): # ============================================================================= -class InteractiveEnvironmentPlugin( VisualizationPlugin ): +class InteractiveEnvironmentPlugin(VisualizationPlugin): """ Serves web-based REPLs such as Jupyter and RStudio. """ INTENV_REQUEST_FACTORY = interactive_environments.InteractiveEnvironmentRequest - def __init__( self, app, path, name, config, context=None, **kwargs ): + def __init__(self, app, path, name, config, context=None, **kwargs): # TODO: this is a hack until we can get int envs seperated from the vis reg and into their own framework - context[ 'base_url' ] = 'interactive_environments' - super( InteractiveEnvironmentPlugin, self ).__init__( app, path, name, config, context=context, **kwargs ) + context['base_url'] = 'interactive_environments' + super(InteractiveEnvironmentPlugin, self).__init__(app, path, name, config, context=context, **kwargs) - def _render( self, render_vars, trans=None, embedded=None, **kwargs ): + def _render(self, render_vars, trans=None, embedded=None, **kwargs): """ Override to add interactive environment specific template vars. """ - render_vars[ 'embedded' ] = self._parse_embedded( embedded ) + render_vars['embedded'] = self._parse_embedded(embedded) # NOTE: (mako specific) vars is a dictionary for shared data in the template # this feels hacky to me but it's what mako recommends: # http://docs.makotemplates.org/en/latest/runtime.html - render_vars.update( vars={} ) + render_vars.update(vars={}) # No longer needed but being left around for a few releases as jupyter-galaxy # as an external visualization plugin is deprecated in favor of core interactive # environment plugin. if 'get_api_key' not in render_vars: def get_api_key(): - return api_keys.ApiKeyManager( trans.app ).get_or_create_api_key( trans.user ) - render_vars[ 'get_api_key' ] = get_api_key + return api_keys.ApiKeyManager(trans.app).get_or_create_api_key(trans.user) + render_vars['get_api_key'] = get_api_key if 'plugin_path' not in render_vars: - render_vars[ 'plugin_path' ] = os.path.abspath( self.path ) + render_vars['plugin_path'] = os.path.abspath(self.path) - if self.config.get( 'plugin_type', 'visualization' ) == "interactive_environment": + if self.config.get('plugin_type', 'visualization') == "interactive_environment": try: - request = self.INTENV_REQUEST_FACTORY( trans, self ) + request = self.INTENV_REQUEST_FACTORY(trans, self) except: log.exception("IE plugin request handling failed") - return trans.fill_template( 'message.mako', + return trans.fill_template('message.mako', message='Loading the interactive environment failed, please contact the {admin_tag} for assistance'.format( admin_tag='Galaxy administrator'.format( admin_mail=trans.app.config.error_email_to) if trans.app.config.error_email_to else 'Galaxy administrator'), status='error') - render_vars[ "ie_request" ] = request + render_vars["ie_request"] = request - template_filename = self.config[ 'entry_point' ][ 'file' ] - return trans.fill_template( template_filename, template_lookup=self.template_lookup, **render_vars ) + template_filename = self.config['entry_point']['file'] + return trans.fill_template(template_filename, template_lookup=self.template_lookup, **render_vars) # ============================================================================= -class ScriptVisualizationPlugin( VisualizationPlugin ): +class ScriptVisualizationPlugin(VisualizationPlugin): """ A visualization plugin that starts by loading a single (js) script. @@ -300,45 +300,46 @@ class ScriptVisualizationPlugin( VisualizationPlugin ): """ MAKO_TEMPLATE = 'script_entry_point.mako' - def _is_template_plugin( self ): + def _is_template_plugin(self): """ Override to always yield true since this plugin type always uses the pre-determined mako template. """ return True - def _render( self, render_vars, trans=None, embedded=None, **kwargs ): + def _render(self, render_vars, trans=None, embedded=None, **kwargs): """ Override to add script attributes and point mako at the script entry point template. """ - render_vars[ 'embedded' ] = self._parse_embedded( embedded ) - render_vars.update( vars={} ) + render_vars['embedded'] = self._parse_embedded(embedded) + render_vars.update(vars={}) render_vars.update({ - "script_tag_attributes" : self.config[ 'entry_point' ][ 'attr' ] + "script_tag_attributes" : self.config['entry_point']['attr'] }) - template_filename = os.path.join( self.MAKO_TEMPLATE ) - return trans.fill_template( template_filename, template_lookup=self.template_lookup, **render_vars ) + template_filename = os.path.join(self.MAKO_TEMPLATE) + return trans.fill_template(template_filename, template_lookup=self.template_lookup, **render_vars) # ============================================================================= -class StaticFileVisualizationPlugin( VisualizationPlugin ): +class StaticFileVisualizationPlugin(VisualizationPlugin): """ A visualiztion plugin that starts by loading a static html file defined in the visualization's config file. """ # TODO: these are not embeddable by their nature - update config # TODO: should do render/render_saved here since most of the calc done there is unneeded in this case - def _render( self, render_vars, trans=None, embedded=None, **kwargs ): + + def _render(self, render_vars, trans=None, embedded=None, **kwargs): """ Render the static file simply by reading and returning it. """ - render_vars[ 'embedded' ] = self._parse_embedded( embedded ) - render_vars.update( vars={} ) + render_vars['embedded'] = self._parse_embedded(embedded) + render_vars.update(vars={}) - static_file_path = self.config[ 'entry_point' ][ 'file' ] - static_file_path = os.path.join( self.path, static_file_path ) - with open( static_file_path, 'r' ) as outfile: + static_file_path = self.config['entry_point']['file'] + static_file_path = os.path.join(self.path, static_file_path) + with open(static_file_path, 'r') as outfile: return outfile.read() diff --git a/lib/galaxy/visualization/plugins/registry.py b/lib/galaxy/visualization/plugins/registry.py index 7fc78326e3cf..1a562265382b 100644 --- a/lib/galaxy/visualization/plugins/registry.py +++ b/lib/galaxy/visualization/plugins/registry.py @@ -17,11 +17,11 @@ import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) # ------------------------------------------------------------------- -class VisualizationsRegistry( pluginframework.PageServingPluginManager ): +class VisualizationsRegistry(pluginframework.PageServingPluginManager): """ Main responsibilities are: - discovering visualization plugins in the filesystem @@ -43,15 +43,15 @@ class VisualizationsRegistry( pluginframework.PageServingPluginManager ): 'phyloviz' ] - def __str__( self ): + def __str__(self): return self.__class__.__name__ - def __init__( self, app, skip_bad_plugins=True, **kwargs ): - self.app = weakref.ref( app ) + def __init__(self, app, skip_bad_plugins=True, **kwargs): + self.app = weakref.ref(app) self.config_parser = config_parser.VisualizationsConfigParser() - super( VisualizationsRegistry, self ).__init__( app, skip_bad_plugins=skip_bad_plugins, **kwargs ) + super(VisualizationsRegistry, self).__init__(app, skip_bad_plugins=skip_bad_plugins, **kwargs) - def is_plugin( self, plugin_path ): + def is_plugin(self, plugin_path): """ Determines whether the given filesystem path contains a plugin. @@ -64,17 +64,17 @@ def is_plugin( self, plugin_path ): :returns: True if the path contains a plugin """ # plugin_path must be a directory, have a config dir, and a config file matching the plugin dir name - if not os.path.isdir( plugin_path ): + if not os.path.isdir(plugin_path): # super won't work here - different criteria return False - if 'config' not in os.listdir( plugin_path ): + if 'config' not in os.listdir(plugin_path): return False - expected_config_filename = '%s.xml' % ( os.path.split( plugin_path )[1] ) - if not os.path.isfile( os.path.join( plugin_path, 'config', expected_config_filename ) ): + expected_config_filename = '%s.xml' % (os.path.split(plugin_path)[1]) + if not os.path.isfile(os.path.join(plugin_path, 'config', expected_config_filename)): return False return True - def load_plugin( self, plugin_path ): + def load_plugin(self, plugin_path): """ Create the visualization plugin object, parse its configuration file, and return it. @@ -84,48 +84,48 @@ def load_plugin( self, plugin_path ): :rtype: ``VisualizationPlugin`` :returns: the loaded plugin """ - plugin_name = os.path.split( plugin_path )[1] + plugin_name = os.path.split(plugin_path)[1] # TODO: this is the standard/older way to config - config_file = os.path.join( plugin_path, 'config', ( plugin_name + '.xml' ) ) - config = self.config_parser.parse_file( config_file ) + config_file = os.path.join(plugin_path, 'config', (plugin_name + '.xml')) + config = self.config_parser.parse_file(config_file) # config file is required, otherwise skip this visualization if not config: return None - plugin = self._build_plugin( plugin_name, plugin_path, config ) + plugin = self._build_plugin(plugin_name, plugin_path, config) return plugin - def _build_plugin( self, plugin_name, plugin_path, config ): + def _build_plugin(self, plugin_name, plugin_path, config): # TODO: as builder not factory # default class plugin_class = vis_plugins.VisualizationPlugin # jupyter, etc - if config[ 'plugin_type' ] == 'interactive_environment': + if config['plugin_type'] == 'interactive_environment': plugin_class = vis_plugins.InteractiveEnvironmentPlugin # js only - elif config[ 'entry_point' ][ 'type' ] == 'script': + elif config['entry_point']['type'] == 'script': plugin_class = vis_plugins.ScriptVisualizationPlugin # from a static file (html, etc) - elif config[ 'entry_point' ][ 'type' ] == 'html': + elif config['entry_point']['type'] == 'html': plugin_class = vis_plugins.StaticFileVisualizationPlugin - plugin = plugin_class( self.app(), plugin_path, plugin_name, config, context=dict( + plugin = plugin_class(self.app(), plugin_path, plugin_name, config, context=dict( base_url=self.base_url, template_cache_dir=self.template_cache_dir, additional_template_paths=self.additional_template_paths )) return plugin - def get_plugin( self, key ): + def get_plugin(self, key): """ Wrap to throw error if plugin not in registry. """ if key not in self.plugins: - raise galaxy.exceptions.ObjectNotFound( 'Unknown or invalid visualization: ' + key ) - return self.plugins[ key ] + raise galaxy.exceptions.ObjectNotFound('Unknown or invalid visualization: ' + key) + return self.plugins[key] # -- building links to visualizations from objects -- - def get_visualizations( self, trans, target_object ): + def get_visualizations(self, trans, target_object): """ Get the names of visualizations usable on the `target_object` and the urls to call in order to render the visualizations. @@ -133,43 +133,43 @@ def get_visualizations( self, trans, target_object ): # TODO:?? a list of objects? YAGNI? applicable_visualizations = [] for vis_name in self.plugins: - url_data = self.get_visualization( trans, vis_name, target_object ) + url_data = self.get_visualization(trans, vis_name, target_object) if url_data: - applicable_visualizations.append( url_data ) + applicable_visualizations.append(url_data) return applicable_visualizations - def get_visualization( self, trans, visualization_name, target_object ): + def get_visualization(self, trans, visualization_name, target_object): """ Return data to build a url to the visualization with the given `visualization_name` if it's applicable to `target_object` or `None` if it's not. """ # log.debug( 'VisReg.get_visualization: %s, %s', visualization_name, target_object ) - visualization = self.plugins.get( visualization_name, None ) + visualization = self.plugins.get(visualization_name, None) if not visualization: return None - data_sources = visualization.config[ 'data_sources' ] + data_sources = visualization.config['data_sources'] for data_source in data_sources: # log.debug( 'data_source: %s', data_source ) # currently a model class is required - model_class = data_source[ 'model_class' ] + model_class = data_source['model_class'] # log.debug( '\t model_class: %s', model_class ) - if not isinstance( target_object, model_class ): + if not isinstance(target_object, model_class): continue # log.debug( '\t passed model_class' ) # TODO: not true: must have test currently - tests = data_source[ 'tests' ] - if tests and not self.is_object_applicable( trans, target_object, tests ): + tests = data_source['tests'] + if tests and not self.is_object_applicable(trans, target_object, tests): continue # log.debug( '\t passed tests' ) - param_data = data_source[ 'to_params' ] - url = self.get_visualization_url( trans, target_object, visualization, param_data ) - display_name = visualization.config.get( 'name', None ) - render_target = visualization.config.get( 'render_target', 'galaxy_main' ) - embeddable = visualization.config.get( 'embeddable', False ) + param_data = data_source['to_params'] + url = self.get_visualization_url(trans, target_object, visualization, param_data) + display_name = visualization.config.get('name', None) + render_target = visualization.config.get('render_target', 'galaxy_main') + embeddable = visualization.config.get('embeddable', False) # remap some of these vars for direct use in ui.js, PopupMenu (e.g. text->html) return { 'href' : url, @@ -180,17 +180,17 @@ def get_visualization( self, trans, visualization_name, target_object ): return None - def is_object_applicable( self, trans, target_object, data_source_tests ): + def is_object_applicable(self, trans, target_object, data_source_tests): """ Run a visualization's data_source tests to find out if it can be applied to the target_object. """ # log.debug( 'is_object_applicable( self, trans, %s, %s )', target_object, data_source_tests ) for test in data_source_tests: - test_type = test[ 'type' ] - result_type = test[ 'result_type' ] - test_result = test[ 'result' ] - test_fn = test[ 'fn' ] + test_type = test['type'] + result_type = test['result_type'] + test_result = test['result'] + test_fn = test['fn'] # log.debug( '%s %s: %s, %s, %s, %s', str( target_object ), 'is_object_applicable', # test_type, result_type, test_result, test_fn ) @@ -199,7 +199,7 @@ def is_object_applicable( self, trans, target_object, data_source_tests ): if result_type == 'datatype': # convert datatypes to their actual classes (for use with isinstance) datatype_class_name = test_result - test_result = trans.app.datatypes_registry.get_datatype_class_by_name( datatype_class_name ) + test_result = trans.app.datatypes_registry.get_datatype_class_by_name(datatype_class_name) if not test_result: # but continue (with other tests) if can't find class by that name # if self.debug: @@ -209,13 +209,13 @@ def is_object_applicable( self, trans, target_object, data_source_tests ): continue # NOTE: tests are OR'd, if any test passes - the visualization can be applied - if test_fn( target_object, test_result ): + if test_fn(target_object, test_result): # log.debug( '\t test passed' ) return True return False - def get_visualization_url( self, trans, target_object, visualization, param_data ): + def get_visualization_url(self, trans, target_object, visualization, param_data): """ Generates a url for the visualization with `visualization` for use with the given `target_object` with a query string built @@ -223,23 +223,23 @@ def get_visualization_url( self, trans, target_object, visualization, param_data """ # precondition: the target_object should be usable by the visualization (accrd. to data_sources) # convert params using vis.data_source.to_params - params = self.get_url_params( trans, target_object, param_data ) + params = self.get_url_params(trans, target_object, param_data) # we want existing visualizations to work as normal but still be part of the registry (without mod'ing) # so generate their urls differently url = None if visualization.name in self.BUILT_IN_VISUALIZATIONS: - url = url_for( controller='visualization', action=visualization.name, **params ) + url = url_for(controller='visualization', action=visualization.name, **params) # TODO: needs to be split off as it's own registry - elif isinstance( visualization, vis_plugins.InteractiveEnvironmentPlugin ): - url = url_for( 'interactive_environment_plugin', visualization_name=visualization.name, **params ) + elif isinstance(visualization, vis_plugins.InteractiveEnvironmentPlugin): + url = url_for('interactive_environment_plugin', visualization_name=visualization.name, **params) else: - url = url_for( self.NAMED_ROUTE, visualization_name=visualization.name, **params ) + url = url_for(self.NAMED_ROUTE, visualization_name=visualization.name, **params) # TODO:?? not sure if embedded would fit/used here? or added in client... return url - def get_url_params( self, trans, target_object, param_data ): + def get_url_params(self, trans, target_object, param_data): """ Convert the applicable objects and assoc. data into a param dict for a url query string to add to the url that loads the visualization. @@ -247,20 +247,20 @@ def get_url_params( self, trans, target_object, param_data ): params = {} for to_param_name, to_param_data in param_data.items(): # TODO??: look into params as well? what is required, etc. - target_attr = to_param_data.get( 'param_attr', None ) - assign = to_param_data.get( 'assign', None ) + target_attr = to_param_data.get('param_attr', None) + assign = to_param_data.get('assign', None) # one or the other is needed # assign takes precedence (goes last, overwrites)? # NOTE this is only one level - if target_attr and vis_utils.hasattr_recursive( target_object, target_attr ): - params[ to_param_name ] = vis_utils.getattr_recursive( target_object, target_attr ) + if target_attr and vis_utils.hasattr_recursive(target_object, target_attr): + params[to_param_name] = vis_utils.getattr_recursive(target_object, target_attr) if assign: - params[ to_param_name ] = assign + params[to_param_name] = assign # NOTE!: don't expose raw ids: encode id, _id # TODO: double encodes if from config if params: - params = trans.security.encode_dict_ids( params ) + params = trans.security.encode_dict_ids(params) return params diff --git a/lib/galaxy/visualization/plugins/resource_parser.py b/lib/galaxy/visualization/plugins/resource_parser.py index c808ddf7d9b5..1bb0743ad585 100644 --- a/lib/galaxy/visualization/plugins/resource_parser.py +++ b/lib/galaxy/visualization/plugins/resource_parser.py @@ -12,10 +12,10 @@ from galaxy.managers import visualizations as visualization_manager from galaxy.managers import hdas as hda_manager -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class ResourceParser( object ): +class ResourceParser(object): """ Given a parameter dictionary (often a converted query string) and a configuration dictionary (curr. only VisualizationsRegistry uses this), @@ -27,26 +27,26 @@ class ResourceParser( object ): new keys (e.g. dataset_id="NNN" -> hda=). """ primitive_parsers = { - 'str' : lambda param: galaxy.util.sanitize_html.sanitize_html( param, 'utf-8' ), - 'bool' : lambda param: galaxy.util.string_as_bool( param ), + 'str' : lambda param: galaxy.util.sanitize_html.sanitize_html(param, 'utf-8'), + 'bool' : lambda param: galaxy.util.string_as_bool(param), 'int' : int, 'float' : float, # 'date' : lambda param: , - 'json' : ( lambda param: json.loads( - galaxy.util.sanitize_html.sanitize_html( param ) ) ), + 'json' : (lambda param: json.loads( + galaxy.util.sanitize_html.sanitize_html(param))), } - def __init__( self, app, *args, **kwargs ): - self.app = weakref.ref( app ) - self.managers = self._init_managers( app ) + def __init__(self, app, *args, **kwargs): + self.app = weakref.ref(app) + self.managers = self._init_managers(app) - def _init_managers( self, app ): + def _init_managers(self, app): return bunch.Bunch( - visualization=visualization_manager.VisualizationManager( app ), - hda=hda_manager.HDAManager( app ) + visualization=visualization_manager.VisualizationManager(app), + hda=hda_manager.HDAManager(app) ) - def parse_parameter_dictionary( self, trans, param_config_dict, query_params, param_modifiers=None ): + def parse_parameter_dictionary(self, trans, param_config_dict, query_params, param_modifiers=None): """ Parse all expected params from the query dictionary `query_params`. @@ -57,43 +57,43 @@ def parse_parameter_dictionary( self, trans, param_config_dict, query_params, pa # parse the modifiers first since they modify the params coming next # TODO: this is all really for hda_ldda - which we could replace with model polymorphism params_that_modify_other_params = self.parse_parameter_modifiers( - trans, param_modifiers, query_params ) + trans, param_modifiers, query_params) resources = {} for param_name, param_config in param_config_dict.items(): # optionally rename the variable returned, defaulting to the original name - var_name_in_template = param_config.get( 'var_name_in_template', param_name ) + var_name_in_template = param_config.get('var_name_in_template', param_name) # if the param is present, get its value, any param modifiers for that param, and parse it into a resource # use try catch here and not caller to fall back on the default value or re-raise if required resource = None - query_val = query_params.get( param_name, None ) + query_val = query_params.get(param_name, None) if query_val is not None: try: - target_param_modifiers = params_that_modify_other_params.get( param_name, None ) - resource = self.parse_parameter( trans, param_config, - query_val, param_modifiers=target_param_modifiers ) + target_param_modifiers = params_that_modify_other_params.get(param_name, None) + resource = self.parse_parameter(trans, param_config, + query_val, param_modifiers=target_param_modifiers) except Exception as exception: if trans.debug: raise else: - log.warning( 'Exception parsing visualization param from query: %s, %s, (%s) %s', - param_name, query_val, str( type( exception ) ), str( exception ) ) + log.warning('Exception parsing visualization param from query: %s, %s, (%s) %s', + param_name, query_val, str(type(exception)), str(exception)) resource = None # here - we've either had no value in the query_params or there was a failure to parse # so: error if required, otherwise get a default (which itself defaults to None) if resource is None: - if param_config[ 'required' ]: - raise KeyError( 'required param %s not found in URL' % ( param_name ) ) - resource = self.parse_parameter_default( trans, param_config ) + if param_config['required']: + raise KeyError('required param %s not found in URL' % (param_name)) + resource = self.parse_parameter_default(trans, param_config) - resources[ var_name_in_template ] = resource + resources[var_name_in_template] = resource return resources - def parse_config( self, trans, param_config_dict, query_params ): + def parse_config(self, trans, param_config_dict, query_params): """ Return `query_params` dict parsing only JSON serializable params. Complex params such as models, etc. are left as the original query value. @@ -103,29 +103,29 @@ def parse_config( self, trans, param_config_dict, query_params ): # log.debug( 'parse_config, query_params:\n%s', query_params ) config = {} for param_name, param_config in param_config_dict.items(): - config_val = query_params.get( param_name, None ) - if config_val is not None and param_config[ 'type' ] in self.primitive_parsers: + config_val = query_params.get(param_name, None) + if config_val is not None and param_config['type'] in self.primitive_parsers: try: - config_val = self.parse_parameter( trans, param_config, config_val ) + config_val = self.parse_parameter(trans, param_config, config_val) except Exception as exception: - log.warning( 'Exception parsing visualization param from query: ' + - '%s, %s, (%s) %s' % ( param_name, config_val, str( type( exception ) ), str( exception ) )) + log.warning('Exception parsing visualization param from query: ' + + '%s, %s, (%s) %s' % (param_name, config_val, str(type(exception)), str(exception))) config_val = None # here - we've either had no value in the query_params or there was a failure to parse # so: if there's a default and it's not None, add it to the config if config_val is None: - if param_config.get( 'default', None ) is None: + if param_config.get('default', None) is None: continue - config_val = self.parse_parameter_default( trans, param_config ) + config_val = self.parse_parameter_default(trans, param_config) - config[ param_name ] = config_val + config[param_name] = config_val return config # TODO: I would LOVE to rip modifiers out completely - def parse_parameter_modifiers( self, trans, param_modifiers, query_params ): + def parse_parameter_modifiers(self, trans, param_modifiers, query_params): """ Parse and return parameters that are meant to modify other parameters, be grouped with them, or are needed to successfully parse other parameters. @@ -138,92 +138,92 @@ def parse_parameter_modifiers( self, trans, param_modifiers, query_params ): # precondition: expects a two level dictionary # { target_param_name -> { param_modifier_name -> { param_modifier_data }}} for target_param_name, modifier_dict in param_modifiers.items(): - parsed_modifiers[ target_param_name ] = target_modifiers = {} + parsed_modifiers[target_param_name] = target_modifiers = {} for modifier_name, modifier_config in modifier_dict.items(): - query_val = query_params.get( modifier_name, None ) + query_val = query_params.get(modifier_name, None) if query_val is not None: - modifier = self.parse_parameter( trans, modifier_config, query_val ) - target_modifiers[ modifier_name ] = modifier + modifier = self.parse_parameter(trans, modifier_config, query_val) + target_modifiers[modifier_name] = modifier else: # TODO: required attr? - target_modifiers[ modifier_name ] = self.parse_parameter_default( trans, modifier_config ) + target_modifiers[modifier_name] = self.parse_parameter_default(trans, modifier_config) return parsed_modifiers - def parse_parameter_default( self, trans, param_config ): + def parse_parameter_default(self, trans, param_config): """ Parse any default values for the given param, defaulting the default to `None`. """ # currently, *default* default is None, so this is quaranteed to be part of the dictionary - default = param_config[ 'default' ] + default = param_config['default'] # if default is None, do not attempt to parse it if default is None: return default # otherwise, parse (currently param_config['default'] is a string just like query param and needs to be parsed) # this saves us the trouble of parsing the default when the config file is read # (and adding this code to the xml parser) - return self.parse_parameter( trans, param_config, default ) + return self.parse_parameter(trans, param_config, default) - def parse_parameter( self, trans, expected_param_data, query_param, - recurse=True, param_modifiers=None ): + def parse_parameter(self, trans, expected_param_data, query_param, + recurse=True, param_modifiers=None): """ Use data in `expected_param_data` to parse `query_param` from a string into a resource usable directly by a template. """ - param_type = expected_param_data.get( 'type' ) + param_type = expected_param_data.get('type') # constrain_to = expected_param_data.get( 'constrain_to' ) - csv = expected_param_data.get( 'csv' ) + csv = expected_param_data.get('csv') parsed_param = None # handle recursion for csv values if csv and recurse: parsed_param = [] - query_param_list = galaxy.util.listify( query_param ) + query_param_list = galaxy.util.listify(query_param) for query_param in query_param_list: - parsed_param.append( self._parse_param( trans, expected_param_data, query_param, recurse=False ) ) + parsed_param.append(self._parse_param(trans, expected_param_data, query_param, recurse=False)) return parsed_param if param_type in self.primitive_parsers: # TODO: what about param modifiers on primitives? - parsed_param = self.primitive_parsers[ param_type ]( query_param ) + parsed_param = self.primitive_parsers[param_type](query_param) # TODO: constrain_to: this gets complicated - remove? # db models elif param_type == 'visualization': # ?: is this even used anymore/anywhere? - decoded_visualization_id = self._decode_id( query_param ) - parsed_param = self.managers.visualization.get_accessible( decoded_visualization_id, trans.user ) + decoded_visualization_id = self._decode_id(query_param) + parsed_param = self.managers.visualization.get_accessible(decoded_visualization_id, trans.user) elif param_type == 'dataset': - decoded_dataset_id = self._decode_id( query_param ) - parsed_param = self.managers.hda.get_accessible( decoded_dataset_id, trans.user ) + decoded_dataset_id = self._decode_id(query_param) + parsed_param = self.managers.hda.get_accessible(decoded_dataset_id, trans.user) elif param_type == 'hda_or_ldda': encoded_dataset_id = query_param # needs info from another param... - hda_ldda = param_modifiers.get( 'hda_ldda' ) + hda_ldda = param_modifiers.get('hda_ldda') if hda_ldda == 'hda': - decoded_dataset_id = self._decode_id( encoded_dataset_id ) - parsed_param = self.managers.hda.get_accessible( decoded_dataset_id, trans.user ) + decoded_dataset_id = self._decode_id(encoded_dataset_id) + parsed_param = self.managers.hda.get_accessible(decoded_dataset_id, trans.user) else: - parsed_param = self.managers.ldda.get( trans, encoded_dataset_id ) + parsed_param = self.managers.ldda.get(trans, encoded_dataset_id) # TODO: ideally this would check v. a list of valid dbkeys elif param_type == 'dbkey': dbkey = query_param - parsed_param = galaxy.util.sanitize_html.sanitize_html( dbkey, 'utf-8' ) + parsed_param = galaxy.util.sanitize_html.sanitize_html(dbkey, 'utf-8') return parsed_param - def _decode_id( self, id ): + def _decode_id(self, id): try: - return self.app().security.decode_id( str( id ) ) - except ( ValueError, TypeError ): + return self.app().security.decode_id(str(id)) + except (ValueError, TypeError): raise galaxy.exceptions.MalformedId( - "Malformed id ( %s ) specified, unable to decode" % ( str( id ) ), - id=str( id ) + "Malformed id ( %s ) specified, unable to decode" % (str(id)), + id=str(id) ) diff --git a/lib/galaxy/visualization/plugins/utils.py b/lib/galaxy/visualization/plugins/utils.py index a6db021d31af..5fb48abd793a 100644 --- a/lib/galaxy/visualization/plugins/utils.py +++ b/lib/galaxy/visualization/plugins/utils.py @@ -4,7 +4,7 @@ # ============================================================================= -class OpenObject( dict ): +class OpenObject(dict): # note: not a Bunch # TODO: move to util.data_structures """ @@ -15,34 +15,35 @@ class OpenObject( dict ): KeyError). JSON-serializable. """ - def __getitem__( self, key ): + + def __getitem__(self, key): if key not in self: return None - return super( OpenObject, self ).__getitem__( key ) + return super(OpenObject, self).__getitem__(key) - def __getattr__( self, key ): - return self.__getitem__( key ) + def __getattr__(self, key): + return self.__getitem__(key) # ------------------------------------------------------------------- misc # TODO: move to utils? -def getattr_recursive( item, attr_key, *args ): +def getattr_recursive(item, attr_key, *args): """ Allows dot member notation in attribute name when getting an item's attribute. NOTE: also searches dictionaries """ - using_default = len( args ) >= 1 + using_default = len(args) >= 1 default = args[0] if using_default else None - for attr_key in attr_key.split( '.' ): + for attr_key in attr_key.split('.'): try: - if isinstance( item, dict ): - item = item.__getitem__( attr_key ) + if isinstance(item, dict): + item = item.__getitem__(attr_key) else: - item = getattr( item, attr_key ) + item = getattr(item, attr_key) - except ( KeyError, AttributeError ): + except (KeyError, AttributeError): if using_default: return default raise @@ -50,26 +51,26 @@ def getattr_recursive( item, attr_key, *args ): return item -def hasattr_recursive( item, attr_key ): +def hasattr_recursive(item, attr_key): """ Allows dot member notation in attribute name when getting an item's attribute. NOTE: also searches dictionaries """ if '.' in attr_key: - attr_key, last_key = attr_key.rsplit( '.', 1 ) - item = getattr_recursive( item, attr_key, None ) + attr_key, last_key = attr_key.rsplit('.', 1) + item = getattr_recursive(item, attr_key, None) if item is None: return False attr_key = last_key try: - if isinstance( item, dict ): - return item.__contains__( attr_key ) + if isinstance(item, dict): + return item.__contains__(attr_key) else: - return hasattr( item, attr_key ) + return hasattr(item, attr_key) - except ( KeyError, AttributeError ): + except (KeyError, AttributeError): return False return True diff --git a/lib/galaxy/web/base/controller.py b/lib/galaxy/web/base/controller.py index e28a70e1fa72..07b3b13bd857 100644 --- a/lib/galaxy/web/base/controller.py +++ b/lib/galaxy/web/base/controller.py @@ -2,78 +2,101 @@ Contains functionality needed in every web interface """ import logging -import operator import re -from six import string_types, text_type +from paste.httpexceptions import ( + HTTPBadRequest, + HTTPInternalServerError, + HTTPNotImplemented, + HTTPRequestRangeNotSatisfiable +) +from six import ( + string_types, + text_type +) from sqlalchemy import true -from paste.httpexceptions import HTTPBadRequest, HTTPInternalServerError -from paste.httpexceptions import HTTPNotImplemented, HTTPRequestRangeNotSatisfiable -from galaxy import exceptions -from galaxy import web -from galaxy import model -from galaxy import security -from galaxy import util - -from galaxy.web import error, url_for -from galaxy.web.form_builder import AddressField, CheckboxField, SelectField, TextArea, TextField -from galaxy.web.form_builder import build_select_field, HistoryField, PasswordField, WorkflowField, WorkflowMappingField -from galaxy.workflow.modules import WorkflowModuleInjector -from galaxy.security.validate_user_input import validate_publicname -from galaxy.util.sanitize_html import sanitize_html +from galaxy import ( + exceptions, + model, + security, + util, + web +) +from galaxy.datatypes.interval import ChromatinInteractions +from galaxy.managers import ( + api_keys, + base as managers_base, + configuration, + tags, + users, + workflows +) +from galaxy.model import ( + ExtendedMetadata, + ExtendedMetadataIndex, + HistoryDatasetAssociation, + LibraryDatasetDatasetAssociation +) from galaxy.model.item_attrs import UsesAnnotations +from galaxy.security.validate_user_input import validate_publicname from galaxy.util.dictifiable import Dictifiable +from galaxy.util.sanitize_html import sanitize_html +from galaxy.web import ( + error, + url_for +) +from galaxy.web.form_builder import ( + AddressField, + build_select_field, + CheckboxField, + HistoryField, + PasswordField, + SelectField, + TextArea, + TextField, + WorkflowField, + WorkflowMappingField +) +from galaxy.workflow.modules import WorkflowModuleInjector -from galaxy.datatypes.interval import ChromatinInteractions - -from galaxy.model import ExtendedMetadata, ExtendedMetadataIndex, LibraryDatasetDatasetAssociation, HistoryDatasetAssociation - -from galaxy.managers import api_keys -from galaxy.managers import tags -from galaxy.managers import workflows -from galaxy.managers import base as managers_base -from galaxy.managers import users -from galaxy.managers import configuration - - -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) # States for passing messages SUCCESS, INFO, WARNING, ERROR = "done", "info", "warning", "error" -def _is_valid_slug( slug ): +def _is_valid_slug(slug): """ Returns true if slug is valid. """ - VALID_SLUG_RE = re.compile( "^[a-z0-9\-]+$" ) - return VALID_SLUG_RE.match( slug ) + VALID_SLUG_RE = re.compile("^[a-z0-9\-]+$") + return VALID_SLUG_RE.match(slug) -class BaseController( object ): +class BaseController(object): """ Base class for Galaxy web application controllers. """ - def __init__( self, app ): + def __init__(self, app): """Initialize an interface for application 'app'""" self.app = app self.sa_session = app.model.context - self.user_manager = users.UserManager( app ) + self.user_manager = users.UserManager(app) def get_toolbox(self): """Returns the application toolbox""" return self.app.toolbox - def get_class( self, class_name ): + def get_class(self, class_name): """ Returns the class object that a string denotes. Without this method, we'd have to do eval(). """ - return managers_base.get_class( class_name ) + return managers_base.get_class(class_name) - def get_object( self, trans, id, class_name, check_ownership=False, check_accessible=False, deleted=None ): + def get_object(self, trans, id, class_name, check_ownership=False, check_accessible=False, deleted=None): """ Convenience method to get a model object with the specified checks. """ - return managers_base.get_object( trans, id, class_name, check_ownership=check_ownership, check_accessible=check_accessible, deleted=deleted ) + return managers_base.get_object(trans, id, class_name, check_ownership=check_ownership, check_accessible=check_accessible, deleted=deleted) # this should be here - but catching errors from sharable item controllers that *should* have SharableItemMixin # but *don't* then becomes difficult @@ -82,28 +105,28 @@ def get_object( self, trans, id, class_name, check_ownership=False, check_access # # meant to be overridden in SharableSecurityMixin # return item - def get_user( self, trans, id, check_ownership=False, check_accessible=False, deleted=None ): - return self.get_object( trans, id, 'User', check_ownership=False, check_accessible=False, deleted=deleted ) + def get_user(self, trans, id, check_ownership=False, check_accessible=False, deleted=None): + return self.get_object(trans, id, 'User', check_ownership=False, check_accessible=False, deleted=deleted) - def get_group( self, trans, id, check_ownership=False, check_accessible=False, deleted=None ): - return self.get_object( trans, id, 'Group', check_ownership=False, check_accessible=False, deleted=deleted ) + def get_group(self, trans, id, check_ownership=False, check_accessible=False, deleted=None): + return self.get_object(trans, id, 'Group', check_ownership=False, check_accessible=False, deleted=deleted) - def get_role( self, trans, id, check_ownership=False, check_accessible=False, deleted=None ): - return self.get_object( trans, id, 'Role', check_ownership=False, check_accessible=False, deleted=deleted ) + def get_role(self, trans, id, check_ownership=False, check_accessible=False, deleted=None): + return self.get_object(trans, id, 'Role', check_ownership=False, check_accessible=False, deleted=deleted) # ---- parsing query params - def decode_id( self, id ): - return managers_base.decode_id( self.app, id ) + def decode_id(self, id): + return managers_base.decode_id(self.app, id) - def encode_all_ids( self, trans, rval, recursive=False ): + def encode_all_ids(self, trans, rval, recursive=False): """ Encodes all integer values in the dict rval whose keys are 'id' or end with '_id' It might be useful to turn this in to a decorator """ - return trans.security.encode_all_ids( rval, recursive=recursive ) + return trans.security.encode_all_ids(rval, recursive=recursive) - def parse_filter_params( self, qdict, filter_attr_key='q', filter_value_key='qv', attr_op_split_char='-' ): + def parse_filter_params(self, qdict, filter_attr_key='q', filter_value_key='qv', attr_op_split_char='-'): """ """ # TODO: import DEFAULT_OP from FilterParser @@ -111,9 +134,9 @@ def parse_filter_params( self, qdict, filter_attr_key='q', filter_value_key='qv' if filter_attr_key not in qdict: return [] # precondition: attrs/value pairs are in-order in the qstring - attrs = qdict.get( filter_attr_key ) - if not isinstance( attrs, list ): - attrs = [ attrs ] + attrs = qdict.get(filter_attr_key) + if not isinstance(attrs, list): + attrs = [attrs] # ops are strings placed after the attr strings and separated by a split char (e.g. 'create_time-lt') # ops are optional and default to 'eq' reparsed_attrs = [] @@ -122,110 +145,110 @@ def parse_filter_params( self, qdict, filter_attr_key='q', filter_value_key='qv' op = DEFAULT_OP if attr_op_split_char in attr: # note: only split the last (e.g. q=community-tags-in&qv=rna yields ( 'community-tags', 'in', 'rna' ) - attr, op = attr.rsplit( attr_op_split_char, 1 ) - ops.append( op ) - reparsed_attrs.append( attr ) + attr, op = attr.rsplit(attr_op_split_char, 1) + ops.append(op) + reparsed_attrs.append(attr) attrs = reparsed_attrs - values = qdict.get( filter_value_key, [] ) - if not isinstance( values, list ): - values = [ values ] + values = qdict.get(filter_value_key, []) + if not isinstance(values, list): + values = [values] # TODO: it may be more helpful to the consumer if we error on incomplete 3-tuples # (instead of relying on zip to shorten) - return zip( attrs, ops, values ) + return list(zip(attrs, ops, values)) - def parse_limit_offset( self, qdict ): + def parse_limit_offset(self, qdict): """ """ - def _parse_pos_int( i ): + def _parse_pos_int(i): try: - new_val = int( i ) + new_val = int(i) if new_val >= 0: return new_val - except ( TypeError, ValueError ): + except (TypeError, ValueError): pass return None - limit = _parse_pos_int( qdict.get( 'limit', None ) ) - offset = _parse_pos_int( qdict.get( 'offset', None ) ) - return ( limit, offset ) + limit = _parse_pos_int(qdict.get('limit', None)) + offset = _parse_pos_int(qdict.get('offset', None)) + return (limit, offset) Root = BaseController -class BaseUIController( BaseController ): +class BaseUIController(BaseController): - def get_object( self, trans, id, class_name, check_ownership=False, check_accessible=False, deleted=None ): + def get_object(self, trans, id, class_name, check_ownership=False, check_accessible=False, deleted=None): try: - return BaseController.get_object( self, trans, id, class_name, - check_ownership=check_ownership, check_accessible=check_accessible, deleted=deleted ) + return BaseController.get_object(self, trans, id, class_name, + check_ownership=check_ownership, check_accessible=check_accessible, deleted=deleted) except exceptions.MessageException: raise # handled in the caller except: - log.exception( "Exception in get_object check for %s %s:", class_name, str( id ) ) - raise Exception( 'Server error retrieving %s id ( %s ).' % ( class_name, str( id ) ) ) + log.exception("Exception in get_object check for %s %s:", class_name, str(id)) + raise Exception('Server error retrieving %s id ( %s ).' % (class_name, str(id))) -class BaseAPIController( BaseController ): +class BaseAPIController(BaseController): - def get_object( self, trans, id, class_name, check_ownership=False, check_accessible=False, deleted=None ): + def get_object(self, trans, id, class_name, check_ownership=False, check_accessible=False, deleted=None): try: - return BaseController.get_object( self, trans, id, class_name, - check_ownership=check_ownership, check_accessible=check_accessible, deleted=deleted ) + return BaseController.get_object(self, trans, id, class_name, + check_ownership=check_ownership, check_accessible=check_accessible, deleted=deleted) except exceptions.ItemDeletionException as e: - raise HTTPBadRequest( detail="Invalid %s id ( %s ) specified: %s" % ( class_name, str( id ), str( e ) ) ) + raise HTTPBadRequest(detail="Invalid %s id ( %s ) specified: %s" % (class_name, str(id), str(e))) except exceptions.MessageException as e: - raise HTTPBadRequest( detail=e.err_msg ) + raise HTTPBadRequest(detail=e.err_msg) except Exception as e: - log.exception( "Exception in get_object check for %s %s.", class_name, str( id ) ) - raise HTTPInternalServerError( comment=str( e ) ) + log.exception("Exception in get_object check for %s %s.", class_name, str(id)) + raise HTTPInternalServerError(comment=str(e)) - def validate_in_users_and_groups( self, trans, payload ): + def validate_in_users_and_groups(self, trans, payload): """ For convenience, in_users and in_groups can be encoded IDs or emails/group names in the API. """ - def get_id( item, model_class, column ): + def get_id(item, model_class, column): try: - return trans.security.decode_id( item ) + return trans.security.decode_id(item) except Exception: pass # maybe an email/group name # this will raise if the item is invalid - return trans.sa_session.query( model_class ).filter( column == item ).first().id + return trans.sa_session.query(model_class).filter(column == item).first().id new_in_users = [] new_in_groups = [] invalid = [] - for item in util.listify( payload.get( 'in_users', [] ) ): + for item in util.listify(payload.get('in_users', [])): try: - new_in_users.append( get_id( item, trans.app.model.User, trans.app.model.User.table.c.email ) ) + new_in_users.append(get_id(item, trans.app.model.User, trans.app.model.User.table.c.email)) except Exception: - invalid.append( item ) - for item in util.listify( payload.get( 'in_groups', [] ) ): + invalid.append(item) + for item in util.listify(payload.get('in_groups', [])): try: - new_in_groups.append( get_id( item, trans.app.model.Group, trans.app.model.Group.table.c.name ) ) + new_in_groups.append(get_id(item, trans.app.model.Group, trans.app.model.Group.table.c.name)) except Exception: - invalid.append( item ) + invalid.append(item) if invalid: - msg = "The following value(s) for associated users and/or groups could not be parsed: %s." % ', '.join( invalid ) + msg = "The following value(s) for associated users and/or groups could not be parsed: %s." % ', '.join(invalid) msg += " Valid values are email addresses of users, names of groups, or IDs of both." - raise Exception( msg ) - payload['in_users'] = map( str, new_in_users ) - payload['in_groups'] = map( str, new_in_groups ) + raise Exception(msg) + payload['in_users'] = list(map(str, new_in_users)) + payload['in_groups'] = list(map(str, new_in_groups)) - def not_implemented( self, trans, **kwd ): + def not_implemented(self, trans, **kwd): raise HTTPNotImplemented() - def _parse_serialization_params( self, kwd, default_view ): - view = kwd.get( 'view', None ) - keys = kwd.get( 'keys' ) - if isinstance( keys, string_types ): - keys = keys.split( ',' ) - return dict( view=view, keys=keys, default_view=default_view ) + def _parse_serialization_params(self, kwd, default_view): + view = kwd.get('view', None) + keys = kwd.get('keys') + if isinstance(keys, string_types): + keys = keys.split(',') + return dict(view=view, keys=keys, default_view=default_view) -class JSAppLauncher( BaseUIController ): +class JSAppLauncher(BaseUIController): """ A controller that launches JavaScript web applications. """ @@ -235,45 +258,57 @@ class JSAppLauncher( BaseUIController ): #: window-scoped js function to call to start the app (will be passed options, bootstrapped) DEFAULT_ENTRY_FN = "app" #: keys used when serializing current user for bootstrapped data - USER_BOOTSTRAP_KEYS = ( 'id', 'email', 'username', 'is_admin', 'tags_used', 'requests', - 'total_disk_usage', 'nice_total_disk_usage', 'quota_percent', 'preferences' ) + USER_BOOTSTRAP_KEYS = ('id', 'email', 'username', 'is_admin', 'tags_used', 'requests', + 'total_disk_usage', 'nice_total_disk_usage', 'quota_percent', 'preferences') - def __init__( self, app ): - super( JSAppLauncher, self ).__init__( app ) - self.user_manager = users.UserManager( app ) - self.user_serializer = users.CurrentUserSerializer( app ) - self.config_serializer = configuration.ConfigSerializer( app ) - self.admin_config_serializer = configuration.AdminConfigSerializer( app ) + def __init__(self, app): + super(JSAppLauncher, self).__init__(app) + self.user_manager = users.UserManager(app) + self.user_serializer = users.CurrentUserSerializer(app) + self.config_serializer = configuration.ConfigSerializer(app) + self.admin_config_serializer = configuration.AdminConfigSerializer(app) - def _get_js_options( self, trans, root=None ): + @web.expose + def client(self, trans, **kwd): + """ + Endpoint for clientside routes. Currently a passthrough to index + (minus kwargs) though we can differentiate it more in the future. + Should not be used with url_for -- see + (https://github.com/galaxyproject/galaxy/issues/1878) for why. + """ + return self.index(trans) + + def _get_js_options(self, trans, root=None): """ Return a dictionary of session/site configuration/options to jsonify and pass onto the js app. Defaults to `config`, `user`, and the root url. Pass kwargs to update further. """ - root = root or web.url_for( '/' ) + root = root or web.url_for('/') js_options = { - 'root' : root, - 'user' : self.user_serializer.serialize( trans.user, self.USER_BOOTSTRAP_KEYS, trans=trans ), - 'config' : self._get_site_configuration( trans ) + 'root' : root, + 'user' : self.user_serializer.serialize(trans.user, self.USER_BOOTSTRAP_KEYS, trans=trans), + 'config' : self._get_site_configuration(trans), + 'params' : dict(trans.request.params), + 'session_csrf_token' : trans.session_csrf_token, } return js_options - def _get_site_configuration( self, trans ): + def _get_site_configuration(self, trans): """ Return a dictionary representing Galaxy's current configuration. """ try: serializer = self.config_serializer - if self.user_manager.is_admin( trans.user ): + if self.user_manager.is_admin(trans.user): serializer = self.admin_config_serializer - return serializer.serialize_to_view( self.app.config, view='all' ) + return serializer.serialize_to_view(self.app.config, view='all') except Exception as exc: - log.exception( exc ) + log.exception(exc) return {} - def template( self, trans, app_name, entry_fn='app', options=None, bootstrapped_data=None, masthead=True, **additional_options ): + def template(self, trans, app_name, entry_fn='app', options=None, bootstrapped_data=None, masthead=True, **additional_options): """ Render and return the single page mako template that starts the app. @@ -286,22 +321,22 @@ def template( self, trans, app_name, entry_fn='app', options=None, bootstrapped_ the initial page dom. `additional_options` (kwargs): update to the options sent to the app. """ - options = options or self._get_js_options( trans ) - options.update( additional_options ) + options = options or self._get_js_options(trans) + options.update(additional_options) return trans.fill_template( self.JS_APP_MAKO_FILEPATH, js_app_name=app_name, - js_app_entry_fn=( entry_fn or self.DEFAULT_ENTRY_FN ), + js_app_entry_fn=(entry_fn or self.DEFAULT_ENTRY_FN), options=options, - bootstrapped=( bootstrapped_data or {} ), + bootstrapped=(bootstrapped_data or {}), masthead=masthead ) -class Datatype( object ): +class Datatype(object): """Used for storing in-memory list of datatypes currently in the datatypes registry.""" - def __init__( self, extension, dtype, type_extension, mimetype, display_in_upload ): + def __init__(self, extension, dtype, type_extension, mimetype, display_in_upload): self.extension = extension self.dtype = dtype self.type_extension = type_extension @@ -321,21 +356,21 @@ class CreatesUsersMixin: user forms, etc.... API created users are much more vanilla for the time being. """ - def create_user( self, trans, email, username, password ): - user = trans.app.model.User( email=email ) - user.set_password_cleartext( password ) + def create_user(self, trans, email, username, password): + user = trans.app.model.User(email=email) + user.set_password_cleartext(password) user.username = username if trans.app.config.user_activation_on: user.active = False else: user.active = True # Activation is off, every new user is active by default. - trans.sa_session.add( user ) + trans.sa_session.add(user) trans.sa_session.flush() - trans.app.security_agent.create_private_user_role( user ) + trans.app.security_agent.create_private_user_role(user) if trans.webapp.name == 'galaxy': # We set default user permissions, before we log in and set the default history permissions - trans.app.security_agent.user_set_default_permissions( user, - default_access_private=trans.app.config.new_user_dataset_access_role_default_private ) + trans.app.security_agent.user_set_default_permissions(user, + default_access_private=trans.app.config.new_user_dataset_access_role_default_private) return user @@ -346,41 +381,41 @@ class CreatesApiKeysMixin: Deprecated - please use api_keys.ApiKeyManager for new development. """ - def create_api_key( self, trans, user ): - return api_keys.ApiKeyManager( trans.app ).create_api_key( user ) + def create_api_key(self, trans, user): + return api_keys.ApiKeyManager(trans.app).create_api_key(user) class SharableItemSecurityMixin: """ Mixin for handling security for sharable items. """ - def security_check( self, trans, item, check_ownership=False, check_accessible=False ): + def security_check(self, trans, item, check_ownership=False, check_accessible=False): """ Security checks for an item: checks if (a) user owns item or (b) item is accessible to user. """ - return managers_base.security_check( trans, item, check_ownership=check_ownership, check_accessible=check_accessible ) + return managers_base.security_check(trans, item, check_ownership=check_ownership, check_accessible=check_accessible) class ExportsHistoryMixin: - def serve_ready_history_export( self, trans, jeha ): + def serve_ready_history_export(self, trans, jeha): assert jeha.ready if jeha.compressed: - trans.response.set_content_type( 'application/x-gzip' ) + trans.response.set_content_type('application/x-gzip') else: - trans.response.set_content_type( 'application/x-tar' ) + trans.response.set_content_type('application/x-tar') disposition = 'attachment; filename="%s"' % jeha.export_name trans.response.headers["Content-Disposition"] = disposition - return open( trans.app.object_store.get_filename( jeha.dataset ) ) + return open(trans.app.object_store.get_filename(jeha.dataset)) - def queue_history_export( self, trans, history, gzip=True, include_hidden=False, include_deleted=False ): + def queue_history_export(self, trans, history, gzip=True, include_hidden=False, include_deleted=False): # Convert options to booleans. - if isinstance( gzip, string_types ): - gzip = ( gzip in [ 'True', 'true', 'T', 't' ] ) - if isinstance( include_hidden, string_types ): - include_hidden = ( include_hidden in [ 'True', 'true', 'T', 't' ] ) - if isinstance( include_deleted, string_types ): - include_deleted = ( include_deleted in [ 'True', 'true', 'T', 't' ] ) + if isinstance(gzip, string_types): + gzip = (gzip in ['True', 'true', 'T', 't']) + if isinstance(include_hidden, string_types): + include_hidden = (include_hidden in ['True', 'true', 'T', 't']) + if isinstance(include_deleted, string_types): + include_deleted = (include_deleted in ['True', 'true', 'T', 't']) # Run job to do export. - history_exp_tool = trans.app.toolbox.get_tool( '__EXPORT_HISTORY__' ) + history_exp_tool = trans.app.toolbox.get_tool('__EXPORT_HISTORY__') params = { 'history_to_export': history, 'compress': gzip, @@ -388,43 +423,43 @@ def queue_history_export( self, trans, history, gzip=True, include_hidden=False, 'include_deleted': include_deleted } - history_exp_tool.execute( trans, incoming=params, history=history, set_output_hid=True ) + history_exp_tool.execute(trans, incoming=params, history=history, set_output_hid=True) class ImportsHistoryMixin: - def queue_history_import( self, trans, archive_type, archive_source ): + def queue_history_import(self, trans, archive_type, archive_source): # Run job to do import. - history_imp_tool = trans.app.toolbox.get_tool( '__IMPORT_HISTORY__' ) - incoming = { '__ARCHIVE_SOURCE__' : archive_source, '__ARCHIVE_TYPE__' : archive_type } - history_imp_tool.execute( trans, incoming=incoming ) + history_imp_tool = trans.app.toolbox.get_tool('__IMPORT_HISTORY__') + incoming = {'__ARCHIVE_SOURCE__' : archive_source, '__ARCHIVE_TYPE__' : archive_type} + history_imp_tool.execute(trans, incoming=incoming) class UsesLibraryMixin: - def get_library( self, trans, id, check_ownership=False, check_accessible=True ): - l = self.get_object( trans, id, 'Library' ) - if check_accessible and not ( trans.user_is_admin() or trans.app.security_agent.can_access_library( trans.get_current_user_roles(), l ) ): - error( "LibraryFolder is not accessible to the current user" ) + def get_library(self, trans, id, check_ownership=False, check_accessible=True): + l = self.get_object(trans, id, 'Library') + if check_accessible and not (trans.user_is_admin() or trans.app.security_agent.can_access_library(trans.get_current_user_roles(), l)): + error("LibraryFolder is not accessible to the current user") return l -class UsesLibraryMixinItems( SharableItemSecurityMixin ): +class UsesLibraryMixinItems(SharableItemSecurityMixin): - def get_library_folder( self, trans, id, check_ownership=False, check_accessible=True ): - return self.get_object( trans, id, 'LibraryFolder', - check_ownership=False, check_accessible=check_accessible ) + def get_library_folder(self, trans, id, check_ownership=False, check_accessible=True): + return self.get_object(trans, id, 'LibraryFolder', + check_ownership=False, check_accessible=check_accessible) - def get_library_dataset_dataset_association( self, trans, id, check_ownership=False, check_accessible=True ): + def get_library_dataset_dataset_association(self, trans, id, check_ownership=False, check_accessible=True): # Deprecated in lieu to galaxy.managers.lddas.LDDAManager.get() but not # reusing that exactly because of subtle differences in exception handling # logic (API controller override get_object to be slightly different). - return self.get_object( trans, id, 'LibraryDatasetDatasetAssociation', - check_ownership=False, check_accessible=check_accessible ) + return self.get_object(trans, id, 'LibraryDatasetDatasetAssociation', + check_ownership=False, check_accessible=check_accessible) - def get_library_dataset( self, trans, id, check_ownership=False, check_accessible=True ): - return self.get_object( trans, id, 'LibraryDataset', - check_ownership=False, check_accessible=check_accessible ) + def get_library_dataset(self, trans, id, check_ownership=False, check_accessible=True): + return self.get_object(trans, id, 'LibraryDataset', + check_ownership=False, check_accessible=check_accessible) # TODO: it makes no sense that I can get roles from a user but not user.is_admin() # def can_user_add_to_library_item( self, trans, user, item ): @@ -432,13 +467,13 @@ def get_library_dataset( self, trans, id, check_ownership=False, check_accessibl # return ( ( user.is_admin() ) # or ( trans.app.security_agent.can_add_library_item( user.all_roles(), item ) ) ) - def can_current_user_add_to_library_item( self, trans, item ): + def can_current_user_add_to_library_item(self, trans, item): if not trans.user: return False - return ( ( trans.user_is_admin() ) or - ( trans.app.security_agent.can_add_library_item( trans.get_current_user_roles(), item ) ) ) + return ((trans.user_is_admin()) or + (trans.app.security_agent.can_add_library_item(trans.get_current_user_roles(), item))) - def check_user_can_add_to_library_item( self, trans, item, check_accessible=True ): + def check_user_can_add_to_library_item(self, trans, item, check_accessible=True): """ Raise exception if user cannot add to the specified library item (i.e. Folder). Can set check_accessible to False if folder was loaded with @@ -452,12 +487,12 @@ def check_user_can_add_to_library_item( self, trans, item, check_accessible=True return True if check_accessible: - if not trans.app.security_agent.can_access_library_item( current_user_roles, item, trans.user ): + if not trans.app.security_agent.can_access_library_item(current_user_roles, item, trans.user): raise exceptions.ItemAccessibilityException('You do not have access to the requested item') - if not trans.app.security_agent.can_add_library_item( trans.get_current_user_roles(), item ): + if not trans.app.security_agent.can_add_library_item(trans.get_current_user_roles(), item): # Slight misuse of ItemOwnershipException? - raise exceptions.ItemOwnershipException( "User cannot add to library item." ) + raise exceptions.ItemOwnershipException("User cannot add to library item.") def _copy_hdca_to_library_folder(self, trans, hda_manager, from_hdca_id, folder_id, ldda_message=''): """ @@ -477,7 +512,7 @@ def _copy_hdca_to_library_folder(self, trans, hda_manager, from_hdca_id, folder_ ldda_message=ldda_message, element_identifier=element_identifier) for (element_identifier, hda_id) in hdas] - def _copy_hda_to_library_folder( self, trans, hda_manager, from_hda_id, folder_id, ldda_message='', element_identifier=None ): + def _copy_hda_to_library_folder(self, trans, hda_manager, from_hda_id, folder_id, ldda_message='', element_identifier=None): """ Copies hda ``from_hda_id`` to library folder ``folder_id``, optionally adding ``ldda_message`` to the new ldda's ``message``. @@ -485,37 +520,37 @@ def _copy_hda_to_library_folder( self, trans, hda_manager, from_hda_id, folder_i ``library_contents.create`` will branch to this if called with 'from_hda_id' in its payload. """ - log.debug( '_copy_hda_to_library_folder: %s' % ( str(( from_hda_id, folder_id, ldda_message )) ) ) + log.debug('_copy_hda_to_library_folder: %s' % (str((from_hda_id, folder_id, ldda_message)))) # PRECONDITION: folder_id has already been altered to remove the folder prefix ('F') # TODO: allow name and other, editable ldda attrs? if ldda_message: - ldda_message = util.sanitize_html.sanitize_html( ldda_message, 'utf-8' ) + ldda_message = util.sanitize_html.sanitize_html(ldda_message, 'utf-8') # check permissions on (all three?) resources: hda, library, folder # TODO: do we really need the library?? - hda = hda_manager.get_owned( from_hda_id, trans.user, current_history=trans.history ) - hda = hda_manager.error_if_uploading( hda ) - folder = self.get_library_folder( trans, folder_id, check_accessible=True ) + hda = hda_manager.get_owned(from_hda_id, trans.user, current_history=trans.history) + hda = hda_manager.error_if_uploading(hda) + folder = self.get_library_folder(trans, folder_id, check_accessible=True) # TOOD: refactor to use check_user_can_add_to_library_item, eliminate boolean # can_current_user_add_to_library_item. if folder.parent_library.deleted: raise exceptions.ObjectAttributeInvalidException('You cannot add datasets into deleted library. Undelete it first.') - if not self.can_current_user_add_to_library_item( trans, folder ): + if not self.can_current_user_add_to_library_item(trans, folder): raise exceptions.InsufficientPermissionsException('You do not have proper permissions to add a dataset to this folder,') - ldda = self.copy_hda_to_library_folder( trans, hda, folder, ldda_message=ldda_message, element_identifier=element_identifier ) + ldda = self.copy_hda_to_library_folder(trans, hda, folder, ldda_message=ldda_message, element_identifier=element_identifier) # I don't see a reason why hdas copied into libraries should not be visible. # If there is, refactor `ldda.visible = True` to do this only when adding HDCAs. ldda.visible = True trans.sa_session.flush() ldda_dict = ldda.to_dict() - rval = trans.security.encode_dict_ids( ldda_dict ) + rval = trans.security.encode_dict_ids(ldda_dict) update_time = ldda.update_time.strftime("%Y-%m-%d %I:%M %p") rval['update_time'] = update_time return rval - def copy_hda_to_library_folder( self, trans, hda, library_folder, roles=None, ldda_message='', element_identifier=None ): + def copy_hda_to_library_folder(self, trans, hda, library_folder, roles=None, ldda_message='', element_identifier=None): # PRECONDITION: permissions for this action on hda and library_folder have been checked roles = roles or [] @@ -523,27 +558,27 @@ def copy_hda_to_library_folder( self, trans, hda, library_folder, roles=None, ld # TODO: refactor library_common.add_history_datasets_to_library to use this for each hda to copy # create the new ldda and apply the folder perms to it - ldda = hda.to_library_dataset_dataset_association( trans, target_folder=library_folder, - roles=roles, ldda_message=ldda_message, element_identifier=element_identifier ) - self._apply_library_folder_permissions_to_ldda( trans, library_folder, ldda ) - self._apply_hda_permissions_to_ldda( trans, hda, ldda ) + ldda = hda.to_library_dataset_dataset_association(trans, target_folder=library_folder, + roles=roles, ldda_message=ldda_message, element_identifier=element_identifier) + self._apply_library_folder_permissions_to_ldda(trans, library_folder, ldda) + self._apply_hda_permissions_to_ldda(trans, hda, ldda) # TODO:?? not really clear on how permissions are being traded here # seems like hda -> ldda permissions should be set in to_library_dataset_dataset_association # then they get reset in _apply_library_folder_permissions_to_ldda # then finally, re-applies hda -> ldda for missing actions in _apply_hda_permissions_to_ldda?? return ldda - def _apply_library_folder_permissions_to_ldda( self, trans, library_folder, ldda ): + def _apply_library_folder_permissions_to_ldda(self, trans, library_folder, ldda): """ Copy actions/roles from library folder to an ldda (and its library_dataset). """ # PRECONDITION: permissions for this action on library_folder and ldda have been checked security_agent = trans.app.security_agent - security_agent.copy_library_permissions( trans, library_folder, ldda ) - security_agent.copy_library_permissions( trans, library_folder, ldda.library_dataset ) - return security_agent.get_permissions( ldda ) + security_agent.copy_library_permissions(trans, library_folder, ldda) + security_agent.copy_library_permissions(trans, library_folder, ldda.library_dataset) + return security_agent.get_permissions(ldda) - def _apply_hda_permissions_to_ldda( self, trans, hda, ldda ): + def _apply_hda_permissions_to_ldda(self, trans, hda, ldda): """ Copy actions/roles from hda to ldda.library_dataset (and then ldda) if ldda doesn't already have roles for the given action. @@ -552,14 +587,14 @@ def _apply_hda_permissions_to_ldda( self, trans, hda, ldda ): # Make sure to apply any defined dataset permissions, allowing the permissions inherited from the # library_dataset to over-ride the same permissions on the dataset, if they exist. security_agent = trans.app.security_agent - dataset_permissions_dict = security_agent.get_permissions( hda.dataset ) + dataset_permissions_dict = security_agent.get_permissions(hda.dataset) library_dataset = ldda.library_dataset - library_dataset_actions = [ permission.action for permission in library_dataset.actions ] + library_dataset_actions = [permission.action for permission in library_dataset.actions] # except that: if DATASET_MANAGE_PERMISSIONS exists in the hda.dataset permissions, # we need to instead apply those roles to the LIBRARY_MANAGE permission to the library dataset - dataset_manage_permissions_action = security_agent.get_action( 'DATASET_MANAGE_PERMISSIONS' ).action - library_manage_permissions_action = security_agent.get_action( 'LIBRARY_MANAGE' ).action + dataset_manage_permissions_action = security_agent.get_action('DATASET_MANAGE_PERMISSIONS').action + library_manage_permissions_action = security_agent.get_action('LIBRARY_MANAGE').action # TODO: test this and remove if in loop below # TODO: doesn't handle action.action # if dataset_manage_permissions_action in dataset_permissions_dict: @@ -568,7 +603,7 @@ def _apply_hda_permissions_to_ldda( self, trans, hda, ldda ): flush_needed = False for action, dataset_permissions_roles in dataset_permissions_dict.items(): - if isinstance( action, security.Action ): + if isinstance(action, security.Action): action = action.action # alter : DATASET_MANAGE_PERMISSIONS -> LIBRARY_MANAGE (see above) @@ -580,42 +615,42 @@ def _apply_hda_permissions_to_ldda( self, trans, hda, ldda ): # NOTE: only apply an hda perm if it's NOT set in the library_dataset perms (don't overwrite) if action not in library_dataset_actions: for role in dataset_permissions_roles: - ldps = trans.model.LibraryDatasetPermissions( action, library_dataset, role ) - ldps = [ ldps ] if not isinstance( ldps, list ) else ldps + ldps = trans.model.LibraryDatasetPermissions(action, library_dataset, role) + ldps = [ldps] if not isinstance(ldps, list) else ldps for ldp in ldps: - trans.sa_session.add( ldp ) + trans.sa_session.add(ldp) flush_needed = True if flush_needed: trans.sa_session.flush() # finally, apply the new library_dataset to its associated ldda (must be the same) - security_agent.copy_library_permissions( trans, library_dataset, ldda ) - return security_agent.get_permissions( ldda ) + security_agent.copy_library_permissions(trans, library_dataset, ldda) + return security_agent.get_permissions(ldda) -class UsesVisualizationMixin( UsesLibraryMixinItems ): +class UsesVisualizationMixin(UsesLibraryMixinItems): """ Mixin for controllers that use Visualization objects. """ - viz_types = [ "trackster" ] + viz_types = ["trackster"] - def get_visualization( self, trans, id, check_ownership=True, check_accessible=False ): + def get_visualization(self, trans, id, check_ownership=True, check_accessible=False): """ Get a Visualization from the database by id, verifying ownership. """ # Load workflow from database try: - visualization = trans.sa_session.query( trans.model.Visualization ).get( trans.security.decode_id( id ) ) + visualization = trans.sa_session.query(trans.model.Visualization).get(trans.security.decode_id(id)) except TypeError: visualization = None if not visualization: - error( "Visualization not found" ) + error("Visualization not found") else: - return self.security_check( trans, visualization, check_ownership, check_accessible ) + return self.security_check(trans, visualization, check_ownership, check_accessible) - def get_visualizations_by_user( self, trans, user, order_by=None, query_only=False ): + def get_visualizations_by_user(self, trans, user, order_by=None, query_only=False): """ Return query or query results of visualizations filtered by a user. @@ -625,20 +660,20 @@ def get_visualizations_by_user( self, trans, user, order_by=None, query_only=Fal processing. """ # TODO: move into model (as class attr) - DEFAULT_ORDER_BY = [ model.Visualization.title ] + DEFAULT_ORDER_BY = [model.Visualization.title] if not order_by: order_by = DEFAULT_ORDER_BY - if not isinstance( order_by, list ): - order_by = [ order_by ] - query = trans.sa_session.query( model.Visualization ) - query = query.filter( model.Visualization.user == user ) + if not isinstance(order_by, list): + order_by = [order_by] + query = trans.sa_session.query(model.Visualization) + query = query.filter(model.Visualization.user == user) if order_by: - query = query.order_by( *order_by ) + query = query.order_by(*order_by) if query_only: return query return query.all() - def get_visualizations_shared_with_user( self, trans, user, order_by=None, query_only=False ): + def get_visualizations_shared_with_user(self, trans, user, order_by=None, query_only=False): """ Return query or query results for visualizations shared with the given user. @@ -647,22 +682,22 @@ def get_visualizations_shared_with_user( self, trans, user, order_by=None, query Set `query_only` to return just the query for further filtering or processing. """ - DEFAULT_ORDER_BY = [ model.Visualization.title ] + DEFAULT_ORDER_BY = [model.Visualization.title] if not order_by: order_by = DEFAULT_ORDER_BY - if not isinstance( order_by, list ): - order_by = [ order_by ] - query = trans.sa_session.query( model.Visualization ).join( model.VisualizationUserShareAssociation ) - query = query.filter( model.VisualizationUserShareAssociation.user_id == user.id ) + if not isinstance(order_by, list): + order_by = [order_by] + query = trans.sa_session.query(model.Visualization).join(model.VisualizationUserShareAssociation) + query = query.filter(model.VisualizationUserShareAssociation.user_id == user.id) # remove duplicates when a user shares with themselves? - query = query.filter( model.Visualization.user_id != user.id ) + query = query.filter(model.Visualization.user_id != user.id) if order_by: - query = query.order_by( *order_by ) + query = query.order_by(*order_by) if query_only: return query return query.all() - def get_published_visualizations( self, trans, exclude_user=None, order_by=None, query_only=False ): + def get_published_visualizations(self, trans, exclude_user=None, order_by=None, query_only=False): """ Return query or query results for published visualizations optionally excluding the user in `exclude_user`. @@ -672,23 +707,23 @@ def get_published_visualizations( self, trans, exclude_user=None, order_by=None, Set `query_only` to return just the query for further filtering or processing. """ - DEFAULT_ORDER_BY = [ model.Visualization.title ] + DEFAULT_ORDER_BY = [model.Visualization.title] if not order_by: order_by = DEFAULT_ORDER_BY - if not isinstance( order_by, list ): - order_by = [ order_by ] - query = trans.sa_session.query( model.Visualization ) - query = query.filter( model.Visualization.published == true() ) + if not isinstance(order_by, list): + order_by = [order_by] + query = trans.sa_session.query(model.Visualization) + query = query.filter(model.Visualization.published == true()) if exclude_user: - query = query.filter( model.Visualization.user != exclude_user ) + query = query.filter(model.Visualization.user != exclude_user) if order_by: - query = query.order_by( *order_by ) + query = query.order_by(*order_by) if query_only: return query return query.all() # TODO: move into model (to_dict) - def get_visualization_summary_dict( self, visualization ): + def get_visualization_summary_dict(self, visualization): """ Return a set of summary attributes for a visualization in dictionary form. NOTE: that encoding ids isn't done here should happen at the caller level. @@ -702,7 +737,7 @@ def get_visualization_summary_dict( self, visualization ): 'dbkey' : visualization.dbkey, } - def get_visualization_dict( self, visualization ): + def get_visualization_dict(self, visualization): """ Return a set of detailed attributes for a visualization in dictionary form. The visualization's latest_revision is returned in its own sub-dictionary. @@ -717,11 +752,11 @@ def get_visualization_dict( self, visualization ): 'dbkey' : visualization.dbkey, 'slug' : visualization.slug, # to_dict only the latest revision (allow older to be fetched elsewhere) - 'latest_revision' : self.get_visualization_revision_dict( visualization.latest_revision ), - 'revisions' : [ r.id for r in visualization.revisions ], + 'latest_revision' : self.get_visualization_revision_dict(visualization.latest_revision), + 'revisions' : [r.id for r in visualization.revisions], } - def get_visualization_revision_dict( self, revision ): + def get_visualization_revision_dict(self, revision): """ Return a set of detailed attributes for a visualization in dictionary form. NOTE: that encoding ids isn't done here should happen at the caller level. @@ -735,7 +770,7 @@ def get_visualization_revision_dict( self, revision ): 'config' : revision.config, } - def import_visualization( self, trans, id, user=None ): + def import_visualization(self, trans, id, user=None): """ Copy the visualization with the given id and associate the copy with the given user (defaults to trans.user). @@ -747,47 +782,47 @@ def import_visualization( self, trans, id, user=None ): # default to trans.user, error if anon if not user: if not trans.user: - raise exceptions.ItemAccessibilityException( "You must be logged in to import Galaxy visualizations" ) + raise exceptions.ItemAccessibilityException("You must be logged in to import Galaxy visualizations") user = trans.user # check accessibility - visualization = self.get_visualization( trans, id, check_ownership=False ) + visualization = self.get_visualization(trans, id, check_ownership=False) if not visualization.importable: - raise exceptions.ItemAccessibilityException( "The owner of this visualization has disabled imports via this link." ) + raise exceptions.ItemAccessibilityException("The owner of this visualization has disabled imports via this link.") if visualization.deleted: - raise exceptions.ItemDeletionException( "You can't import this visualization because it has been deleted." ) + raise exceptions.ItemDeletionException("You can't import this visualization because it has been deleted.") # copy vis and alter title # TODO: need to handle custom db keys. - imported_visualization = visualization.copy( user=user, title="imported: " + visualization.title ) - trans.sa_session.add( imported_visualization ) + imported_visualization = visualization.copy(user=user, title="imported: " + visualization.title) + trans.sa_session.add(imported_visualization) trans.sa_session.flush() return imported_visualization - def create_visualization( self, trans, type, title="Untitled Visualization", slug=None, - dbkey=None, annotation=None, config={}, save=True ): + def create_visualization(self, trans, type, title="Untitled Visualization", slug=None, + dbkey=None, annotation=None, config={}, save=True): """ Create visualiation and first revision. """ - visualization = self._create_visualization( trans, title, type, dbkey, slug, annotation, save ) + visualization = self._create_visualization(trans, title, type, dbkey, slug, annotation, save) # TODO: handle this error structure better either in _create or here - if isinstance( visualization, dict ): + if isinstance(visualization, dict): err_dict = visualization - raise ValueError( err_dict[ 'title_err' ] or err_dict[ 'slug_err' ] ) + raise ValueError(err_dict['title_err'] or err_dict['slug_err']) # Create and save first visualization revision - revision = trans.model.VisualizationRevision( visualization=visualization, title=title, - config=config, dbkey=dbkey ) + revision = trans.model.VisualizationRevision(visualization=visualization, title=title, + config=config, dbkey=dbkey) visualization.latest_revision = revision if save: session = trans.sa_session - session.add( revision ) + session.add(revision) session.flush() return visualization - def add_visualization_revision( self, trans, visualization, config, title, dbkey ): + def add_visualization_revision(self, trans, visualization, config, title, dbkey): """ Adds a new `VisualizationRevision` to the given `visualization` with the given parameters and set its parent visualization's `latest_revision` @@ -795,23 +830,23 @@ def add_visualization_revision( self, trans, visualization, config, title, dbkey """ # precondition: only add new revision on owned vis's # TODO:?? should we default title, dbkey, config? to which: visualization or latest_revision? - revision = trans.model.VisualizationRevision( visualization, title, dbkey, config ) + revision = trans.model.VisualizationRevision(visualization, title, dbkey, config) visualization.latest_revision = revision # TODO:?? does this automatically add revision to visualzation.revisions? - trans.sa_session.add( revision ) + trans.sa_session.add(revision) trans.sa_session.flush() return revision - def save_visualization( self, trans, config, type, id=None, title=None, dbkey=None, slug=None, annotation=None ): + def save_visualization(self, trans, config, type, id=None, title=None, dbkey=None, slug=None, annotation=None): session = trans.sa_session # Create/get visualization. if not id: # Create new visualization. - vis = self._create_visualization( trans, title, type, dbkey, slug, annotation ) + vis = self._create_visualization(trans, title, type, dbkey, slug, annotation) else: - decoded_id = trans.security.decode_id( id ) - vis = session.query( trans.model.Visualization ).get( decoded_id ) + decoded_id = trans.security.decode_id(id) + vis = session.query(trans.model.Visualization).get(decoded_id) # TODO: security check? # Create new VisualizationRevision that will be attached to the viz @@ -825,11 +860,11 @@ def save_visualization( self, trans, config, type, id=None, title=None, dbkey=No # -- Validate config. -- if vis.type == 'trackster': - def unpack_track( track_dict ): + def unpack_track(track_dict): """ Unpack a track from its json. """ - dataset_dict = track_dict[ 'dataset' ] + dataset_dict = track_dict['dataset'] return { - "dataset_id": trans.security.decode_id( dataset_dict['id'] ), + "dataset_id": trans.security.decode_id(dataset_dict['id']), "hda_ldda": dataset_dict.get('hda_ldda', 'hda'), "track_type": track_dict['track_type'], "prefs": track_dict['prefs'], @@ -838,49 +873,49 @@ def unpack_track( track_dict ): "tool_state": track_dict['tool_state'] } - def unpack_collection( collection_json ): + def unpack_collection(collection_json): """ Unpack a collection from its json. """ unpacked_drawables = [] - drawables = collection_json[ 'drawables' ] + drawables = collection_json['drawables'] for drawable_json in drawables: if 'track_type' in drawable_json: - drawable = unpack_track( drawable_json ) + drawable = unpack_track(drawable_json) else: - drawable = unpack_collection( drawable_json ) - unpacked_drawables.append( drawable ) + drawable = unpack_collection(drawable_json) + unpacked_drawables.append(drawable) return { - "obj_type": collection_json[ 'obj_type' ], + "obj_type": collection_json['obj_type'], "drawables": unpacked_drawables, - "prefs": collection_json.get( 'prefs', [] ), - "filters": collection_json.get( 'filters', None ) + "prefs": collection_json.get('prefs', []), + "filters": collection_json.get('filters', None) } # TODO: unpack and validate bookmarks: - def unpack_bookmarks( bookmarks_json ): + def unpack_bookmarks(bookmarks_json): return bookmarks_json # Unpack and validate view content. - view_content = unpack_collection( config[ 'view' ] ) - bookmarks = unpack_bookmarks( config[ 'bookmarks' ] ) - vis_rev.config = { "view": view_content, "bookmarks": bookmarks } + view_content = unpack_collection(config['view']) + bookmarks = unpack_bookmarks(config['bookmarks']) + vis_rev.config = {"view": view_content, "bookmarks": bookmarks} # Viewport from payload if 'viewport' in config: chrom = config['viewport']['chrom'] start = config['viewport']['start'] end = config['viewport']['end'] overview = config['viewport']['overview'] - vis_rev.config[ "viewport" ] = { 'chrom': chrom, 'start': start, 'end': end, 'overview': overview } + vis_rev.config["viewport"] = {'chrom': chrom, 'start': start, 'end': end, 'overview': overview} else: # Default action is to save the config as is with no validation. vis_rev.config = config vis.latest_revision = vis_rev - session.add( vis_rev ) + session.add(vis_rev) session.flush() - encoded_id = trans.security.encode_id( vis.id ) - return { "vis_id": encoded_id, "url": url_for( controller='visualization', action=vis.type, id=encoded_id ) } + encoded_id = trans.security.encode_id(vis.id) + return {"vis_id": encoded_id, "url": url_for(controller='visualization', action=vis.type, id=encoded_id)} - def get_tool_def( self, trans, hda ): + def get_tool_def(self, trans, hda): """ Returns definition of an interactive tool for an HDA. """ # Get dataset's job. @@ -891,7 +926,7 @@ def get_tool_def( self, trans, hda ): if not job: return None - tool = trans.app.toolbox.get_tool( job.tool_id ) + tool = trans.app.toolbox.get_tool(job.tool_id, tool_version=job.tool_version) if not tool: return None @@ -900,103 +935,103 @@ def get_tool_def( self, trans, hda ): return None # -- Get tool definition and add input values from job. -- - tool_dict = tool.to_dict( trans, io_details=True ) - tool_param_values = dict( [ ( p.name, p.value ) for p in job.parameters ] ) - tool_param_values = tool.params_from_strings( tool_param_values, trans.app, ignore_errors=True ) + tool_dict = tool.to_dict(trans, io_details=True) + tool_param_values = dict([(p.name, p.value) for p in job.parameters]) + tool_param_values = tool.params_from_strings(tool_param_values, trans.app, ignore_errors=True) # Only get values for simple inputs for now. - inputs_dict = [ i for i in tool_dict[ 'inputs' ] if i[ 'type' ] not in [ 'data', 'hidden_data', 'conditional' ] ] + inputs_dict = [i for i in tool_dict['inputs'] if i['type'] not in ['data', 'hidden_data', 'conditional']] for t_input in inputs_dict: # Add value to tool. if 'name' in t_input: - name = t_input[ 'name' ] + name = t_input['name'] if name in tool_param_values: - value = tool_param_values[ name ] - if isinstance( value, Dictifiable ): + value = tool_param_values[name] + if isinstance(value, Dictifiable): value = value.to_dict() - t_input[ 'value' ] = value + t_input['value'] = value return tool_dict - def get_visualization_config( self, trans, visualization ): + def get_visualization_config(self, trans, visualization): """ Returns a visualization's configuration. Only works for trackster visualizations right now. """ config = None - if visualization.type in [ 'trackster', 'genome' ]: + if visualization.type in ['trackster', 'genome']: # Unpack Trackster config. latest_revision = visualization.latest_revision - bookmarks = latest_revision.config.get( 'bookmarks', [] ) + bookmarks = latest_revision.config.get('bookmarks', []) - def pack_track( track_dict ): + def pack_track(track_dict): dataset_id = track_dict['dataset_id'] hda_ldda = track_dict.get('hda_ldda', 'hda') if hda_ldda == 'ldda': # HACK: need to encode library dataset ID because get_hda_or_ldda # only works for encoded datasets. - dataset_id = trans.security.encode_id( dataset_id ) - dataset = self.get_hda_or_ldda( trans, hda_ldda, dataset_id ) + dataset_id = trans.security.encode_id(dataset_id) + dataset = self.get_hda_or_ldda(trans, hda_ldda, dataset_id) try: prefs = track_dict['prefs'] except KeyError: prefs = {} - track_data_provider = trans.app.data_provider_registry.get_data_provider( trans, - original_dataset=dataset, - source='data' ) + track_data_provider = trans.app.data_provider_registry.get_data_provider(trans, + original_dataset=dataset, + source='data') return { "track_type": dataset.datatype.track_type, - "dataset": trans.security.encode_dict_ids( dataset.to_dict() ), + "dataset": trans.security.encode_dict_ids(dataset.to_dict()), "prefs": prefs, - "mode": track_dict.get( 'mode', 'Auto' ), - "filters": track_dict.get( 'filters', { 'filters' : track_data_provider.get_filters() } ), - "tool": self.get_tool_def( trans, dataset ), - "tool_state": track_dict.get( 'tool_state', {} ) + "mode": track_dict.get('mode', 'Auto'), + "filters": track_dict.get('filters', {'filters' : track_data_provider.get_filters()}), + "tool": self.get_tool_def(trans, dataset), + "tool_state": track_dict.get('tool_state', {}) } - def pack_collection( collection_dict ): + def pack_collection(collection_dict): drawables = [] - for drawable_dict in collection_dict[ 'drawables' ]: + for drawable_dict in collection_dict['drawables']: if 'track_type' in drawable_dict: - drawables.append( pack_track( drawable_dict ) ) + drawables.append(pack_track(drawable_dict)) else: - drawables.append( pack_collection( drawable_dict ) ) + drawables.append(pack_collection(drawable_dict)) return { - 'obj_type': collection_dict[ 'obj_type' ], + 'obj_type': collection_dict['obj_type'], 'drawables': drawables, - 'prefs': collection_dict.get( 'prefs', [] ), - 'filters': collection_dict.get( 'filters', {} ) + 'prefs': collection_dict.get('prefs', []), + 'filters': collection_dict.get('filters', {}) } - def encode_dbkey( dbkey ): + def encode_dbkey(dbkey): """ Encodes dbkey as needed. For now, prepends user's public name to custom dbkey keys. """ encoded_dbkey = dbkey user = visualization.user - if 'dbkeys' in user.preferences and dbkey in user.preferences[ 'dbkeys' ]: - encoded_dbkey = "%s:%s" % ( user.username, dbkey ) + if 'dbkeys' in user.preferences and dbkey in user.preferences['dbkeys']: + encoded_dbkey = "%s:%s" % (user.username, dbkey) return encoded_dbkey # Set tracks. tracks = [] if 'tracks' in latest_revision.config: # Legacy code. - for track_dict in visualization.latest_revision.config[ 'tracks' ]: - tracks.append( pack_track( track_dict ) ) + for track_dict in visualization.latest_revision.config['tracks']: + tracks.append(pack_track(track_dict)) elif 'view' in latest_revision.config: - for drawable_dict in visualization.latest_revision.config[ 'view' ][ 'drawables' ]: + for drawable_dict in visualization.latest_revision.config['view']['drawables']: if 'track_type' in drawable_dict: - tracks.append( pack_track( drawable_dict ) ) + tracks.append(pack_track(drawable_dict)) else: - tracks.append( pack_collection( drawable_dict ) ) + tracks.append(pack_collection(drawable_dict)) - config = { "title": visualization.title, - "vis_id": trans.security.encode_id( visualization.id ), - "tracks": tracks, - "bookmarks": bookmarks, - "chrom": "", - "dbkey": encode_dbkey( visualization.dbkey ) } + config = {"title": visualization.title, + "vis_id": trans.security.encode_id(visualization.id), + "tracks": tracks, + "bookmarks": bookmarks, + "chrom": "", + "dbkey": encode_dbkey(visualization.dbkey)} if 'viewport' in latest_revision.config: config['viewport'] = latest_revision.config['viewport'] @@ -1007,72 +1042,72 @@ def encode_dbkey( dbkey ): return config - def get_new_track_config( self, trans, dataset ): + def get_new_track_config(self, trans, dataset): """ Returns track configuration dict for a dataset. """ # Get data provider. - track_data_provider = trans.app.data_provider_registry.get_data_provider( trans, original_dataset=dataset ) + track_data_provider = trans.app.data_provider_registry.get_data_provider(trans, original_dataset=dataset) # Get track definition. return { "track_type": dataset.datatype.track_type, "name": dataset.name, - "dataset": trans.security.encode_dict_ids( dataset.to_dict() ), + "dataset": trans.security.encode_dict_ids(dataset.to_dict()), "prefs": {}, - "filters": { 'filters' : track_data_provider.get_filters() }, - "tool": self.get_tool_def( trans, dataset ), + "filters": {'filters' : track_data_provider.get_filters()}, + "tool": self.get_tool_def(trans, dataset), "tool_state": {} } - def get_hda_or_ldda( self, trans, hda_ldda, dataset_id ): + def get_hda_or_ldda(self, trans, hda_ldda, dataset_id): """ Returns either HDA or LDDA for hda/ldda and id combination. """ if hda_ldda == "hda": - return self.get_hda( trans, dataset_id, check_ownership=False, check_accessible=True ) + return self.get_hda(trans, dataset_id, check_ownership=False, check_accessible=True) else: - return self.get_library_dataset_dataset_association( trans, dataset_id ) + return self.get_library_dataset_dataset_association(trans, dataset_id) - def get_hda( self, trans, dataset_id, check_ownership=True, check_accessible=False, check_state=True ): + def get_hda(self, trans, dataset_id, check_ownership=True, check_accessible=False, check_state=True): """ Get an HDA object by id performing security checks using the current transaction. """ try: - dataset_id = trans.security.decode_id( dataset_id ) - except ( AttributeError, TypeError ): + dataset_id = trans.security.decode_id(dataset_id) + except (AttributeError, TypeError): # DEPRECATION: We still support unencoded ids for backward compatibility try: - dataset_id = int( dataset_id ) + dataset_id = int(dataset_id) except ValueError: - raise HTTPBadRequest( "Invalid dataset id: %s." % str( dataset_id ) ) + raise HTTPBadRequest("Invalid dataset id: %s." % str(dataset_id)) try: - data = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( int( dataset_id ) ) + data = trans.sa_session.query(trans.app.model.HistoryDatasetAssociation).get(int(dataset_id)) except: - raise HTTPRequestRangeNotSatisfiable( "Invalid dataset id: %s." % str( dataset_id ) ) + raise HTTPRequestRangeNotSatisfiable("Invalid dataset id: %s." % str(dataset_id)) if check_ownership: # Verify ownership. user = trans.get_user() if not user: - error( "Must be logged in to manage Galaxy items" ) + error("Must be logged in to manage Galaxy items") if data.history.user != user: - error( "%s is not owned by current user" % data.__class__.__name__ ) + error("%s is not owned by current user" % data.__class__.__name__) if check_accessible: current_user_roles = trans.get_current_user_roles() - if not trans.app.security_agent.can_access_dataset( current_user_roles, data.dataset ): - error( "You are not allowed to access this dataset" ) + if not trans.app.security_agent.can_access_dataset(current_user_roles, data.dataset): + error("You are not allowed to access this dataset") if check_state and data.state == trans.model.Dataset.states.UPLOAD: - return trans.show_error_message( "Please wait until this dataset finishes uploading " + - "before attempting to view it." ) + return trans.show_error_message("Please wait until this dataset finishes uploading " + + "before attempting to view it.") return data # -- Helper functions -- - def _create_visualization( self, trans, title, type, dbkey=None, slug=None, annotation=None, save=True ): + def _create_visualization(self, trans, title, type, dbkey=None, slug=None, annotation=None, save=True): """ Create visualization but not first revision. Returns Visualization object. """ user = trans.get_user() @@ -1080,72 +1115,72 @@ def _create_visualization( self, trans, title, type, dbkey=None, slug=None, anno title_err = slug_err = "" if not title: title_err = "visualization name is required" - elif slug and not _is_valid_slug( slug ): + elif slug and not _is_valid_slug(slug): slug_err = "visualization identifier must consist of only lowercase letters, numbers, and the '-' character" - elif slug and trans.sa_session.query( trans.model.Visualization ).filter_by( user=user, slug=slug, deleted=False ).first(): + elif slug and trans.sa_session.query(trans.model.Visualization).filter_by(user=user, slug=slug, deleted=False).first(): slug_err = "visualization identifier must be unique" if title_err or slug_err: - return { 'title_err': title_err, 'slug_err': slug_err } + return {'title_err': title_err, 'slug_err': slug_err} # Create visualization - visualization = trans.model.Visualization( user=user, title=title, dbkey=dbkey, type=type ) + visualization = trans.model.Visualization(user=user, title=title, dbkey=dbkey, type=type) if slug: visualization.slug = slug else: - self.create_item_slug( trans.sa_session, visualization ) + self.create_item_slug(trans.sa_session, visualization) if annotation: - annotation = sanitize_html( annotation, 'utf-8', 'text/html' ) + annotation = sanitize_html(annotation, 'utf-8', 'text/html') # TODO: if this is to stay in the mixin, UsesAnnotations should be added to the superclasses # right now this is depending on the classes that include this mixin to have UsesAnnotations - self.add_item_annotation( trans.sa_session, trans.user, visualization, annotation ) + self.add_item_annotation(trans.sa_session, trans.user, visualization, annotation) if save: session = trans.sa_session - session.add( visualization ) + session.add(visualization) session.flush() return visualization - def _get_genome_data( self, trans, dataset, dbkey=None ): + def _get_genome_data(self, trans, dataset, dbkey=None): """ Returns genome-wide data for dataset if available; if not, message is returned. """ rval = None # Get data sources. - data_sources = dataset.get_datasources( trans ) + data_sources = dataset.get_datasources(trans) query_dbkey = dataset.dbkey if query_dbkey == "?": query_dbkey = dbkey - chroms_info = self.app.genomes.chroms( trans, dbkey=query_dbkey ) + chroms_info = self.app.genomes.chroms(trans, dbkey=query_dbkey) # If there are no messages (messages indicate data is not ready/available), get data. - messages_list = [ data_source_dict[ 'message' ] for data_source_dict in data_sources.values() ] - message = self._get_highest_priority_msg( messages_list ) + messages_list = [data_source_dict['message'] for data_source_dict in data_sources.values()] + message = self._get_highest_priority_msg(messages_list) if message: rval = message else: # HACK: chromatin interactions tracks use data as source. source = 'index' - if isinstance( dataset.datatype, ChromatinInteractions ): + if isinstance(dataset.datatype, ChromatinInteractions): source = 'data' - data_provider = trans.app.data_provider_registry.get_data_provider( trans, - original_dataset=dataset, - source=source ) + data_provider = trans.app.data_provider_registry.get_data_provider(trans, + original_dataset=dataset, + source=source) # HACK: pass in additional params which are used for only some # types of data providers; level, cutoffs used for summary tree, # num_samples for BBI, and interchromosomal used for chromatin interactions. - rval = data_provider.get_genome_data( chroms_info, - level=4, detail_cutoff=0, draw_cutoff=0, - num_samples=150, - interchromosomal=True ) + rval = data_provider.get_genome_data(chroms_info, + level=4, detail_cutoff=0, draw_cutoff=0, + num_samples=150, + interchromosomal=True) return rval # FIXME: this method probably belongs down in the model.Dataset class. - def _get_highest_priority_msg( self, message_list ): + def _get_highest_priority_msg(self, message_list): """ Returns highest priority message from a list of messages. """ @@ -1164,39 +1199,39 @@ def _get_highest_priority_msg( self, message_list ): return return_message -class UsesStoredWorkflowMixin( SharableItemSecurityMixin, UsesAnnotations ): +class UsesStoredWorkflowMixin(SharableItemSecurityMixin, UsesAnnotations): """ Mixin for controllers that use StoredWorkflow objects. """ - def get_stored_workflow( self, trans, id, check_ownership=True, check_accessible=False ): + def get_stored_workflow(self, trans, id, check_ownership=True, check_accessible=False): """ Get a StoredWorkflow from the database by id, verifying ownership. """ # Load workflow from database try: - workflow = trans.sa_session.query( trans.model.StoredWorkflow ).get( trans.security.decode_id( id ) ) + workflow = trans.sa_session.query(trans.model.StoredWorkflow).get(trans.security.decode_id(id)) except TypeError: workflow = None if not workflow: - error( "Workflow not found" ) + error("Workflow not found") else: - self.security_check( trans, workflow, check_ownership, check_accessible ) + self.security_check(trans, workflow, check_ownership, check_accessible) # Older workflows may be missing slugs, so set them here. if not workflow.slug: - self.create_item_slug( trans.sa_session, workflow ) + self.create_item_slug(trans.sa_session, workflow) trans.sa_session.flush() return workflow - def get_stored_workflow_steps( self, trans, stored_workflow ): + def get_stored_workflow_steps(self, trans, stored_workflow): """ Restores states for a stored workflow's steps. """ - module_injector = WorkflowModuleInjector( trans ) + module_injector = WorkflowModuleInjector(trans) for step in stored_workflow.latest_workflow.steps: try: - module_injector.inject( step ) + module_injector.inject(step) except exceptions.ToolMissingException: pass - def _import_shared_workflow( self, trans, stored): + def _import_shared_workflow(self, trans, stored): """ Imports a shared workflow """ # Copy workflow. imported_stored = model.StoredWorkflow() @@ -1207,23 +1242,23 @@ def _import_shared_workflow( self, trans, stored): imported_stored.user = trans.user # Save new workflow. session = trans.sa_session - session.add( imported_stored ) + session.add(imported_stored) session.flush() # Copy annotations. - self.copy_item_annotation( session, stored.user, stored, imported_stored.user, imported_stored ) - for order_index, step in enumerate( stored.latest_workflow.steps ): - self.copy_item_annotation( session, stored.user, step, - imported_stored.user, imported_stored.latest_workflow.steps[order_index] ) + self.copy_item_annotation(session, stored.user, stored, imported_stored.user, imported_stored) + for order_index, step in enumerate(stored.latest_workflow.steps): + self.copy_item_annotation(session, stored.user, step, + imported_stored.user, imported_stored.latest_workflow.steps[order_index]) session.flush() return imported_stored - def _workflow_from_dict( self, trans, data, source=None, add_to_menu=False, publish=False, exact_tools=False ): + def _workflow_from_dict(self, trans, data, source=None, add_to_menu=False, publish=False, exact_tools=False): """ Creates a workflow from a dict. Created workflow is stored in the database and returned. """ # TODO: replace this method with direct access to manager. - workflow_contents_manager = workflows.WorkflowContentsManager( self.app ) + workflow_contents_manager = workflows.WorkflowContentsManager(self.app) created_workflow = workflow_contents_manager.build_workflow_from_dict( trans, data, @@ -1234,11 +1269,11 @@ def _workflow_from_dict( self, trans, data, source=None, add_to_menu=False, publ ) return created_workflow.stored_workflow, created_workflow.missing_tools - def _workflow_to_dict( self, trans, stored ): + def _workflow_to_dict(self, trans, stored): """ Converts a workflow to a dict of attributes suitable for exporting. """ - workflow_contents_manager = workflows.WorkflowContentsManager( self.app ) + workflow_contents_manager = workflows.WorkflowContentsManager(self.app) return workflow_contents_manager.workflow_to_dict( trans, stored, @@ -1248,245 +1283,245 @@ def _workflow_to_dict( self, trans, stored ): class UsesFormDefinitionsMixin: """Mixin for controllers that use Galaxy form objects.""" - def get_all_forms( self, trans, all_versions=False, filter=None, form_type='All' ): + def get_all_forms(self, trans, all_versions=False, filter=None, form_type='All'): """ Return all the latest forms from the form_definition_current table if all_versions is set to True. Otherwise return all the versions of all the forms from the form_definition table. """ if all_versions: - return trans.sa_session.query( trans.app.model.FormDefinition ) + return trans.sa_session.query(trans.app.model.FormDefinition) if filter: - fdc_list = trans.sa_session.query( trans.app.model.FormDefinitionCurrent ).filter_by( **filter ) + fdc_list = trans.sa_session.query(trans.app.model.FormDefinitionCurrent).filter_by(**filter) else: - fdc_list = trans.sa_session.query( trans.app.model.FormDefinitionCurrent ) + fdc_list = trans.sa_session.query(trans.app.model.FormDefinitionCurrent) if form_type == 'All': - return [ fdc.latest_form for fdc in fdc_list ] + return [fdc.latest_form for fdc in fdc_list] else: - return [ fdc.latest_form for fdc in fdc_list if fdc.latest_form.type == form_type ] + return [fdc.latest_form for fdc in fdc_list if fdc.latest_form.type == form_type] - def get_all_forms_by_type( self, trans, cntrller, form_type ): - forms = self.get_all_forms( trans, - filter=dict( deleted=False ), - form_type=form_type ) + def get_all_forms_by_type(self, trans, cntrller, form_type): + forms = self.get_all_forms(trans, + filter=dict(deleted=False), + form_type=form_type) if not forms: message = "There are no forms on which to base the template, so create a form and then add the template." - return trans.response.send_redirect( web.url_for( controller='forms', - action='create_form_definition', - cntrller=cntrller, - message=message, - status='done', - form_type=form_type ) ) + return trans.response.send_redirect(web.url_for(controller='forms', + action='create_form_definition', + cntrller=cntrller, + message=message, + status='done', + form_type=form_type)) return forms @web.expose - def add_template( self, trans, cntrller, item_type, form_type, **kwd ): - params = util.Params( kwd ) - form_id = params.get( 'form_id', 'none' ) - message = util.restore_text( params.get( 'message', '' ) ) + def add_template(self, trans, cntrller, item_type, form_type, **kwd): + params = util.Params(kwd) + form_id = params.get('form_id', 'none') + message = util.restore_text(params.get('message', '')) action = '' - status = params.get( 'status', 'done' ) - forms = self.get_all_forms_by_type( trans, cntrller, form_type ) + status = params.get('status', 'done') + forms = self.get_all_forms_by_type(trans, cntrller, form_type) # form_type must be one of: RUN_DETAILS_TEMPLATE, LIBRARY_INFO_TEMPLATE in_library = form_type == trans.model.FormDefinition.types.LIBRARY_INFO_TEMPLATE in_sample_tracking = form_type == trans.model.FormDefinition.types.RUN_DETAILS_TEMPLATE if in_library: - show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) ) - use_panels = util.string_as_bool( params.get( 'use_panels', False ) ) - library_id = params.get( 'library_id', None ) - folder_id = params.get( 'folder_id', None ) - ldda_id = params.get( 'ldda_id', None ) - is_admin = trans.user_is_admin() and cntrller in [ 'library_admin', 'requests_admin' ] + show_deleted = util.string_as_bool(params.get('show_deleted', False)) + use_panels = util.string_as_bool(params.get('use_panels', False)) + library_id = params.get('library_id', None) + folder_id = params.get('folder_id', None) + ldda_id = params.get('ldda_id', None) + is_admin = trans.user_is_admin() and cntrller in ['library_admin', 'requests_admin'] current_user_roles = trans.get_current_user_roles() elif in_sample_tracking: - request_type_id = params.get( 'request_type_id', None ) - sample_id = params.get( 'sample_id', None ) + request_type_id = params.get('request_type_id', None) + sample_id = params.get('sample_id', None) try: if in_sample_tracking: - item, item_desc, action, id = self.get_item_and_stuff( trans, - item_type=item_type, - request_type_id=request_type_id, - sample_id=sample_id ) + item, item_desc, action, id = self.get_item_and_stuff(trans, + item_type=item_type, + request_type_id=request_type_id, + sample_id=sample_id) elif in_library: - item, item_desc, action, id = self.get_item_and_stuff( trans, - item_type=item_type, - library_id=library_id, - folder_id=folder_id, - ldda_id=ldda_id, - is_admin=is_admin ) + item, item_desc, action, id = self.get_item_and_stuff(trans, + item_type=item_type, + library_id=library_id, + folder_id=folder_id, + ldda_id=ldda_id, + is_admin=is_admin) if not item: - message = "Invalid %s id ( %s ) specified." % ( item_desc, str( id ) ) + message = "Invalid %s id ( %s ) specified." % (item_desc, str(id)) if in_sample_tracking: - return trans.response.send_redirect( web.url_for( controller='request_type', - action='browse_request_types', - id=request_type_id, - message=util.sanitize_text( message ), - status='error' ) ) + return trans.response.send_redirect(web.url_for(controller='request_type', + action='browse_request_types', + id=request_type_id, + message=util.sanitize_text(message), + status='error')) if in_library: - return trans.response.send_redirect( web.url_for( controller='library_common', - action='browse_library', - cntrller=cntrller, - id=library_id, - show_deleted=show_deleted, - message=util.sanitize_text( message ), - status='error' ) ) + return trans.response.send_redirect(web.url_for(controller='library_common', + action='browse_library', + cntrller=cntrller, + id=library_id, + show_deleted=show_deleted, + message=util.sanitize_text(message), + status='error')) except ValueError: # At this point, the client has already redirected, so this is just here to prevent the unnecessary traceback return None if in_library: # Make sure the user is authorized to do what they are trying to do. authorized = True - if not ( is_admin or trans.app.security_agent.can_modify_library_item( current_user_roles, item ) ): + if not (is_admin or trans.app.security_agent.can_modify_library_item(current_user_roles, item)): authorized = False unauthorized = 'modify' - if not ( is_admin or trans.app.security_agent.can_access_library_item( current_user_roles, item, trans.user ) ): + if not (is_admin or trans.app.security_agent.can_access_library_item(current_user_roles, item, trans.user)): authorized = False unauthorized = 'access' if not authorized: - message = "You are not authorized to %s %s '%s'." % ( unauthorized, item_desc, item.name ) - return trans.response.send_redirect( web.url_for( controller='library_common', - action='browse_library', - cntrller=cntrller, - id=library_id, - show_deleted=show_deleted, - message=util.sanitize_text( message ), - status='error' ) ) + message = "You are not authorized to %s %s '%s'." % (unauthorized, item_desc, item.name) + return trans.response.send_redirect(web.url_for(controller='library_common', + action='browse_library', + cntrller=cntrller, + id=library_id, + show_deleted=show_deleted, + message=util.sanitize_text(message), + status='error')) # If the inheritable checkbox is checked, the param will be in the request - inheritable = CheckboxField.is_checked( params.get( 'inheritable', '' ) ) - if params.get( 'add_template_button', False ): - if form_id not in [ None, 'None', 'none' ]: - form = trans.sa_session.query( trans.app.model.FormDefinition ).get( trans.security.decode_id( form_id ) ) - form_values = trans.app.model.FormValues( form, {} ) - trans.sa_session.add( form_values ) + inheritable = CheckboxField.is_checked(params.get('inheritable', '')) + if params.get('add_template_button', False): + if form_id not in [None, 'None', 'none']: + form = trans.sa_session.query(trans.app.model.FormDefinition).get(trans.security.decode_id(form_id)) + form_values = trans.app.model.FormValues(form, {}) + trans.sa_session.add(form_values) trans.sa_session.flush() if item_type == 'library': - assoc = trans.model.LibraryInfoAssociation( item, form, form_values, inheritable=inheritable ) + assoc = trans.model.LibraryInfoAssociation(item, form, form_values, inheritable=inheritable) elif item_type == 'folder': - assoc = trans.model.LibraryFolderInfoAssociation( item, form, form_values, inheritable=inheritable ) + assoc = trans.model.LibraryFolderInfoAssociation(item, form, form_values, inheritable=inheritable) elif item_type == 'ldda': - assoc = trans.model.LibraryDatasetDatasetInfoAssociation( item, form, form_values ) - elif item_type in [ 'request_type', 'sample' ]: - run = trans.model.Run( form, form_values ) - trans.sa_session.add( run ) + assoc = trans.model.LibraryDatasetDatasetInfoAssociation(item, form, form_values) + elif item_type in ['request_type', 'sample']: + run = trans.model.Run(form, form_values) + trans.sa_session.add(run) trans.sa_session.flush() if item_type == 'request_type': # Delete current RequestTypeRunAssociation, if one exists. rtra = item.run_details if rtra: - trans.sa_session.delete( rtra ) + trans.sa_session.delete(rtra) trans.sa_session.flush() # Add the new RequestTypeRunAssociation. Templates associated with a RequestType # are automatically inherited to the samples. - assoc = trans.model.RequestTypeRunAssociation( item, run ) + assoc = trans.model.RequestTypeRunAssociation(item, run) elif item_type == 'sample': - assoc = trans.model.SampleRunAssociation( item, run ) - trans.sa_session.add( assoc ) + assoc = trans.model.SampleRunAssociation(item, run) + trans.sa_session.add(assoc) trans.sa_session.flush() - message = 'A template based on the form "%s" has been added to this %s.' % ( form.name, item_desc ) - new_kwd = dict( action=action, - cntrller=cntrller, - message=util.sanitize_text( message ), - status='done' ) + message = 'A template based on the form "%s" has been added to this %s.' % (form.name, item_desc) + new_kwd = dict(action=action, + cntrller=cntrller, + message=util.sanitize_text(message), + status='done') if in_sample_tracking: - new_kwd.update( dict( controller='request_type', - request_type_id=request_type_id, - sample_id=sample_id, - id=id ) ) - return trans.response.send_redirect( web.url_for( **new_kwd ) ) + new_kwd.update(dict(controller='request_type', + request_type_id=request_type_id, + sample_id=sample_id, + id=id)) + return trans.response.send_redirect(web.url_for(**new_kwd)) elif in_library: - new_kwd.update( dict( controller='library_common', - use_panels=use_panels, - library_id=library_id, - folder_id=folder_id, - id=id, - show_deleted=show_deleted ) ) - return trans.response.send_redirect( web.url_for( **new_kwd ) ) + new_kwd.update(dict(controller='library_common', + use_panels=use_panels, + library_id=library_id, + folder_id=folder_id, + id=id, + show_deleted=show_deleted)) + return trans.response.send_redirect(web.url_for(**new_kwd)) else: message = "Select a form on which to base the template." status = "error" - form_id_select_field = self.build_form_id_select_field( trans, forms, selected_value=kwd.get( 'form_id', 'none' ) ) + form_id_select_field = self.build_form_id_select_field(trans, forms, selected_value=kwd.get('form_id', 'none')) try: - decoded_form_id = trans.security.decode_id( form_id ) + decoded_form_id = trans.security.decode_id(form_id) except Exception: decoded_form_id = None if decoded_form_id: for form in forms: if decoded_form_id == form.id: - widgets = form.get_widgets( trans.user ) + widgets = form.get_widgets(trans.user) break else: widgets = [] - new_kwd = dict( cntrller=cntrller, - item_name=item.name, - item_desc=item_desc, - item_type=item_type, - form_type=form_type, - widgets=widgets, - form_id_select_field=form_id_select_field, - message=message, - status=status ) + new_kwd = dict(cntrller=cntrller, + item_name=item.name, + item_desc=item_desc, + item_type=item_type, + form_type=form_type, + widgets=widgets, + form_id_select_field=form_id_select_field, + message=message, + status=status) if in_sample_tracking: - new_kwd.update( dict( request_type_id=request_type_id, - sample_id=sample_id ) ) + new_kwd.update(dict(request_type_id=request_type_id, + sample_id=sample_id)) elif in_library: - new_kwd.update( dict( use_panels=use_panels, - library_id=library_id, - folder_id=folder_id, - ldda_id=ldda_id, - inheritable_checked=inheritable, - show_deleted=show_deleted ) ) - return trans.fill_template( '/common/select_template.mako', - **new_kwd ) + new_kwd.update(dict(use_panels=use_panels, + library_id=library_id, + folder_id=folder_id, + ldda_id=ldda_id, + inheritable_checked=inheritable, + show_deleted=show_deleted)) + return trans.fill_template('/common/select_template.mako', + **new_kwd) @web.expose - def edit_template( self, trans, cntrller, item_type, form_type, **kwd ): + def edit_template(self, trans, cntrller, item_type, form_type, **kwd): # Edit the template itself, keeping existing field contents, if any. - params = util.Params( kwd ) - message = util.restore_text( params.get( 'message', '' ) ) - edited = util.string_as_bool( params.get( 'edited', False ) ) + params = util.Params(kwd) + message = util.restore_text(params.get('message', '')) + edited = util.string_as_bool(params.get('edited', False)) action = '' # form_type must be one of: RUN_DETAILS_TEMPLATE, LIBRARY_INFO_TEMPLATE in_library = form_type == trans.model.FormDefinition.types.LIBRARY_INFO_TEMPLATE in_sample_tracking = form_type == trans.model.FormDefinition.types.RUN_DETAILS_TEMPLATE if in_library: - show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) ) - use_panels = util.string_as_bool( params.get( 'use_panels', False ) ) - library_id = params.get( 'library_id', None ) - folder_id = params.get( 'folder_id', None ) - ldda_id = params.get( 'ldda_id', None ) - is_admin = trans.user_is_admin() and cntrller in [ 'library_admin', 'requests_admin' ] + show_deleted = util.string_as_bool(params.get('show_deleted', False)) + use_panels = util.string_as_bool(params.get('use_panels', False)) + library_id = params.get('library_id', None) + folder_id = params.get('folder_id', None) + ldda_id = params.get('ldda_id', None) + is_admin = trans.user_is_admin() and cntrller in ['library_admin', 'requests_admin'] current_user_roles = trans.get_current_user_roles() elif in_sample_tracking: - request_type_id = params.get( 'request_type_id', None ) - sample_id = params.get( 'sample_id', None ) + request_type_id = params.get('request_type_id', None) + sample_id = params.get('sample_id', None) try: if in_library: - item, item_desc, action, id = self.get_item_and_stuff( trans, - item_type=item_type, - library_id=library_id, - folder_id=folder_id, - ldda_id=ldda_id, - is_admin=is_admin ) + item, item_desc, action, id = self.get_item_and_stuff(trans, + item_type=item_type, + library_id=library_id, + folder_id=folder_id, + ldda_id=ldda_id, + is_admin=is_admin) elif in_sample_tracking: - item, item_desc, action, id = self.get_item_and_stuff( trans, - item_type=item_type, - request_type_id=request_type_id, - sample_id=sample_id ) + item, item_desc, action, id = self.get_item_and_stuff(trans, + item_type=item_type, + request_type_id=request_type_id, + sample_id=sample_id) except ValueError: return None if in_library: - if not ( is_admin or trans.app.security_agent.can_modify_library_item( current_user_roles, item ) ): - message = "You are not authorized to modify %s '%s'." % ( item_desc, item.name ) - return trans.response.send_redirect( web.url_for( controller='library_common', - action='browse_library', - cntrller=cntrller, - id=library_id, - show_deleted=show_deleted, - message=util.sanitize_text( message ), - status='error' ) ) + if not (is_admin or trans.app.security_agent.can_modify_library_item(current_user_roles, item)): + message = "You are not authorized to modify %s '%s'." % (item_desc, item.name) + return trans.response.send_redirect(web.url_for(controller='library_common', + action='browse_library', + cntrller=cntrller, + id=library_id, + show_deleted=show_deleted, + message=util.sanitize_text(message), + status='error')) # An info_association must exist at this point if in_library: - info_association, inherited = item.get_info_association( restrict=True ) + info_association, inherited = item.get_info_association(restrict=True) elif in_sample_tracking: # Here run_details is a RequestTypeRunAssociation rtra = item.run_details @@ -1495,143 +1530,143 @@ def edit_template( self, trans, cntrller, item_type, form_type, **kwd ): if edited: # The form on which the template is based has been edited, so we need to update the # info_association with the current form - fdc = trans.sa_session.query( trans.app.model.FormDefinitionCurrent ).get( template.form_definition_current_id ) + fdc = trans.sa_session.query(trans.app.model.FormDefinitionCurrent).get(template.form_definition_current_id) info_association.template = fdc.latest_form - trans.sa_session.add( info_association ) + trans.sa_session.add(info_association) trans.sa_session.flush() message = "The template for this %s has been updated with your changes." % item_desc - new_kwd = dict( action=action, - cntrller=cntrller, - id=id, - message=util.sanitize_text( message ), - status='done' ) + new_kwd = dict(action=action, + cntrller=cntrller, + id=id, + message=util.sanitize_text(message), + status='done') if in_library: - new_kwd.update( dict( controller='library_common', - use_panels=use_panels, - library_id=library_id, - folder_id=folder_id, - show_deleted=show_deleted ) ) - return trans.response.send_redirect( web.url_for( **new_kwd ) ) + new_kwd.update(dict(controller='library_common', + use_panels=use_panels, + library_id=library_id, + folder_id=folder_id, + show_deleted=show_deleted)) + return trans.response.send_redirect(web.url_for(**new_kwd)) elif in_sample_tracking: - new_kwd.update( dict( controller='request_type', - request_type_id=request_type_id, - sample_id=sample_id ) ) - return trans.response.send_redirect( web.url_for( **new_kwd ) ) + new_kwd.update(dict(controller='request_type', + request_type_id=request_type_id, + sample_id=sample_id)) + return trans.response.send_redirect(web.url_for(**new_kwd)) # "template" is a FormDefinition, so since we're changing it, we need to use the latest version of it. - vars = dict( id=trans.security.encode_id( template.form_definition_current_id ), - response_redirect=web.url_for( controller='request_type', - action='edit_template', - cntrller=cntrller, - item_type=item_type, - form_type=form_type, - edited=True, - **kwd ) ) - return trans.response.send_redirect( web.url_for( controller='forms', action='edit_form_definition', **vars ) ) + vars = dict(id=trans.security.encode_id(template.form_definition_current_id), + response_redirect=web.url_for(controller='request_type', + action='edit_template', + cntrller=cntrller, + item_type=item_type, + form_type=form_type, + edited=True, + **kwd)) + return trans.response.send_redirect(web.url_for(controller='forms', action='edit_form_definition', **vars)) @web.expose - def edit_template_info( self, trans, cntrller, item_type, form_type, **kwd ): + def edit_template_info(self, trans, cntrller, item_type, form_type, **kwd): # Edit the contents of the template fields without altering the template itself. - params = util.Params( kwd ) + params = util.Params(kwd) # form_type must be one of: RUN_DETAILS_TEMPLATE, LIBRARY_INFO_TEMPLATE in_library = form_type == trans.model.FormDefinition.types.LIBRARY_INFO_TEMPLATE in_sample_tracking = form_type == trans.model.FormDefinition.types.RUN_DETAILS_TEMPLATE if in_library: - library_id = params.get( 'library_id', None ) - folder_id = params.get( 'folder_id', None ) - ldda_id = params.get( 'ldda_id', None ) - show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) ) - use_panels = util.string_as_bool( params.get( 'use_panels', False ) ) - is_admin = ( trans.user_is_admin() and cntrller == 'library_admin' ) + library_id = params.get('library_id', None) + folder_id = params.get('folder_id', None) + ldda_id = params.get('ldda_id', None) + show_deleted = util.string_as_bool(params.get('show_deleted', False)) + use_panels = util.string_as_bool(params.get('use_panels', False)) + is_admin = (trans.user_is_admin() and cntrller == 'library_admin') current_user_roles = trans.get_current_user_roles() elif in_sample_tracking: - request_type_id = params.get( 'request_type_id', None ) - sample_id = params.get( 'sample_id', None ) - sample = trans.sa_session.query( trans.model.Sample ).get( trans.security.decode_id( sample_id ) ) - message = util.restore_text( params.get( 'message', '' ) ) + request_type_id = params.get('request_type_id', None) + sample_id = params.get('sample_id', None) + sample = trans.sa_session.query(trans.model.Sample).get(trans.security.decode_id(sample_id)) + message = util.restore_text(params.get('message', '')) try: if in_library: - item, item_desc, action, id = self.get_item_and_stuff( trans, - item_type=item_type, - library_id=library_id, - folder_id=folder_id, - ldda_id=ldda_id, - is_admin=is_admin ) + item, item_desc, action, id = self.get_item_and_stuff(trans, + item_type=item_type, + library_id=library_id, + folder_id=folder_id, + ldda_id=ldda_id, + is_admin=is_admin) elif in_sample_tracking: - item, item_desc, action, id = self.get_item_and_stuff( trans, - item_type=item_type, - request_type_id=request_type_id, - sample_id=sample_id ) + item, item_desc, action, id = self.get_item_and_stuff(trans, + item_type=item_type, + request_type_id=request_type_id, + sample_id=sample_id) except ValueError: if cntrller == 'api': trans.response.status = 400 return None return None if in_library: - if not ( is_admin or trans.app.security_agent.can_modify_library_item( current_user_roles, item ) ): - message = "You are not authorized to modify %s '%s'." % ( item_desc, item.name ) + if not (is_admin or trans.app.security_agent.can_modify_library_item(current_user_roles, item)): + message = "You are not authorized to modify %s '%s'." % (item_desc, item.name) if cntrller == 'api': trans.response.status = 400 return message - return trans.response.send_redirect( web.url_for( controller='library_common', - action='browse_library', - cntrller=cntrller, - id=library_id, - show_deleted=show_deleted, - message=util.sanitize_text( message ), - status='error' ) ) + return trans.response.send_redirect(web.url_for(controller='library_common', + action='browse_library', + cntrller=cntrller, + id=library_id, + show_deleted=show_deleted, + message=util.sanitize_text(message), + status='error')) # We need the type of each template field widget - widgets = item.get_template_widgets( trans ) + widgets = item.get_template_widgets(trans) # The list of widgets may include an AddressField which we need to save if it is new for widget_dict in widgets: - widget = widget_dict[ 'widget' ] - if isinstance( widget, AddressField ): - value = util.restore_text( params.get( widget.name, '' ) ) + widget = widget_dict['widget'] + if isinstance(widget, AddressField): + value = util.restore_text(params.get(widget.name, '')) if value == 'new': - if params.get( 'edit_info_button', False ): - if self.field_param_values_ok( widget.name, 'AddressField', **kwd ): + if params.get('edit_info_button', False): + if self.field_param_values_ok(widget.name, 'AddressField', **kwd): # Save the new address - address = trans.app.model.UserAddress( user=trans.user ) - self.save_widget_field( trans, address, widget.name, **kwd ) - widget.value = str( address.id ) + address = trans.app.model.UserAddress(user=trans.user) + self.save_widget_field(trans, address, widget.name, **kwd) + widget.value = str(address.id) else: message = 'Required fields are missing contents.' if cntrller == 'api': trans.response.status = 400 return message - new_kwd = dict( action=action, - id=id, - message=util.sanitize_text( message ), - status='error' ) + new_kwd = dict(action=action, + id=id, + message=util.sanitize_text(message), + status='error') if in_library: - new_kwd.update( dict( controller='library_common', - cntrller=cntrller, - use_panels=use_panels, - library_id=library_id, - folder_id=folder_id, - show_deleted=show_deleted ) ) - return trans.response.send_redirect( web.url_for( **new_kwd ) ) + new_kwd.update(dict(controller='library_common', + cntrller=cntrller, + use_panels=use_panels, + library_id=library_id, + folder_id=folder_id, + show_deleted=show_deleted)) + return trans.response.send_redirect(web.url_for(**new_kwd)) if in_sample_tracking: - new_kwd.update( dict( controller='request_type', - request_type_id=request_type_id, - sample_id=sample_id ) ) - return trans.response.send_redirect( web.url_for( **new_kwd ) ) + new_kwd.update(dict(controller='request_type', + request_type_id=request_type_id, + sample_id=sample_id)) + return trans.response.send_redirect(web.url_for(**new_kwd)) else: # Form was submitted via refresh_on_change widget.value = 'new' - elif value == text_type( 'none' ): + elif value == text_type('none'): widget.value = '' else: widget.value = value - elif isinstance( widget, CheckboxField ): + elif isinstance(widget, CheckboxField): # We need to check the value from kwd since util.Params would have munged the list if # the checkbox is checked. - value = kwd.get( widget.name, '' ) - if CheckboxField.is_checked( value ): + value = kwd.get(widget.name, '') + if CheckboxField.is_checked(value): widget.value = 'true' else: - widget.value = util.restore_text( params.get( widget.name, '' ) ) + widget.value = util.restore_text(params.get(widget.name, '')) # Save updated template field contents - field_contents = self.clean_field_contents( widgets, **kwd ) + field_contents = self.clean_field_contents(widgets, **kwd) if field_contents: if in_library: # In in a library, since information templates are inherited, the template fields can be displayed @@ -1639,7 +1674,7 @@ def edit_template_info( self, trans, cntrller, item_type, form_type, **kwd ): # has added field contents on an inherited template via a parent's info_association, we'll need to # create a new form_values and info_association for the current object. The value for the returned # inherited variable is not applicable at this level. - info_association, inherited = item.get_info_association( restrict=True ) + info_association, inherited = item.get_info_association(restrict=True) elif in_sample_tracking: assoc = item.run_details if item_type == 'request_type' and assoc: @@ -1650,16 +1685,16 @@ def edit_template_info( self, trans, cntrller, item_type, form_type, **kwd ): # Sample has one. If the Sample does not have a SampleRunAssociation, assoc will # be the Sample's RequestType RequestTypeRunAssociation, in which case we need to # create a SampleRunAssociation using the inherited template from the RequestType. - if isinstance( assoc, trans.model.RequestTypeRunAssociation ): + if isinstance(assoc, trans.model.RequestTypeRunAssociation): form_definition = assoc.run.template - new_form_values = trans.model.FormValues( form_definition, {} ) - trans.sa_session.add( new_form_values ) + new_form_values = trans.model.FormValues(form_definition, {}) + trans.sa_session.add(new_form_values) trans.sa_session.flush() - new_run = trans.model.Run( form_definition, new_form_values ) - trans.sa_session.add( new_run ) + new_run = trans.model.Run(form_definition, new_form_values) + trans.sa_session.add(new_run) trans.sa_session.flush() - sra = trans.model.SampleRunAssociation( item, new_run ) - trans.sa_session.add( sra ) + sra = trans.model.SampleRunAssociation(item, new_run) + trans.sa_session.add(sra) trans.sa_session.flush() info_association = sra.run else: @@ -1669,19 +1704,19 @@ def edit_template_info( self, trans, cntrller, item_type, form_type, **kwd ): if info_association: template = info_association.template info = info_association.info - form_values = trans.sa_session.query( trans.app.model.FormValues ).get( info.id ) + form_values = trans.sa_session.query(trans.app.model.FormValues).get(info.id) # Update existing content only if it has changed flush_required = False for field_contents_key, field_contents_value in field_contents.items(): if field_contents_key in form_values.content: - if form_values.content[ field_contents_key ] != field_contents_value: + if form_values.content[field_contents_key] != field_contents_value: flush_required = True - form_values.content[ field_contents_key ] = field_contents_value + form_values.content[field_contents_key] = field_contents_value else: flush_required = True - form_values.content[ field_contents_key ] = field_contents_value + form_values.content[field_contents_key] = field_contents_value if flush_required: - trans.sa_session.add( form_values ) + trans.sa_session.add(form_values) trans.sa_session.flush() else: if in_library: @@ -1689,8 +1724,8 @@ def edit_template_info( self, trans, cntrller, item_type, form_type, **kwd ): info_association, inherited = item.get_info_association() template = info_association.template # Create a new FormValues object - form_values = trans.app.model.FormValues( template, field_contents ) - trans.sa_session.add( form_values ) + form_values = trans.app.model.FormValues(template, field_contents) + trans.sa_session.add(form_values) trans.sa_session.flush() # Create a new info_association between the current library item and form_values if item_type == 'folder': @@ -1699,77 +1734,77 @@ def edit_template_info( self, trans, cntrller, item_type, form_type, **kwd ): # inheritance to be False for each level in the Library hierarchy unless we're creating a new # level in the hierarchy, in which case we'll inherit the "inheritable" setting from the parent # level. - info_association = trans.app.model.LibraryFolderInfoAssociation( item, template, form_values, inheritable=inherited ) - trans.sa_session.add( info_association ) + info_association = trans.app.model.LibraryFolderInfoAssociation(item, template, form_values, inheritable=inherited) + trans.sa_session.add(info_association) trans.sa_session.flush() elif item_type == 'ldda': - info_association = trans.app.model.LibraryDatasetDatasetInfoAssociation( item, template, form_values ) - trans.sa_session.add( info_association ) + info_association = trans.app.model.LibraryDatasetDatasetInfoAssociation(item, template, form_values) + trans.sa_session.add(info_association) trans.sa_session.flush() message = 'The information has been updated.' if cntrller == 'api': return 200, message - new_kwd = dict( action=action, - cntrller=cntrller, - id=id, - message=util.sanitize_text( message ), - status='done' ) + new_kwd = dict(action=action, + cntrller=cntrller, + id=id, + message=util.sanitize_text(message), + status='done') if in_library: - new_kwd.update( dict( controller='library_common', - use_panels=use_panels, - library_id=library_id, - folder_id=folder_id, - show_deleted=show_deleted ) ) + new_kwd.update(dict(controller='library_common', + use_panels=use_panels, + library_id=library_id, + folder_id=folder_id, + show_deleted=show_deleted)) if in_sample_tracking: - new_kwd.update( dict( controller='requests_common', - cntrller='requests_admin', - id=trans.security.encode_id( sample.id ), - sample_id=sample_id ) ) - return trans.response.send_redirect( web.url_for( **new_kwd ) ) + new_kwd.update(dict(controller='requests_common', + cntrller='requests_admin', + id=trans.security.encode_id(sample.id), + sample_id=sample_id)) + return trans.response.send_redirect(web.url_for(**new_kwd)) @web.expose - def delete_template( self, trans, cntrller, item_type, form_type, **kwd ): - params = util.Params( kwd ) + def delete_template(self, trans, cntrller, item_type, form_type, **kwd): + params = util.Params(kwd) # form_type must be one of: RUN_DETAILS_TEMPLATE, LIBRARY_INFO_TEMPLATE in_library = form_type == trans.model.FormDefinition.types.LIBRARY_INFO_TEMPLATE in_sample_tracking = form_type == trans.model.FormDefinition.types.RUN_DETAILS_TEMPLATE if in_library: - is_admin = ( trans.user_is_admin() and cntrller == 'library_admin' ) + is_admin = (trans.user_is_admin() and cntrller == 'library_admin') current_user_roles = trans.get_current_user_roles() - show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) ) - use_panels = util.string_as_bool( params.get( 'use_panels', False ) ) - library_id = params.get( 'library_id', None ) - folder_id = params.get( 'folder_id', None ) - ldda_id = params.get( 'ldda_id', None ) + show_deleted = util.string_as_bool(params.get('show_deleted', False)) + use_panels = util.string_as_bool(params.get('use_panels', False)) + library_id = params.get('library_id', None) + folder_id = params.get('folder_id', None) + ldda_id = params.get('ldda_id', None) elif in_sample_tracking: - request_type_id = params.get( 'request_type_id', None ) - sample_id = params.get( 'sample_id', None ) - message = util.restore_text( params.get( 'message', '' ) ) + request_type_id = params.get('request_type_id', None) + sample_id = params.get('sample_id', None) + message = util.restore_text(params.get('message', '')) try: if in_library: - item, item_desc, action, id = self.get_item_and_stuff( trans, - item_type=item_type, - library_id=library_id, - folder_id=folder_id, - ldda_id=ldda_id, - is_admin=is_admin ) + item, item_desc, action, id = self.get_item_and_stuff(trans, + item_type=item_type, + library_id=library_id, + folder_id=folder_id, + ldda_id=ldda_id, + is_admin=is_admin) elif in_sample_tracking: - item, item_desc, action, id = self.get_item_and_stuff( trans, - item_type=item_type, - request_type_id=request_type_id, - sample_id=sample_id ) + item, item_desc, action, id = self.get_item_and_stuff(trans, + item_type=item_type, + request_type_id=request_type_id, + sample_id=sample_id) except ValueError: return None if in_library: - if not ( is_admin or trans.app.security_agent.can_modify_library_item( current_user_roles, item ) ): - message = "You are not authorized to modify %s '%s'." % ( item_desc, item.name ) - return trans.response.send_redirect( web.url_for( controller='library_common', - action='browse_library', - cntrller=cntrller, - id=library_id, - show_deleted=show_deleted, - message=util.sanitize_text( message ), - status='error' ) ) + if not (is_admin or trans.app.security_agent.can_modify_library_item(current_user_roles, item)): + message = "You are not authorized to modify %s '%s'." % (item_desc, item.name) + return trans.response.send_redirect(web.url_for(controller='library_common', + action='browse_library', + cntrller=cntrller, + id=library_id, + show_deleted=show_deleted, + message=util.sanitize_text(message), + status='error')) if in_library: info_association, inherited = item.get_info_association() elif in_sample_tracking: @@ -1779,216 +1814,216 @@ def delete_template( self, trans, cntrller, item_type, form_type, **kwd ): else: if in_library: info_association.deleted = True - trans.sa_session.add( info_association ) + trans.sa_session.add(info_association) trans.sa_session.flush() elif in_sample_tracking: - trans.sa_session.delete( info_association ) + trans.sa_session.delete(info_association) trans.sa_session.flush() message = 'The template for this %s has been deleted.' % item_type - new_kwd = dict( action=action, - cntrller=cntrller, - id=id, - message=util.sanitize_text( message ), - status='done' ) + new_kwd = dict(action=action, + cntrller=cntrller, + id=id, + message=util.sanitize_text(message), + status='done') if in_library: - new_kwd.update( dict( controller='library_common', - use_panels=use_panels, - library_id=library_id, - folder_id=folder_id, - show_deleted=show_deleted ) ) - return trans.response.send_redirect( web.url_for( **new_kwd ) ) + new_kwd.update(dict(controller='library_common', + use_panels=use_panels, + library_id=library_id, + folder_id=folder_id, + show_deleted=show_deleted)) + return trans.response.send_redirect(web.url_for(**new_kwd)) if in_sample_tracking: - new_kwd.update( dict( controller='request_type', - request_type_id=request_type_id, - sample_id=sample_id ) ) - return trans.response.send_redirect( web.url_for( **new_kwd ) ) + new_kwd.update(dict(controller='request_type', + request_type_id=request_type_id, + sample_id=sample_id)) + return trans.response.send_redirect(web.url_for(**new_kwd)) - def widget_fields_have_contents( self, widgets ): + def widget_fields_have_contents(self, widgets): # Return True if any of the fields in widgets contain contents, widgets is a list of dictionaries that looks something like: # [{'widget': , 'helptext': 'Field 0 help (Optional)', 'label': 'Field 0'}] for field in widgets: - if ( isinstance( field[ 'widget' ], TextArea ) or isinstance( field[ 'widget' ], TextField ) ) and field[ 'widget' ].value: + if (isinstance(field['widget'], TextArea) or isinstance(field['widget'], TextField)) and field['widget'].value: return True - if isinstance( field[ 'widget' ], SelectField ) and field[ 'widget' ].options: - for option_label, option_value, selected in field[ 'widget' ].options: + if isinstance(field['widget'], SelectField) and field['widget'].options: + for option_label, option_value, selected in field['widget'].options: if selected: return True - if isinstance( field[ 'widget' ], CheckboxField ) and field[ 'widget' ].checked: + if isinstance(field['widget'], CheckboxField) and field['widget'].checked: return True - if isinstance( field[ 'widget' ], WorkflowField ) and str( field[ 'widget' ].value ).lower() not in [ 'none' ]: + if isinstance(field['widget'], WorkflowField) and str(field['widget'].value).lower() not in ['none']: return True - if isinstance( field[ 'widget' ], WorkflowMappingField ) and str( field[ 'widget' ].value ).lower() not in [ 'none' ]: + if isinstance(field['widget'], WorkflowMappingField) and str(field['widget'].value).lower() not in ['none']: return True - if isinstance( field[ 'widget' ], HistoryField ) and str( field[ 'widget' ].value ).lower() not in [ 'none' ]: + if isinstance(field['widget'], HistoryField) and str(field['widget'].value).lower() not in ['none']: return True - if isinstance( field[ 'widget' ], AddressField ) and str( field[ 'widget' ].value ).lower() not in [ 'none' ]: + if isinstance(field['widget'], AddressField) and str(field['widget'].value).lower() not in ['none']: return True return False - def clean_field_contents( self, widgets, **kwd ): + def clean_field_contents(self, widgets, **kwd): field_contents = {} for widget_dict in widgets: - widget = widget_dict[ 'widget' ] - value = kwd.get( widget.name, '' ) - if isinstance( widget, CheckboxField ): + widget = widget_dict['widget'] + value = kwd.get(widget.name, '') + if isinstance(widget, CheckboxField): # CheckboxField values are lists if the checkbox is checked - value = str( widget.is_checked( value ) ).lower() - elif isinstance( widget, AddressField ): + value = str(widget.is_checked(value)).lower() + elif isinstance(widget, AddressField): # If the address was new, is has already been saved and widget.value is the new address.id value = widget.value - field_contents[ widget.name ] = util.restore_text( value ) + field_contents[widget.name] = util.restore_text(value) return field_contents - def field_param_values_ok( self, widget_name, widget_type, **kwd ): + def field_param_values_ok(self, widget_name, widget_type, **kwd): # Make sure required fields have contents, etc - params = util.Params( kwd ) + params = util.Params(kwd) if widget_type == 'AddressField': - if not util.restore_text( params.get( '%s_short_desc' % widget_name, '' ) ) \ - or not util.restore_text( params.get( '%s_name' % widget_name, '' ) ) \ - or not util.restore_text( params.get( '%s_institution' % widget_name, '' ) ) \ - or not util.restore_text( params.get( '%s_address' % widget_name, '' ) ) \ - or not util.restore_text( params.get( '%s_city' % widget_name, '' ) ) \ - or not util.restore_text( params.get( '%s_state' % widget_name, '' ) ) \ - or not util.restore_text( params.get( '%s_postal_code' % widget_name, '' ) ) \ - or not util.restore_text( params.get( '%s_country' % widget_name, '' ) ): + if not util.restore_text(params.get('%s_short_desc' % widget_name, '')) \ + or not util.restore_text(params.get('%s_name' % widget_name, '')) \ + or not util.restore_text(params.get('%s_institution' % widget_name, '')) \ + or not util.restore_text(params.get('%s_address' % widget_name, '')) \ + or not util.restore_text(params.get('%s_city' % widget_name, '')) \ + or not util.restore_text(params.get('%s_state' % widget_name, '')) \ + or not util.restore_text(params.get('%s_postal_code' % widget_name, '')) \ + or not util.restore_text(params.get('%s_country' % widget_name, '')): return False return True - def save_widget_field( self, trans, field_obj, widget_name, **kwd ): + def save_widget_field(self, trans, field_obj, widget_name, **kwd): # Save a form_builder field object - params = util.Params( kwd ) - if isinstance( field_obj, trans.model.UserAddress ): - field_obj.desc = util.restore_text( params.get( '%s_short_desc' % widget_name, '' ) ) - field_obj.name = util.restore_text( params.get( '%s_name' % widget_name, '' ) ) - field_obj.institution = util.restore_text( params.get( '%s_institution' % widget_name, '' ) ) - field_obj.address = util.restore_text( params.get( '%s_address' % widget_name, '' ) ) - field_obj.city = util.restore_text( params.get( '%s_city' % widget_name, '' ) ) - field_obj.state = util.restore_text( params.get( '%s_state' % widget_name, '' ) ) - field_obj.postal_code = util.restore_text( params.get( '%s_postal_code' % widget_name, '' ) ) - field_obj.country = util.restore_text( params.get( '%s_country' % widget_name, '' ) ) - field_obj.phone = util.restore_text( params.get( '%s_phone' % widget_name, '' ) ) - trans.sa_session.add( field_obj ) + params = util.Params(kwd) + if isinstance(field_obj, trans.model.UserAddress): + field_obj.desc = util.restore_text(params.get('%s_short_desc' % widget_name, '')) + field_obj.name = util.restore_text(params.get('%s_name' % widget_name, '')) + field_obj.institution = util.restore_text(params.get('%s_institution' % widget_name, '')) + field_obj.address = util.restore_text(params.get('%s_address' % widget_name, '')) + field_obj.city = util.restore_text(params.get('%s_city' % widget_name, '')) + field_obj.state = util.restore_text(params.get('%s_state' % widget_name, '')) + field_obj.postal_code = util.restore_text(params.get('%s_postal_code' % widget_name, '')) + field_obj.country = util.restore_text(params.get('%s_country' % widget_name, '')) + field_obj.phone = util.restore_text(params.get('%s_phone' % widget_name, '')) + trans.sa_session.add(field_obj) trans.sa_session.flush() - def get_form_values( self, trans, user, form_definition, **kwd ): + def get_form_values(self, trans, user, form_definition, **kwd): ''' Returns the name:value dictionary containing all the form values ''' - params = util.Params( kwd ) + params = util.Params(kwd) values = {} for field in form_definition.fields: - field_type = field[ 'type' ] - field_name = field[ 'name' ] - input_value = params.get( field_name, '' ) + field_type = field['type'] + field_name = field['name'] + input_value = params.get(field_name, '') if field_type == AddressField.__name__: - input_text_value = util.restore_text( input_value ) + input_text_value = util.restore_text(input_value) if input_text_value == 'new': # Save this new address in the list of this user's addresses - user_address = trans.model.UserAddress( user=user ) - self.save_widget_field( trans, user_address, field_name, **kwd ) - trans.sa_session.refresh( user ) - field_value = int( user_address.id ) - elif input_text_value in [ '', 'none', 'None', None ]: + user_address = trans.model.UserAddress(user=user) + self.save_widget_field(trans, user_address, field_name, **kwd) + trans.sa_session.refresh(user) + field_value = int(user_address.id) + elif input_text_value in ['', 'none', 'None', None]: field_value = '' else: - field_value = int( input_text_value ) + field_value = int(input_text_value) elif field_type == CheckboxField.__name__: - field_value = CheckboxField.is_checked( input_value ) + field_value = CheckboxField.is_checked(input_value) elif field_type == PasswordField.__name__: - field_value = kwd.get( field_name, '' ) + field_value = kwd.get(field_name, '') else: - field_value = util.restore_text( input_value ) - values[ field_name ] = field_value + field_value = util.restore_text(input_value) + values[field_name] = field_value return values - def populate_widgets_from_kwd( self, trans, widgets, **kwd ): + def populate_widgets_from_kwd(self, trans, widgets, **kwd): # A form submitted via refresh_on_change requires us to populate the widgets with the contents of # the form fields the user may have entered so that when the form refreshes the contents are retained. - params = util.Params( kwd ) + params = util.Params(kwd) populated_widgets = [] for widget_dict in widgets: - widget = widget_dict[ 'widget' ] - if params.get( widget.name, False ): + widget = widget_dict['widget'] + if params.get(widget.name, False): # The form included a field whose contents should be used to set the # value of the current widget (widget.name is the name set by the # user when they defined the FormDefinition). - if isinstance( widget, AddressField ): - value = util.restore_text( params.get( widget.name, '' ) ) + if isinstance(widget, AddressField): + value = util.restore_text(params.get(widget.name, '')) if value == 'none': value = '' widget.value = value - widget_dict[ 'widget' ] = widget + widget_dict['widget'] = widget # Populate the AddressField params with the form field contents widget_params_dict = {} for field_name, label, help_text in widget.fields(): - form_param_name = '%s_%s' % ( widget.name, field_name ) - widget_params_dict[ form_param_name ] = util.restore_text( params.get( form_param_name, '' ) ) + form_param_name = '%s_%s' % (widget.name, field_name) + widget_params_dict[form_param_name] = util.restore_text(params.get(form_param_name, '')) widget.params = widget_params_dict - elif isinstance( widget, CheckboxField ): + elif isinstance(widget, CheckboxField): # Check the value from kwd since util.Params would have # stringify'd the list if the checkbox is checked. - value = kwd.get( widget.name, '' ) - if CheckboxField.is_checked( value ): + value = kwd.get(widget.name, '') + if CheckboxField.is_checked(value): widget.value = 'true' - widget_dict[ 'widget' ] = widget - elif isinstance( widget, SelectField ): + widget_dict['widget'] = widget + elif isinstance(widget, SelectField): # Ensure the selected option remains selected. - value = util.restore_text( params.get( widget.name, '' ) ) + value = util.restore_text(params.get(widget.name, '')) processed_options = [] for option_label, option_value, option_selected in widget.options: selected = value == option_value - processed_options.append( ( option_label, option_value, selected ) ) + processed_options.append((option_label, option_value, selected)) widget.options = processed_options else: - widget.value = util.restore_text( params.get( widget.name, '' ) ) - widget_dict[ 'widget' ] = widget - populated_widgets.append( widget_dict ) + widget.value = util.restore_text(params.get(widget.name, '')) + widget_dict['widget'] = widget + populated_widgets.append(widget_dict) return populated_widgets - def get_item_and_stuff( self, trans, item_type, **kwd ): + def get_item_and_stuff(self, trans, item_type, **kwd): # Return an item, description, action and an id based on the item_type. Valid item_types are # library, folder, ldda, request_type, sample. if item_type == 'library': - library_id = kwd.get( 'library_id', None ) + library_id = kwd.get('library_id', None) id = library_id try: - item = trans.sa_session.query( trans.app.model.Library ).get( trans.security.decode_id( library_id ) ) + item = trans.sa_session.query(trans.app.model.Library).get(trans.security.decode_id(library_id)) except Exception: item = None item_desc = 'data library' action = 'library_info' elif item_type == 'folder': - folder_id = kwd.get( 'folder_id', None ) + folder_id = kwd.get('folder_id', None) id = folder_id try: - item = trans.sa_session.query( trans.app.model.LibraryFolder ).get( trans.security.decode_id( folder_id ) ) + item = trans.sa_session.query(trans.app.model.LibraryFolder).get(trans.security.decode_id(folder_id)) except Exception: item = None item_desc = 'folder' action = 'folder_info' elif item_type == 'ldda': - ldda_id = kwd.get( 'ldda_id', None ) + ldda_id = kwd.get('ldda_id', None) id = ldda_id try: - item = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( trans.security.decode_id( ldda_id ) ) + item = trans.sa_session.query(trans.app.model.LibraryDatasetDatasetAssociation).get(trans.security.decode_id(ldda_id)) except Exception: item = None item_desc = 'dataset' action = 'ldda_edit_info' elif item_type == 'request_type': - request_type_id = kwd.get( 'request_type_id', None ) + request_type_id = kwd.get('request_type_id', None) id = request_type_id try: - item = trans.sa_session.query( trans.app.model.RequestType ).get( trans.security.decode_id( request_type_id ) ) + item = trans.sa_session.query(trans.app.model.RequestType).get(trans.security.decode_id(request_type_id)) except Exception: item = None item_desc = 'request type' action = 'view_editable_request_type' elif item_type == 'sample': - sample_id = kwd.get( 'sample_id', None ) + sample_id = kwd.get('sample_id', None) id = sample_id try: - item = trans.sa_session.query( trans.app.model.Sample ).get( trans.security.decode_id( sample_id ) ) + item = trans.sa_session.query(trans.app.model.Sample).get(trans.security.decode_id(sample_id)) except Exception: item = None item_desc = 'sample' @@ -2001,13 +2036,13 @@ def get_item_and_stuff( self, trans, item_type, **kwd ): id = None return item, item_desc, action, id - def build_form_id_select_field( self, trans, forms, selected_value='none' ): - return build_select_field( trans, - objs=forms, - label_attr='name', - select_field_name='form_id', - selected_value=selected_value, - refresh_on_change=True ) + def build_form_id_select_field(self, trans, forms, selected_value='none'): + return build_select_field(trans, + objs=forms, + label_attr='name', + select_field_name='form_id', + selected_value=selected_value, + refresh_on_change=True) class SharableMixin: @@ -2015,44 +2050,44 @@ class SharableMixin: # -- Implemented methods. -- - def _is_valid_slug( self, slug ): + def _is_valid_slug(self, slug): """ Returns true if slug is valid. """ - return _is_valid_slug( slug ) + return _is_valid_slug(slug) @web.expose - @web.require_login( "share Galaxy items" ) - def set_public_username( self, trans, id, username, **kwargs ): + @web.require_login("share Galaxy items") + def set_public_username(self, trans, id, username, **kwargs): """ Set user's public username and delegate to sharing() """ user = trans.get_user() # message from validate_publicname does not contain input, no need # to escape. - message = validate_publicname( trans, username, user ) + message = validate_publicname(trans, username, user) if message: - return trans.fill_template( '/sharing_base.mako', item=self.get_item( trans, id ), message=message, status='error' ) + return trans.fill_template('/sharing_base.mako', item=self.get_item(trans, id), message=message, status='error') user.username = username trans.sa_session.flush() - return self.sharing( trans, id, **kwargs ) + return self.sharing(trans, id, **kwargs) @web.expose - @web.require_login( "modify Galaxy items" ) - def set_slug_async( self, trans, id, new_slug ): - item = self.get_item( trans, id ) + @web.require_login("modify Galaxy items") + def set_slug_async(self, trans, id, new_slug): + item = self.get_item(trans, id) if item: # Only update slug if slug is not already in use. - if trans.sa_session.query( item.__class__ ).filter_by( user=item.user, slug=new_slug ).count() == 0: + if trans.sa_session.query(item.__class__).filter_by(user=item.user, slug=new_slug).count() == 0: item.slug = new_slug trans.sa_session.flush() return item.slug - def _make_item_accessible( self, sa_session, item ): + def _make_item_accessible(self, sa_session, item): """ Makes item accessible--viewable and importable--and sets item's slug. Does not flush/commit changes, however. Item must have name, user, importable, and slug attributes. """ item.importable = True - self.create_item_slug( sa_session, item ) + self.create_item_slug(sa_session, item) - def create_item_slug( self, sa_session, item ): + def create_item_slug(self, sa_session, item): """ Create/set item slug. Slug is unique among user's importable items for item's class. Returns true if item's slug was set/changed; false otherwise. @@ -2062,11 +2097,11 @@ def create_item_slug( self, sa_session, item ): # Setup slug base. if cur_slug is None or cur_slug == "": # Item can have either a name or a title. - if hasattr( item, 'name' ): + if hasattr(item, 'name'): item_name = item.name - elif hasattr( item, 'title' ): + elif hasattr(item, 'title'): item_name = item.title - slug_base = util.ready_name_for_url( item_name.lower() ) + slug_base = util.ready_name_for_url(item_name.lower()) else: slug_base = cur_slug @@ -2076,10 +2111,10 @@ def create_item_slug( self, sa_session, item ): count = 1 # Ensure unique across model class and user and don't include this item # in the check in case it has previously been assigned a valid slug. - while sa_session.query( item.__class__ ).filter( item.__class__.user == item.user, item.__class__.slug == new_slug, item.__class__.id != item.id).count() != 0: + while sa_session.query(item.__class__).filter(item.__class__.user == item.user, item.__class__.slug == new_slug, item.__class__.id != item.id).count() != 0: # Slug taken; choose a new slug based on count. This approach can # handle numerous items with the same name gracefully. - new_slug = '%s-%i' % ( slug_base, count ) + new_slug = '%s-%i' % (slug_base, count) count += 1 # Set slug and return. @@ -2089,86 +2124,85 @@ def create_item_slug( self, sa_session, item ): # -- Abstract methods. -- @web.expose - @web.require_login( "share Galaxy items" ) - def sharing( self, trans, id, **kwargs ): + @web.require_login("share Galaxy items") + def sharing(self, trans, id, **kwargs): """ Handle item sharing. """ raise NotImplementedError() @web.expose - @web.require_login( "share Galaxy items" ) - def share( self, trans, id=None, email="", **kwd ): + @web.require_login("share Galaxy items") + def share(self, trans, id=None, email="", **kwd): """ Handle sharing an item with a particular user. """ raise NotImplementedError() @web.expose - def display_by_username_and_slug( self, trans, username, slug ): + def display_by_username_and_slug(self, trans, username, slug): """ Display item by username and slug. """ raise NotImplementedError() @web.json - @web.require_login( "get item name and link" ) - def get_name_and_link_async( self, trans, id=None ): + @web.require_login("get item name and link") + def get_name_and_link_async(self, trans, id=None): """ Returns item's name and link. """ raise NotImplementedError() @web.expose @web.require_login("get item content asynchronously") - def get_item_content_async( self, trans, id ): + def get_item_content_async(self, trans, id): """ Returns item content in HTML format. """ raise NotImplementedError() - def get_item( self, trans, id ): + def get_item(self, trans, id): """ Return item based on id. """ raise NotImplementedError() -class UsesQuotaMixin( object ): +class UsesQuotaMixin(object): - def get_quota( self, trans, id, check_ownership=False, check_accessible=False, deleted=None ): - return self.get_object( trans, id, 'Quota', check_ownership=False, check_accessible=False, deleted=deleted ) + def get_quota(self, trans, id, check_ownership=False, check_accessible=False, deleted=None): + return self.get_object(trans, id, 'Quota', check_ownership=False, check_accessible=False, deleted=deleted) -class UsesTagsMixin( SharableItemSecurityMixin ): +class UsesTagsMixin(SharableItemSecurityMixin): - def get_tag_handler( self, trans ): + def get_tag_handler(self, trans): return trans.app.tag_handler - def _get_user_tags( self, trans, item_class_name, id ): + def _get_user_tags(self, trans, item_class_name, id): user = trans.user - tagged_item = self._get_tagged_item( trans, item_class_name, id ) - return [ tag for tag in tagged_item.tags if tag.user == user ] + tagged_item = self._get_tagged_item(trans, item_class_name, id) + return [tag for tag in tagged_item.tags if tag.user == user] - def _get_tagged_item( self, trans, item_class_name, id, check_ownership=True ): - tagged_item = self.get_object( trans, id, item_class_name, check_ownership=check_ownership, check_accessible=True ) + def _get_tagged_item(self, trans, item_class_name, id, check_ownership=True): + tagged_item = self.get_object(trans, id, item_class_name, check_ownership=check_ownership, check_accessible=True) return tagged_item - def _remove_items_tag( self, trans, item_class_name, id, tag_name ): + def _remove_items_tag(self, trans, item_class_name, id, tag_name): """Remove a tag from an item.""" user = trans.user - tagged_item = self._get_tagged_item( trans, item_class_name, id ) - deleted = tagged_item and self.get_tag_handler( trans ).remove_item_tag( trans, user, tagged_item, tag_name ) + tagged_item = self._get_tagged_item(trans, item_class_name, id) + deleted = tagged_item and self.get_tag_handler(trans).remove_item_tag(trans, user, tagged_item, tag_name) trans.sa_session.flush() return deleted - def _apply_item_tag( self, trans, item_class_name, id, tag_name, tag_value=None ): + def _apply_item_tag(self, trans, item_class_name, id, tag_name, tag_value=None): user = trans.user - tagged_item = self._get_tagged_item( trans, item_class_name, id ) - tag_assoc = self.get_tag_handler( trans ).apply_item_tag( user, tagged_item, tag_name, tag_value ) + tagged_item = self._get_tagged_item(trans, item_class_name, id) + tag_assoc = self.get_tag_handler(trans).apply_item_tag(user, tagged_item, tag_name, tag_value) trans.sa_session.flush() return tag_assoc - def _get_item_tag_assoc( self, trans, item_class_name, id, tag_name ): + def _get_item_tag_assoc(self, trans, item_class_name, id, tag_name): user = trans.user - tagged_item = self._get_tagged_item( trans, item_class_name, id ) - log.debug( "In get_item_tag_assoc with tagged_item %s" % tagged_item ) - return self.get_tag_handler( trans )._get_item_tag_assoc( user, tagged_item, tag_name ) + tagged_item = self._get_tagged_item(trans, item_class_name, id) + log.debug("In get_item_tag_assoc with tagged_item %s" % tagged_item) + return self.get_tag_handler(trans)._get_item_tag_assoc(user, tagged_item, tag_name) - def set_tags_from_list( self, trans, item, new_tags_list, user=None ): - # Method deprecated - try to use TagsHandler instead. - tags_manager = tags.GalaxyTagManager( trans.app.model.context ) - return tags_manager.set_tags_from_list( user, item, new_tags_list ) + def set_tags_from_list(self, trans, item, new_tags_list, user=None): + tags_manager = tags.GalaxyTagManager(trans.app.model.context) + return tags_manager.set_tags_from_list(user, item, new_tags_list) - def get_user_tags_used( self, trans, user=None ): + def get_user_tags_used(self, trans, user=None): """ Return a list of distinct 'user_tname:user_value' strings that the given user has used. @@ -2182,13 +2216,13 @@ def get_user_tags_used( self, trans, user=None ): return [] # get all the taggable model TagAssociations - tag_models = [ v.tag_assoc_class for v in trans.app.tag_handler.item_tag_assoc_info.values() ] + tag_models = [v.tag_assoc_class for v in trans.app.tag_handler.item_tag_assoc_info.values()] # create a union of subqueries for each for this user - getting only the tname and user_value all_tags_query = None for tag_model in tag_models: - subq = ( trans.sa_session.query( tag_model.user_tname, tag_model.user_value ) - .filter( tag_model.user == trans.user ) ) - all_tags_query = subq if all_tags_query is None else all_tags_query.union( subq ) + subq = (trans.sa_session.query(tag_model.user_tname, tag_model.user_value) + .filter(tag_model.user == trans.user)) + all_tags_query = subq if all_tags_query is None else all_tags_query.union(subq) # if nothing init'd the query, bail if all_tags_query is None: @@ -2196,14 +2230,14 @@ def get_user_tags_used( self, trans, user=None ): # boil the tag tuples down into a sorted list of DISTINCT name:val strings tags = all_tags_query.distinct().all() - tags = [( ( name + ':' + val ) if val else name ) for name, val in tags ] - return sorted( tags ) + tags = [((name + ':' + val) if val else name) for name, val in tags] + return sorted(tags) -class UsesExtendedMetadataMixin( SharableItemSecurityMixin ): +class UsesExtendedMetadataMixin(SharableItemSecurityMixin): """ Mixin for getting and setting item extended metadata. """ - def get_item_extended_metadata_obj( self, trans, item ): + def get_item_extended_metadata_obj(self, trans, item): """ Given an item object (such as a LibraryDatasetDatasetAssociation), find the object of the associated extended metadata @@ -2212,32 +2246,32 @@ def get_item_extended_metadata_obj( self, trans, item ): return item.extended_metadata return None - def set_item_extended_metadata_obj( self, trans, item, extmeta_obj, check_writable=False): + def set_item_extended_metadata_obj(self, trans, item, extmeta_obj, check_writable=False): if item.__class__ == LibraryDatasetDatasetAssociation: - if not check_writable or trans.app.security_agent.can_modify_library_item( trans.get_current_user_roles(), item, trans.user ): + if not check_writable or trans.app.security_agent.can_modify_library_item(trans.get_current_user_roles(), item, trans.user): item.extended_metadata = extmeta_obj trans.sa_session.flush() if item.__class__ == HistoryDatasetAssociation: history = None if check_writable: - history = self.security_check( trans, item, check_ownership=True, check_accessible=True ) + history = self.security_check(trans, item, check_ownership=True, check_accessible=True) else: - history = self.security_check( trans, item, check_ownership=False, check_accessible=True ) + history = self.security_check(trans, item, check_ownership=False, check_accessible=True) if history: item.extended_metadata = extmeta_obj trans.sa_session.flush() - def unset_item_extended_metadata_obj( self, trans, item, check_writable=False): + def unset_item_extended_metadata_obj(self, trans, item, check_writable=False): if item.__class__ == LibraryDatasetDatasetAssociation: - if not check_writable or trans.app.security_agent.can_modify_library_item( trans.get_current_user_roles(), item, trans.user ): + if not check_writable or trans.app.security_agent.can_modify_library_item(trans.get_current_user_roles(), item, trans.user): item.extended_metadata = None trans.sa_session.flush() if item.__class__ == HistoryDatasetAssociation: history = None if check_writable: - history = self.security_check( trans, item, check_ownership=True, check_accessible=True ) + history = self.security_check(trans, item, check_ownership=True, check_accessible=True) else: - history = self.security_check( trans, item, check_ownership=False, check_accessible=True ) + history = self.security_check(trans, item, check_ownership=False, check_accessible=True) if history: item.extended_metadata = None trans.sa_session.flush() @@ -2248,7 +2282,7 @@ def create_extended_metadata(self, trans, extmeta): not associated with any items """ ex_meta = ExtendedMetadata(extmeta) - trans.sa_session.add( ex_meta ) + trans.sa_session.add(ex_meta) trans.sa_session.flush() for path, value in self._scan_json_block(extmeta): meta_i = ExtendedMetadataIndex(ex_meta, path, value) @@ -2256,9 +2290,9 @@ def create_extended_metadata(self, trans, extmeta): trans.sa_session.flush() return ex_meta - def delete_extended_metadata( self, trans, item): + def delete_extended_metadata(self, trans, item): if item.__class__ == ExtendedMetadata: - trans.sa_session.delete( item ) + trans.sa_session.delete(item) trans.sa_session.flush() def _scan_json_block(self, meta, prefix=""): @@ -2289,7 +2323,7 @@ def _scan_json_block(self, meta, prefix=""): yield prefix, ("%s" % (meta)).encode("utf8", errors='replace') -class ControllerUnavailable( Exception ): +class ControllerUnavailable(Exception): """ Deprecated: `BaseController` used to be available under the name `Root` """ @@ -2298,7 +2332,7 @@ class ControllerUnavailable( Exception ): # ---- Utility methods ------------------------------------------------------- -def sort_by_attr( seq, attr ): +def sort_by_attr(seq, attr): """ Sort the sequence of objects by object's attribute Arguments: @@ -2310,6 +2344,6 @@ def sort_by_attr( seq, attr ): # (seq[i].attr, i, seq[i]) and sort it. The second item of tuple is needed not # only to provide stable sorting, but mainly to eliminate comparison of objects # (which can be expensive or prohibited) in case of equal attribute values. - intermed = map( None, map( getattr, seq, ( attr, ) * len( seq ) ), xrange( len( seq ) ), seq ) + intermed = [(getattr(v, attr), i, v) for i, v in enumerate(seq)] intermed.sort() - return map( operator.getitem, intermed, ( -1, ) * len( intermed ) ) + return [_[-1] for _ in intermed] diff --git a/lib/galaxy/web/base/controllers/__init__.py b/lib/galaxy/web/base/controllers/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/lib/galaxy/web/base/controllers/admin.py b/lib/galaxy/web/base/controllers/admin.py deleted file mode 100644 index 00035c875490..000000000000 --- a/lib/galaxy/web/base/controllers/admin.py +++ /dev/null @@ -1,1213 +0,0 @@ -import logging -import os -from datetime import datetime, timedelta -import six -from string import punctuation as PUNCTUATION - -from sqlalchemy import and_, false, func, or_ - -import galaxy.queue_worker -from galaxy import util, web -from galaxy.util import inflector -from galaxy.web.form_builder import CheckboxField -from tool_shed.util import repository_util -from tool_shed.util.web_util import escape - -log = logging.getLogger( __name__ ) - - -class Admin( object ): - # Override these - user_list_grid = None - role_list_grid = None - group_list_grid = None - quota_list_grid = None - repository_list_grid = None - tool_version_list_grid = None - delete_operation = None - undelete_operation = None - purge_operation = None - - @web.expose - @web.require_admin - def index( self, trans, **kwd ): - message = escape( kwd.get( 'message', '' ) ) - status = kwd.get( 'status', 'done' ) - return trans.fill_template( '/webapps/tool_shed/admin/index.mako', - message=message, - status=status ) - - @web.expose - @web.require_admin - def center( self, trans, **kwd ): - message = escape( kwd.get( 'message', '' ) ) - status = kwd.get( 'status', 'done' ) - if trans.webapp.name == 'galaxy': - is_repo_installed = trans.install_model.context.query( trans.install_model.ToolShedRepository ).first() is not None - installing_repository_ids = repository_util.get_ids_of_tool_shed_repositories_being_installed( trans.app, as_string=True ) - return trans.fill_template( '/webapps/galaxy/admin/center.mako', - is_repo_installed=is_repo_installed, - installing_repository_ids=installing_repository_ids, - message=message, - status=status ) - else: - return trans.fill_template( '/webapps/tool_shed/admin/center.mako', - message=message, - status=status ) - - @web.expose - @web.require_admin - def package_tool( self, trans, **kwd ): - params = util.Params( kwd ) - message = util.restore_text( params.get( 'message', '' ) ) - toolbox = self.app.toolbox - tool_id = None - if params.get( 'package_tool_button', False ): - tool_id = params.get('tool_id', None) - try: - tool_tarball = trans.app.toolbox.package_tool( trans, tool_id ) - trans.response.set_content_type( 'application/x-gzip' ) - download_file = open( tool_tarball ) - os.unlink( tool_tarball ) - tarball_path, filename = os.path.split( tool_tarball ) - trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s.tgz"' % ( tool_id ) - return download_file - except Exception: - return trans.fill_template( '/admin/package_tool.mako', - tool_id=tool_id, - toolbox=toolbox, - message=message, - status='error' ) - - @web.expose - @web.require_admin - def reload_tool( self, trans, **kwd ): - params = util.Params( kwd ) - message = util.restore_text( params.get( 'message', '' ) ) - status = params.get( 'status', 'done' ) - toolbox = self.app.toolbox - tool_id = None - if params.get( 'reload_tool_button', False ): - tool_id = kwd.get( 'tool_id', None ) - galaxy.queue_worker.send_control_task(trans.app, 'reload_tool', noop_self=True, kwargs={'tool_id': tool_id} ) - message, status = trans.app.toolbox.reload_tool_by_id( tool_id ) - return trans.fill_template( '/admin/reload_tool.mako', - tool_id=tool_id, - toolbox=toolbox, - message=message, - status=status ) - - @web.expose - @web.require_admin - def tool_versions( self, trans, **kwd ): - if 'message' not in kwd or not kwd[ 'message' ]: - kwd[ 'message' ] = 'Tool ids for tools that are currently loaded into the tool panel are highlighted in green (click to display).' - return self.tool_version_list_grid( trans, **kwd ) - - @web.expose - @web.require_admin - def roles( self, trans, **kwargs ): - if 'operation' in kwargs: - operation = kwargs[ 'operation' ].lower().replace( '+', ' ' ) - if operation == "roles": - return self.role( trans, **kwargs ) - if operation == "create": - return self.create_role( trans, **kwargs ) - if operation == "delete": - return self.mark_role_deleted( trans, **kwargs ) - if operation == "undelete": - return self.undelete_role( trans, **kwargs ) - if operation == "purge": - return self.purge_role( trans, **kwargs ) - if operation == "manage users and groups": - return self.manage_users_and_groups_for_role( trans, **kwargs ) - if operation == "manage role associations": - # This is currently used only in the Tool Shed. - return self.manage_role_associations( trans, **kwargs ) - if operation == "rename": - return self.rename_role( trans, **kwargs ) - # Render the list view - return self.role_list_grid( trans, **kwargs ) - - @web.expose - @web.require_admin - def create_role( self, trans, **kwd ): - params = util.Params( kwd ) - message = util.restore_text( params.get( 'message', '' ) ) - status = params.get( 'status', 'done' ) - name = util.restore_text( params.get( 'name', '' ) ) - description = util.restore_text( params.get( 'description', '' ) ) - in_users = util.listify( params.get( 'in_users', [] ) ) - out_users = util.listify( params.get( 'out_users', [] ) ) - in_groups = util.listify( params.get( 'in_groups', [] ) ) - out_groups = util.listify( params.get( 'out_groups', [] ) ) - create_group_for_role = params.get( 'create_group_for_role', '' ) - create_group_for_role_checked = CheckboxField.is_checked( create_group_for_role ) - ok = True - if params.get( 'create_role_button', False ): - if not name or not description: - message = "Enter a valid name and a description." - status = 'error' - ok = False - elif trans.sa_session.query( trans.app.model.Role ).filter( trans.app.model.Role.table.c.name == name ).first(): - message = "Role names must be unique and a role with that name already exists, so choose another name." - status = 'error' - ok = False - else: - # Create the role - role = trans.app.model.Role( name=name, description=description, type=trans.app.model.Role.types.ADMIN ) - trans.sa_session.add( role ) - # Create the UserRoleAssociations - for user in [ trans.sa_session.query( trans.app.model.User ).get( x ) for x in in_users ]: - ura = trans.app.model.UserRoleAssociation( user, role ) - trans.sa_session.add( ura ) - # Create the GroupRoleAssociations - for group in [ trans.sa_session.query( trans.app.model.Group ).get( x ) for x in in_groups ]: - gra = trans.app.model.GroupRoleAssociation( group, role ) - trans.sa_session.add( gra ) - if create_group_for_role_checked: - # Create the group - group = trans.app.model.Group( name=name ) - trans.sa_session.add( group ) - # Associate the group with the role - gra = trans.model.GroupRoleAssociation( group, role ) - trans.sa_session.add( gra ) - num_in_groups = len( in_groups ) + 1 - else: - num_in_groups = len( in_groups ) - trans.sa_session.flush() - message = "Role '%s' has been created with %d associated users and %d associated groups. " \ - % ( role.name, len( in_users ), num_in_groups ) - if create_group_for_role_checked: - message += 'One of the groups associated with this role is the newly created group with the same name.' - trans.response.send_redirect( web.url_for( controller='admin', - action='roles', - message=util.sanitize_text( message ), - status='done' ) ) - if ok: - for user in trans.sa_session.query( trans.app.model.User ) \ - .filter( trans.app.model.User.table.c.deleted == false() ) \ - .order_by( trans.app.model.User.table.c.email ): - out_users.append( ( user.id, user.email ) ) - for group in trans.sa_session.query( trans.app.model.Group ) \ - .filter( trans.app.model.Group.table.c.deleted == false() ) \ - .order_by( trans.app.model.Group.table.c.name ): - out_groups.append( ( group.id, group.name ) ) - return trans.fill_template( '/admin/dataset_security/role/role_create.mako', - name=name, - description=description, - in_users=in_users, - out_users=out_users, - in_groups=in_groups, - out_groups=out_groups, - create_group_for_role_checked=create_group_for_role_checked, - message=message, - status=status ) - - @web.expose - @web.require_admin - def rename_role( self, trans, **kwd ): - params = util.Params( kwd ) - message = util.restore_text( params.get( 'message', '' ) ) - status = params.get( 'status', 'done' ) - id = params.get( 'id', None ) - if not id: - message = "No role ids received for renaming" - trans.response.send_redirect( web.url_for( controller='admin', - action='roles', - message=message, - status='error' ) ) - role = get_role( trans, id ) - if params.get( 'rename_role_button', False ): - old_name = role.name - new_name = util.restore_text( params.name ) - new_description = util.restore_text( params.description ) - if not new_name: - message = 'Enter a valid name' - status = 'error' - else: - existing_role = trans.sa_session.query( trans.app.model.Role ).filter( trans.app.model.Role.table.c.name == new_name ).first() - if existing_role and existing_role.id != role.id: - message = 'A role with that name already exists' - status = 'error' - else: - if not ( role.name == new_name and role.description == new_description ): - role.name = new_name - role.description = new_description - trans.sa_session.add( role ) - trans.sa_session.flush() - message = "Role '%s' has been renamed to '%s'" % ( old_name, new_name ) - return trans.response.send_redirect( web.url_for( controller='admin', - action='roles', - message=util.sanitize_text( message ), - status='done' ) ) - return trans.fill_template( '/admin/dataset_security/role/role_rename.mako', - role=role, - message=message, - status=status ) - - @web.expose - @web.require_admin - def manage_users_and_groups_for_role( self, trans, **kwd ): - params = util.Params( kwd ) - message = util.restore_text( params.get( 'message', '' ) ) - status = params.get( 'status', 'done' ) - id = params.get( 'id', None ) - if not id: - message = "No role ids received for managing users and groups" - trans.response.send_redirect( web.url_for( controller='admin', - action='roles', - message=message, - status='error' ) ) - role = get_role( trans, id ) - if params.get( 'role_members_edit_button', False ): - in_users = [ trans.sa_session.query( trans.app.model.User ).get( x ) for x in util.listify( params.in_users ) ] - if trans.webapp.name == 'galaxy': - for ura in role.users: - user = trans.sa_session.query( trans.app.model.User ).get( ura.user_id ) - if user not in in_users: - # Delete DefaultUserPermissions for previously associated users that have been removed from the role - for dup in user.default_permissions: - if role == dup.role: - trans.sa_session.delete( dup ) - # Delete DefaultHistoryPermissions for previously associated users that have been removed from the role - for history in user.histories: - for dhp in history.default_permissions: - if role == dhp.role: - trans.sa_session.delete( dhp ) - trans.sa_session.flush() - in_groups = [ trans.sa_session.query( trans.app.model.Group ).get( x ) for x in util.listify( params.in_groups ) ] - trans.app.security_agent.set_entity_role_associations( roles=[ role ], users=in_users, groups=in_groups ) - trans.sa_session.refresh( role ) - message = "Role '%s' has been updated with %d associated users and %d associated groups" % ( role.name, len( in_users ), len( in_groups ) ) - trans.response.send_redirect( web.url_for( controller='admin', - action='roles', - message=util.sanitize_text( message ), - status=status ) ) - in_users = [] - out_users = [] - in_groups = [] - out_groups = [] - for user in trans.sa_session.query( trans.app.model.User ) \ - .filter( trans.app.model.User.table.c.deleted == false() ) \ - .order_by( trans.app.model.User.table.c.email ): - if user in [ x.user for x in role.users ]: - in_users.append( ( user.id, user.email ) ) - else: - out_users.append( ( user.id, user.email ) ) - for group in trans.sa_session.query( trans.app.model.Group ) \ - .filter( trans.app.model.Group.table.c.deleted == false() ) \ - .order_by( trans.app.model.Group.table.c.name ): - if group in [ x.group for x in role.groups ]: - in_groups.append( ( group.id, group.name ) ) - else: - out_groups.append( ( group.id, group.name ) ) - library_dataset_actions = {} - if trans.webapp.name == 'galaxy' and len(role.dataset_actions) < 25: - # Build a list of tuples that are LibraryDatasetDatasetAssociationss followed by a list of actions - # whose DatasetPermissions is associated with the Role - # [ ( LibraryDatasetDatasetAssociation [ action, action ] ) ] - for dp in role.dataset_actions: - for ldda in trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ) \ - .filter( trans.app.model.LibraryDatasetDatasetAssociation.dataset_id == dp.dataset_id ): - root_found = False - folder_path = '' - folder = ldda.library_dataset.folder - while not root_found: - folder_path = '%s / %s' % ( folder.name, folder_path ) - if not folder.parent: - root_found = True - else: - folder = folder.parent - folder_path = '%s %s' % ( folder_path, ldda.name ) - library = trans.sa_session.query( trans.app.model.Library ) \ - .filter( trans.app.model.Library.table.c.root_folder_id == folder.id ) \ - .first() - if library not in library_dataset_actions: - library_dataset_actions[ library ] = {} - try: - library_dataset_actions[ library ][ folder_path ].append( dp.action ) - except: - library_dataset_actions[ library ][ folder_path ] = [ dp.action ] - else: - message = "Not showing associated datasets, there are too many." - status = 'info' - return trans.fill_template( '/admin/dataset_security/role/role.mako', - role=role, - in_users=in_users, - out_users=out_users, - in_groups=in_groups, - out_groups=out_groups, - library_dataset_actions=library_dataset_actions, - message=message, - status=status ) - - @web.expose - @web.require_admin - def mark_role_deleted( self, trans, **kwd ): - id = kwd.get( 'id', None ) - if not id: - message = "No role ids received for deleting" - trans.response.send_redirect( web.url_for( controller='admin', - action='roles', - message=message, - status='error' ) ) - ids = util.listify( id ) - message = "Deleted %d roles: " % len( ids ) - for role_id in ids: - role = get_role( trans, role_id ) - role.deleted = True - trans.sa_session.add( role ) - trans.sa_session.flush() - message += " %s " % role.name - trans.response.send_redirect( web.url_for( controller='admin', - action='roles', - message=util.sanitize_text( message ), - status='done' ) ) - - @web.expose - @web.require_admin - def undelete_role( self, trans, **kwd ): - id = kwd.get( 'id', None ) - if not id: - message = "No role ids received for undeleting" - trans.response.send_redirect( web.url_for( controller='admin', - action='roles', - message=message, - status='error' ) ) - ids = util.listify( id ) - count = 0 - undeleted_roles = "" - for role_id in ids: - role = get_role( trans, role_id ) - if not role.deleted: - message = "Role '%s' has not been deleted, so it cannot be undeleted." % role.name - trans.response.send_redirect( web.url_for( controller='admin', - action='roles', - message=util.sanitize_text( message ), - status='error' ) ) - role.deleted = False - trans.sa_session.add( role ) - trans.sa_session.flush() - count += 1 - undeleted_roles += " %s" % role.name - message = "Undeleted %d roles: %s" % ( count, undeleted_roles ) - trans.response.send_redirect( web.url_for( controller='admin', - action='roles', - message=util.sanitize_text( message ), - status='done' ) ) - - @web.expose - @web.require_admin - def purge_role( self, trans, **kwd ): - # This method should only be called for a Role that has previously been deleted. - # Purging a deleted Role deletes all of the following from the database: - # - UserRoleAssociations where role_id == Role.id - # - DefaultUserPermissions where role_id == Role.id - # - DefaultHistoryPermissions where role_id == Role.id - # - GroupRoleAssociations where role_id == Role.id - # - DatasetPermissionss where role_id == Role.id - id = kwd.get( 'id', None ) - if not id: - message = "No role ids received for purging" - trans.response.send_redirect( web.url_for( controller='admin', - action='roles', - message=util.sanitize_text( message ), - status='error' ) ) - ids = util.listify( id ) - message = "Purged %d roles: " % len( ids ) - for role_id in ids: - role = get_role( trans, role_id ) - if not role.deleted: - message = "Role '%s' has not been deleted, so it cannot be purged." % role.name - trans.response.send_redirect( web.url_for( controller='admin', - action='roles', - message=util.sanitize_text( message ), - status='error' ) ) - # Delete UserRoleAssociations - for ura in role.users: - user = trans.sa_session.query( trans.app.model.User ).get( ura.user_id ) - # Delete DefaultUserPermissions for associated users - for dup in user.default_permissions: - if role == dup.role: - trans.sa_session.delete( dup ) - # Delete DefaultHistoryPermissions for associated users - for history in user.histories: - for dhp in history.default_permissions: - if role == dhp.role: - trans.sa_session.delete( dhp ) - trans.sa_session.delete( ura ) - # Delete GroupRoleAssociations - for gra in role.groups: - trans.sa_session.delete( gra ) - # Delete DatasetPermissionss - for dp in role.dataset_actions: - trans.sa_session.delete( dp ) - trans.sa_session.flush() - message += " %s " % role.name - trans.response.send_redirect( web.url_for( controller='admin', - action='roles', - message=util.sanitize_text( message ), - status='done' ) ) - - @web.expose - @web.require_admin - def groups( self, trans, **kwargs ): - if 'operation' in kwargs: - operation = kwargs[ 'operation' ].lower().replace( '+', ' ' ) - if operation == "groups": - return self.group( trans, **kwargs ) - if operation == "create": - return self.create_group( trans, **kwargs ) - if operation == "delete": - return self.mark_group_deleted( trans, **kwargs ) - if operation == "undelete": - return self.undelete_group( trans, **kwargs ) - if operation == "purge": - return self.purge_group( trans, **kwargs ) - if operation == "manage users and roles": - return self.manage_users_and_roles_for_group( trans, **kwargs ) - if operation == "rename": - return self.rename_group( trans, **kwargs ) - # Render the list view - return self.group_list_grid( trans, **kwargs ) - - @web.expose - @web.require_admin - def rename_group( self, trans, **kwd ): - params = util.Params( kwd ) - message = util.restore_text( params.get( 'message', '' ) ) - status = params.get( 'status', 'done' ) - id = params.get( 'id', None ) - if not id: - message = "No group ids received for renaming" - trans.response.send_redirect( web.url_for( controller='admin', - action='groups', - message=message, - status='error' ) ) - group = get_group( trans, id ) - if params.get( 'rename_group_button', False ): - old_name = group.name - new_name = util.restore_text( params.name ) - if not new_name: - message = 'Enter a valid name' - status = 'error' - else: - existing_group = trans.sa_session.query( trans.app.model.Group ).filter( trans.app.model.Group.table.c.name == new_name ).first() - if existing_group and existing_group.id != group.id: - message = 'A group with that name already exists' - status = 'error' - else: - if group.name != new_name: - group.name = new_name - trans.sa_session.add( group ) - trans.sa_session.flush() - message = "Group '%s' has been renamed to '%s'" % ( old_name, new_name ) - return trans.response.send_redirect( web.url_for( controller='admin', - action='groups', - message=util.sanitize_text( message ), - status='done' ) ) - return trans.fill_template( '/admin/dataset_security/group/group_rename.mako', - group=group, - message=message, - status=status ) - - @web.expose - @web.require_admin - def manage_users_and_roles_for_group( self, trans, **kwd ): - params = util.Params( kwd ) - message = util.restore_text( params.get( 'message', '' ) ) - status = params.get( 'status', 'done' ) - group = get_group( trans, params.id ) - if params.get( 'group_roles_users_edit_button', False ): - in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in util.listify( params.in_roles ) ] - in_users = [ trans.sa_session.query( trans.app.model.User ).get( x ) for x in util.listify( params.in_users ) ] - trans.app.security_agent.set_entity_group_associations( groups=[ group ], roles=in_roles, users=in_users ) - trans.sa_session.refresh( group ) - message += "Group '%s' has been updated with %d associated roles and %d associated users" % ( group.name, len( in_roles ), len( in_users ) ) - trans.response.send_redirect( web.url_for( controller='admin', - action='groups', - message=util.sanitize_text( message ), - status=status ) ) - in_roles = [] - out_roles = [] - in_users = [] - out_users = [] - for role in trans.sa_session.query(trans.app.model.Role ) \ - .filter( trans.app.model.Role.table.c.deleted == false() ) \ - .order_by( trans.app.model.Role.table.c.name ): - if role in [ x.role for x in group.roles ]: - in_roles.append( ( role.id, role.name ) ) - else: - out_roles.append( ( role.id, role.name ) ) - for user in trans.sa_session.query( trans.app.model.User ) \ - .filter( trans.app.model.User.table.c.deleted == false() ) \ - .order_by( trans.app.model.User.table.c.email ): - if user in [ x.user for x in group.users ]: - in_users.append( ( user.id, user.email ) ) - else: - out_users.append( ( user.id, user.email ) ) - message += 'Group %s is currently associated with %d roles and %d users' % ( group.name, len( in_roles ), len( in_users ) ) - return trans.fill_template( '/admin/dataset_security/group/group.mako', - group=group, - in_roles=in_roles, - out_roles=out_roles, - in_users=in_users, - out_users=out_users, - message=message, - status=status ) - - @web.expose - @web.require_admin - def create_group( self, trans, **kwd ): - params = util.Params( kwd ) - message = util.restore_text( params.get( 'message', '' ) ) - status = params.get( 'status', 'done' ) - name = util.restore_text( params.get( 'name', '' ) ) - in_users = util.listify( params.get( 'in_users', [] ) ) - out_users = util.listify( params.get( 'out_users', [] ) ) - in_roles = util.listify( params.get( 'in_roles', [] ) ) - out_roles = util.listify( params.get( 'out_roles', [] ) ) - create_role_for_group = params.get( 'create_role_for_group', '' ) - create_role_for_group_checked = CheckboxField.is_checked( create_role_for_group ) - ok = True - if params.get( 'create_group_button', False ): - if not name: - message = "Enter a valid name." - status = 'error' - ok = False - elif trans.sa_session.query( trans.app.model.Group ).filter( trans.app.model.Group.table.c.name == name ).first(): - message = "Group names must be unique and a group with that name already exists, so choose another name." - status = 'error' - ok = False - else: - # Create the group - group = trans.app.model.Group( name=name ) - trans.sa_session.add( group ) - trans.sa_session.flush() - # Create the UserRoleAssociations - for user in [ trans.sa_session.query( trans.app.model.User ).get( x ) for x in in_users ]: - uga = trans.app.model.UserGroupAssociation( user, group ) - trans.sa_session.add( uga ) - # Create the GroupRoleAssociations - for role in [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in in_roles ]: - gra = trans.app.model.GroupRoleAssociation( group, role ) - trans.sa_session.add( gra ) - if create_role_for_group_checked: - # Create the role - role = trans.app.model.Role( name=name, description='Role for group %s' % name ) - trans.sa_session.add( role ) - # Associate the role with the group - gra = trans.model.GroupRoleAssociation( group, role ) - trans.sa_session.add( gra ) - num_in_roles = len( in_roles ) + 1 - else: - num_in_roles = len( in_roles ) - trans.sa_session.flush() - message = "Group '%s' has been created with %d associated users and %d associated roles. " \ - % ( group.name, len( in_users ), num_in_roles ) - if create_role_for_group_checked: - message += 'One of the roles associated with this group is the newly created role with the same name.' - trans.response.send_redirect( web.url_for( controller='admin', - action='groups', - message=util.sanitize_text( message ), - status='done' ) ) - if ok: - for user in trans.sa_session.query( trans.app.model.User ) \ - .filter( trans.app.model.User.table.c.deleted == false() ) \ - .order_by( trans.app.model.User.table.c.email ): - out_users.append( ( user.id, user.email ) ) - for role in trans.sa_session.query( trans.app.model.Role ) \ - .filter( trans.app.model.Role.table.c.deleted == false() ) \ - .order_by( trans.app.model.Role.table.c.name ): - out_roles.append( ( role.id, role.name ) ) - return trans.fill_template( '/admin/dataset_security/group/group_create.mako', - name=name, - in_users=in_users, - out_users=out_users, - in_roles=in_roles, - out_roles=out_roles, - create_role_for_group_checked=create_role_for_group_checked, - message=message, - status=status ) - - @web.expose - @web.require_admin - def mark_group_deleted( self, trans, **kwd ): - params = util.Params( kwd ) - id = params.get( 'id', None ) - if not id: - message = "No group ids received for marking deleted" - trans.response.send_redirect( web.url_for( controller='admin', - action='groups', - message=message, - status='error' ) ) - ids = util.listify( id ) - message = "Deleted %d groups: " % len( ids ) - for group_id in ids: - group = get_group( trans, group_id ) - group.deleted = True - trans.sa_session.add( group ) - trans.sa_session.flush() - message += " %s " % group.name - trans.response.send_redirect( web.url_for( controller='admin', - action='groups', - message=util.sanitize_text( message ), - status='done' ) ) - - @web.expose - @web.require_admin - def undelete_group( self, trans, **kwd ): - id = kwd.get( 'id', None ) - if not id: - message = "No group ids received for undeleting" - trans.response.send_redirect( web.url_for( controller='admin', - action='groups', - message=message, - status='error' ) ) - ids = util.listify( id ) - count = 0 - undeleted_groups = "" - for group_id in ids: - group = get_group( trans, group_id ) - if not group.deleted: - message = "Group '%s' has not been deleted, so it cannot be undeleted." % group.name - trans.response.send_redirect( web.url_for( controller='admin', - action='groups', - message=util.sanitize_text( message ), - status='error' ) ) - group.deleted = False - trans.sa_session.add( group ) - trans.sa_session.flush() - count += 1 - undeleted_groups += " %s" % group.name - message = "Undeleted %d groups: %s" % ( count, undeleted_groups ) - trans.response.send_redirect( web.url_for( controller='admin', - action='groups', - message=util.sanitize_text( message ), - status='done' ) ) - - @web.expose - @web.require_admin - def purge_group( self, trans, **kwd ): - # This method should only be called for a Group that has previously been deleted. - # Purging a deleted Group simply deletes all UserGroupAssociations and GroupRoleAssociations. - id = kwd.get( 'id', None ) - if not id: - message = "No group ids received for purging" - trans.response.send_redirect( web.url_for( controller='admin', - action='groups', - message=util.sanitize_text( message ), - status='error' ) ) - ids = util.listify( id ) - message = "Purged %d groups: " % len( ids ) - for group_id in ids: - group = get_group( trans, group_id ) - if not group.deleted: - # We should never reach here, but just in case there is a bug somewhere... - message = "Group '%s' has not been deleted, so it cannot be purged." % group.name - trans.response.send_redirect( web.url_for( controller='admin', - action='groups', - message=util.sanitize_text( message ), - status='error' ) ) - # Delete UserGroupAssociations - for uga in group.users: - trans.sa_session.delete( uga ) - # Delete GroupRoleAssociations - for gra in group.roles: - trans.sa_session.delete( gra ) - trans.sa_session.flush() - message += " %s " % group.name - trans.response.send_redirect( web.url_for( controller='admin', - action='groups', - message=util.sanitize_text( message ), - status='done' ) ) - - @web.expose - @web.require_admin - def create_new_user( self, trans, **kwd ): - return trans.response.send_redirect( web.url_for( controller='user', - action='create', - cntrller='admin' ) ) - - @web.expose - @web.require_admin - def reset_user_password( self, trans, **kwd ): - user_id = kwd.get( 'id', None ) - if not user_id: - message = "No users received for resetting passwords." - trans.response.send_redirect( web.url_for( controller='admin', - action='users', - message=message, - status='error' ) ) - user_ids = util.listify( user_id ) - if 'reset_user_password_button' in kwd: - message = '' - status = '' - for user_id in user_ids: - user = get_user( trans, user_id ) - password = kwd.get( 'password', None ) - confirm = kwd.get( 'confirm', None ) - if len( password ) < 6: - message = "Use a password of at least 6 characters." - status = 'error' - break - elif password != confirm: - message = "Passwords do not match." - status = 'error' - break - else: - user.set_password_cleartext( password ) - trans.sa_session.add( user ) - trans.sa_session.flush() - if not message and not status: - message = "Passwords reset for %d %s." % ( len( user_ids ), inflector.cond_plural( len( user_ids ), 'user' ) ) - status = 'done' - trans.response.send_redirect( web.url_for( controller='admin', - action='users', - message=util.sanitize_text( message ), - status=status ) ) - users = [ get_user( trans, user_id ) for user_id in user_ids ] - if len( user_ids ) > 1: - user_id = ','.join( user_ids ) - return trans.fill_template( '/admin/user/reset_password.mako', - id=user_id, - users=users, - password='', - confirm='' ) - - @web.expose - @web.require_admin - def mark_user_deleted( self, trans, **kwd ): - id = kwd.get( 'id', None ) - if not id: - message = "No user ids received for deleting" - trans.response.send_redirect( web.url_for( controller='admin', - action='users', - message=message, - status='error' ) ) - ids = util.listify( id ) - message = "Deleted %d users: " % len( ids ) - for user_id in ids: - user = get_user( trans, user_id ) - user.deleted = True - trans.sa_session.add( user ) - trans.sa_session.flush() - message += " %s " % user.email - trans.response.send_redirect( web.url_for( controller='admin', - action='users', - message=util.sanitize_text( message ), - status='done' ) ) - - @web.expose - @web.require_admin - def undelete_user( self, trans, **kwd ): - id = kwd.get( 'id', None ) - if not id: - message = "No user ids received for undeleting" - trans.response.send_redirect( web.url_for( controller='admin', - action='users', - message=message, - status='error' ) ) - ids = util.listify( id ) - count = 0 - undeleted_users = "" - for user_id in ids: - user = get_user( trans, user_id ) - if not user.deleted: - message = "User '%s' has not been deleted, so it cannot be undeleted." % user.email - trans.response.send_redirect( web.url_for( controller='admin', - action='users', - message=util.sanitize_text( message ), - status='error' ) ) - user.deleted = False - trans.sa_session.add( user ) - trans.sa_session.flush() - count += 1 - undeleted_users += " %s" % user.email - message = "Undeleted %d users: %s" % ( count, undeleted_users ) - trans.response.send_redirect( web.url_for( controller='admin', - action='users', - message=util.sanitize_text( message ), - status='done' ) ) - - @web.expose - @web.require_admin - def purge_user( self, trans, **kwd ): - # This method should only be called for a User that has previously been deleted. - # We keep the User in the database ( marked as purged ), and stuff associated - # with the user's private role in case we want the ability to unpurge the user - # some time in the future. - # Purging a deleted User deletes all of the following: - # - History where user_id = User.id - # - HistoryDatasetAssociation where history_id = History.id - # - Dataset where HistoryDatasetAssociation.dataset_id = Dataset.id - # - UserGroupAssociation where user_id == User.id - # - UserRoleAssociation where user_id == User.id EXCEPT FOR THE PRIVATE ROLE - # - UserAddress where user_id == User.id - # Purging Histories and Datasets must be handled via the cleanup_datasets.py script - id = kwd.get( 'id', None ) - if not id: - message = "No user ids received for purging" - trans.response.send_redirect( web.url_for( controller='admin', - action='users', - message=util.sanitize_text( message ), - status='error' ) ) - ids = util.listify( id ) - message = "Purged %d users: " % len( ids ) - for user_id in ids: - user = get_user( trans, user_id ) - if not user.deleted: - # We should never reach here, but just in case there is a bug somewhere... - message = "User '%s' has not been deleted, so it cannot be purged." % user.email - trans.response.send_redirect( web.url_for( controller='admin', - action='users', - message=util.sanitize_text( message ), - status='error' ) ) - private_role = trans.app.security_agent.get_private_user_role( user ) - # Delete History - for h in user.active_histories: - trans.sa_session.refresh( h ) - for hda in h.active_datasets: - # Delete HistoryDatasetAssociation - d = trans.sa_session.query( trans.app.model.Dataset ).get( hda.dataset_id ) - # Delete Dataset - if not d.deleted: - d.deleted = True - trans.sa_session.add( d ) - hda.deleted = True - trans.sa_session.add( hda ) - h.deleted = True - trans.sa_session.add( h ) - # Delete UserGroupAssociations - for uga in user.groups: - trans.sa_session.delete( uga ) - # Delete UserRoleAssociations EXCEPT FOR THE PRIVATE ROLE - for ura in user.roles: - if ura.role_id != private_role.id: - trans.sa_session.delete( ura ) - # Delete UserAddresses - for address in user.addresses: - trans.sa_session.delete( address ) - # Purge the user - user.purged = True - trans.sa_session.add( user ) - trans.sa_session.flush() - message += "%s " % user.email - trans.response.send_redirect( web.url_for( controller='admin', - action='users', - message=util.sanitize_text( message ), - status='done' ) ) - - @web.expose - @web.require_admin - def users( self, trans, **kwd ): - if 'operation' in kwd: - operation = kwd['operation'].lower() - if operation == "roles": - return self.user( trans, **kwd ) - elif operation == "reset password": - return self.reset_user_password( trans, **kwd ) - elif operation == "delete": - return self.mark_user_deleted( trans, **kwd ) - elif operation == "undelete": - return self.undelete_user( trans, **kwd ) - elif operation == "purge": - return self.purge_user( trans, **kwd ) - elif operation == "create": - return self.create_new_user( trans, **kwd ) - elif operation == "information": - user_id = kwd.get( 'id', None ) - if not user_id: - kwd[ 'message' ] = util.sanitize_text( "Invalid user id (%s) received" % str( user_id ) ) - kwd[ 'status' ] = 'error' - else: - return trans.response.send_redirect( web.url_for( controller='user', action='information', **kwd ) ) - elif operation == "manage roles and groups": - return self.manage_roles_and_groups_for_user( trans, **kwd ) - if trans.app.config.allow_user_deletion: - if self.delete_operation not in self.user_list_grid.operations: - self.user_list_grid.operations.append( self.delete_operation ) - if self.undelete_operation not in self.user_list_grid.operations: - self.user_list_grid.operations.append( self.undelete_operation ) - if self.purge_operation not in self.user_list_grid.operations: - self.user_list_grid.operations.append( self.purge_operation ) - # Render the list view - return self.user_list_grid( trans, **kwd ) - - @web.expose - @web.require_admin - def name_autocomplete_data( self, trans, q=None, limit=None, timestamp=None ): - """Return autocomplete data for user emails""" - ac_data = "" - for user in trans.sa_session.query( trans.app.model.User ).filter_by( deleted=False ).filter( func.lower( trans.app.model.User.email ).like( q.lower() + "%" ) ): - ac_data = ac_data + user.email + "\n" - return ac_data - - @web.expose - @web.require_admin - def manage_roles_and_groups_for_user( self, trans, **kwd ): - user_id = kwd.get( 'id', None ) - message = '' - status = '' - if not user_id: - message += "Invalid user id (%s) received" % str( user_id ) - trans.response.send_redirect( web.url_for( controller='admin', - action='users', - message=util.sanitize_text( message ), - status='error' ) ) - user = get_user( trans, user_id ) - private_role = trans.app.security_agent.get_private_user_role( user ) - if kwd.get( 'user_roles_groups_edit_button', False ): - # Make sure the user is not dis-associating himself from his private role - out_roles = kwd.get( 'out_roles', [] ) - if out_roles: - out_roles = [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in util.listify( out_roles ) ] - if private_role in out_roles: - message += "You cannot eliminate a user's private role association. " - status = 'error' - in_roles = kwd.get( 'in_roles', [] ) - if in_roles: - in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in util.listify( in_roles ) ] - out_groups = kwd.get( 'out_groups', [] ) - if out_groups: - out_groups = [ trans.sa_session.query( trans.app.model.Group ).get( x ) for x in util.listify( out_groups ) ] - in_groups = kwd.get( 'in_groups', [] ) - if in_groups: - in_groups = [ trans.sa_session.query( trans.app.model.Group ).get( x ) for x in util.listify( in_groups ) ] - if in_roles: - trans.app.security_agent.set_entity_user_associations( users=[ user ], roles=in_roles, groups=in_groups ) - trans.sa_session.refresh( user ) - message += "User '%s' has been updated with %d associated roles and %d associated groups (private roles are not displayed)" % \ - ( user.email, len( in_roles ), len( in_groups ) ) - trans.response.send_redirect( web.url_for( controller='admin', - action='users', - message=util.sanitize_text( message ), - status='done' ) ) - in_roles = [] - out_roles = [] - in_groups = [] - out_groups = [] - for role in trans.sa_session.query( trans.app.model.Role ).filter( trans.app.model.Role.table.c.deleted == false() ) \ - .order_by( trans.app.model.Role.table.c.name ): - if role in [ x.role for x in user.roles ]: - in_roles.append( ( role.id, role.name ) ) - elif role.type != trans.app.model.Role.types.PRIVATE: - # There is a 1 to 1 mapping between a user and a PRIVATE role, so private roles should - # not be listed in the roles form fields, except for the currently selected user's private - # role, which should always be in in_roles. The check above is added as an additional - # precaution, since for a period of time we were including private roles in the form fields. - out_roles.append( ( role.id, role.name ) ) - for group in trans.sa_session.query( trans.app.model.Group ).filter( trans.app.model.Group.table.c.deleted == false() ) \ - .order_by( trans.app.model.Group.table.c.name ): - if group in [ x.group for x in user.groups ]: - in_groups.append( ( group.id, group.name ) ) - else: - out_groups.append( ( group.id, group.name ) ) - message += "User '%s' is currently associated with %d roles and is a member of %d groups" % \ - ( user.email, len( in_roles ), len( in_groups ) ) - if not status: - status = 'done' - return trans.fill_template( '/admin/user/user.mako', - user=user, - in_roles=in_roles, - out_roles=out_roles, - in_groups=in_groups, - out_groups=out_groups, - message=message, - status=status ) - - @web.expose - @web.require_admin - def jobs( self, trans, stop=[], stop_msg=None, cutoff=180, job_lock=None, ajl_submit=None, **kwd ): - deleted = [] - msg = None - status = None - job_ids = util.listify( stop ) - if job_ids and stop_msg in [ None, '' ]: - msg = 'Please enter an error message to display to the user describing why the job was terminated' - status = 'error' - elif job_ids: - if stop_msg[-1] not in PUNCTUATION: - stop_msg += '.' - for job_id in job_ids: - error_msg = "This job was stopped by an administrator: %s Contact support for additional help." \ - % ( stop_msg, self.app.config.get("support_url", "https://galaxyproject.org/support/" ) ) - if trans.app.config.track_jobs_in_database: - job = trans.sa_session.query( trans.app.model.Job ).get( job_id ) - job.stderr = error_msg - job.set_state( trans.app.model.Job.states.DELETED_NEW ) - trans.sa_session.add( job ) - else: - trans.app.job_manager.job_stop_queue.put( job_id, error_msg=error_msg ) - deleted.append( str( job_id ) ) - if deleted: - msg = 'Queued job' - if len( deleted ) > 1: - msg += 's' - msg += ' for deletion: ' - msg += ', '.join( deleted ) - status = 'done' - trans.sa_session.flush() - if ajl_submit: - if job_lock == 'on': - galaxy.queue_worker.send_control_task(trans.app, 'admin_job_lock', - kwargs={'job_lock': True } ) - job_lock = True - else: - galaxy.queue_worker.send_control_task(trans.app, 'admin_job_lock', - kwargs={'job_lock': False } ) - job_lock = False - else: - job_lock = trans.app.job_manager.job_lock - cutoff_time = datetime.utcnow() - timedelta( seconds=int( cutoff ) ) - jobs = trans.sa_session.query( trans.app.model.Job ) \ - .filter( and_( trans.app.model.Job.table.c.update_time < cutoff_time, - or_( trans.app.model.Job.state == trans.app.model.Job.states.NEW, - trans.app.model.Job.state == trans.app.model.Job.states.QUEUED, - trans.app.model.Job.state == trans.app.model.Job.states.RUNNING, - trans.app.model.Job.state == trans.app.model.Job.states.UPLOAD ) ) ) \ - .order_by( trans.app.model.Job.table.c.update_time.desc() ).all() - recent_jobs = trans.sa_session.query( trans.app.model.Job ) \ - .filter( and_( trans.app.model.Job.table.c.update_time > cutoff_time, - or_( trans.app.model.Job.state == trans.app.model.Job.states.ERROR, - trans.app.model.Job.state == trans.app.model.Job.states.OK) ) ) \ - .order_by( trans.app.model.Job.table.c.update_time.desc() ).all() - last_updated = {} - for job in jobs: - delta = datetime.utcnow() - job.update_time - if delta.days > 0: - last_updated[job.id] = '%s hours' % ( delta.days * 24 + int( delta.seconds / 60 / 60 ) ) - elif delta > timedelta( minutes=59 ): - last_updated[job.id] = '%s hours' % int( delta.seconds / 60 / 60 ) - else: - last_updated[job.id] = '%s minutes' % int( delta.seconds / 60 ) - finished = {} - for job in recent_jobs: - delta = datetime.utcnow() - job.update_time - if delta.days > 0: - finished[job.id] = '%s hours' % ( delta.days * 24 + int( delta.seconds / 60 / 60 ) ) - elif delta > timedelta( minutes=59 ): - finished[job.id] = '%s hours' % int( delta.seconds / 60 / 60 ) - else: - finished[job.id] = '%s minutes' % int( delta.seconds / 60 ) - return trans.fill_template( '/admin/jobs.mako', - jobs=jobs, - recent_jobs=recent_jobs, - last_updated=last_updated, - finished=finished, - cutoff=cutoff, - msg=msg, - status=status, - job_lock=job_lock) - - @web.expose - @web.require_admin - def job_info( self, trans, jobid=None ): - job = None - if jobid is not None: - job = trans.sa_session.query( trans.app.model.Job ).get(jobid) - return trans.fill_template( '/webapps/reports/job_info.mako', - job=job, - message="Back" ) - - @web.expose - @web.require_admin - def manage_tool_dependencies( self, - trans, - install_dependencies=False, - uninstall_dependencies=False, - remove_unused_dependencies=False, - selected_tool_ids=None, - selected_environments_to_uninstall=None, - viewkey='View tool-centric dependencies'): - if not selected_tool_ids: - selected_tool_ids = [] - if not selected_environments_to_uninstall: - selected_environments_to_uninstall = [] - tools_by_id = trans.app.toolbox.tools_by_id - view = six.next(six.itervalues(trans.app.toolbox.tools_by_id))._view - if selected_tool_ids: - # install the dependencies for the tools in the selected_tool_ids list - if not isinstance(selected_tool_ids, list): - selected_tool_ids = [selected_tool_ids] - requirements = set([tools_by_id[tid].tool_requirements for tid in selected_tool_ids]) - if install_dependencies: - [view.install_dependencies(r) for r in requirements] - elif uninstall_dependencies: - [view.uninstall_dependencies(index=None, requirements=r) for r in requirements] - if selected_environments_to_uninstall and remove_unused_dependencies: - if not isinstance(selected_environments_to_uninstall, list): - selected_environments_to_uninstall = [selected_environments_to_uninstall] - view.remove_unused_dependency_paths(selected_environments_to_uninstall) - return trans.fill_template( '/webapps/galaxy/admin/manage_dependencies.mako', - tools=tools_by_id, - requirements_status=view.toolbox_requirements_status, - tool_ids_by_requirements=view.tool_ids_by_requirements, - unused_environments=view.unused_dependency_paths, - viewkey=viewkey ) - - @web.expose - @web.require_admin - def sanitize_whitelist( self, trans, submit_whitelist=False, tools_to_whitelist=[]): - if submit_whitelist: - # write the configured sanitize_whitelist_file with new whitelist - # and update in-memory list. - with open(trans.app.config.sanitize_whitelist_file, 'wt') as f: - if isinstance(tools_to_whitelist, six.string_types): - tools_to_whitelist = [tools_to_whitelist] - new_whitelist = sorted([tid for tid in tools_to_whitelist if tid in trans.app.toolbox.tools_by_id]) - f.write("\n".join(new_whitelist)) - trans.app.config.sanitize_whitelist = new_whitelist - galaxy.queue_worker.send_control_task(trans.app, 'reload_sanitize_whitelist', noop_self=True) - # dispatch a message to reload list for other processes - return trans.fill_template( '/webapps/galaxy/admin/sanitize_whitelist.mako', - sanitize_all=trans.app.config.sanitize_all_html, - tools=trans.app.toolbox.tools_by_id ) - - -# ---- Utility methods ------------------------------------------------------- - - -def get_user( trans, user_id ): - """Get a User from the database by id.""" - user = trans.sa_session.query( trans.model.User ).get( trans.security.decode_id( user_id ) ) - if not user: - return trans.show_error_message( "User not found for id (%s)" % str( user_id ) ) - return user - - -def get_user_by_username( trans, username ): - """Get a user from the database by username""" - # TODO: Add exception handling here. - return trans.sa_session.query( trans.model.User ) \ - .filter( trans.model.User.table.c.username == username ) \ - .one() - - -def get_role( trans, id ): - """Get a Role from the database by id.""" - # Load user from database - id = trans.security.decode_id( id ) - role = trans.sa_session.query( trans.model.Role ).get( id ) - if not role: - return trans.show_error_message( "Role not found for id (%s)" % str( id ) ) - return role - - -def get_group( trans, id ): - """Get a Group from the database by id.""" - # Load user from database - id = trans.security.decode_id( id ) - group = trans.sa_session.query( trans.model.Group ).get( id ) - if not group: - return trans.show_error_message( "Group not found for id (%s)" % str( id ) ) - return group - - -def get_quota( trans, id ): - """Get a Quota from the database by id.""" - # Load user from database - id = trans.security.decode_id( id ) - quota = trans.sa_session.query( trans.model.Quota ).get( id ) - return quota diff --git a/lib/galaxy/web/base/interactive_environments.py b/lib/galaxy/web/base/interactive_environments.py index 7beee84b52a2..161882e9409b 100644 --- a/lib/galaxy/web/base/interactive_environments.py +++ b/lib/galaxy/web/base/interactive_environments.py @@ -1,4 +1,3 @@ -import ConfigParser import json import logging import os @@ -6,11 +5,11 @@ import stat import tempfile import uuid - from subprocess import PIPE, Popen from sys import platform as _platform import yaml +from six.moves import configparser from galaxy import model, web from galaxy.containers import build_container_interfaces @@ -35,14 +34,14 @@ def __init__(self, trans, plugin): self.attr = Bunch() self.attr.viz_id = plugin.name - self.attr.history_id = trans.security.encode_id( trans.history.id ) + self.attr.history_id = trans.security.encode_id(trans.history.id) self.attr.galaxy_config = trans.app.config self.attr.galaxy_root_dir = os.path.abspath(self.attr.galaxy_config.root) self.attr.root = web.url_for("/") self.attr.app_root = self.attr.root + "plugins/interactive_environments/" + self.attr.viz_id + "/static/" self.attr.import_volume = True - plugin_path = os.path.abspath( plugin.path ) + plugin_path = os.path.abspath(plugin.path) # Store our template and configuration path self.attr.our_config_dir = os.path.join(plugin_path, "config") @@ -63,14 +62,14 @@ def __init__(self, trans, plugin): self.notebook_pw = self.generate_password(length=24) ie_parent_temp_dir = self.attr.viz_config.get("docker", "docker_galaxy_temp_dir") or None - self.temp_dir = os.path.abspath( tempfile.mkdtemp( dir=ie_parent_temp_dir ) ) + self.temp_dir = os.path.abspath(tempfile.mkdtemp(dir=ie_parent_temp_dir)) if self.attr.viz_config.getboolean("docker", "wx_tempdir"): # Ensure permissions are set try: - os.chmod( self.temp_dir, os.stat(self.temp_dir).st_mode | stat.S_IXOTH ) + os.chmod(self.temp_dir, os.stat(self.temp_dir).st_mode | stat.S_IXOTH) except Exception: - log.error( "Could not change permissions of tmpdir %s" % self.temp_dir ) + log.error("Could not change permissions of tmpdir %s" % self.temp_dir) # continue anyway # This duplicates the logic in the proxy manager @@ -121,7 +120,7 @@ def load_allowed_images(self): def load_deploy_config(self, default_dict={}): # For backwards compat, any new variables added to the base .ini file - # will need to be recorded here. The ConfigParser doesn't provide a + # will need to be recorded here. The configparser doesn't provide a # .get() that will ignore missing sections, so we must make use of # their defaults dictionary instead. default_dict = { @@ -133,11 +132,11 @@ def load_deploy_config(self, default_dict={}): 'docker_galaxy_temp_dir': None, 'docker_connect_port': None, } - viz_config = ConfigParser.SafeConfigParser(default_dict) - conf_path = os.path.join( self.attr.our_config_dir, self.attr.viz_id + ".ini" ) - if not os.path.exists( conf_path ): + viz_config = configparser.SafeConfigParser(default_dict) + conf_path = os.path.join(self.attr.our_config_dir, self.attr.viz_id + ".ini") + if not os.path.exists(conf_path): conf_path = "%s.sample" % conf_path - viz_config.read( conf_path ) + viz_config.read(conf_path) self.attr.viz_config = viz_config def _boolean_option(option, default=False): @@ -178,7 +177,7 @@ def get_conf_dict(self): """ trans = self.trans request = trans.request - api_key = api_keys.ApiKeyManager( trans.app ).get_or_create_api_key( trans.user ) + api_key = api_keys.ApiKeyManager(trans.app).get_or_create_api_key(trans.user) conf_file = { 'history_id': self.attr.history_id, 'api_key': api_key, @@ -252,8 +251,9 @@ def _get_env_for_run(self, env_override=None): if env_override is None: env_override = {} conf = self.get_conf_dict() + conf = dict([(key.upper(), item) for key, item in conf.items()]) conf.update(env_override) - return dict([(key.upper(), item) for key, item in conf.items()]) + return conf def _get_import_volume_for_run(self): if self.use_volumes and self.attr.import_volume: @@ -271,8 +271,8 @@ def docker_cmd(self, image, env_override=None, volumes=None): volumes = [] env = self._get_env_for_run(env_override) import_volume_def = self._get_import_volume_for_run() - env_str = ' '.join(['-e "%s=%s"' % (key, item) for key, item in env.items()]) - volume_str = ' '.join(['-v "%s"' % volume for volume in volumes]) if self.use_volumes else '' + env_str = ' '.join('-e "%s=%s"' % (key, item) for key, item in env.items()) + volume_str = ' '.join('-v "%s"' % volume for volume in volumes) if self.use_volumes else '' import_volume_str = '-v "{import_volume}"'.format(import_volume=import_volume_def) if import_volume_def else '' name = None # This is the basic docker command such as "sudo -u docker docker {docker_args}" @@ -370,19 +370,19 @@ def _launch_legacy(self, image, env_override, volumes): self.attr.viz_id, raw_cmd )) - p = Popen( raw_cmd, stdout=PIPE, stderr=PIPE, close_fds=True, shell=True) + p = Popen(raw_cmd, stdout=PIPE, stderr=PIPE, close_fds=True, shell=True) stdout, stderr = p.communicate() if p.returncode != 0: - log.error( "%s\n%s" % (stdout, stderr) ) + log.error("%s\n%s" % (stdout, stderr)) return None else: container_id = stdout.strip() - log.debug( "Container id: %s" % container_id) + log.debug("Container id: %s" % container_id) inspect_data = self.inspect_container(container_id) port_mappings = self.get_container_port_mapping(inspect_data) self.attr.docker_hostname = self.get_container_host(inspect_data) host_port = self._find_port_mapping(port_mappings)[-1] - log.debug( "Container host/port: %s:%s", self.attr.docker_hostname, host_port ) + log.debug("Container host/port: %s:%s", self.attr.docker_hostname, host_port) # Now we configure our proxy_requst object and we manually specify # the port to map to and ensure the proxy is available. @@ -395,7 +395,7 @@ def _launch_legacy(self, image, env_override, volumes): container_ids=[container_id], ) # These variables then become available for use in templating URLs - self.attr.proxy_url = self.attr.proxy_request[ 'proxy_url' ] + self.attr.proxy_url = self.attr.proxy_request['proxy_url'] # Commented out because it needs to be documented and visible that # this variable was moved here. Usually would remove commented # code, but again, needs to be clear where this went. Remove at a @@ -421,7 +421,7 @@ def _launch_container_interface(self, image, env_override, volumes): container_ids=[container.id], container_interface=self.attr.container_interface.key ) - self.attr.proxy_url = self.attr.proxy_request[ 'proxy_url' ] + self.attr.proxy_url = self.attr.proxy_request['proxy_url'] def launch(self, image=None, additional_ids=None, env_override=None, volumes=None): """Launch a docker image. @@ -484,7 +484,7 @@ def inspect_container(self, container_id): p = Popen(command, stdout=PIPE, stderr=PIPE, close_fds=True, shell=True) stdout, stderr = p.communicate() if p.returncode != 0: - log.error( "%s\n%s" % (stdout, stderr) ) + log.error("%s\n%s" % (stdout, stderr)) return None inspect_data = json.loads(stdout) diff --git a/lib/galaxy/web/base/pluginframework.py b/lib/galaxy/web/base/pluginframework.py index 704830716eba..ae7feb7f8c59 100644 --- a/lib/galaxy/web/base/pluginframework.py +++ b/lib/galaxy/web/base/pluginframework.py @@ -7,34 +7,36 @@ * serve templated html * have some configuration at startup """ - +import imp +import logging import os.path import sys -import imp + +import mako.lookup from galaxy import util -from galaxy.util import odict -from galaxy.util import bunch +from galaxy.util import ( + bunch, + odict +) -import mako.lookup -import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class PluginManagerException( Exception ): +class PluginManagerException(Exception): """Base exception for plugin frameworks. """ pass -class PluginManagerConfigException( PluginManagerException ): +class PluginManagerConfigException(PluginManagerException): """Exception for plugin framework configuration errors. """ pass # ============================================================================= base -class PluginManager( object ): +class PluginManager(object): """ Plugins represents an section of code that is not tracked in the Galaxy repository, allowing the addition of custom code to a Galaxy @@ -46,7 +48,7 @@ class PluginManager( object ): to be inherited. """ - def __init__( self, app, directories_setting=None, skip_bad_plugins=True, **kwargs ): + def __init__(self, app, directories_setting=None, skip_bad_plugins=True, **kwargs): """ Set up the manager and load all plugins. @@ -64,19 +66,19 @@ def __init__( self, app, directories_setting=None, skip_bad_plugins=True, **kwar self.skip_bad_plugins = skip_bad_plugins self.plugins = odict.odict() - self.directories = util.config_directories_from_setting( directories_setting, app.config.root ) + self.directories = util.config_directories_from_setting(directories_setting, app.config.root) self.load_configuration() self.load_plugins() - def load_configuration( self ): + def load_configuration(self): """ Override to load some framework/plugin specifc configuration. """ # Abstract method return True - def load_plugins( self ): + def load_plugins(self): """ Search ``self.directories`` for potential plugins, load them, and cache in ``self.plugins``. @@ -85,24 +87,24 @@ def load_plugins( self ): """ for plugin_path in self.find_plugins(): try: - plugin = self.load_plugin( plugin_path ) + plugin = self.load_plugin(plugin_path) if plugin and plugin.name not in self.plugins: - self.plugins[ plugin.name ] = plugin - log.info( '%s, loaded plugin: %s', self, plugin.name ) + self.plugins[plugin.name] = plugin + log.info('%s, loaded plugin: %s', self, plugin.name) # NOTE: prevent silent, implicit overwrite here (two plugins in two diff directories) # TODO: overwriting may be desired elif plugin and plugin.name in self.plugins: - log.warning( '%s, plugin with name already exists: %s. Skipping...', self, plugin.name ) + log.warning('%s, plugin with name already exists: %s. Skipping...', self, plugin.name) except Exception: if not self.skip_bad_plugins: raise - log.exception( 'Plugin loading raised exception: %s. Skipping...', plugin_path ) + log.exception('Plugin loading raised exception: %s. Skipping...', plugin_path) return self.plugins - def find_plugins( self ): + def find_plugins(self): """ Return the directory paths of plugins within ``self.directories``. @@ -113,12 +115,12 @@ def find_plugins( self ): # due to the ordering of listdir, there is an implicit plugin loading order here # could instead explicitly list on/off in master config file for directory in self.directories: - for plugin_dir in sorted( os.listdir( directory ) ): - plugin_path = os.path.join( directory, plugin_dir ) - if self.is_plugin( plugin_path ): + for plugin_dir in sorted(os.listdir(directory)): + plugin_path = os.path.join(directory, plugin_dir) + if self.is_plugin(plugin_path): yield plugin_path - def is_plugin( self, plugin_path ): + def is_plugin(self, plugin_path): """ Determines whether the given filesystem path contains a plugin. @@ -130,11 +132,11 @@ def is_plugin( self, plugin_path ): :rtype: bool :returns: True if the path contains a plugin """ - if not os.path.isdir( plugin_path ): + if not os.path.isdir(plugin_path): return False return True - def load_plugin( self, plugin_path ): + def load_plugin(self, plugin_path): """ Create, load, and/or initialize the plugin and return it. @@ -151,14 +153,14 @@ def load_plugin( self, plugin_path ): # TODO: need a better way to define plugin names # pro: filesystem name ensures uniqueness # con: rel. inflexible - name=os.path.split( plugin_path )[1], + name=os.path.split(plugin_path)[1], path=plugin_path ) return plugin # ============================================================================= plugin managers using hooks -class HookPluginManager( PluginManager ): +class HookPluginManager(PluginManager): """ A hook plugin is a directory containing python modules or packages that: * allow creating, including, and running custom code at specific 'hook' @@ -173,7 +175,7 @@ class HookPluginManager( PluginManager ): loading_point_filename = 'plugin.py' hook_fn_prefix = 'hook_' - def is_plugin( self, plugin_path ): + def is_plugin(self, plugin_path): """ Determines whether the given filesystem path contains a hookable plugin. @@ -186,14 +188,14 @@ def is_plugin( self, plugin_path ): :rtype: bool :returns: True if the path contains a plugin """ - if not super( HookPluginManager, self ).is_plugin( plugin_path ): + if not super(HookPluginManager, self).is_plugin(plugin_path): return False # TODO: possibly switch to .py or __init__.py - if self.loading_point_filename not in os.listdir( plugin_path ): + if self.loading_point_filename not in os.listdir(plugin_path): return False return True - def load_plugin( self, plugin_path ): + def load_plugin(self, plugin_path): """ Import the plugin ``loading_point_filename`` and attach to the plugin bunch. @@ -207,13 +209,13 @@ def load_plugin( self, plugin_path ): :rtype: ``util.bunch.Bunch`` :returns: the loaded plugin object """ - plugin = super( HookPluginManager, self ).load_plugin( plugin_path ) + plugin = super(HookPluginManager, self).load_plugin(plugin_path) loading_point_name = self.loading_point_filename[:-3] - plugin[ 'module' ] = self.import_plugin_module( loading_point_name, plugin ) + plugin['module'] = self.import_plugin_module(loading_point_name, plugin) return plugin - def import_plugin_module( self, loading_point_name, plugin, import_as=None ): + def import_plugin_module(self, loading_point_name, plugin, import_as=None): """ Import the plugin code and cache the module in the plugin object. @@ -229,20 +231,20 @@ def import_plugin_module( self, loading_point_name, plugin, import_as=None ): :returns: the loaded plugin object """ # add this name to import_as (w/ default to plugin.name) to prevent namespace pollution in sys.modules - import_as = '%s.%s' % ( __name__, ( import_as or plugin.name ) ) - module_file, pathname, description = imp.find_module( loading_point_name, [ plugin.path ] ) + import_as = '%s.%s' % (__name__, (import_as or plugin.name)) + module_file, pathname, description = imp.find_module(loading_point_name, [plugin.path]) try: # TODO: hate this hack but only way to get package imports inside the plugin to work? - sys.path.append( plugin.path ) + sys.path.append(plugin.path) # sys.modules will now have import_as in its list - module = imp.load_module( import_as, module_file, pathname, description ) + module = imp.load_module(import_as, module_file, pathname, description) finally: module_file.close() if plugin.path in sys.path: - sys.path.remove( plugin.path ) + sys.path.remove(plugin.path) return module - def run_hook( self, hook_name, *args, **kwargs ): + def run_hook(self, hook_name, *args, **kwargs): """ Search all plugins for a function named ``hook_fn_prefix`` + ``hook_name`` and run it passing in args and kwargs. @@ -259,23 +261,23 @@ def run_hook( self, hook_name, *args, **kwargs ): # TODO: is hook prefix necessary? # TODO: could be made more efficient if cached by hook_name in the manager on load_plugin # (low maint. overhead since no dynamic loading/unloading of plugins) - hook_fn_name = ''.join([ self.hook_fn_prefix, hook_name ]) + hook_fn_name = ''.join([self.hook_fn_prefix, hook_name]) returned = {} for plugin_name, plugin in self.plugins.items(): - hook_fn = getattr( plugin.module, hook_fn_name, None ) + hook_fn = getattr(plugin.module, hook_fn_name, None) - if hook_fn and hasattr( hook_fn, '__call__' ): + if hook_fn and hasattr(hook_fn, '__call__'): try: - fn_returned = hook_fn( *args, **kwargs ) - returned[ plugin.name ] = fn_returned + fn_returned = hook_fn(*args, **kwargs) + returned[plugin.name] = fn_returned except Exception: # fail gracefully and continue with other plugins - log.exception( 'Hook function "%s" failed for plugin "%s"', hook_name, plugin.name ) + log.exception('Hook function "%s" failed for plugin "%s"', hook_name, plugin.name) # not sure of utility of this - seems better to be fire-and-forget pub-sub return returned - def filter_hook( self, hook_name, hook_arg, *args, **kwargs ): + def filter_hook(self, hook_name, hook_arg, *args, **kwargs): """ Search all plugins for a function named ``hook_fn_prefix`` + ``hook_name`` and run the first with ``hook_arg`` and every function after with the @@ -291,29 +293,29 @@ def filter_hook( self, hook_name, hook_arg, *args, **kwargs ): :rtype: any :returns: the modified hook_arg """ - hook_fn_name = ''.join([ self.hook_fn_prefix, hook_name ]) + hook_fn_name = ''.join([self.hook_fn_prefix, hook_name]) for plugin_name, plugin in self.plugins.items(): - hook_fn = getattr( plugin.module, hook_fn_name, None ) + hook_fn = getattr(plugin.module, hook_fn_name, None) - if hook_fn and hasattr( hook_fn, '__call__' ): + if hook_fn and hasattr(hook_fn, '__call__'): try: - hook_arg = hook_fn( hook_arg, *args, **kwargs ) + hook_arg = hook_fn(hook_arg, *args, **kwargs) except Exception: # fail gracefully and continue with other plugins - log.exception( 'Filter hook function "%s" failed for plugin "%s"', hook_name, plugin.name ) + log.exception('Filter hook function "%s" failed for plugin "%s"', hook_name, plugin.name) # may have been altered by hook fns, return return hook_arg -class PluginManagerStaticException( PluginManagerException ): +class PluginManagerStaticException(PluginManagerException): """Exception for plugin framework static directory set up errors. """ pass -class PluginManagerTemplateException( PluginManagerException ): +class PluginManagerTemplateException(PluginManagerException): """Exception for plugin framework template directory and template rendering errors. """ @@ -321,7 +323,7 @@ class PluginManagerTemplateException( PluginManagerException ): # ============================================================================= base -class PageServingPluginManager( PluginManager ): +class PageServingPluginManager(PluginManager): """ Page serving plugins are files/directories that: * are not tracked in the Galaxy repository and allow adding custom code @@ -346,7 +348,7 @@ class PageServingPluginManager( PluginManager ): #: name of files to search for additional template lookup directories additional_template_paths_config_filename = 'additional_template_paths.xml' - def __init__( self, app, base_url='', template_cache_dir=None, **kwargs ): + def __init__(self, app, base_url='', template_cache_dir=None, **kwargs): """ Set up the manager and load all plugins. @@ -360,24 +362,24 @@ def __init__( self, app, base_url='', template_cache_dir=None, **kwargs ): """ self.base_url = base_url or self.DEFAULT_BASE_URL if not self.base_url: - raise PluginManagerException( 'base_url or DEFAULT_BASE_URL required' ) + raise PluginManagerException('base_url or DEFAULT_BASE_URL required') self.template_cache_dir = template_cache_dir self.additional_template_paths = [] - super( PageServingPluginManager, self ).__init__( app, **kwargs ) + super(PageServingPluginManager, self).__init__(app, **kwargs) - def load_configuration( self ): + def load_configuration(self): """ Load framework wide configuration, including: additional template lookup directories """ for directory in self.directories: - possible_path = os.path.join( directory, self.additional_template_paths_config_filename ) - if os.path.exists( possible_path ): - added_paths = self.parse_additional_template_paths( possible_path, directory ) - self.additional_template_paths.extend( added_paths ) + possible_path = os.path.join(directory, self.additional_template_paths_config_filename) + if os.path.exists(possible_path): + added_paths = self.parse_additional_template_paths(possible_path, directory) + self.additional_template_paths.extend(added_paths) - def parse_additional_template_paths( self, config_filepath, base_directory ): + def parse_additional_template_paths(self, config_filepath, base_directory): """ Parse an XML config file at `config_filepath` for template paths (relative to `base_directory`) to add to each plugin's template lookup. @@ -391,14 +393,14 @@ def parse_additional_template_paths( self, config_filepath, base_directory ): :param base_directory: path prefixed to new, relative template paths """ additional_paths = [] - xml_tree = util.parse_xml( config_filepath ) + xml_tree = util.parse_xml(config_filepath) paths_list = xml_tree.getroot() - for rel_path_elem in paths_list.findall( 'path' ): + for rel_path_elem in paths_list.findall('path'): if rel_path_elem.text is not None: - additional_paths.append( os.path.join( base_directory, rel_path_elem.text ) ) + additional_paths.append(os.path.join(base_directory, rel_path_elem.text)) return additional_paths - def is_plugin( self, plugin_path ): + def is_plugin(self, plugin_path): """ Determines whether the given filesystem path contains a plugin. @@ -413,15 +415,15 @@ def is_plugin( self, plugin_path ): :rtype: bool :returns: True if the path contains a plugin """ - if not super( PageServingPluginManager, self ).is_plugin( plugin_path ): + if not super(PageServingPluginManager, self).is_plugin(plugin_path): return False # reject only if we don't have either - listdir = os.listdir( plugin_path ) - if( ( 'templates' not in listdir ) and ( 'static' not in listdir ) ): + listdir = os.listdir(plugin_path) + if(('templates' not in listdir) and ('static' not in listdir)): return False return True - def load_plugin( self, plugin_path ): + def load_plugin(self, plugin_path): """ Create the plugin and decorate with static and/or template paths and urls. @@ -435,15 +437,15 @@ def load_plugin( self, plugin_path ): :rtype: ``util.bunch.Bunch`` :returns: the loaded plugin object """ - plugin = super( PageServingPluginManager, self ).load_plugin( plugin_path ) + plugin = super(PageServingPluginManager, self).load_plugin(plugin_path) # TODO: urlencode? - plugin[ 'base_url' ] = '/'.join([ self.base_url, plugin.name ]) - plugin = self._set_up_static_plugin( plugin ) - plugin = self._set_up_template_plugin( plugin ) + plugin['base_url'] = '/'.join([self.base_url, plugin.name]) + plugin = self._set_up_static_plugin(plugin) + plugin = self._set_up_template_plugin(plugin) return plugin - def _set_up_static_plugin( self, plugin ): + def _set_up_static_plugin(self, plugin): """ Decorate the plugin with paths and urls needed to serve static content. @@ -459,15 +461,15 @@ def _set_up_static_plugin( self, plugin ): :rtype: ``util.bunch.Bunch`` :returns: the loaded plugin object """ - plugin[ 'serves_static' ] = False - static_path = os.path.join( plugin.path, 'static' ) - if self.serves_static and os.path.isdir( static_path ): + plugin['serves_static'] = False + static_path = os.path.join(plugin.path, 'static') + if self.serves_static and os.path.isdir(static_path): plugin.serves_static = True - plugin[ 'static_path' ] = static_path - plugin[ 'static_url' ] = '/'.join([ plugin.base_url, 'static' ]) + plugin['static_path'] = static_path + plugin['static_url'] = '/'.join([plugin.base_url, 'static']) return plugin - def _set_up_template_plugin( self, plugin ): + def _set_up_template_plugin(self, plugin): """ Decorate the plugin with paths needed to fill templates. @@ -484,16 +486,16 @@ def _set_up_template_plugin( self, plugin ): :rtype: ``util.bunch.Bunch`` :returns: the loaded plugin object """ - plugin[ 'serves_templates' ] = False - template_path = os.path.join( plugin.path, 'templates' ) - if self.serves_templates and os.path.isdir( template_path ): + plugin['serves_templates'] = False + template_path = os.path.join(plugin.path, 'templates') + if self.serves_templates and os.path.isdir(template_path): plugin.serves_templates = True - plugin[ 'template_path' ] = template_path - plugin[ 'template_lookup' ] = self.build_plugin_template_lookup( plugin ) + plugin['template_path'] = template_path + plugin['template_lookup'] = self.build_plugin_template_lookup(plugin) return plugin # ------------------------------------------------------------------------- serving static files - def get_static_urls_and_paths( self ): + def get_static_urls_and_paths(self): """ For each plugin, return a 2-tuple where the first element is a url path to the plugin's static files and the second is a filesystem path to those @@ -508,11 +510,11 @@ def get_static_urls_and_paths( self ): urls_and_paths = [] for plugin in self.plugins.values(): if plugin.serves_static: - urls_and_paths.append( ( plugin.static_url, plugin.static_path ) ) + urls_and_paths.append((plugin.static_url, plugin.static_path)) return urls_and_paths # ------------------------------------------------------------------------- templates - def build_plugin_template_lookup( self, plugin ): + def build_plugin_template_lookup(self, plugin): """ Builds the object that searches for templates (cached or not) when rendering. @@ -525,12 +527,12 @@ def build_plugin_template_lookup( self, plugin ): return None template_lookup_paths = plugin.template_path if self.additional_template_paths: - template_lookup_paths = [ template_lookup_paths ] + self.additional_template_paths - template_lookup = self._create_mako_template_lookup( self.template_cache_dir, template_lookup_paths ) + template_lookup_paths = [template_lookup_paths] + self.additional_template_paths + template_lookup = self._create_mako_template_lookup(self.template_cache_dir, template_lookup_paths) return template_lookup - def _create_mako_template_lookup( self, cache_dir, paths, - collection_size=DEFAULT_TEMPLATE_COLLECTION_SIZE, output_encoding=DEFAULT_TEMPLATE_ENCODING ): + def _create_mako_template_lookup(self, cache_dir, paths, + collection_size=DEFAULT_TEMPLATE_COLLECTION_SIZE, output_encoding=DEFAULT_TEMPLATE_ENCODING): """ Create a ``TemplateLookup`` with defaults. @@ -542,9 +544,9 @@ def _create_mako_template_lookup( self, cache_dir, paths, directories=paths, module_directory=cache_dir, collection_size=collection_size, - output_encoding=output_encoding ) + output_encoding=output_encoding) - def fill_template( self, trans, plugin, template_filename, **kwargs ): + def fill_template(self, trans, plugin, template_filename, **kwargs): """ Pass control over to trans and render ``template_filename``. @@ -558,7 +560,7 @@ def fill_template( self, trans, plugin, template_filename, **kwargs ): :returns: rendered template """ # defined here to be overridden - return trans.fill_template( template_filename, template_lookup=plugin.template_lookup, **kwargs ) + return trans.fill_template(template_filename, template_lookup=plugin.template_lookup, **kwargs) # TODO: add fill_template fn that is able to load extra libraries beforehand (and remove after) # TODO: add template helpers specific to the plugins @@ -566,12 +568,12 @@ def fill_template( self, trans, plugin, template_filename, **kwargs ): # ============================================================================= -class Plugin( object ): +class Plugin(object): """ Plugin as object/class. """ - def __init__( self, app, path, name, config, context=None, **kwargs ): + def __init__(self, app, path, name, config, context=None, **kwargs): context = context or {} self.app = app diff --git a/lib/galaxy/web/form_builder.py b/lib/galaxy/web/form_builder.py index f69c8b9490b0..ffb9cbbdb0d2 100644 --- a/lib/galaxy/web/form_builder.py +++ b/lib/galaxy/web/form_builder.py @@ -11,25 +11,25 @@ class BaseField(object): - def __init__( self, name, value=None, label=None, **kwds ): + def __init__(self, name, value=None, label=None, **kwds): self.name = name self.label = label self.value = value - self.disabled = kwds.get( 'disabled', False ) - self.optional = kwds.get( 'optional', True ) and kwds.get( 'required', 'optional' ) == 'optional' - self.help = kwds.get( 'helptext' ) + self.disabled = kwds.get('disabled', False) + self.optional = kwds.get('optional', True) and kwds.get('required', 'optional') == 'optional' + self.help = kwds.get('helptext') - def get_html( self, prefix="" ): + def get_html(self, prefix=""): """Returns the html widget corresponding to the parameter""" - raise TypeError( "Abstract Method" ) + raise TypeError("Abstract Method") - def get_disabled_str( self, disabled=False ): + def get_disabled_str(self, disabled=False): if disabled: return ' disabled="disabled"' else: return '' - def to_dict( self ): + def to_dict(self): return { 'name' : self.name, 'label' : self.label, @@ -49,21 +49,22 @@ class TextField(BaseField): >>> print TextField( "bins", size=4, value="default" ).get_html() """ - def __init__( self, name, size=None, value=None, **kwds ): - super( TextField, self ).__init__( name, value, **kwds ) - self.size = int( size or 10 ) - def get_html( self, prefix="", disabled=False ): - value = unicodify( self.value or "" ) - return unicodify( '' - % ( prefix, self.name, self.size, escape( value, quote=True ), self.get_disabled_str( disabled ) ) ) + def __init__(self, name, size=None, value=None, **kwds): + super(TextField, self).__init__(name, value, **kwds) + self.size = int(size or 10) + + def get_html(self, prefix="", disabled=False): + value = unicodify(self.value or "") + return unicodify('' + % (prefix, self.name, self.size, escape(value, quote=True), self.get_disabled_str(disabled))) def set_size(self, size): - self.size = int( size ) + self.size = int(size) - def to_dict( self ): - d = super( TextField, self ).to_dict() - d[ 'type' ] = 'text' + def to_dict(self): + d = super(TextField, self).to_dict() + d['type'] = 'text' return d @@ -76,22 +77,23 @@ class PasswordField(BaseField): >>> print PasswordField( "bins", size=4, value="default" ).get_html() """ - def __init__( self, name, size=None, value=None, **kwds ): - super( PasswordField, self ).__init__( name, value, **kwds ) + + def __init__(self, name, size=None, value=None, **kwds): + super(PasswordField, self).__init__(name, value, **kwds) self.name = name - self.size = int( size or 10 ) + self.size = int(size or 10) self.value = value or "" - def get_html( self, prefix="", disabled=False ): - return unicodify( '' - % ( prefix, self.name, self.size, escape( str( self.value ), quote=True ), self.get_disabled_str( disabled ) ) ) + def get_html(self, prefix="", disabled=False): + return unicodify('' + % (prefix, self.name, self.size, escape(str(self.value), quote=True), self.get_disabled_str(disabled))) def set_size(self, size): - self.size = int( size ) + self.size = int(size) - def to_dict( self ): - d = super( PasswordField, self ).to_dict() - d[ 'type' ] = 'password' + def to_dict(self): + d = super(PasswordField, self).to_dict() + d['type'] = 'password' return d @@ -106,8 +108,8 @@ class TextArea(BaseField): """ _DEFAULT_SIZE = "5x25" - def __init__( self, name, size=None, value=None, **kwds ): - super( TextArea, self ).__init__( name, value, **kwds ) + def __init__(self, name, size=None, value=None, **kwds): + super(TextArea, self).__init__(name, value, **kwds) self.name = name size = size or self._DEFAULT_SIZE self.size = size.split("x") @@ -115,18 +117,18 @@ def __init__( self, name, size=None, value=None, **kwds ): self.cols = int(self.size[-1]) self.value = value or "" - def get_html( self, prefix="", disabled=False ): - return unicodify( '' - % ( prefix, self.name, self.rows, self.cols, self.get_disabled_str( disabled ), escape( str( self.value ), quote=True ) ) ) + def get_html(self, prefix="", disabled=False): + return unicodify('' + % (prefix, self.name, self.rows, self.cols, self.get_disabled_str(disabled), escape(str(self.value), quote=True))) def set_size(self, rows, cols): self.rows = rows self.cols = cols - def to_dict( self ): - d = super( TextArea, self ).to_dict() - d[ 'type' ] = 'text' - d[ 'area' ] = True + def to_dict(self): + d = super(TextArea, self).to_dict() + d['type'] = 'text' + d['area'] = True return d @@ -140,43 +142,43 @@ class CheckboxField(BaseField): """ - def __init__( self, name, checked=None, refresh_on_change=False, refresh_on_change_values=None, value=None, **kwds ): - super( CheckboxField, self ).__init__( name, value, **kwds ) + def __init__(self, name, checked=None, refresh_on_change=False, refresh_on_change_values=None, value=None, **kwds): + super(CheckboxField, self).__init__(name, value, **kwds) self.name = name - self.checked = ( checked is True ) or ( isinstance( checked, string_types ) and ( checked.lower() in ( "yes", "true", "on" ) ) ) + self.checked = (checked is True) or (isinstance(checked, string_types) and (checked.lower() in ("yes", "true", "on"))) self.refresh_on_change = refresh_on_change self.refresh_on_change_values = refresh_on_change_values or [] if self.refresh_on_change: self.refresh_on_change_text = ' refresh_on_change="true" ' if self.refresh_on_change_values: - self.refresh_on_change_text = '%s refresh_on_change_values="%s" ' % ( self.refresh_on_change_text, ",".join( self.refresh_on_change_values ) ) + self.refresh_on_change_text = '%s refresh_on_change_values="%s" ' % (self.refresh_on_change_text, ",".join(self.refresh_on_change_values)) else: self.refresh_on_change_text = '' - def get_html( self, prefix="", disabled=False ): + def get_html(self, prefix="", disabled=False): if self.checked: checked_text = ' checked="checked"' else: checked_text = '' id_name = prefix + self.name - return unicodify( '' - % ( id_name, id_name, checked_text, self.get_disabled_str( disabled ), self.refresh_on_change_text, id_name, self.get_disabled_str( disabled ) ) ) + return unicodify('' + % (id_name, id_name, checked_text, self.get_disabled_str(disabled), self.refresh_on_change_text, id_name, self.get_disabled_str(disabled))) @staticmethod - def is_checked( value ): - if value in [ True, "true" ]: + def is_checked(value): + if value in [True, "true"]: return True - return isinstance( value, list ) and ( '__CHECKED__' in value or len( value ) == 2 ) + return isinstance(value, list) and ('__CHECKED__' in value or len(value) == 2) def set_checked(self, value): - if isinstance( value, string_types ): - self.checked = value.lower() in [ "yes", "true", "on" ] + if isinstance(value, string_types): + self.checked = value.lower() in ["yes", "true", "on"] else: self.checked = value - def to_dict( self ): - d = super( CheckboxField, self ).to_dict() - d[ 'type' ] = 'boolean' + def to_dict(self): + d = super(CheckboxField, self).to_dict() + d['type'] = 'boolean' return d @@ -190,20 +192,41 @@ class FileField(BaseField): """ - def __init__( self, name, value=None, ajax=False, **kwds ): - super( FileField, self ).__init__( name, value, **kwds ) + def __init__(self, name, value=None, ajax=False, **kwds): + super(FileField, self).__init__(name, value, **kwds) self.name = name self.ajax = ajax self.value = value - def get_html( self, prefix="" ): + def get_html(self, prefix=""): value_text = "" if self.value: - value_text = ' value="%s"' % escape( str( self.value ), quote=True ) + value_text = ' value="%s"' % escape(str(self.value), quote=True) ajax_text = "" if self.ajax: ajax_text = ' galaxy-ajax-upload="true"' - return unicodify( '' % ( prefix, self.name, ajax_text, value_text ) ) + return unicodify('' % (prefix, self.name, ajax_text, value_text)) + + +class GenomespaceFileField(BaseField): + """ + A genomspace file browser field. + """ + def __init__(self, name, value=None): + self.name = name + self.value = value or "" + + def get_html(self, prefix=""): + return unicodify('' + ' ' + '' + 'Browse'.format(prefix, self.name, escape(str(self.value), quote=True))) + + def to_dict(self): + return dict(name=self.name, + token_field=self.token_field) class HiddenField(BaseField): @@ -213,18 +236,19 @@ class HiddenField(BaseField): >>> print HiddenField( "foo", 100 ).get_html() """ - def __init__( self, name, value=None, **kwds ): - super( HiddenField, self ).__init__( name, value, **kwds ) + + def __init__(self, name, value=None, **kwds): + super(HiddenField, self).__init__(name, value, **kwds) self.name = name self.value = value or "" - def get_html( self, prefix="" ): - return unicodify( '' % ( prefix, self.name, escape( str( self.value ), quote=True ) ) ) + def get_html(self, prefix=""): + return unicodify('' % (prefix, self.name, escape(str(self.value), quote=True))) - def to_dict( self ): - d = super( HiddenField, self ).to_dict() - d[ 'type' ] = 'hidden' - d[ 'hidden' ] = True + def to_dict(self): + d = super(HiddenField, self).to_dict() + d['type'] = 'hidden' + d['hidden'] = True return d @@ -265,8 +289,9 @@ class SelectField(BaseField):
    """ - def __init__( self, name, multiple=None, display=None, refresh_on_change=False, refresh_on_change_values=None, size=None, field_id=None, value=None, selectlist=None, **kwds ): - super( SelectField, self ).__init__( name, value, **kwds ) + + def __init__(self, name, multiple=None, display=None, refresh_on_change=False, refresh_on_change_values=None, size=None, field_id=None, value=None, selectlist=None, **kwds): + super(SelectField, self).__init__(name, value, **kwds) self.name = name self.field_id = field_id self.multiple = multiple or False @@ -277,88 +302,88 @@ def __init__( self, name, multiple=None, display=None, refresh_on_change=False, if display == "checkboxes": assert multiple, "Checkbox display only supported for multiple select" elif display == "radio": - assert not( multiple ), "Radio display only supported for single select" + assert not(multiple), "Radio display only supported for single select" elif display is not None: - raise Exception( "Unknown display type: %s" % display ) + raise Exception("Unknown display type: %s" % display) self.display = display self.refresh_on_change = refresh_on_change self.refresh_on_change_values = refresh_on_change_values or [] if self.refresh_on_change: self.refresh_on_change_text = ' refresh_on_change="true"' if self.refresh_on_change_values: - self.refresh_on_change_text = '%s refresh_on_change_values="%s"' % ( self.refresh_on_change_text, escape( ",".join( self.refresh_on_change_values ), quote=True ) ) + self.refresh_on_change_text = '%s refresh_on_change_values="%s"' % (self.refresh_on_change_text, escape(",".join(self.refresh_on_change_values), quote=True)) else: self.refresh_on_change_text = '' - def add_option( self, text, value, selected=False ): - self.options.append( ( text, value, selected ) ) + def add_option(self, text, value, selected=False): + self.options.append((text, value, selected)) - def get_html( self, prefix="", disabled=False, extra_attr=None ): + def get_html(self, prefix="", disabled=False, extra_attr=None): if extra_attr is not None: - self.extra_attributes = ' %s' % ' '.join( [ '%s="%s"' % ( k, escape( v ) ) for k, v in extra_attr.items() ] ) + self.extra_attributes = ' %s' % ' '.join(['%s="%s"' % (k, escape(v)) for k, v in extra_attr.items()]) else: self.extra_attributes = '' if self.display == "checkboxes": - return self.get_html_checkboxes( prefix, disabled ) + return self.get_html_checkboxes(prefix, disabled) elif self.display == "radio": - return self.get_html_radio( prefix, disabled ) + return self.get_html_radio(prefix, disabled) else: - return self.get_html_default( prefix, disabled ) + return self.get_html_default(prefix, disabled) - def get_html_checkboxes( self, prefix="", disabled=False ): + def get_html_checkboxes(self, prefix="", disabled=False): rval = [] ctr = 0 - if len( self.options ) > 1: - rval.append( '
    ' % ( prefix, self.name ) ) # placeholder for the insertion of the Select All/Unselect All buttons + if len(self.options) > 1: + rval.append('
    ' % (prefix, self.name)) # placeholder for the insertion of the Select All/Unselect All buttons for text, value, selected in self.options: style = "" - text = unicodify( text ) - escaped_value = escape( unicodify( value ), quote=True ) + text = unicodify(text) + escaped_value = escape(unicodify(value), quote=True) uniq_id = "%s%s|%s" % (prefix, self.name, escaped_value) if len(self.options) > 2 and ctr % 2 == 1: style = " class=\"odd_row\"" selected_text = "" if selected: selected_text = " checked='checked'" - rval.append( '' - % ( style, prefix, self.name, escaped_value, uniq_id, selected_text, self.get_disabled_str( disabled ), self.extra_attributes, uniq_id, escape( text, quote=True ) ) ) + rval.append('' + % (style, prefix, self.name, escaped_value, uniq_id, selected_text, self.get_disabled_str(disabled), self.extra_attributes, uniq_id, escape(text, quote=True))) ctr += 1 - return unicodify( "\n".join( rval ) ) + return unicodify("\n".join(rval)) - def get_html_radio( self, prefix="", disabled=False ): + def get_html_radio(self, prefix="", disabled=False): rval = [] ctr = 0 for text, value, selected in self.options: style = "" - escaped_value = escape( str( value ), quote=True ) + escaped_value = escape(str(value), quote=True) uniq_id = "%s%s|%s" % (prefix, self.name, escaped_value) if len(self.options) > 2 and ctr % 2 == 1: style = " class=\"odd_row\"" selected_text = "" if selected: selected_text = " checked='checked'" - rval.append( '' - % ( style, - prefix, - self.name, - self.refresh_on_change_text, - escaped_value, - uniq_id, - selected_text, - self.get_disabled_str( disabled ), - self.extra_attributes, - uniq_id, - text ) ) + rval.append('' + % (style, + prefix, + self.name, + self.refresh_on_change_text, + escaped_value, + uniq_id, + selected_text, + self.get_disabled_str(disabled), + self.extra_attributes, + uniq_id, + text)) ctr += 1 - return unicodify( "\n".join( rval ) ) + return unicodify("\n".join(rval)) - def get_html_default( self, prefix="", disabled=False ): + def get_html_default(self, prefix="", disabled=False): if self.multiple: multiple = " multiple" else: multiple = "" if self.size: - size = ' size="%s"' % str( self.size ) + size = ' size="%s"' % str(self.size) else: size = '' rval = [] @@ -367,23 +392,23 @@ def get_html_default( self, prefix="", disabled=False ): if selected: selected_text = " selected" last_selected_value = value - if not isinstance( last_selected_value, string_types ): - last_selected_value = str( last_selected_value ) + if not isinstance(last_selected_value, string_types): + last_selected_value = str(last_selected_value) else: selected_text = "" - rval.append( '' % ( escape( unicodify( value ), quote=True ), selected_text, escape( unicodify( text ), quote=True ) ) ) + rval.append('' % (escape(unicodify(value), quote=True), selected_text, escape(unicodify(text), quote=True))) if last_selected_value: - last_selected_value = ' last_selected_value="%s"' % escape( unicodify( last_selected_value ), quote=True ) + last_selected_value = ' last_selected_value="%s"' % escape(unicodify(last_selected_value), quote=True) if self.field_id is not None: id_string = ' id="%s"' % self.field_id else: id_string = '' - rval.insert( 0, '' ) - return unicodify( "\n".join( rval ) ) + rval.insert(0, '') + return unicodify("\n".join(rval)) - def get_selected( self, return_label=False, return_value=False, multi=False ): + def get_selected(self, return_label=False, return_value=False, multi=False): ''' Return the currently selected option's label, value or both as a tuple. For multi-select lists, a list is returned. @@ -394,73 +419,73 @@ def get_selected( self, return_label=False, return_value=False, multi=False ): if selected: if return_label and return_value: if multi: - selected_options.append( ( label, value ) ) + selected_options.append((label, value)) else: - return ( label, value ) + return (label, value) elif return_label: if multi: - selected_options.append( label ) + selected_options.append(label) else: return label elif return_value: if multi: - selected_options.append( value ) + selected_options.append(value) else: return value if multi: return selected_options return None - def to_dict( self ): - d = super( SelectField, self ).to_dict() - d[ 'type' ] = 'select' - d[ 'display' ] = self.display - d[ 'multiple' ] = self.multiple - d[ 'data' ] = [] + def to_dict(self): + d = super(SelectField, self).to_dict() + d['type'] = 'select' + d['display'] = self.display + d['multiple'] = self.multiple + d['data'] = [] for value in self.selectlist: - d[ 'data' ].append( { 'label': value, 'value': value } ) + d['data'].append({'label': value, 'value': value}) return d class AddressField(BaseField): @staticmethod def fields(): - return [ ( "desc", "Short address description", "Required" ), - ( "name", "Name", "" ), - ( "institution", "Institution", "" ), - ( "address", "Address", "" ), - ( "city", "City", "" ), - ( "state", "State/Province/Region", "" ), - ( "postal_code", "Postal Code", "" ), - ( "country", "Country", "" ), - ( "phone", "Phone", "" ) ] + return [("desc", "Short address description", "Required"), + ("name", "Name", ""), + ("institution", "Institution", ""), + ("address", "Address", ""), + ("city", "City", ""), + ("state", "State/Province/Region", ""), + ("postal_code", "Postal Code", ""), + ("country", "Country", ""), + ("phone", "Phone", "")] def __init__(self, name, user=None, value=None, params=None, security=None, **kwds): - super( AddressField, self ).__init__( name, value, **kwds ) + super(AddressField, self).__init__(name, value, **kwds) self.user = user self.security = security self.select_address = None self.params = params - def get_html( self, disabled=False ): + def get_html(self, disabled=False): address_html = '' add_ids = ['none'] if self.user: for a in self.user.addresses: - add_ids.append( str( a.id ) ) - add_ids.append( 'new' ) - self.select_address = SelectField( self.name, - refresh_on_change=True, - refresh_on_change_values=add_ids ) + add_ids.append(str(a.id)) + add_ids.append('new') + self.select_address = SelectField(self.name, + refresh_on_change=True, + refresh_on_change_values=add_ids) if self.value == 'none': - self.select_address.add_option( 'Select one', 'none', selected=True ) + self.select_address.add_option('Select one', 'none', selected=True) else: - self.select_address.add_option( 'Select one', 'none' ) + self.select_address.add_option('Select one', 'none') if self.user: for a in self.user.addresses: if not a.deleted: - if self.value == str( a.id ): - self.select_address.add_option( a.desc, str( a.id ), selected=True ) + if self.value == str(a.id): + self.select_address.add_option(a.desc, str(a.id), selected=True) # Display this address address_html += '''
    @@ -468,18 +493,18 @@ def get_html( self, disabled=False ):
    ''' % a.get_html() else: - self.select_address.add_option( a.desc, str( a.id ) ) + self.select_address.add_option(a.desc, str(a.id)) if self.value == 'new': - self.select_address.add_option( 'Add a new address', 'new', selected=True ) + self.select_address.add_option('Add a new address', 'new', selected=True) for field_name, label, help_text in self.fields(): - add_field = TextField( self.name + '_' + field_name, - 40, - restore_text( self.params.get( self.name + '_' + field_name, '' ) ) ) + add_field = TextField(self.name + '_' + field_name, + 40, + restore_text(self.params.get(self.name + '_' + field_name, ''))) address_html += '''
    %s - ''' % ( label, add_field.get_html( disabled=disabled ) ) + ''' % (label, add_field.get_html(disabled=disabled)) if help_text: address_html += '''
    @@ -490,23 +515,23 @@ def get_html( self, disabled=False ):
    ''' else: - self.select_address.add_option( 'Add a new address', 'new' ) - return self.select_address.get_html( disabled=disabled ) + address_html + self.select_address.add_option('Add a new address', 'new') + return self.select_address.get_html(disabled=disabled) + address_html - def to_dict( self ): - d = super( AddressField, self ).to_dict() - d[ 'type' ] = 'select' - d[ 'data' ] = [] + def to_dict(self): + d = super(AddressField, self).to_dict() + d['type'] = 'select' + d['data'] = [] if self.user and self.security: for a in self.user.addresses: if not a.deleted: - d[ 'data' ].append( { 'label': a.desc, 'value': self.security.encode_id( a.id ) } ) + d['data'].append({'label': a.desc, 'value': self.security.encode_id(a.id)}) return d -class WorkflowField( BaseField ): - def __init__( self, name, user=None, value=None, params=None, security=None, **kwds ): - super( WorkflowField, self ).__init__( name, value, **kwds ) +class WorkflowField(BaseField): + def __init__(self, name, user=None, value=None, params=None, security=None, **kwds): + super(WorkflowField, self).__init__(name, value, **kwds) self.name = name self.user = user self.value = value @@ -514,34 +539,34 @@ def __init__( self, name, user=None, value=None, params=None, security=None, **k self.select_workflow = None self.params = params - def get_html( self, disabled=False ): - self.select_workflow = SelectField( self.name ) + def get_html(self, disabled=False): + self.select_workflow = SelectField(self.name) if self.value == 'none': - self.select_workflow.add_option( 'Select one', 'none', selected=True ) + self.select_workflow.add_option('Select one', 'none', selected=True) else: - self.select_workflow.add_option( 'Select one', 'none' ) + self.select_workflow.add_option('Select one', 'none') if self.user: for a in self.user.stored_workflows: if not a.deleted: - if str( self.value ) == str( a.id ): - self.select_workflow.add_option( a.name, str( a.id ), selected=True ) + if str(self.value) == str(a.id): + self.select_workflow.add_option(a.name, str(a.id), selected=True) else: - self.select_workflow.add_option( a.name, str( a.id ) ) - return self.select_workflow.get_html( disabled=disabled ) + self.select_workflow.add_option(a.name, str(a.id)) + return self.select_workflow.get_html(disabled=disabled) - def to_dict( self ): - d = super( WorkflowField, self ).to_dict() - d[ 'type' ] = 'select' - d[ 'data' ] = [] + def to_dict(self): + d = super(WorkflowField, self).to_dict() + d['type'] = 'select' + d['data'] = [] if self.user and self.security: for a in self.user.stored_workflows: if not a.deleted: - d[ 'data' ].append( { 'label': a.name, 'value': self.security.encode_id( a.id ) } ) + d['data'].append({'label': a.name, 'value': self.security.encode_id(a.id)}) return d -class WorkflowMappingField( BaseField ): - def __init__( self, name, user=None, value=None, params=None, **kwd ): +class WorkflowMappingField(BaseField): + def __init__(self, name, user=None, value=None, params=None, **kwd): # DBTODO integrate this with the new __build_workflow approach in requests_common. As it is, not particularly useful. self.name = name self.user = user @@ -550,20 +575,20 @@ def __init__( self, name, user=None, value=None, params=None, **kwd ): self.params = params self.workflow_inputs = [] - def get_html( self, disabled=False ): - self.select_workflow = SelectField( self.name, refresh_on_change=True ) + def get_html(self, disabled=False): + self.select_workflow = SelectField(self.name, refresh_on_change=True) workflow_inputs = [] if self.value == 'none': - self.select_workflow.add_option( 'Select one', 'none', selected=True ) + self.select_workflow.add_option('Select one', 'none', selected=True) else: - self.select_workflow.add_option( 'Select one', 'none' ) + self.select_workflow.add_option('Select one', 'none') if self.user: for a in self.user.stored_workflows: if not a.deleted: - if str( self.value ) == str( a.id ): - self.select_workflow.add_option( a.name, str( a.id ), selected=True ) + if str(self.value) == str(a.id): + self.select_workflow.add_option(a.name, str(a.id), selected=True) else: - self.select_workflow.add_option( a.name, str( a.id ) ) + self.select_workflow.add_option(a.name, str(a.id)) if self.value and self.value != 'none': # Workflow selected. Find all inputs. for workflow in self.user.stored_workflows: @@ -571,14 +596,14 @@ def get_html( self, disabled=False ): for step in workflow.latest_workflow.steps: if step.type == 'data_input': if step.tool_inputs and "name" in step.tool_inputs: - workflow_inputs.append((step.tool_inputs['name'], TextField( '%s_%s' % (self.name, step.id), 20))) + workflow_inputs.append((step.tool_inputs['name'], TextField('%s_%s' % (self.name, step.id), 20))) # Do something more appropriate here and allow selection of inputs - return self.select_workflow.get_html( disabled=disabled ) + ''.join(['
    %s
    ' % (s[0], s[1].get_html()) for s in workflow_inputs]) + return self.select_workflow.get_html(disabled=disabled) + ''.join(['
    %s
    ' % (s[0], s[1].get_html()) for s in workflow_inputs]) -class HistoryField( BaseField ): - def __init__( self, name, user=None, value=None, params=None, security=None, **kwds ): - super( HistoryField, self ).__init__( name, value, **kwds ) +class HistoryField(BaseField): + def __init__(self, name, user=None, value=None, params=None, security=None, **kwds): + super(HistoryField, self).__init__(name, value, **kwds) self.name = name self.user = user self.value = value @@ -586,34 +611,34 @@ def __init__( self, name, user=None, value=None, params=None, security=None, **k self.select_history = None self.params = params - def get_html( self, disabled=False ): - self.select_history = SelectField( self.name ) + def get_html(self, disabled=False): + self.select_history = SelectField(self.name) if self.value == 'none': - self.select_history.add_option( 'No Import', 'none', selected=True ) - self.select_history.add_option( 'New History', 'new' ) + self.select_history.add_option('No Import', 'none', selected=True) + self.select_history.add_option('New History', 'new') else: - self.select_history.add_option( 'No Import', 'none' ) + self.select_history.add_option('No Import', 'none') if self.value == 'new': - self.select_history.add_option( 'New History', 'new', selected=True ) + self.select_history.add_option('New History', 'new', selected=True) else: - self.select_history.add_option( 'New History', 'new') + self.select_history.add_option('New History', 'new') if self.user: for a in self.user.histories: if not a.deleted: - if str( self.value ) == str( a.id ): - self.select_history.add_option( a.name, str( a.id ), selected=True ) + if str(self.value) == str(a.id): + self.select_history.add_option(a.name, str(a.id), selected=True) else: - self.select_history.add_option( a.name, str( a.id ) ) - return self.select_history.get_html( disabled=disabled ) + self.select_history.add_option(a.name, str(a.id)) + return self.select_history.get_html(disabled=disabled) - def to_dict( self ): - d = super( HistoryField, self ).to_dict() - d[ 'type' ] = 'select' - d[ 'data' ] = [{ 'label': 'New History', 'value': 'new' }] + def to_dict(self): + d = super(HistoryField, self).to_dict() + d['type'] = 'select' + d['data'] = [{'label': 'New History', 'value': 'new'}] if self.user and self.security: for a in self.user.histories: if not a.deleted: - d[ 'data' ].append( { 'label': a.name, 'value': self.security.encode_id( a.id ) } ) + d['data'].append({'label': a.name, 'value': self.security.encode_id(a.id)}) return d @@ -621,12 +646,12 @@ def get_suite(): """Get unittest suite for this module""" import doctest import sys - return doctest.DocTestSuite( sys.modules[__name__] ) + return doctest.DocTestSuite(sys.modules[__name__]) # --------- Utility methods ----------------------------- -def build_select_field( trans, objs, label_attr, select_field_name, initial_value='none', - selected_value='none', refresh_on_change=False, multiple=False, display=None, size=None ): +def build_select_field(trans, objs, label_attr, select_field_name, initial_value='none', + selected_value='none', refresh_on_change=False, multiple=False, display=None, size=None): """ Build a SelectField given a set of objects. The received params are: @@ -641,36 +666,36 @@ def build_select_field( trans, objs, label_attr, select_field_name, initial_valu - refresh_on_change: True if the SelectField should perform a refresh_on_change """ if initial_value == 'none': - values = [ initial_value ] + values = [initial_value] else: values = [] for obj in objs: if label_attr == 'self': # Each obj is a string - values.append( obj ) + values.append(obj) else: - values.append( trans.security.encode_id( obj.id ) ) + values.append(trans.security.encode_id(obj.id)) if refresh_on_change: refresh_on_change_values = values else: refresh_on_change_values = [] - select_field = SelectField( name=select_field_name, - multiple=multiple, - display=display, - refresh_on_change=refresh_on_change, - refresh_on_change_values=refresh_on_change_values, - size=size ) + select_field = SelectField(name=select_field_name, + multiple=multiple, + display=display, + refresh_on_change=refresh_on_change, + refresh_on_change_values=refresh_on_change_values, + size=size) for obj in objs: if label_attr == 'self': # Each obj is a string - if str( selected_value ) == str( obj ): - select_field.add_option( obj, obj, selected=True ) + if str(selected_value) == str(obj): + select_field.add_option(obj, obj, selected=True) else: - select_field.add_option( obj, obj ) + select_field.add_option(obj, obj) else: - label = getattr( obj, label_attr ) - if str( selected_value ) == str( obj.id ) or str( selected_value ) == trans.security.encode_id( obj.id ): - select_field.add_option( label, trans.security.encode_id( obj.id ), selected=True ) + label = getattr(obj, label_attr) + if str(selected_value) == str(obj.id) or str(selected_value) == trans.security.encode_id(obj.id): + select_field.add_option(label, trans.security.encode_id(obj.id), selected=True) else: - select_field.add_option( label, trans.security.encode_id( obj.id ) ) + select_field.add_option(label, trans.security.encode_id(obj.id)) return select_field diff --git a/lib/galaxy/web/framework/base.py b/lib/galaxy/web/framework/base.py index 93bb7ab4879a..e5a9d13cb1a6 100644 --- a/lib/galaxy/web/framework/base.py +++ b/lib/galaxy/web/framework/base.py @@ -20,31 +20,31 @@ from galaxy.util import smart_str -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) #: time of the most recent server startup -server_starttime = int( time.time() ) +server_starttime = int(time.time()) -def __resource_with_deleted( self, member_name, collection_name, **kwargs ): +def __resource_with_deleted(self, member_name, collection_name, **kwargs): """ Method to monkeypatch on to routes.mapper.Mapper which does the same thing as resource() with the addition of standardized routes for handling elements in Galaxy's "deleted but not really deleted" fashion. """ - collection_path = kwargs.get( 'path_prefix', '' ) + '/' + collection_name + '/deleted' + collection_path = kwargs.get('path_prefix', '') + '/' + collection_name + '/deleted' member_path = collection_path + '/{id}' - self.connect( 'deleted_' + collection_name, collection_path, controller=collection_name, action='index', deleted=True, conditions=dict( method=['GET'] ) ) - self.connect( 'deleted_' + member_name, member_path, controller=collection_name, action='show', deleted=True, conditions=dict( method=['GET'] ) ) - self.connect( 'undelete_deleted_' + member_name, member_path + '/undelete', controller=collection_name, action='undelete', - conditions=dict( method=['POST'] ) ) - self.resource( member_name, collection_name, **kwargs ) + self.connect('deleted_' + collection_name, collection_path, controller=collection_name, action='index', deleted=True, conditions=dict(method=['GET'])) + self.connect('deleted_' + member_name, member_path, controller=collection_name, action='show', deleted=True, conditions=dict(method=['GET'])) + self.connect('undelete_deleted_' + member_name, member_path + '/undelete', controller=collection_name, action='undelete', + conditions=dict(method=['POST'])) + self.resource(member_name, collection_name, **kwargs) routes.Mapper.resource_with_deleted = __resource_with_deleted -class WebApplication( object ): +class WebApplication(object): """ A simple web application which maps requests to objects using routes, and to methods on those objects in the CherryPy style. Thus simple @@ -52,7 +52,8 @@ class WebApplication( object ): complicated encoding of arguments in the PATH_INFO can be performed with routes. """ - def __init__( self ): + + def __init__(self): """ Create a new web application object. To actually connect some controllers use `add_controller` and `add_route`. Call @@ -70,22 +71,22 @@ def __init__( self ): # Set if trace logging is enabled self.trace_logger = None - def add_ui_controller( self, controller_name, controller ): + def add_ui_controller(self, controller_name, controller): """ Add a controller class to this application. A controller class has methods which handle web requests. To connect a URL to a controller's method use `add_route`. """ - log.debug( "Enabling '%s' controller, class: %s", - controller_name, controller.__class__.__name__ ) - self.controllers[ controller_name ] = controller + log.debug("Enabling '%s' controller, class: %s", + controller_name, controller.__class__.__name__) + self.controllers[controller_name] = controller - def add_api_controller( self, controller_name, controller ): - log.debug( "Enabling '%s' API controller, class: %s", - controller_name, controller.__class__.__name__ ) - self.api_controllers[ controller_name ] = controller + def add_api_controller(self, controller_name, controller): + log.debug("Enabling '%s' API controller, class: %s", + controller_name, controller.__class__.__name__) + self.api_controllers[controller_name] = controller - def add_route( self, route, **kwargs ): + def add_route(self, route, **kwargs): """ Add a route to match a URL with a method. Accepts all keyword arguments of `routes.Mapper.connect`. Every route should result in @@ -95,111 +96,111 @@ def add_route( self, route, **kwargs ): on the controller. Additional arguments will be passed to the method as keyword args. """ - self.mapper.connect( route, **kwargs ) + self.mapper.connect(route, **kwargs) - def add_client_route( self, route ): - self.clientside_routes.connect( route, controller='root', action='client' ) + def add_client_route(self, route, controller='root'): + self.clientside_routes.connect(route, controller=controller, action='client') - def set_transaction_factory( self, transaction_factory ): + def set_transaction_factory(self, transaction_factory): """ Use the callable `transaction_factory` to create the transaction which will be passed to requests. """ self.transaction_factory = transaction_factory - def finalize_config( self ): + def finalize_config(self): """ Call when application is completely configured and ready to serve requests """ # Create/compile the regular expressions for route mapping - self.mapper.create_regs( list(self.controllers.keys()) ) + self.mapper.create_regs(list(self.controllers.keys())) self.clientside_routes.create_regs() - def trace( self, **fields ): + def trace(self, **fields): if self.trace_logger: - self.trace_logger.log( "WebApplication", **fields ) + self.trace_logger.log("WebApplication", **fields) - def __call__( self, environ, start_response ): + def __call__(self, environ, start_response): """ Call interface as specified by WSGI. Wraps the environment in user friendly objects, finds the appropriate method to handle the request and calls it. """ # Immediately create request_id which we will use for logging - request_id = environ.get( 'request_id', 'unknown' ) + request_id = environ.get('request_id', 'unknown') if self.trace_logger: - self.trace_logger.context_set( "request_id", request_id ) - self.trace( message="Starting request" ) + self.trace_logger.context_set("request_id", request_id) + self.trace(message="Starting request") try: - return self.handle_request( environ, start_response ) + return self.handle_request(environ, start_response) finally: - self.trace( message="Handle request finished" ) + self.trace(message="Handle request finished") if self.trace_logger: - self.trace_logger.context_remove( "request_id" ) + self.trace_logger.context_remove("request_id") - def _resolve_map_match( self, map_match, path_info, controllers, use_default=True): + def _resolve_map_match(self, map_match, path_info, controllers, use_default=True): # Get the controller class - controller_name = map_match.pop( 'controller', None ) - controller = controllers.get( controller_name, None ) + controller_name = map_match.pop('controller', None) + controller = controllers.get(controller_name, None) if controller is None: - raise httpexceptions.HTTPNotFound( "No controller for " + path_info ) + raise httpexceptions.HTTPNotFound("No controller for " + path_info) # Resolve action method on controller # This is the easiest way to make the controller/action accessible for # url_for invocations. Specifically, grids. - action = map_match.pop( 'action', 'index' ) - method = getattr( controller, action, None ) + action = map_match.pop('action', 'index') + method = getattr(controller, action, None) if method is None and not use_default: # Skip default, we do this, for example, when we want to fail # through to another mapper. - raise httpexceptions.HTTPNotFound( "No action for " + path_info ) + raise httpexceptions.HTTPNotFound("No action for " + path_info) if method is None: # no matching method, we try for a default - method = getattr( controller, 'default', None ) + method = getattr(controller, 'default', None) if method is None: - raise httpexceptions.HTTPNotFound( "No action for " + path_info ) + raise httpexceptions.HTTPNotFound("No action for " + path_info) # Is the method exposed - if not getattr( method, 'exposed', False ): - raise httpexceptions.HTTPNotFound( "Action not exposed for " + path_info ) + if not getattr(method, 'exposed', False): + raise httpexceptions.HTTPNotFound("Action not exposed for " + path_info) # Is the method callable - if not callable( method ): - raise httpexceptions.HTTPNotFound( "Action not callable for " + path_info ) - return ( controller_name, controller, action, method ) + if not callable(method): + raise httpexceptions.HTTPNotFound("Action not callable for " + path_info) + return (controller_name, controller, action, method) - def handle_request( self, environ, start_response, body_renderer=None ): + def handle_request(self, environ, start_response, body_renderer=None): # Grab the request_id (should have been set by middleware) - request_id = environ.get( 'request_id', 'unknown' ) + request_id = environ.get('request_id', 'unknown') # Map url using routes - path_info = environ.get( 'PATH_INFO', '' ) - client_match = self.clientside_routes.match( path_info, environ ) - map_match = self.mapper.match( path_info, environ ) or client_match + path_info = environ.get('PATH_INFO', '') + client_match = self.clientside_routes.match(path_info, environ) + map_match = self.mapper.match(path_info, environ) or client_match if path_info.startswith('/api'): - environ[ 'is_api_request' ] = True + environ['is_api_request'] = True controllers = self.api_controllers else: - environ[ 'is_api_request' ] = False + environ['is_api_request'] = False controllers = self.controllers if map_match is None: - raise httpexceptions.HTTPNotFound( "No route for " + path_info ) - self.trace( path_info=path_info, map_match=map_match ) + raise httpexceptions.HTTPNotFound("No route for " + path_info) + self.trace(path_info=path_info, map_match=map_match) # Setup routes rc = routes.request_config() rc.mapper = self.mapper rc.mapper_dict = map_match rc.environ = environ # Setup the transaction - trans = self.transaction_factory( environ ) + trans = self.transaction_factory(environ) trans.request_id = request_id rc.redirect = trans.response.send_redirect # Resolve mapping to controller/method try: # We don't use default methods if there's a clientside match for this route. use_default = client_match is None - controller_name, controller, action, method = self._resolve_map_match( map_match, path_info, controllers, use_default=use_default) + controller_name, controller, action, method = self._resolve_map_match(map_match, path_info, controllers, use_default=use_default) except httpexceptions.HTTPNotFound: # Failed, let's check client routes - if not environ[ 'is_api_request' ] and client_match is not None: - controller_name, controller, action, method = self._resolve_map_match( client_match, path_info, controllers ) + if not environ['is_api_request'] and client_match is not None: + controller_name, controller, action, method = self._resolve_map_match(client_match, path_info, controllers) else: raise trans.controller = controller_name @@ -207,105 +208,108 @@ def handle_request( self, environ, start_response, body_renderer=None ): environ['controller_action_key'] = "%s.%s.%s" % ('api' if environ['is_api_request'] else 'web', controller_name, action or 'default') # Combine mapper args and query string / form args and call kwargs = trans.request.params.mixed() - kwargs.update( map_match ) + kwargs.update(map_match) # Special key for AJAX debugging, remove to avoid confusing methods - kwargs.pop( '_', None ) + kwargs.pop('_', None) try: - body = method( trans, **kwargs ) + body = method(trans, **kwargs) except Exception as e: - body = self.handle_controller_exception( e, trans, **kwargs ) + body = self.handle_controller_exception(e, trans, **kwargs) if not body: raise body_renderer = body_renderer or self._render_body - return body_renderer( trans, body, environ, start_response ) + return body_renderer(trans, body, environ, start_response) - def _render_body( self, trans, body, environ, start_response ): + def _render_body(self, trans, body, environ, start_response): # Now figure out what we got back and try to get it to the browser in # a smart way - if callable( body ): + if callable(body): # Assume the callable is another WSGI application to run - return body( environ, start_response ) - elif isinstance( body, types.FileType ): + return body(environ, start_response) + elif isinstance(body, types.FileType): # Stream the file back to the browser - return send_file( start_response, trans, body ) - elif isinstance( body, tarfile.ExFileObject ): + return send_file(start_response, trans, body) + elif isinstance(body, tarfile.ExFileObject): # Stream the tarfile member back to the browser - body = iterate_file( body ) - start_response( trans.response.wsgi_status(), - trans.response.wsgi_headeritems() ) + body = iterate_file(body) + start_response(trans.response.wsgi_status(), + trans.response.wsgi_headeritems()) return body else: - start_response( trans.response.wsgi_status(), - trans.response.wsgi_headeritems() ) - return self.make_body_iterable( trans, body ) + start_response(trans.response.wsgi_status(), + trans.response.wsgi_headeritems()) + return self.make_body_iterable(trans, body) - def make_body_iterable( self, trans, body ): - if isinstance( body, ( types.GeneratorType, list, tuple ) ): + def make_body_iterable(self, trans, body): + if isinstance(body, (types.GeneratorType, list, tuple)): # Recursively stream the iterable - return flatten( body ) + return flatten(body) elif body is None: # Returns an empty body return [] else: # Worst case scenario - return [ smart_str( body ) ] + return [smart_str(body)] - def handle_controller_exception( self, e, trans, **kwargs ): + def handle_controller_exception(self, e, trans, **kwargs): """ Allow handling of exceptions raised in controller methods. """ return False -class WSGIEnvironmentProperty( object ): +class WSGIEnvironmentProperty(object): """ Descriptor that delegates a property to a key in the environ member of the associated object (provides property style access to keys in the WSGI environment) """ - def __init__( self, key, default='' ): + + def __init__(self, key, default=''): self.key = key self.default = default - def __get__( self, obj, type=None ): + def __get__(self, obj, type=None): if obj is None: return self - return obj.environ.get( self.key, self.default ) + return obj.environ.get(self.key, self.default) -class LazyProperty( object ): +class LazyProperty(object): """ Property that replaces itself with a calculated value the first time it is used. """ - def __init__( self, func ): + + def __init__(self, func): self.func = func - def __get__(self, obj, type=None ): + def __get__(self, obj, type=None): if obj is None: return self - value = self.func( obj ) - setattr( obj, self.func.__name__, value ) + value = self.func(obj) + setattr(obj, self.func.__name__, value) return value lazy_property = LazyProperty -class DefaultWebTransaction( object ): +class DefaultWebTransaction(object): """ Wraps the state of a single web transaction (request/response cycle). TODO: Provide hooks to allow application specific state to be included in here. """ - def __init__( self, environ ): + + def __init__(self, environ): self.environ = environ - self.request = Request( environ ) + self.request = Request(environ) self.response = Response() @lazy_property - def session( self ): + def session(self): """ Get the user's session state. This is laze since we rarely use it and the creation/serialization cost is high. @@ -318,7 +322,7 @@ def session( self ): return None -class FieldStorage( cgi.FieldStorage ): +class FieldStorage(cgi.FieldStorage): def make_file(self, binary=None): # For request.params, override cgi.FieldStorage.make_file to create persistent # tempfiles. Necessary for externalizing the upload tool. It's a little hacky @@ -339,50 +343,51 @@ def read_lines(self): cgi.FieldStorage = FieldStorage -class Request( webob.Request ): +class Request(webob.Request): """ Encapsulates an HTTP request. """ - def __init__( self, environ ): + + def __init__(self, environ): """ Create a new request wrapping the WSGI environment `environ` """ # self.environ = environ - webob.Request.__init__( self, environ, charset='utf-8', decode_param_names=False ) + webob.Request.__init__(self, environ, charset='utf-8', decode_param_names=False) # Properties that are computed and cached on first use @lazy_property - def remote_host( self ): + def remote_host(self): try: - return socket.gethostbyname( self.remote_addr ) + return socket.gethostbyname(self.remote_addr) except socket.error: return self.remote_addr @lazy_property - def remote_hostname( self ): + def remote_hostname(self): try: - return socket.gethostbyaddr( self.remote_addr )[0] + return socket.gethostbyaddr(self.remote_addr)[0] except socket.error: return self.remote_addr @lazy_property - def cookies( self ): - return get_cookies( self.environ ) + def cookies(self): + return get_cookies(self.environ) @lazy_property - def base( self ): - return ( self.scheme + "://" + self.host ) + def base(self): + return (self.scheme + "://" + self.host) # @lazy_property # def params( self ): # return parse_formvars( self.environ ) @lazy_property - def path( self ): + def path(self): return self.environ.get('SCRIPT_NAME', '') + self.environ['PATH_INFO'] @lazy_property - def browser_url( self ): + def browser_url(self): return self.base + self.path # Descriptors that map properties to the associated environment @@ -390,63 +395,64 @@ def browser_url( self ): # scheme = WSGIEnvironmentProperty( 'wsgi.url_scheme' ) # remote_addr = WSGIEnvironmentProperty( 'REMOTE_ADDR' ) - remote_port = WSGIEnvironmentProperty( 'REMOTE_PORT' ) + remote_port = WSGIEnvironmentProperty('REMOTE_PORT') # method = WSGIEnvironmentProperty( 'REQUEST_METHOD' ) # script_name = WSGIEnvironmentProperty( 'SCRIPT_NAME' ) - protocol = WSGIEnvironmentProperty( 'SERVER_PROTOCOL' ) + protocol = WSGIEnvironmentProperty('SERVER_PROTOCOL') # query_string = WSGIEnvironmentProperty( 'QUERY_STRING' ) # path_info = WSGIEnvironmentProperty( 'PATH_INFO' ) -class Response( object ): +class Response(object): """ Describes an HTTP response. Currently very simple since the actual body of the request is handled separately. """ - def __init__( self ): + + def __init__(self): """ Create a new Response defaulting to HTML content and "200 OK" status """ self.status = "200 OK" - self.headers = HeaderDict( { "content-type": "text/html" } ) + self.headers = HeaderDict({"content-type": "text/html"}) self.cookies = SimpleCookie() - def set_content_type( self, type_ ): + def set_content_type(self, type_): """ Sets the Content-Type header """ - self.headers[ "content-type" ] = type_ + self.headers["content-type"] = type_ - def get_content_type( self ): + def get_content_type(self): return self.headers.get("content-type", None) - def send_redirect( self, url ): + def send_redirect(self, url): """ Send an HTTP redirect response to (target `url`) """ - raise httpexceptions.HTTPFound( url.encode('utf-8'), headers=self.wsgi_headeritems() ) + raise httpexceptions.HTTPFound(url.encode('utf-8'), headers=self.wsgi_headeritems()) - def wsgi_headeritems( self ): + def wsgi_headeritems(self): """ Return headers in format appropriate for WSGI `start_response` """ result = self.headers.headeritems() # Add cookie to header for name, crumb in self.cookies.items(): - header, value = str( crumb ).split( ': ', 1 ) - result.append( ( header, value ) ) + header, value = str(crumb).split(': ', 1) + result.append((header, value)) return result - def wsgi_status( self ): + def wsgi_status(self): """ Return status line in format appropriate for WSGI `start_response` """ - if isinstance( self.status, int ): - exception = httpexceptions.get_exception( self.status ) - return "%d %s" % ( exception.code, exception.title ) + if isinstance(self.status, int): + exception = httpexceptions.get_exception(self.status) + return "%d %s" % (exception.code, exception.title) else: return self.status @@ -456,43 +462,43 @@ def wsgi_status( self ): CHUNK_SIZE = 2 ** 16 -def send_file( start_response, trans, body ): +def send_file(start_response, trans, body): # If configured use X-Accel-Redirect header for nginx base = trans.app.config.nginx_x_accel_redirect_base apache_xsendfile = trans.app.config.apache_xsendfile if base: trans.response.headers['X-Accel-Redirect'] = \ - base + os.path.abspath( body.name ) - body = [ "" ] + base + os.path.abspath(body.name) + body = [""] elif apache_xsendfile: - trans.response.headers['X-Sendfile'] = os.path.abspath( body.name ) - body = [ "" ] + trans.response.headers['X-Sendfile'] = os.path.abspath(body.name) + body = [""] # Fall back on sending the file in chunks else: - body = iterate_file( body ) - start_response( trans.response.wsgi_status(), - trans.response.wsgi_headeritems() ) + body = iterate_file(body) + start_response(trans.response.wsgi_status(), + trans.response.wsgi_headeritems()) return body -def iterate_file( file ): +def iterate_file(file): """ Progressively return chunks from `file`. """ while 1: - chunk = file.read( CHUNK_SIZE ) + chunk = file.read(CHUNK_SIZE) if not chunk: break yield chunk -def flatten( seq ): +def flatten(seq): """ Flatten a possible nested set of iterables """ for x in seq: - if isinstance( x, ( types.GeneratorType, list, tuple ) ): - for y in flatten( x ): - yield smart_str( y ) + if isinstance(x, (types.GeneratorType, list, tuple)): + for y in flatten(x): + yield smart_str(y) else: - yield smart_str( x ) + yield smart_str(x) diff --git a/lib/galaxy/web/framework/decorators.py b/lib/galaxy/web/framework/decorators.py index bec881522c4b..9d56f851995f 100644 --- a/lib/galaxy/web/framework/decorators.py +++ b/lib/galaxy/web/framework/decorators.py @@ -12,25 +12,25 @@ from galaxy.util.json import safe_dumps from galaxy.web.framework import url_for -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) JSON_CONTENT_TYPE = "application/json" JSONP_CONTENT_TYPE = "application/javascript" JSONP_CALLBACK_KEY = 'callback' -def error( message ): - raise MessageException( message, type='error' ) +def error(message): + raise MessageException(message, type='error') # ----------------------------------------------------------------------------- web controller decorators -def _save_orig_fn( wrapped, orig ): - if not hasattr( orig, '_orig' ): +def _save_orig_fn(wrapped, orig): + if not hasattr(orig, '_orig'): wrapped._orig = orig return wrapped -def expose( func ): +def expose(func): """ Decorator: mark a function as 'exposed' and thus web accessible """ @@ -38,51 +38,51 @@ def expose( func ): return func -def json( func, pretty=False ): +def json(func, pretty=False): """ Format the response as JSON and set the response content type to JSON_CONTENT_TYPE. """ @wraps(func) - def call_and_format( self, trans, *args, **kwargs ): + def call_and_format(self, trans, *args, **kwargs): # pull out any callback argument to the api endpoint and set the content type to json or javascript - jsonp_callback = kwargs.pop( JSONP_CALLBACK_KEY, None ) + jsonp_callback = kwargs.pop(JSONP_CALLBACK_KEY, None) if jsonp_callback: - trans.response.set_content_type( JSONP_CONTENT_TYPE ) + trans.response.set_content_type(JSONP_CONTENT_TYPE) else: - trans.response.set_content_type( JSON_CONTENT_TYPE ) - rval = func( self, trans, *args, **kwargs ) - return _format_return_as_json( rval, jsonp_callback, pretty=( pretty or trans.debug ) ) + trans.response.set_content_type(JSON_CONTENT_TYPE) + rval = func(self, trans, *args, **kwargs) + return _format_return_as_json(rval, jsonp_callback, pretty=(pretty or trans.debug)) - if not hasattr( func, '_orig' ): + if not hasattr(func, '_orig'): call_and_format._orig = func - return expose( _save_orig_fn( call_and_format, func ) ) + return expose(_save_orig_fn(call_and_format, func)) -def json_pretty( func ): +def json_pretty(func): """ Indent and sort returned JSON. """ - return json( func, pretty=True ) + return json(func, pretty=True) -def require_login( verb="perform this action", use_panels=False, webapp='galaxy' ): - def argcatcher( func ): +def require_login(verb="perform this action", use_panels=False, webapp='galaxy'): + def argcatcher(func): @wraps(func) - def decorator( self, trans, *args, **kwargs ): + def decorator(self, trans, *args, **kwargs): if trans.get_user(): - return func( self, trans, *args, **kwargs ) + return func(self, trans, *args, **kwargs) else: return trans.show_error_message( 'You must be logged in to %s.' - % ( url_for( controller='user', action='login', webapp=webapp ), verb ), use_panels=use_panels ) + % (url_for(controller='user', action='login', webapp=webapp), verb), use_panels=use_panels) return decorator return argcatcher -def require_admin( func ): +def require_admin(func): @wraps(func) - def decorator( self, trans, *args, **kwargs ): + def decorator(self, trans, *args, **kwargs): if not trans.user_is_admin(): msg = "You must be an administrator to access this feature." user = trans.get_user() @@ -94,20 +94,20 @@ def decorator( self, trans, *args, **kwargs ): if trans.response.get_content_type() == 'application/json': return msg else: - return trans.show_error_message( msg ) - return func( self, trans, *args, **kwargs ) + return trans.show_error_message(msg) + return func(self, trans, *args, **kwargs) return decorator # ----------------------------------------------------------------------------- (original) api decorators -def expose_api( func, to_json=True, user_required=True ): +def expose_api(func, to_json=True, user_required=True): """ Expose this function via the API. """ @wraps(func) - def decorator( self, trans, *args, **kwargs ): - def error( environ, start_response ): - start_response( error_status, [('Content-type', 'text/plain')] ) + def decorator(self, trans, *args, **kwargs): + def error(environ, start_response): + start_response(error_status, [('Content-type', 'text/plain')]) return error_message error_status = '403 Forbidden' if trans.error_message: @@ -124,14 +124,14 @@ def error( environ, start_response ): return error # pull out any callback argument to the api endpoint and set the content type to json or javascript - jsonp_callback = kwargs.pop( JSONP_CALLBACK_KEY, None ) + jsonp_callback = kwargs.pop(JSONP_CALLBACK_KEY, None) if jsonp_callback: - trans.response.set_content_type( JSONP_CONTENT_TYPE ) + trans.response.set_content_type(JSONP_CONTENT_TYPE) else: - trans.response.set_content_type( JSON_CONTENT_TYPE ) + trans.response.set_content_type(JSON_CONTENT_TYPE) # send 'do not cache' headers to handle IE's caching of ajax get responses - trans.response.headers[ 'Cache-Control' ] = "max-age=0,no-cache,no-store" + trans.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store" # Perform api_run_as processing, possibly changing identity if 'payload' in kwargs and 'run_as' in kwargs['payload']: @@ -139,33 +139,33 @@ def error( environ, start_response ): error_message = 'User does not have permissions to run jobs as another user' return error try: - decoded_user_id = trans.security.decode_id( kwargs['payload']['run_as'] ) + decoded_user_id = trans.security.decode_id(kwargs['payload']['run_as']) except TypeError: trans.response.status = 400 - return "Malformed user id ( %s ) specified, unable to decode." % str( kwargs['payload']['run_as'] ) + return "Malformed user id ( %s ) specified, unable to decode." % str(kwargs['payload']['run_as']) try: - user = trans.sa_session.query( trans.app.model.User ).get( decoded_user_id ) + user = trans.sa_session.query(trans.app.model.User).get(decoded_user_id) trans.api_inherit_admin = trans.user_is_admin() trans.set_user(user) except: trans.response.status = 400 return "That user does not exist." try: - rval = func( self, trans, *args, **kwargs) + rval = func(self, trans, *args, **kwargs) if to_json: - rval = _format_return_as_json( rval, jsonp_callback, pretty=trans.debug ) + rval = _format_return_as_json(rval, jsonp_callback, pretty=trans.debug) return rval except paste.httpexceptions.HTTPException: raise # handled except: - log.exception( 'Uncaught exception in exposed API method:' ) + log.exception('Uncaught exception in exposed API method:') raise paste.httpexceptions.HTTPServerError() - return expose( _save_orig_fn( decorator, func ) ) + return expose(_save_orig_fn(decorator, func)) -def __extract_payload_from_request( trans, func, kwargs ): +def __extract_payload_from_request(trans, func, kwargs): content_type = trans.request.headers.get('content-type', '') - if content_type.startswith( 'application/x-www-form-urlencoded' ) or content_type.startswith( 'multipart/form-data' ): + if content_type.startswith('application/x-www-form-urlencoded') or content_type.startswith('multipart/form-data'): # If the content type is a standard type such as multipart/form-data, the wsgi framework parses the request body # and loads all field values into kwargs. However, kwargs also contains formal method parameters etc. which # are not a part of the request body. This is a problem because it's not possible to differentiate between values @@ -173,125 +173,125 @@ def __extract_payload_from_request( trans, func, kwargs ): # in the payload. Therefore, the decorated method's formal arguments are discovered through reflection and removed from # the payload dictionary. This helps to prevent duplicate argument conflicts in downstream methods. payload = kwargs.copy() - named_args, _, _, _ = inspect.getargspec( func ) + named_args, _, _, _ = inspect.getargspec(func) for arg in named_args: - payload.pop( arg, None ) + payload.pop(arg, None) for k, v in payload.items(): - if isinstance( v, string_types ): + if isinstance(v, string_types): try: # note: parse_non_hex_float only needed here for single string values where something like # 40000000000000e5 will be parsed as a scientific notation float. This is as opposed to hex strings # in larger JSON structures where quoting prevents this (further below) - payload[ k ] = loads( v, parse_float=util.parse_non_hex_float ) + payload[k] = loads(v, parse_float=util.parse_non_hex_float) except: # may not actually be json, just continue pass - payload = util.recursively_stringify_dictionary_keys( payload ) + payload = util.recursively_stringify_dictionary_keys(payload) else: # Assume application/json content type and parse request body manually, since wsgi won't do it. However, the order of this check # should ideally be in reverse, with the if clause being a check for application/json and the else clause assuming a standard encoding # such as multipart/form-data. Leaving it as is for backward compatibility, just in case. - payload = util.recursively_stringify_dictionary_keys( loads( trans.request.body ) ) + payload = util.recursively_stringify_dictionary_keys(loads(trans.request.body)) return payload -def expose_api_raw( func ): +def expose_api_raw(func): """ Expose this function via the API but don't dump the results to JSON. """ - return expose_api( func, to_json=False ) + return expose_api(func, to_json=False) -def expose_api_raw_anonymous( func ): +def expose_api_raw_anonymous(func): """ Expose this function via the API but don't dump the results to JSON. """ - return expose_api( func, to_json=False, user_required=False ) + return expose_api(func, to_json=False, user_required=False) -def expose_api_anonymous( func, to_json=True ): +def expose_api_anonymous(func, to_json=True): """ Expose this function via the API but don't require a set user. """ - return expose_api( func, to_json=to_json, user_required=False ) + return expose_api(func, to_json=to_json, user_required=False) # ----------------------------------------------------------------------------- (new) api decorators # TODO: rename as expose_api and make default. -def _future_expose_api( func, to_json=True, user_required=True, user_or_session_required=True, handle_jsonp=True ): +def _future_expose_api(func, to_json=True, user_required=True, user_or_session_required=True, handle_jsonp=True): """ Expose this function via the API. """ @wraps(func) - def decorator( self, trans, *args, **kwargs ): + def decorator(self, trans, *args, **kwargs): # errors passed in from trans._authenicate_api if trans.error_message: - return __api_error_response( trans, status_code=403, err_code=error_codes.USER_NO_API_KEY, - err_msg=trans.error_message ) + return __api_error_response(trans, status_code=403, err_code=error_codes.USER_NO_API_KEY, + err_msg=trans.error_message) if trans.anonymous: # error if anon and user required if user_required: - return __api_error_response( trans, status_code=403, err_code=error_codes.USER_NO_API_KEY, - err_msg="API authentication required for this request" ) + return __api_error_response(trans, status_code=403, err_code=error_codes.USER_NO_API_KEY, + err_msg="API authentication required for this request") # error if anon and no session if not trans.galaxy_session and user_or_session_required: - return __api_error_response( trans, status_code=403, err_code=error_codes.USER_NO_API_KEY, - err_msg="API authentication required for this request" ) + return __api_error_response(trans, status_code=403, err_code=error_codes.USER_NO_API_KEY, + err_msg="API authentication required for this request") if trans.request.body: try: kwargs['payload'] = __extract_payload_from_request(trans, func, kwargs) except ValueError: error_code = error_codes.USER_INVALID_JSON - return __api_error_response( trans, status_code=400, err_code=error_code ) + return __api_error_response(trans, status_code=400, err_code=error_code) # pull out any callback argument to the api endpoint and set the content type to json or javascript # TODO: use handle_jsonp to NOT overwrite existing tool_shed JSONP - jsonp_callback = kwargs.pop( JSONP_CALLBACK_KEY, None ) if handle_jsonp else None + jsonp_callback = kwargs.pop(JSONP_CALLBACK_KEY, None) if handle_jsonp else None if jsonp_callback: - trans.response.set_content_type( JSONP_CONTENT_TYPE ) + trans.response.set_content_type(JSONP_CONTENT_TYPE) else: - trans.response.set_content_type( JSON_CONTENT_TYPE ) + trans.response.set_content_type(JSON_CONTENT_TYPE) # send 'do not cache' headers to handle IE's caching of ajax get responses - trans.response.headers[ 'Cache-Control' ] = "max-age=0,no-cache,no-store" + trans.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store" # TODO: Refactor next block out into a helper procedure. # Perform api_run_as processing, possibly changing identity if 'payload' in kwargs and 'run_as' in kwargs['payload']: if not trans.user_can_do_run_as(): error_code = error_codes.USER_CANNOT_RUN_AS - return __api_error_response( trans, err_code=error_code, status_code=403 ) + return __api_error_response(trans, err_code=error_code, status_code=403) try: - decoded_user_id = trans.security.decode_id( kwargs['payload']['run_as'] ) + decoded_user_id = trans.security.decode_id(kwargs['payload']['run_as']) except TypeError: - error_message = "Malformed user id ( %s ) specified, unable to decode." % str( kwargs['payload']['run_as'] ) + error_message = "Malformed user id ( %s ) specified, unable to decode." % str(kwargs['payload']['run_as']) error_code = error_codes.USER_INVALID_RUN_AS - return __api_error_response( trans, err_code=error_code, err_msg=error_message, status_code=400) + return __api_error_response(trans, err_code=error_code, err_msg=error_message, status_code=400) try: - user = trans.sa_session.query( trans.app.model.User ).get( decoded_user_id ) + user = trans.sa_session.query(trans.app.model.User).get(decoded_user_id) trans.api_inherit_admin = trans.user_is_admin() trans.set_user(user) except: error_code = error_codes.USER_INVALID_RUN_AS - return __api_error_response( trans, err_code=error_code, status_code=400 ) + return __api_error_response(trans, err_code=error_code, status_code=400) try: - rval = func( self, trans, *args, **kwargs ) + rval = func(self, trans, *args, **kwargs) if to_json: - rval = _format_return_as_json( rval, jsonp_callback, pretty=trans.debug ) + rval = _format_return_as_json(rval, jsonp_callback, pretty=trans.debug) return rval except MessageException as e: traceback_string = format_exc() - return __api_error_response( trans, exception=e, traceback=traceback_string ) + return __api_error_response(trans, exception=e, traceback=traceback_string) except paste.httpexceptions.HTTPException: # TODO: Allow to pass or format for the API??? raise # handled except Exception as e: traceback_string = format_exc() error_message = 'Uncaught exception in exposed API method:' - log.exception( error_message ) + log.exception(error_message) return __api_error_response( trans, status_code=500, @@ -306,35 +306,35 @@ def decorator( self, trans, *args, **kwargs ): return decorator -def _format_return_as_json( rval, jsonp_callback=None, pretty=False ): +def _format_return_as_json(rval, jsonp_callback=None, pretty=False): """ Formats a return value as JSON or JSONP if `jsonp_callback` is present. Use `pretty=True` to return pretty printed json. """ - dumps_kwargs = dict( indent=4, sort_keys=True ) if pretty else {} - json = safe_dumps( rval, **dumps_kwargs ) + dumps_kwargs = dict(indent=4, sort_keys=True) if pretty else {} + json = safe_dumps(rval, **dumps_kwargs) if jsonp_callback: - json = "{}({});".format( jsonp_callback, json ) + json = "{}({});".format(jsonp_callback, json) return json -def __api_error_message( trans, **kwds ): - exception = kwds.get( "exception", None ) +def __api_error_message(trans, **kwds): + exception = kwds.get("exception", None) if exception: # If we are passed a MessageException use err_msg. - default_error_code = getattr( exception, "err_code", error_codes.UNKNOWN ) - default_error_message = getattr( exception, "err_msg", default_error_code.default_error_message ) - extra_error_info = getattr( exception, 'extra_error_info', {} ) - if not isinstance( extra_error_info, dict ): + default_error_code = getattr(exception, "err_code", error_codes.UNKNOWN) + default_error_message = getattr(exception, "err_msg", default_error_code.default_error_message) + extra_error_info = getattr(exception, 'extra_error_info', {}) + if not isinstance(extra_error_info, dict): extra_error_info = {} else: default_error_message = "Error processing API request." default_error_code = error_codes.UNKNOWN extra_error_info = {} - traceback_string = kwds.get( "traceback", "No traceback available." ) - err_msg = kwds.get( "err_msg", default_error_message ) - error_code_object = kwds.get( "err_code", default_error_code ) + traceback_string = kwds.get("traceback", "No traceback available.") + err_msg = kwds.get("err_msg", default_error_message) + error_code_object = kwds.get("err_code", default_error_code) try: error_code = error_code_object.code except AttributeError: @@ -344,21 +344,21 @@ def __api_error_message( trans, **kwds ): # Would prefer the terminology of error_code and error_message, but # err_msg used a good number of places already. Might as well not change # it? - error_response = dict( err_msg=err_msg, err_code=error_code, **extra_error_info ) + error_response = dict(err_msg=err_msg, err_code=error_code, **extra_error_info) if trans.debug: # TODO: Should admins get to see traceback as well? - error_response[ "traceback" ] = traceback_string + error_response["traceback"] = traceback_string return error_response -def __api_error_response( trans, **kwds ): - error_dict = __api_error_message( trans, **kwds ) - exception = kwds.get( "exception", None ) +def __api_error_response(trans, **kwds): + error_dict = __api_error_message(trans, **kwds) + exception = kwds.get("exception", None) # If we are given an status code directly - use it - otherwise check # the exception for a status_code attribute. if "status_code" in kwds: - status_code = int( kwds.get( "status_code" ) ) - elif hasattr( exception, "status_code" ): - status_code = int( exception.status_code ) + status_code = int(kwds.get("status_code")) + elif hasattr(exception, "status_code"): + status_code = int(exception.status_code) else: status_code = 500 response = trans.response @@ -367,32 +367,32 @@ def __api_error_response( trans, **kwds ): # non-success (i.e. not 200 or 201) has been set, do not override # underlying controller. response.status = status_code - return safe_dumps( error_dict ) + return safe_dumps(error_dict) -def _future_expose_api_anonymous( func, to_json=True ): +def _future_expose_api_anonymous(func, to_json=True): """ Expose this function via the API but don't require a set user. """ - return _future_expose_api( func, to_json=to_json, user_required=False ) + return _future_expose_api(func, to_json=to_json, user_required=False) -def _future_expose_api_anonymous_and_sessionless( func, to_json=True ): +def _future_expose_api_anonymous_and_sessionless(func, to_json=True): """ Expose this function via the API but don't require a user or a galaxy_session. """ - return _future_expose_api( func, to_json=to_json, user_required=False, user_or_session_required=False ) + return _future_expose_api(func, to_json=to_json, user_required=False, user_or_session_required=False) -def _future_expose_api_raw( func ): - return _future_expose_api( func, to_json=False, user_required=True ) +def _future_expose_api_raw(func): + return _future_expose_api(func, to_json=False, user_required=True) -def _future_expose_api_raw_anonymous( func ): - return _future_expose_api( func, to_json=False, user_required=False ) +def _future_expose_api_raw_anonymous(func): + return _future_expose_api(func, to_json=False, user_required=False) -def _future_expose_api_raw_anonymous_and_sessionless( func ): +def _future_expose_api_raw_anonymous_and_sessionless(func): # TODO: tool_shed api implemented JSONP first on a method-by-method basis, don't overwrite that for now return _future_expose_api( func, diff --git a/lib/galaxy/web/framework/formbuilder.py b/lib/galaxy/web/framework/formbuilder.py index a1caf68e27ee..33c14c47fc77 100644 --- a/lib/galaxy/web/framework/formbuilder.py +++ b/lib/galaxy/web/framework/formbuilder.py @@ -1,18 +1,19 @@ from galaxy.util import bunch import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -def form( *args, **kwargs ): - return FormBuilder( *args, **kwargs ) +def form(*args, **kwargs): + return FormBuilder(*args, **kwargs) -class FormBuilder( object ): +class FormBuilder(object): """ Simple class describing an HTML form """ - def __init__( self, action="", title="", name="form", submit_text="submit", use_panels=False ): + + def __init__(self, action="", title="", name="form", submit_text="submit", use_panels=False): self.title = title self.name = name self.action = action @@ -20,29 +21,30 @@ def __init__( self, action="", title="", name="form", submit_text="submit", use_ self.inputs = [] self.use_panels = use_panels - def add_input( self, type, name, label, value=None, error=None, help=None, use_label=True ): - self.inputs.append( FormInput( type, label, name, value, error, help, use_label ) ) + def add_input(self, type, name, label, value=None, error=None, help=None, use_label=True): + self.inputs.append(FormInput(type, label, name, value, error, help, use_label)) return self - def add_checkbox( self, name, label, value=None, error=None, help=None ): - return self.add_input( 'checkbox', label, name, value, error, help ) + def add_checkbox(self, name, label, value=None, error=None, help=None): + return self.add_input('checkbox', label, name, value, error, help) - def add_text( self, name, label, value=None, error=None, help=None ): - return self.add_input( 'text', label, name, value, error, help ) + def add_text(self, name, label, value=None, error=None, help=None): + return self.add_input('text', label, name, value, error, help) - def add_password( self, name, label, value=None, error=None, help=None ): - return self.add_input( 'password', label, name, value, error, help ) + def add_password(self, name, label, value=None, error=None, help=None): + return self.add_input('password', label, name, value, error, help) - def add_select( self, name, label, value=None, options=[], error=None, help=None, use_label=True ): - self.inputs.append( SelectInput( name, label, value=value, options=options, error=error, help=help, use_label=use_label ) ) + def add_select(self, name, label, value=None, options=[], error=None, help=None, use_label=True): + self.inputs.append(SelectInput(name, label, value=value, options=options, error=error, help=help, use_label=use_label)) return self -class FormInput( object ): +class FormInput(object): """ Simple class describing a form input element """ - def __init__( self, type, name, label, value=None, error=None, help=None, use_label=True, extra_attributes={}, **kwargs ): + + def __init__(self, type, name, label, value=None, error=None, help=None, use_label=True, extra_attributes={}, **kwargs): self.type = type self.name = name self.label = label @@ -53,34 +55,36 @@ def __init__( self, type, name, label, value=None, error=None, help=None, use_la self.extra_attributes = extra_attributes -class DatalistInput( FormInput ): +class DatalistInput(FormInput): """ Data list input """ - def __init__( self, name, *args, **kwargs ): + def __init__(self, name, *args, **kwargs): if 'extra_attributes' not in kwargs: - kwargs[ 'extra_attributes' ] = {} - kwargs[ 'extra_attributes' ][ 'list' ] = name - FormInput.__init__( self, None, name, *args, **kwargs ) - self.options = kwargs.get( 'options', {} ) + kwargs['extra_attributes'] = {} + kwargs['extra_attributes']['list'] = name + FormInput.__init__(self, None, name, *args, **kwargs) + self.options = kwargs.get('options', {}) - def body_html( self ): - options = "".join( [ "" % ( key, value ) for key, value in self.options.iteritems() ] ) - return """%s""" % ( self.name, options ) + def body_html(self): + options = "".join(["" % (key, value) for key, value in self.options.iteritems()]) + return """%s""" % (self.name, options) -class SelectInput( FormInput ): +class SelectInput(FormInput): """ A select form input. """ - def __init__( self, name, label, value=None, options=[], error=None, help=None, use_label=True ): - FormInput.__init__( self, "select", name, label, value=value, error=error, help=help, use_label=use_label ) + + def __init__(self, name, label, value=None, options=[], error=None, help=None, use_label=True): + FormInput.__init__(self, "select", name, label, value=value, error=error, help=help, use_label=use_label) self.options = options -class FormData( object ): +class FormData(object): """ Class for passing data about a form to a template, very rudimentary, could be combined with the tool form handling to build something more general. """ - def __init__( self ): + + def __init__(self): # TODO: galaxy's two Bunchs are defined differently. Is this right? self.values = bunch.Bunch() self.errors = bunch.Bunch() diff --git a/lib/galaxy/web/framework/helpers/__init__.py b/lib/galaxy/web/framework/helpers/__init__.py index 2dfda1f1dca9..b54ade34301a 100644 --- a/lib/galaxy/web/framework/helpers/__init__.py +++ b/lib/galaxy/web/framework/helpers/__init__.py @@ -14,7 +14,7 @@ from routes import url_for -def time_ago( x ): +def time_ago(x): """ Convert a datetime to a string. """ @@ -24,11 +24,11 @@ def time_ago( x ): if (datetime.utcnow() - x) > delta: # Greater than a week difference return x.strftime("%b %d, %Y") else: - date_array = date.distance_of_time_in_words( x, datetime.utcnow() ).replace(",", "").split(" ") + date_array = date.distance_of_time_in_words(x, datetime.utcnow()).replace(",", "").split(" ") return "~%s %s ago" % (date_array[0], date_array[1]) -def iff( a, b, c ): +def iff(a, b, c): """ Ternary shortcut """ @@ -50,64 +50,64 @@ def truncate(content, length=100, suffix='...'): # Quick helpers for static content -def css( *args ): +def css(*args): """ Take a list of stylesheet names (no extension) and return appropriate string of link tags. Cache-bust with time that server started running on """ - return "\n".join( [ stylesheet_link( url_for( "/static/style/%s.css?v=%s" % (name, server_starttime) ) ) for name in args ] ) + return "\n".join([stylesheet_link(url_for("/static/style/%s.css?v=%s" % (name, server_starttime))) for name in args]) -def js_helper( prefix, *args ): +def js_helper(prefix, *args): """ Take a prefix and list of javascript names and return appropriate string of script tags. Cache-bust with time that server started running on """ - return "\n".join( [ javascript_link( url_for( "/%s%s.js?v=%s" % (prefix, name, server_starttime ) ) ) for name in args ] ) + return "\n".join([javascript_link(url_for("/%s%s.js?v=%s" % (prefix, name, server_starttime))) for name in args]) -def js( *args ): +def js(*args): """ Take a prefix and list of javascript names and return appropriate string of script tags. """ - return js_helper( 'static/scripts/', *args ) + return js_helper('static/scripts/', *args) -def templates( *args ): +def templates(*args): """ Take a list of template names (no extension) and return appropriate string of script tags. """ - return js_helper( 'static/scripts/templates/compiled/', *args ) + return js_helper('static/scripts/templates/compiled/', *args) # Hashes -def md5( s ): +def md5(s): """ Return hex encoded md5 hash of string s """ m = hash_util.md5() - m.update( s ) + m.update(s) return m.hexdigest() # Unicode help -def to_unicode( a_string ): +def to_unicode(a_string): """ Convert a string to unicode in utf-8 format; if string is already unicode, does nothing because string's encoding cannot be determined by introspection. """ - return unicodify( a_string, 'utf-8' ) + return unicodify(a_string, 'utf-8') -def is_true( val ): +def is_true(val): """ Returns true if input is a boolean and true or is a string and looks like a true value. """ - return val is True or val in [ 'True', 'true', 'T', 't' ] + return val is True or val in ['True', 'true', 'T', 't'] diff --git a/lib/galaxy/web/framework/helpers/grids.py b/lib/galaxy/web/framework/helpers/grids.py index 16934753d65f..52f4d1e84e78 100644 --- a/lib/galaxy/web/framework/helpers/grids.py +++ b/lib/galaxy/web/framework/helpers/grids.py @@ -13,10 +13,10 @@ from galaxy.web.framework.helpers import iff -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class Grid( object ): +class Grid(object): """ Specifies the content and format of a grid (data table). """ @@ -46,7 +46,7 @@ class Grid( object ): legend = None info_text = None - def __init__( self ): + def __init__(self): # Determine if any multiple row operations are defined self.has_multiple_item_operations = False for operation in self.operations: @@ -60,12 +60,12 @@ def __init__( self ): if not column.model_class: column.model_class = self.model_class - def __call__( self, trans, **kwargs ): + def __call__(self, trans, **kwargs): # Get basics. # FIXME: pretty sure this is only here to pass along, can likely be eliminated - status = kwargs.get( 'status', None ) - message = kwargs.get( 'message', None ) - dict_format = kwargs.get( 'dict_format', False ) + status = kwargs.get('status', None) + message = kwargs.get('message', None) + dict_format = kwargs.get('dict_format', False) # Build a base filter and sort key that is the combination of the saved state and defaults. # Saved state takes preference over defaults. base_filter = {} @@ -74,23 +74,23 @@ def __call__( self, trans, **kwargs ): base_filter = self.default_filter.copy() base_sort_key = self.default_sort_key if self.preserve_state: - pref_name = text_type( self.__class__.__name__ + self.cur_filter_pref_name ) + pref_name = text_type(self.__class__.__name__ + self.cur_filter_pref_name) if pref_name in trans.get_user().preferences: - saved_filter = loads( trans.get_user().preferences[pref_name] ) - base_filter.update( saved_filter ) - pref_name = text_type( self.__class__.__name__ + self.cur_sort_key_pref_name ) + saved_filter = loads(trans.get_user().preferences[pref_name]) + base_filter.update(saved_filter) + pref_name = text_type(self.__class__.__name__ + self.cur_sort_key_pref_name) if pref_name in trans.get_user().preferences: - base_sort_key = loads( trans.get_user().preferences[pref_name] ) + base_sort_key = loads(trans.get_user().preferences[pref_name]) # Build initial query - query = self.build_initial_query( trans, **kwargs ) - query = self.apply_query_filter( trans, query, **kwargs ) + query = self.build_initial_query(trans, **kwargs) + query = self.apply_query_filter(trans, query, **kwargs) # Maintain sort state in generated urls extra_url_args = {} # Determine whether use_default_filter flag is set. - use_default_filter_str = kwargs.get( 'use_default_filter' ) + use_default_filter_str = kwargs.get('use_default_filter') use_default_filter = False if use_default_filter_str: - use_default_filter = ( use_default_filter_str.lower() == 'true' ) + use_default_filter = (use_default_filter_str.lower() == 'true') # Process filtering arguments to (a) build a query that represents the filter and (b) build a # dictionary that denotes the current filter. cur_filter_dict = {} @@ -101,47 +101,47 @@ def __call__( self, trans, **kwargs ): column_filter = None if use_default_filter: if self.default_filter: - column_filter = self.default_filter.get( column.key ) + column_filter = self.default_filter.get(column.key) elif "f-" + column.model_class.__name__ + ".%s" % column.key in kwargs: # Queries that include table joins cannot guarantee unique column names. This problem is # handled by setting the column_filter value to .. - column_filter = kwargs.get( "f-" + column.model_class.__name__ + ".%s" % column.key ) + column_filter = kwargs.get("f-" + column.model_class.__name__ + ".%s" % column.key) elif "f-" + column.key in kwargs: - column_filter = kwargs.get( "f-" + column.key ) + column_filter = kwargs.get("f-" + column.key) elif column.key in base_filter: - column_filter = base_filter.get( column.key ) + column_filter = base_filter.get(column.key) # Method (1) combines a mix of strings and lists of strings into a single string and (2) attempts to de-jsonify all strings. def loads_recurse(item): decoded_list = [] - if isinstance( item, string_types): + if isinstance(item, string_types): try: # Not clear what we're decoding, so recurse to ensure that we catch everything. - decoded_item = loads( item ) - if isinstance( decoded_item, list): - decoded_list = loads_recurse( decoded_item ) + decoded_item = loads(item) + if isinstance(decoded_item, list): + decoded_list = loads_recurse(decoded_item) else: - decoded_list = [ text_type( decoded_item ) ] + decoded_list = [text_type(decoded_item)] except ValueError: - decoded_list = [ text_type( item ) ] - elif isinstance( item, list): + decoded_list = [text_type(item)] + elif isinstance(item, list): for element in item: - a_list = loads_recurse( element ) + a_list = loads_recurse(element) decoded_list = decoded_list + a_list return decoded_list # If column filter found, apply it. if column_filter is not None: # TextColumns may have a mix of json and strings. - if isinstance( column, TextColumn ): - column_filter = loads_recurse( column_filter ) - if len( column_filter ) == 1: + if isinstance(column, TextColumn): + column_filter = loads_recurse(column_filter) + if len(column_filter) == 1: column_filter = column_filter[0] # Interpret ',' as a separator for multiple terms. - if isinstance( column_filter, string_types ) and column_filter.find(',') != -1: + if isinstance(column_filter, string_types) and column_filter.find(',') != -1: column_filter = column_filter.split(',') # Check if filter is empty - if isinstance( column_filter, list ): + if isinstance(column_filter, list): # Remove empty strings from filter list column_filter = [x for x in column_filter if x != ''] if len(column_filter) == 0: @@ -152,21 +152,21 @@ def loads_recurse(item): continue # Update query. - query = column.filter( trans, trans.user, query, column_filter ) + query = column.filter(trans, trans.user, query, column_filter) # Upate current filter dict. # Column filters are rendered in various places, sanitize them all here. - cur_filter_dict[ column.key ] = sanitize_text(column_filter) + cur_filter_dict[column.key] = sanitize_text(column_filter) # Carry filter along to newly generated urls; make sure filter is a string so # that we can encode to UTF-8 and thus handle user input to filters. - if isinstance( column_filter, list ): + if isinstance(column_filter, list): # Filter is a list; process each item. - column_filter = [ text_type(_).encode('utf-8') if not isinstance(_, string_types) else _ for _ in column_filter ] - extra_url_args[ "f-" + column.key ] = dumps( column_filter ) + column_filter = [text_type(_).encode('utf-8') if not isinstance(_, string_types) else _ for _ in column_filter] + extra_url_args["f-" + column.key] = dumps(column_filter) else: # Process singleton filter. - if not isinstance( column_filter, string_types ): + if not isinstance(column_filter, string_types): column_filter = text_type(column_filter) - extra_url_args[ "f-" + column.key ] = column_filter.encode("utf-8") + extra_url_args["f-" + column.key] = column_filter.encode("utf-8") # Process sort arguments. sort_key = None if 'sort' in kwargs: @@ -174,12 +174,12 @@ def loads_recurse(item): elif base_sort_key: sort_key = base_sort_key if sort_key: - ascending = not( sort_key.startswith( "-" ) ) + ascending = not(sort_key.startswith("-")) # Queries that include table joins cannot guarantee unique column names. This problem is # handled by setting the column_filter value to .. table_name = None - if sort_key.find( '.' ) > -1: - a_list = sort_key.split( '.' ) + if sort_key.find('.') > -1: + a_list = sort_key.split('.') if ascending: table_name = a_list[0] else: @@ -191,23 +191,23 @@ def loads_recurse(item): column_name = sort_key[1:] # Sort key is a column key. for column in self.columns: - if column.key and column.key.find( '.' ) > -1: - column_key = column.key.split( '.' )[1] + if column.key and column.key.find('.') > -1: + column_key = column.key.split('.')[1] else: column_key = column.key - if ( table_name is None or table_name == column.model_class.__name__ ) and column_key == column_name: - query = column.sort( trans, query, ascending, column_name=column_name ) + if (table_name is None or table_name == column.model_class.__name__) and column_key == column_name: + query = column.sort(trans, query, ascending, column_name=column_name) break extra_url_args['sort'] = sort_key # There might be a current row - current_item = self.get_current_item( trans, **kwargs ) + current_item = self.get_current_item(trans, **kwargs) # Process page number. if self.use_paging: if 'page' in kwargs: if kwargs['page'] == 'all': page_num = 0 else: - page_num = int( kwargs['page'] ) + page_num = int(kwargs['page']) else: page_num = 1 if page_num == 0: @@ -219,8 +219,8 @@ def loads_recurse(item): # Show a limited number of rows. Before modifying query, get the total number of rows that query # returns so that the total number of pages can be computed. total_num_rows = query.count() - query = query.limit( self.num_rows_per_page ).offset( ( page_num - 1 ) * self.num_rows_per_page ) - num_pages = int( math.ceil( float( total_num_rows ) / self.num_rows_per_page ) ) + query = query.limit(self.num_rows_per_page).offset((page_num - 1) * self.num_rows_per_page) + num_pages = int(math.ceil(float(total_num_rows) / self.num_rows_per_page)) else: # Defaults. page_num = 1 @@ -230,17 +230,17 @@ def loads_recurse(item): self.cur_filter_dict = cur_filter_dict # Preserve grid state: save current filter and sort key. if self.preserve_state: - pref_name = text_type( self.__class__.__name__ + self.cur_filter_pref_name ) - trans.get_user().preferences[pref_name] = text_type( dumps( cur_filter_dict ) ) + pref_name = text_type(self.__class__.__name__ + self.cur_filter_pref_name) + trans.get_user().preferences[pref_name] = text_type(dumps(cur_filter_dict)) if sort_key: - pref_name = text_type( self.__class__.__name__ + self.cur_sort_key_pref_name ) - trans.get_user().preferences[pref_name] = text_type( dumps( sort_key ) ) + pref_name = text_type(self.__class__.__name__ + self.cur_sort_key_pref_name) + trans.get_user().preferences[pref_name] = text_type(dumps(sort_key)) trans.sa_session.flush() # Log grid view. - context = text_type( self.__class__.__name__ ) + context = text_type(self.__class__.__name__) params = cur_filter_dict.copy() params['sort'] = sort_key - params['async'] = ( 'async' in kwargs ) + params['async'] = ('async' in kwargs) # TODO:?? # commenting this out; when this fn calls session.add( action ) and session.flush the query from this fn @@ -250,67 +250,67 @@ def loads_recurse(item): # trans.log_action( trans.get_user(), text_type( "grid.view" ), context, params ) # Render grid. - def url( *args, **kwargs ): - route_name = kwargs.pop( '__route_name__', None ) + def url(*args, **kwargs): + route_name = kwargs.pop('__route_name__', None) # Only include sort/filter arguments if not linking to another # page. This is a bit of a hack. if 'action' in kwargs: new_kwargs = dict() else: - new_kwargs = dict( extra_url_args ) + new_kwargs = dict(extra_url_args) # Extend new_kwargs with first argument if found if len(args) > 0: - new_kwargs.update( args[0] ) - new_kwargs.update( kwargs ) + new_kwargs.update(args[0]) + new_kwargs.update(kwargs) # We need to encode item ids if 'id' in new_kwargs: - id = new_kwargs[ 'id' ] - if isinstance( id, list ): - new_kwargs[ 'id' ] = [ trans.security.encode_id( i ) for i in id ] + id = new_kwargs['id'] + if isinstance(id, list): + new_kwargs['id'] = [trans.security.encode_id(i) for i in id] else: - new_kwargs[ 'id' ] = trans.security.encode_id( id ) + new_kwargs['id'] = trans.security.encode_id(id) # The url_for invocation *must* include a controller and action. if 'controller' not in new_kwargs: new_kwargs['controller'] = trans.controller if 'action' not in new_kwargs: new_kwargs['action'] = trans.action if route_name: - return url_for( route_name, **new_kwargs ) - return url_for( **new_kwargs ) + return url_for(route_name, **new_kwargs) + return url_for(**new_kwargs) - self.use_panels = ( kwargs.get( 'use_panels', False ) in [ True, 'True', 'true' ] ) - self.advanced_search = ( kwargs.get( 'advanced_search', False ) in [ True, 'True', 'true' ] ) - async_request = ( ( self.use_async ) and ( kwargs.get( 'async', False ) in [ True, 'True', 'true'] ) ) + self.use_panels = (kwargs.get('use_panels', False) in [True, 'True', 'true']) + self.advanced_search = (kwargs.get('advanced_search', False) in [True, 'True', 'true']) + async_request = ((self.use_async) and (kwargs.get('async', False) in [True, 'True', 'true'])) # Currently, filling the template returns a str object; this requires decoding the string into a # unicode object within mako templates. What probably should be done is to return the template as # utf-8 unicode; however, this would require encoding the object as utf-8 before returning the grid # results via a controller method, which is require substantial changes. Hence, for now, return grid # as str. if not dict_format: - page = trans.fill_template( iff( async_request, self.async_template, self.template ), - grid=self, - query=query, - cur_page_num=page_num, - num_pages=num_pages, - num_page_links=self.num_page_links, - default_filter_dict=self.default_filter, - cur_filter_dict=cur_filter_dict, - sort_key=sort_key, - current_item=current_item, - ids=kwargs.get( 'id', [] ), - url=url, - status=status, - message=message, - info_text=self.info_text, - use_panels=self.use_panels, - use_hide_message=self.use_hide_message, - advanced_search=self.advanced_search, - show_item_checkboxes=( self.show_item_checkboxes or - kwargs.get( 'show_item_checkboxes', '' ) in [ 'True', 'true' ] ), - # Pass back kwargs so that grid template can set and use args without - # grid explicitly having to pass them. - kwargs=kwargs ) - trans.log_action( trans.get_user(), text_type( "grid.view" ), context, params ) + page = trans.fill_template(iff(async_request, self.async_template, self.template), + grid=self, + query=query, + cur_page_num=page_num, + num_pages=num_pages, + num_page_links=self.num_page_links, + default_filter_dict=self.default_filter, + cur_filter_dict=cur_filter_dict, + sort_key=sort_key, + current_item=current_item, + ids=kwargs.get('id', []), + url=url, + status=status, + message=message, + info_text=self.info_text, + use_panels=self.use_panels, + use_hide_message=self.use_hide_message, + advanced_search=self.advanced_search, + show_item_checkboxes=(self.show_item_checkboxes or + kwargs.get('show_item_checkboxes', '') in ['True', 'true']), + # Pass back kwargs so that grid template can set and use args without + # grid explicitly having to pass them. + kwargs=kwargs) + trans.log_action(trans.get_user(), text_type("grid.view"), context, params) return page grid_config = { @@ -321,7 +321,7 @@ def url( *args, **kwargs ): 'categorical_filters' : {}, 'filters' : cur_filter_dict, 'sort_key' : sort_key, - 'show_item_checkboxes' : self.show_item_checkboxes or kwargs.get( 'show_item_checkboxes', '' ) in [ 'True', 'true' ], + 'show_item_checkboxes' : self.show_item_checkboxes or kwargs.get('show_item_checkboxes', '') in ['True', 'true'], 'cur_page_num' : page_num, 'num_pages' : num_pages, 'num_page_links' : self.num_page_links, @@ -331,7 +331,7 @@ def url( *args, **kwargs ): 'operations' : [], 'items' : [], 'columns' : [], - 'model_class' : str( self.model_class ), + 'model_class' : str(self.model_class), 'use_paging' : self.use_paging, 'legend' : self.legend, 'current_item_id' : False, @@ -340,7 +340,7 @@ def url( *args, **kwargs ): 'advanced_search' : self.advanced_search, 'info_text' : self.info_text, 'url' : url(dict()), - 'refresh_frames' : kwargs.get( 'refresh_frames', [] ) + 'refresh_frames' : kwargs.get('refresh_frames', []) } if current_item: grid_config['current_item_id'] = current_item.id @@ -350,13 +350,13 @@ def url( *args, **kwargs ): if column.sortable: if sort_key.endswith(column.key): if not sort_key.startswith("-"): - href = url( sort=( "-" + column.key ) ) + href = url(sort=("-" + column.key)) extra = "↓" else: - href = url( sort=( column.key ) ) + href = url(sort=(column.key)) extra = "↑" else: - href = url( sort=column.key ) + href = url(sort=column.key) grid_config['columns'].append({ 'key' : column.key, 'visible' : column.visible, @@ -377,12 +377,13 @@ def url( *args, **kwargs ): 'target' : operation.target, 'label' : operation.label, 'confirm' : operation.confirm, + 'href' : url(**operation.url_args) if isinstance(operation.url_args, dict) else None, 'global_operation' : False }) if operation.allow_multiple: grid_config['show_item_checkboxes'] = True if operation.global_operation: - grid_config['global_operation'] = url( ** (operation.global_operation()) ) + grid_config['global_operation'] = url(** (operation.global_operation())) for action in self.global_actions: grid_config['global_actions'].append({ 'url_args' : url(**action.url_args), @@ -392,9 +393,9 @@ def url( *args, **kwargs ): for operation in [op for op in self.operations if op.async_compatible]: grid_config['async_ops'].append(operation.label.lower()) for column in self.columns: - if column.filterable is not None and not isinstance( column, TextColumn ): - grid_config['categorical_filters'][column.key] = dict([ (filter.label, filter.args) for filter in column.get_accepted_filters() ]) - for i, item in enumerate( query ): + if column.filterable is not None and not isinstance(column, TextColumn): + grid_config['categorical_filters'][column.key] = dict([(filter.label, filter.args) for filter in column.get_accepted_filters()]) + for i, item in enumerate(query): item_dict = { 'id' : item.id, 'encode_id' : trans.security.encode_id(item.id), @@ -410,7 +411,7 @@ def url( *args, **kwargs ): else: link = None target = column.target - value = column.get_value( trans, self, item ) + value = column.get_value(trans, self, item) if isinstance(value, str): value = unicode(value, 'utf-8') value = value.replace('/', '//') @@ -422,48 +423,48 @@ def url( *args, **kwargs ): for operation in self.operations: item_dict['operation_config'][operation.label] = { 'allowed' : operation.allowed(item), - 'url_args' : url( **operation.get_url_args( item ) ) + 'url_args' : url(**operation.get_url_args(item)) } grid_config['items'].append(item_dict) - trans.log_action( trans.get_user(), text_type( "grid.view" ), context, params ) + trans.log_action(trans.get_user(), text_type("grid.view"), context, params) return grid_config - def get_ids( self, **kwargs ): + def get_ids(self, **kwargs): id = [] if 'id' in kwargs: id = kwargs['id'] # Coerce ids to list - if not isinstance( id, list ): - id = id.split( "," ) + if not isinstance(id, list): + id = id.split(",") # Ensure ids are integers try: - id = map( int, id ) + id = map(int, id) except: - decorators.error( "Invalid id" ) + decorators.error("Invalid id") return id # ---- Override these ---------------------------------------------------- - def handle_operation( self, trans, operation, ids, **kwargs ): + def handle_operation(self, trans, operation, ids, **kwargs): pass - def get_current_item( self, trans, **kwargs ): + def get_current_item(self, trans, **kwargs): return None - def build_initial_query( self, trans, **kwargs ): - return trans.sa_session.query( self.model_class ) + def build_initial_query(self, trans, **kwargs): + return trans.sa_session.query(self.model_class) - def apply_query_filter( self, trans, query, **kwargs ): + def apply_query_filter(self, trans, query, **kwargs): # Applies a database filter that holds for all items in the grid. # (gvk) Is this method necessary? Why not simply build the entire query, # including applying filters in the build_initial_query() method? return query -class GridColumn( object ): - def __init__( self, label, key=None, model_class=None, method=None, format=None, - link=None, attach_popup=False, visible=True, nowrap=False, - # Valid values for filterable are ['standard', 'advanced', None] - filterable=None, sortable=True, label_id_prefix=None, target=None ): +class GridColumn(object): + def __init__(self, label, key=None, model_class=None, method=None, format=None, + link=None, attach_popup=False, visible=True, nowrap=False, + # Valid values for filterable are ['standard', 'advanced', None] + filterable=None, sortable=True, label_id_prefix=None, target=None): """Create a grid column.""" self.label = label self.key = key @@ -477,127 +478,129 @@ def __init__( self, label, key=None, model_class=None, method=None, format=None, self.visible = visible self.filterable = filterable # Column must have a key to be sortable. - self.sortable = ( self.key is not None and sortable ) + self.sortable = (self.key is not None and sortable) self.label_id_prefix = label_id_prefix or '' - def get_value( self, trans, grid, item ): + def get_value(self, trans, grid, item): if self.method: - value = getattr( grid, self.method )( trans, item ) + value = getattr(grid, self.method)(trans, item) elif self.key: - value = getattr( item, self.key ) + value = getattr(item, self.key) else: value = None if self.format: - value = self.format( value ) + value = self.format(value) return escape(unicodify(value)) - def get_link( self, trans, grid, item ): - if self.link and self.link( item ): - return self.link( item ) + def get_link(self, trans, grid, item): + if self.link and self.link(item): + return self.link(item) return None - def filter( self, trans, user, query, column_filter ): + def filter(self, trans, user, query, column_filter): """ Modify query to reflect the column filter. """ if column_filter == "All": pass if column_filter == "True": - query = query.filter_by( **{ self.key: True } ) + query = query.filter_by(**{self.key: True}) elif column_filter == "False": - query = query.filter_by( **{ self.key: False } ) + query = query.filter_by(**{self.key: False}) return query - def get_accepted_filters( self ): + def get_accepted_filters(self): """ Returns a list of accepted filters for this column. """ - accepted_filters_vals = [ "False", "True", "All" ] + accepted_filters_vals = ["False", "True", "All"] accepted_filters = [] for val in accepted_filters_vals: - args = { self.key: val } - accepted_filters.append( GridColumnFilter( val, args) ) + args = {self.key: val} + accepted_filters.append(GridColumnFilter(val, args)) return accepted_filters - def sort( self, trans, query, ascending, column_name=None ): + def sort(self, trans, query, ascending, column_name=None): """Sort query using this column.""" if column_name is None: column_name = self.key if ascending: - query = query.order_by( self.model_class.table.c.get( column_name ).asc() ) + query = query.order_by(self.model_class.table.c.get(column_name).asc()) else: - query = query.order_by( self.model_class.table.c.get( column_name ).desc() ) + query = query.order_by(self.model_class.table.c.get(column_name).desc()) return query -class ReverseSortColumn( GridColumn ): +class ReverseSortColumn(GridColumn): """ Column that reverses sorting; this is useful when the natural sort is descending. """ - def sort( self, trans, query, ascending, column_name=None ): - return GridColumn.sort( self, trans, query, (not ascending), column_name=column_name ) + def sort(self, trans, query, ascending, column_name=None): + return GridColumn.sort(self, trans, query, (not ascending), column_name=column_name) -class TextColumn( GridColumn ): + +class TextColumn(GridColumn): """ Generic column that employs freetext and, hence, supports freetext, case-independent filtering. """ - def filter( self, trans, user, query, column_filter ): + + def filter(self, trans, user, query, column_filter): """ Modify query to filter using free text, case independence. """ if column_filter == "All": pass elif column_filter: - query = query.filter( self.get_filter( trans, user, column_filter ) ) + query = query.filter(self.get_filter(trans, user, column_filter)) return query - def get_filter( self, trans, user, column_filter ): + def get_filter(self, trans, user, column_filter): """ Returns a SQLAlchemy criterion derived from column_filter. """ - if isinstance( column_filter, string_types ): - return self.get_single_filter( user, column_filter ) - elif isinstance( column_filter, list ): + if isinstance(column_filter, string_types): + return self.get_single_filter(user, column_filter) + elif isinstance(column_filter, list): clause_list = [] for filter in column_filter: - clause_list.append( self.get_single_filter( user, filter ) ) - return and_( *clause_list ) + clause_list.append(self.get_single_filter(user, filter)) + return and_(*clause_list) - def get_single_filter( self, user, a_filter ): + def get_single_filter(self, user, a_filter): """ Returns a SQLAlchemy criterion derived for a single filter. Single filter is the most basic filter--usually a string--and cannot be a list. """ # Queries that include table joins cannot guarantee that table column names will be # unique, so check to see if a_filter is of type .. - if self.key.find( '.' ) > -1: - a_key = self.key.split( '.' )[1] + if self.key.find('.') > -1: + a_key = self.key.split('.')[1] else: a_key = self.key - model_class_key_field = getattr( self.model_class, a_key ) - return func.lower( model_class_key_field ).like( "%" + a_filter.lower() + "%" ) + model_class_key_field = getattr(self.model_class, a_key) + return func.lower(model_class_key_field).like("%" + a_filter.lower() + "%") - def sort( self, trans, query, ascending, column_name=None ): + def sort(self, trans, query, ascending, column_name=None): """Sort column using case-insensitive alphabetical sorting.""" if column_name is None: column_name = self.key if ascending: - query = query.order_by( func.lower( self.model_class.table.c.get( column_name ) ).asc() ) + query = query.order_by(func.lower(self.model_class.table.c.get(column_name)).asc()) else: - query = query.order_by( func.lower( self.model_class.table.c.get( column_name ) ).desc() ) + query = query.order_by(func.lower(self.model_class.table.c.get(column_name)).desc()) return query -class DateTimeColumn( TextColumn ): - def sort( self, trans, query, ascending, column_name=None ): +class DateTimeColumn(TextColumn): + def sort(self, trans, query, ascending, column_name=None): """Sort query using this column.""" - return GridColumn.sort( self, trans, query, ascending, column_name=column_name ) + return GridColumn.sort(self, trans, query, ascending, column_name=column_name) -class BooleanColumn( TextColumn ): - def sort( self, trans, query, ascending, column_name=None ): +class BooleanColumn(TextColumn): + def sort(self, trans, query, ascending, column_name=None): """Sort query using this column.""" - return GridColumn.sort( self, trans, query, ascending, column_name=column_name ) + return GridColumn.sort(self, trans, query, ascending, column_name=column_name) - def get_single_filter( self, user, a_filter ): - if self.key.find( '.' ) > -1: - a_key = self.key.split( '.' )[1] + def get_single_filter(self, user, a_filter): + if self.key.find('.') > -1: + a_key = self.key.split('.')[1] else: a_key = self.key - model_class_key_field = getattr( self.model_class, a_key ) + model_class_key_field = getattr(self.model_class, a_key) return model_class_key_field == a_filter -class IntegerColumn( TextColumn ): +class IntegerColumn(TextColumn): """ Integer column that employs freetext, but checks that the text is an integer, so support filtering on integer values. @@ -614,197 +617,204 @@ class IntegerColumn( TextColumn ): JobIdColumn column in the SpecifiedDateListGrid class in the jobs controller of the reports webapp for an example. """ - def get_single_filter( self, user, a_filter ): - model_class_key_field = getattr( self.model_class, self.key ) - assert int( a_filter ), "The search entry must be an integer" - return model_class_key_field == int( a_filter ) - def sort( self, trans, query, ascending, column_name=None ): + def get_single_filter(self, user, a_filter): + model_class_key_field = getattr(self.model_class, self.key) + assert int(a_filter), "The search entry must be an integer" + return model_class_key_field == int(a_filter) + + def sort(self, trans, query, ascending, column_name=None): """Sort query using this column.""" - return GridColumn.sort( self, trans, query, ascending, column_name=column_name ) + return GridColumn.sort(self, trans, query, ascending, column_name=column_name) -class CommunityRatingColumn( GridColumn, UsesItemRatings ): +class CommunityRatingColumn(GridColumn, UsesItemRatings): """ Column that displays community ratings for an item. """ - def get_value( self, trans, grid, item ): - ave_item_rating, num_ratings = self.get_ave_item_rating_data( trans.sa_session, item, webapp_model=trans.model ) - return trans.fill_template( "tool_shed_rating.mako", - ave_item_rating=ave_item_rating, - num_ratings=num_ratings, - item_id=trans.security.encode_id( item.id ) ) - - def sort( self, trans, query, ascending, column_name=None ): - def get_foreign_key( source_class, target_class ): + + def get_value(self, trans, grid, item): + ave_item_rating, num_ratings = self.get_ave_item_rating_data(trans.sa_session, item, webapp_model=trans.model) + return trans.fill_template("tool_shed_rating.mako", + ave_item_rating=ave_item_rating, + num_ratings=num_ratings, + item_id=trans.security.encode_id(item.id)) + + def sort(self, trans, query, ascending, column_name=None): + def get_foreign_key(source_class, target_class): """ Returns foreign key in source class that references target class. """ target_fk = None for fk in source_class.table.foreign_keys: - if fk.references( target_class.table ): + if fk.references(target_class.table): target_fk = fk break if not target_fk: - raise RuntimeException( "No foreign key found between objects: %s, %s" % source_class.table, target_class.table ) + raise RuntimeException("No foreign key found between objects: %s, %s" % source_class.table, target_class.table) return target_fk # Get the columns that connect item's table and item's rating association table. - item_rating_assoc_class = getattr( trans.model, '%sRatingAssociation' % self.model_class.__name__ ) - foreign_key = get_foreign_key( item_rating_assoc_class, self.model_class ) + item_rating_assoc_class = getattr(trans.model, '%sRatingAssociation' % self.model_class.__name__) + foreign_key = get_foreign_key(item_rating_assoc_class, self.model_class) fk_col = foreign_key.parent - referent_col = foreign_key.get_referent( self.model_class.table ) + referent_col = foreign_key.get_referent(self.model_class.table) # Do sorting using a subquery. # Subquery to get average rating for each item. - ave_rating_subquery = trans.sa_session.query( fk_col, - func.avg( item_rating_assoc_class.table.c.rating ).label('avg_rating') ) \ - .group_by( fk_col ) \ - .subquery() + ave_rating_subquery = trans.sa_session.query(fk_col, + func.avg(item_rating_assoc_class.table.c.rating).label('avg_rating')) \ + .group_by(fk_col).subquery() # Integrate subquery into main query. - query = query.outerjoin( (ave_rating_subquery, referent_col == ave_rating_subquery.columns[fk_col.name]) ) + query = query.outerjoin((ave_rating_subquery, referent_col == ave_rating_subquery.columns[fk_col.name])) # Sort using subquery results; use coalesce to avoid null values. if not ascending: # TODO: for now, reverse sorting b/c first sort is ascending, and that should be the natural sort. - query = query.order_by( func.coalesce( ave_rating_subquery.c.avg_rating, 0 ).asc() ) + query = query.order_by(func.coalesce(ave_rating_subquery.c.avg_rating, 0).asc()) else: - query = query.order_by( func.coalesce( ave_rating_subquery.c.avg_rating, 0 ).desc() ) + query = query.order_by(func.coalesce(ave_rating_subquery.c.avg_rating, 0).desc()) return query -class OwnerAnnotationColumn( TextColumn, UsesAnnotations ): +class OwnerAnnotationColumn(TextColumn, UsesAnnotations): """ Column that displays and filters item owner's annotations. """ - def __init__( self, col_name, key, model_class=None, model_annotation_association_class=None, filterable=None ): - GridColumn.__init__( self, col_name, key=key, model_class=model_class, filterable=filterable ) + + def __init__(self, col_name, key, model_class=None, model_annotation_association_class=None, filterable=None): + GridColumn.__init__(self, col_name, key=key, model_class=model_class, filterable=filterable) self.sortable = False self.model_annotation_association_class = model_annotation_association_class - def get_value( self, trans, grid, item ): + def get_value(self, trans, grid, item): """ Returns first 150 characters of annotation. """ - annotation = self.get_item_annotation_str( trans.sa_session, item.user, item ) + annotation = self.get_item_annotation_str(trans.sa_session, item.user, item) if annotation: ann_snippet = annotation[:155] - if len( annotation ) > 155: - ann_snippet = ann_snippet[ :ann_snippet.rfind(' ') ] + if len(annotation) > 155: + ann_snippet = ann_snippet[:ann_snippet.rfind(' ')] ann_snippet += "..." else: ann_snippet = "" - return escape( ann_snippet ) + return escape(ann_snippet) - def get_single_filter( self, user, a_filter ): + def get_single_filter(self, user, a_filter): """ Filter by annotation and annotation owner. """ return self.model_class.annotations.any( - and_( func.lower( self.model_annotation_association_class.annotation ).like( "%" + a_filter.lower() + "%" ), + and_(func.lower(self.model_annotation_association_class.annotation).like("%" + a_filter.lower() + "%"), # TODO: not sure why, to filter by owner's annotations, we have to do this rather than # 'self.model_class.user==self.model_annotation_association_class.user' - self.model_annotation_association_class.table.c.user_id == self.model_class.table.c.user_id ) ) + self.model_annotation_association_class.table.c.user_id == self.model_class.table.c.user_id)) -class CommunityTagsColumn( TextColumn ): +class CommunityTagsColumn(TextColumn): """ Column that supports community tags. """ - def __init__( self, col_name, key, model_class=None, model_tag_association_class=None, filterable=None, grid_name=None ): - GridColumn.__init__( self, col_name, key=key, model_class=model_class, nowrap=True, filterable=filterable, sortable=False ) + + def __init__(self, col_name, key, model_class=None, model_tag_association_class=None, filterable=None, grid_name=None): + GridColumn.__init__(self, col_name, key=key, model_class=model_class, nowrap=True, filterable=filterable, sortable=False) self.model_tag_association_class = model_tag_association_class # Column-specific attributes. self.grid_name = grid_name - def get_value( self, trans, grid, item ): - return trans.fill_template( "/tagging_common.mako", tag_type="community", trans=trans, user=trans.get_user(), tagged_item=item, elt_context=self.grid_name, - in_form=True, input_size="20", tag_click_fn="add_tag_to_grid_filter", use_toggle_link=True ) + def get_value(self, trans, grid, item): + return trans.fill_template("/tagging_common.mako", tag_type="community", trans=trans, user=trans.get_user(), tagged_item=item, elt_context=self.grid_name, + in_form=True, input_size="20", tag_click_fn="add_tag_to_grid_filter", use_toggle_link=True) - def filter( self, trans, user, query, column_filter ): + def filter(self, trans, user, query, column_filter): """ Modify query to filter model_class by tag. Multiple filters are ANDed. """ if column_filter == "All": pass elif column_filter: - query = query.filter( self.get_filter( trans, user, column_filter ) ) + query = query.filter(self.get_filter(trans, user, column_filter)) return query - def get_filter( self, trans, user, column_filter ): - # Parse filter to extract multiple tags. - if isinstance( column_filter, list ): - # Collapse list of tags into a single string; this is redundant but effective. TODO: fix this by iterating over tags. - column_filter = ",".join( column_filter ) - raw_tags = trans.app.tag_handler.parse_tags( column_filter.encode( "utf-8" ) ) - clause_list = [] - for name, value in raw_tags: - if name: - # Filter by all tags. - clause_list.append( self.model_class.tags.any( func.lower( self.model_tag_association_class.user_tname ).like( "%" + name.lower() + "%" ) ) ) - if value: - # Filter by all values. - clause_list.append( self.model_class.tags.any( func.lower( self.model_tag_association_class.user_value ).like( "%" + value.lower() + "%" ) ) ) - return and_( *clause_list ) - - -class IndividualTagsColumn( CommunityTagsColumn ): + def get_filter(self, trans, user, column_filter): + # Parse filter to extract multiple tags. + if isinstance(column_filter, list): + # Collapse list of tags into a single string; this is redundant but effective. TODO: fix this by iterating over tags. + column_filter = ",".join(column_filter) + raw_tags = trans.app.tag_handler.parse_tags(column_filter.encode("utf-8")) + clause_list = [] + for name, value in raw_tags: + if name: + # Filter by all tags. + clause_list.append(self.model_class.tags.any(func.lower(self.model_tag_association_class.user_tname).like("%" + name.lower() + "%"))) + if value: + # Filter by all values. + clause_list.append(self.model_class.tags.any(func.lower(self.model_tag_association_class.user_value).like("%" + value.lower() + "%"))) + return and_(*clause_list) + + +class IndividualTagsColumn(CommunityTagsColumn): """ Column that supports individual tags. """ - def get_value( self, trans, grid, item ): - return trans.fill_template( "/tagging_common.mako", - tag_type="individual", - user=trans.user, - tagged_item=item, - elt_context=self.grid_name, - in_form=True, - input_size="20", - tag_click_fn="add_tag_to_grid_filter", - use_toggle_link=True ) - - def get_filter( self, trans, user, column_filter ): - # Parse filter to extract multiple tags. - if isinstance( column_filter, list ): - # Collapse list of tags into a single string; this is redundant but effective. TODO: fix this by iterating over tags. - column_filter = ",".join( column_filter ) - raw_tags = trans.app.tag_handler.parse_tags( column_filter.encode( "utf-8" ) ) - clause_list = [] - for name, value in raw_tags: - if name: - # Filter by individual's tag names. - clause_list.append( self.model_class.tags.any( and_( func.lower( self.model_tag_association_class.user_tname ).like( "%" + name.lower() + "%" ), self.model_tag_association_class.user == user ) ) ) - if value: - # Filter by individual's tag values. - clause_list.append( self.model_class.tags.any( and_( func.lower( self.model_tag_association_class.user_value ).like( "%" + value.lower() + "%" ), self.model_tag_association_class.user == user ) ) ) - return and_( *clause_list ) - -class MulticolFilterColumn( TextColumn ): + def get_value(self, trans, grid, item): + return trans.fill_template("/tagging_common.mako", + tag_type="individual", + user=trans.user, + tagged_item=item, + elt_context=self.grid_name, + in_form=True, + input_size="20", + tag_click_fn="add_tag_to_grid_filter", + use_toggle_link=True) + + def get_filter(self, trans, user, column_filter): + # Parse filter to extract multiple tags. + if isinstance(column_filter, list): + # Collapse list of tags into a single string; this is redundant but effective. TODO: fix this by iterating over tags. + column_filter = ",".join(column_filter) + raw_tags = trans.app.tag_handler.parse_tags(column_filter.encode("utf-8")) + clause_list = [] + for name, value in raw_tags: + if name: + # Filter by individual's tag names. + clause_list.append(self.model_class.tags.any(and_(func.lower(self.model_tag_association_class.user_tname).like("%" + name.lower() + "%"), self.model_tag_association_class.user == user))) + if value: + # Filter by individual's tag values. + clause_list.append(self.model_class.tags.any(and_(func.lower(self.model_tag_association_class.user_value).like("%" + value.lower() + "%"), self.model_tag_association_class.user == user))) + return and_(*clause_list) + + +class MulticolFilterColumn(TextColumn): """ Column that performs multicolumn filtering. """ - def __init__( self, col_name, cols_to_filter, key, visible, filterable="default" ): - GridColumn.__init__( self, col_name, key=key, visible=visible, filterable=filterable) + + def __init__(self, col_name, cols_to_filter, key, visible, filterable="default"): + GridColumn.__init__(self, col_name, key=key, visible=visible, filterable=filterable) self.cols_to_filter = cols_to_filter - def filter( self, trans, user, query, column_filter ): + def filter(self, trans, user, query, column_filter): """ Modify query to filter model_class by tag. Multiple filters are ANDed. """ if column_filter == "All": return query - if isinstance( column_filter, list): + if isinstance(column_filter, list): clause_list = [] for filter in column_filter: part_clause_list = [] for column in self.cols_to_filter: - part_clause_list.append( column.get_filter( trans, user, filter ) ) - clause_list.append( or_( *part_clause_list ) ) - complete_filter = and_( *clause_list ) + part_clause_list.append(column.get_filter(trans, user, filter)) + clause_list.append(or_(*part_clause_list)) + complete_filter = and_(*clause_list) else: clause_list = [] for column in self.cols_to_filter: - clause_list.append( column.get_filter( trans, user, column_filter ) ) - complete_filter = or_( *clause_list ) - return query.filter( complete_filter ) + clause_list.append(column.get_filter(trans, user, column_filter)) + complete_filter = or_(*clause_list) + return query.filter(complete_filter) -class OwnerColumn( TextColumn ): +class OwnerColumn(TextColumn): """ Column that lists item's owner. """ - def get_value( self, trans, grid, item ): + + def get_value(self, trans, grid, item): return item.user.username - def sort( self, trans, query, ascending, column_name=None ): + def sort(self, trans, query, ascending, column_name=None): """ Sort column using case-insensitive alphabetical sorting on item's username. """ if ascending: - query = query.order_by( func.lower( self.model_class.username ).asc() ) + query = query.order_by(func.lower(self.model_class.username).asc()) else: - query = query.order_by( func.lower( self.model_class.username ).desc() ) + query = query.order_by(func.lower(self.model_class.username).desc()) return query -class PublicURLColumn( TextColumn ): +class PublicURLColumn(TextColumn): """ Column displays item's public URL based on username and slug. """ - def get_link( self, trans, grid, item ): + + def get_link(self, trans, grid, item): if item.user.username and item.slug: - return dict( action='display_by_username_and_slug', username=item.user.username, slug=item.slug ) + return dict(action='display_by_username_and_slug', username=item.user.username, slug=item.slug) elif not item.user.username: # TODO: provide link to set username. return None @@ -813,92 +823,95 @@ def get_link( self, trans, grid, item ): return None -class DeletedColumn( GridColumn ): +class DeletedColumn(GridColumn): """ Column that tracks and filters for items with deleted attribute. """ - def get_accepted_filters( self ): + + def get_accepted_filters(self): """ Returns a list of accepted filters for this column. """ - accepted_filter_labels_and_vals = { "active" : "False", "deleted" : "True", "all": "All" } + accepted_filter_labels_and_vals = {"active" : "False", "deleted" : "True", "all": "All"} accepted_filters = [] for label, val in accepted_filter_labels_and_vals.items(): - args = { self.key: val } - accepted_filters.append( GridColumnFilter( label, args) ) + args = {self.key: val} + accepted_filters.append(GridColumnFilter(label, args)) return accepted_filters - def filter( self, trans, user, query, column_filter ): + def filter(self, trans, user, query, column_filter): """Modify query to filter self.model_class by state.""" if column_filter == "All": pass - elif column_filter in [ "True", "False" ]: - query = query.filter( self.model_class.deleted == ( column_filter == "True" ) ) + elif column_filter in ["True", "False"]: + query = query.filter(self.model_class.deleted == (column_filter == "True")) return query -class StateColumn( GridColumn ): +class StateColumn(GridColumn): """ Column that tracks and filters for items with state attribute. IMPORTANT NOTE: self.model_class must have a states Bunch or dict if this column type is used in the grid. """ - def get_value( self, trans, grid, item ): + + def get_value(self, trans, grid, item): return item.state - def filter( self, trans, user, query, column_filter ): + def filter(self, trans, user, query, column_filter): """Modify query to filter self.model_class by state.""" if column_filter == "All": pass - elif column_filter in [ v for k, v in self.model_class.states.items() ]: - query = query.filter( self.model_class.state == column_filter ) + elif column_filter in [v for k, v in self.model_class.states.items()]: + query = query.filter(self.model_class.state == column_filter) return query - def get_accepted_filters( self ): + def get_accepted_filters(self): """Returns a list of accepted filters for this column.""" - all = GridColumnFilter( 'all', { self.key : 'All' } ) - accepted_filters = [ all ] + all = GridColumnFilter('all', {self.key : 'All'}) + accepted_filters = [all] for k, v in self.model_class.states.items(): - args = { self.key: v } - accepted_filters.append( GridColumnFilter( v, args) ) + args = {self.key: v} + accepted_filters.append(GridColumnFilter(v, args)) return accepted_filters -class SharingStatusColumn( GridColumn ): +class SharingStatusColumn(GridColumn): """ Grid column to indicate sharing status. """ - def get_value( self, trans, grid, item ): + + def get_value(self, trans, grid, item): # Delete items cannot be shared. if item.deleted: return "" # Build a list of sharing for this item. sharing_statuses = [] if item.users_shared_with: - sharing_statuses.append( "Shared" ) + sharing_statuses.append("Shared") if item.importable: - sharing_statuses.append( "Accessible" ) + sharing_statuses.append("Accessible") if item.published: - sharing_statuses.append( "Published" ) - return ", ".join( sharing_statuses ) + sharing_statuses.append("Published") + return ", ".join(sharing_statuses) - def get_link( self, trans, grid, item ): - if not item.deleted and ( item.users_shared_with or item.importable or item.published ): - return dict( operation="share or publish", id=item.id ) + def get_link(self, trans, grid, item): + if not item.deleted and (item.users_shared_with or item.importable or item.published): + return dict(operation="share or publish", id=item.id) return None - def filter( self, trans, user, query, column_filter ): + def filter(self, trans, user, query, column_filter): """ Modify query to filter histories by sharing status. """ if column_filter == "All": pass elif column_filter: if column_filter == "private": - query = query.filter( self.model_class.users_shared_with == null() ) - query = query.filter( self.model_class.importable == false() ) + query = query.filter(self.model_class.users_shared_with == null()) + query = query.filter(self.model_class.importable == false()) elif column_filter == "shared": - query = query.filter( self.model_class.users_shared_with != null() ) + query = query.filter(self.model_class.users_shared_with != null()) elif column_filter == "accessible": - query = query.filter( self.model_class.importable == true() ) + query = query.filter(self.model_class.importable == true()) elif column_filter == "published": - query = query.filter( self.model_class.published == true() ) + query = query.filter(self.model_class.published == true()) return query - def get_accepted_filters( self ): + def get_accepted_filters(self): """ Returns a list of accepted filters for this column. """ accepted_filter_labels_and_vals = odict() accepted_filter_labels_and_vals["private"] = "private" @@ -908,15 +921,15 @@ def get_accepted_filters( self ): accepted_filter_labels_and_vals["all"] = "All" accepted_filters = [] for label, val in accepted_filter_labels_and_vals.items(): - args = { self.key: val } - accepted_filters.append( GridColumnFilter( label, args) ) + args = {self.key: val} + accepted_filters.append(GridColumnFilter(label, args)) return accepted_filters -class GridOperation( object ): - def __init__( self, label, key=None, condition=None, allow_multiple=True, allow_popup=True, - target=None, url_args=None, async_compatible=False, confirm=None, - global_operation=None ): +class GridOperation(object): + def __init__(self, label, key=None, condition=None, allow_multiple=True, allow_popup=True, + target=None, url_args=None, async_compatible=False, confirm=None, + global_operation=None): self.label = label self.key = key self.allow_multiple = allow_multiple @@ -933,44 +946,45 @@ def __init__( self, label, key=None, condition=None, allow_multiple=True, allow_ # global_operation=(lambda: dict(operation="download") self.global_operation = global_operation - def get_url_args( self, item ): + def get_url_args(self, item): if self.url_args: - if hasattr( self.url_args, '__call__' ): - url_args = self.url_args( item ) + if hasattr(self.url_args, '__call__'): + url_args = self.url_args(item) else: - url_args = dict( self.url_args ) + url_args = dict(self.url_args) url_args['id'] = item.id return url_args else: - return dict( operation=self.label, id=item.id ) + return dict(operation=self.label, id=item.id) - def allowed( self, item ): + def allowed(self, item): if self.condition: - return bool(self.condition( item )) + return bool(self.condition(item)) else: return True -class DisplayByUsernameAndSlugGridOperation( GridOperation ): +class DisplayByUsernameAndSlugGridOperation(GridOperation): """ Operation to display an item by username and slug. """ - def get_url_args( self, item ): - return { 'action' : 'display_by_username_and_slug', 'username' : item.user.username, 'slug' : item.slug } + + def get_url_args(self, item): + return {'action' : 'display_by_username_and_slug', 'username' : item.user.username, 'slug' : item.slug} -class GridAction( object ): - def __init__( self, label=None, url_args=None, target=None ): +class GridAction(object): + def __init__(self, label=None, url_args=None, target=None): self.label = label self.url_args = url_args self.target = target -class GridColumnFilter( object ): - def __init__( self, label, args=None ): +class GridColumnFilter(object): + def __init__(self, label, args=None): self.label = label self.args = args - def get_url_args( self ): + def get_url_args(self): rval = {} for k, v in self.args.items(): - rval[ "f-" + k ] = v + rval["f-" + k] = v return rval diff --git a/lib/galaxy/web/framework/middleware/batch.py b/lib/galaxy/web/framework/middleware/batch.py index 631b8db57964..9d666c08d817 100644 --- a/lib/galaxy/web/framework/middleware/batch.py +++ b/lib/galaxy/web/framework/middleware/batch.py @@ -28,10 +28,10 @@ import routes import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class BatchMiddleware( object ): +class BatchMiddleware(object): """ Adds a URL endpoint for processing batch API calls formatted as a JSON array of JSON dictionaries. These dictionaries are in the form: @@ -59,69 +59,69 @@ class BatchMiddleware( object ): ] } - def __init__( self, application, galaxy, config=None ): + def __init__(self, application, galaxy, config=None): #: the wrapped webapp self.application = application #: the original galaxy webapp self.galaxy = galaxy self.config = self.DEFAULT_CONFIG.copy() - self.config.update( config ) - self.base_url = routes.url_for( '/' ) + self.config.update(config) + self.base_url = routes.url_for('/') self.handle_request = self.galaxy.handle_request - def __call__( self, environ, start_response ): - if environ[ 'PATH_INFO' ] == self.config[ 'route' ]: - return self.process_batch_requests( environ, start_response ) - return self.application( environ, start_response ) + def __call__(self, environ, start_response): + if environ['PATH_INFO'] == self.config['route']: + return self.process_batch_requests(environ, start_response) + return self.application(environ, start_response) - def process_batch_requests( self, batch_environ, start_response ): + def process_batch_requests(self, batch_environ, start_response): """ Loops through any provided JSON formatted 'requests', aggregates their JSON responses, and wraps them in the batch call response. """ - payload = self._read_post_payload( batch_environ ) - requests = payload.get( 'batch', [] ) + payload = self._read_post_payload(batch_environ) + requests = payload.get('batch', []) responses = [] for request in requests: - if not self._is_allowed_route( request[ 'url' ] ): - responses.append( self._disallowed_route_response( request[ 'url' ] ) ) + if not self._is_allowed_route(request['url']): + responses.append(self._disallowed_route_response(request['url'])) continue - request_environ = self._build_request_environ( batch_environ, request ) - response = self._process_batch_request( request, request_environ, start_response ) - responses.append( response ) + request_environ = self._build_request_environ(batch_environ, request) + response = self._process_batch_request(request, request_environ, start_response) + responses.append(response) - batch_response_body = json.dumps( responses ) - start_response( '200 OK', [ - ( 'Content-Length', len( batch_response_body ) ), - ( 'Content-Type', 'application/json' ), + batch_response_body = json.dumps(responses) + start_response('200 OK', [ + ('Content-Length', len(batch_response_body)), + ('Content-Type', 'application/json'), ]) return batch_response_body - def _read_post_payload( self, environ ): - request_body_size = int( environ.get( 'CONTENT_LENGTH', 0 ) ) - request_body = environ[ 'wsgi.input' ].read( request_body_size ) or '{}' + def _read_post_payload(self, environ): + request_body_size = int(environ.get('CONTENT_LENGTH', 0)) + request_body = environ['wsgi.input'].read(request_body_size) or '{}' # TODO: json decode error handling # log.debug( 'request_body: (%s)\n%s', type( request_body ), request_body ) - payload = json.loads( request_body ) + payload = json.loads(request_body) return payload - def _is_allowed_route( self, route ): - if self.config.get( 'allowed_routes', None ): - shortened_route = route.replace( self.base_url, '', 1 ) - matches = [ re.match( allowed, shortened_route ) for allowed in self.config[ 'allowed_routes' ] ] - return any( matches ) + def _is_allowed_route(self, route): + if self.config.get('allowed_routes', None): + shortened_route = route.replace(self.base_url, '', 1) + matches = [re.match(allowed, shortened_route) for allowed in self.config['allowed_routes']] + return any(matches) return True - def _disallowed_route_response( self, route ): - return dict( status=403, headers=self._default_headers(), body={ + def _disallowed_route_response(self, route): + return dict(status=403, headers=self._default_headers(), body={ 'err_msg' : 'Disallowed route used for batch operation', 'route' : route, - 'allowed' : self.config[ 'allowed_routes' ] + 'allowed' : self.config['allowed_routes'] }) - def _build_request_environ( self, original_environ, request ): + def _build_request_environ(self, original_environ, request): """ Given a request and the original environ used to call the batch, return a new environ parsable/suitable for the individual api call. @@ -130,27 +130,27 @@ def _build_request_environ( self, original_environ, request ): # copy the original environ and reconstruct a fake version for each batched request request_environ = original_environ.copy() # TODO: for now, do not overwrite the other headers used in the main api/batch request - request_environ[ 'CONTENT_TYPE' ] = request.get( 'contentType', 'application/json' ) - request_environ[ 'REQUEST_METHOD' ] = request.get( 'method', request.get( 'type', 'GET' ) ) - url = '{0}://{1}{2}'.format( request_environ.get( 'wsgi.url_scheme' ), - request_environ.get( 'HTTP_HOST' ), - request[ 'url' ] ) - parsed = urlparse( url ) - request_environ[ 'PATH_INFO' ] = parsed.path - request_environ[ 'QUERY_STRING' ] = parsed.query - - request_body = request.get( 'body', u'' ) + request_environ['CONTENT_TYPE'] = request.get('contentType', 'application/json') + request_environ['REQUEST_METHOD'] = request.get('method', request.get('type', 'GET')) + url = '{0}://{1}{2}'.format(request_environ.get('wsgi.url_scheme'), + request_environ.get('HTTP_HOST'), + request['url']) + parsed = urlparse(url) + request_environ['PATH_INFO'] = parsed.path + request_environ['QUERY_STRING'] = parsed.query + + request_body = request.get('body', u'') # set this to None so webob/request will copy the body using the raw bytes # if we set it, webob will try to use the buffer interface on a unicode string - request_environ[ 'CONTENT_LENGTH' ] = None + request_environ['CONTENT_LENGTH'] = None # this may well need to change in py3 - request_body = io.BytesIO( bytearray( request_body, encoding='utf8' ) ) - request_environ[ 'wsgi.input' ] = request_body + request_body = io.BytesIO(bytearray(request_body, encoding='utf8')) + request_environ['wsgi.input'] = request_body # log.debug( 'request_environ:\n%s', pprint.pformat( request_environ ) ) return request_environ - def _process_batch_request( self, request, environ, start_response ): + def _process_batch_request(self, request, environ, start_response): # We may need to include middleware to record various reponses, but this way of doing that won't work: # status, headers, body = self.application( environ, start_response, body_renderer=self.body_renderer ) @@ -159,27 +159,27 @@ def _process_batch_request( self, request, environ, start_response ): # File "./eggs/Paste-1.7.5.1-py2.7.egg/paste/httpserver.py", line 166, in wsgi_start_response # assert 0, "Attempt to set headers a second time w/o an exc_info" try: - response = self.galaxy.handle_request( environ, start_response, body_renderer=self.body_renderer ) + response = self.galaxy.handle_request(environ, start_response, body_renderer=self.body_renderer) # handle errors from galaxy.handle_request (only 404s) except httpexceptions.HTTPNotFound: - response = dict( status=404, headers=self._default_headers(), body={} ) + response = dict(status=404, headers=self._default_headers(), body={}) return response - def body_renderer( self, trans, body, environ, start_response ): + def body_renderer(self, trans, body, environ, start_response): # this is a dummy renderer that does not call start_response # See 'We have to re-create the handle request method...' in _process_batch_request above return dict( status=trans.response.status, headers=trans.response.headers, - body=json.loads( self.galaxy.make_body_iterable( trans, body )[0] ) + body=json.loads(self.galaxy.make_body_iterable(trans, body)[0]) ) - def _default_headers( self ): + def _default_headers(self): return { 'x-frame-options': 'SAMEORIGIN', 'content-type' : 'application/json', 'cache-control' : 'max-age=0,no-cache,no-store' } - def handle_exception( self, environ ): + def handle_exception(self, environ): return False diff --git a/lib/galaxy/web/framework/middleware/profile.py b/lib/galaxy/web/framework/middleware/profile.py index d4fa6bcfb432..b5cf31c94a76 100644 --- a/lib/galaxy/web/framework/middleware/profile.py +++ b/lib/galaxy/web/framework/middleware/profile.py @@ -48,7 +48,7 @@ class ProfileMiddleware(object): data from previous requests. """ - def __init__( self, app, global_conf=None, limit=40 ): + def __init__(self, app, global_conf=None, limit=40): self.app = app self.lock = threading.Lock() self.limit = limit @@ -66,7 +66,7 @@ def run_app(): body.extend(self.app(environ, replace_start_response)) # Run in profiler prof = cProfile.Profile() - prof.runctx( "run_app()", globals(), locals() ) + prof.runctx("run_app()", globals(), locals()) # Build up body with stats body = ''.join(body) headers = catch_response[1] @@ -74,74 +74,74 @@ def run_app(): if not content_type.startswith('text/html'): # We can't add info to non-HTML output return [body] - stats = pstats.Stats( prof ) + stats = pstats.Stats(prof) stats.strip_dirs() - stats.sort_stats( 'time', 'calls' ) - output = pstats_as_html( stats, self.limit ) + stats.sort_stats('time', 'calls') + output = pstats_as_html(stats, self.limit) body += template % output return [body] -def pstats_as_html( stats, *sel_list ): +def pstats_as_html(stats, *sel_list): """ Return an HTML representation of a pstats.Stats object. """ rval = [] # Number of function calls, primitive calls, total time - rval.append( "
    %d function calls (%d primitive) in %0.3f CPU seconds
    " - % ( stats.total_calls, stats.prim_calls, stats.total_tt ) ) + rval.append("
    %d function calls (%d primitive) in %0.3f CPU seconds
    " + % (stats.total_calls, stats.prim_calls, stats.total_tt)) # Extract functions that match 'sel_list' - funcs, order_message, select_message = get_func_list( stats, sel_list ) + funcs, order_message, select_message = get_func_list(stats, sel_list) # Deal with any ordering or selection messages if order_message: - rval.append( "
    %s
    " % cgi.escape( order_message ) ) + rval.append("
    %s
    " % cgi.escape(order_message)) if select_message: - rval.append( "
    %s
    " % cgi.escape( select_message ) ) + rval.append("
    %s
    " % cgi.escape(select_message)) # Build a table for the functions if list: - rval.append( "
    " ) + rval.append("
    ") # Header - rval.append( "" - "" - "" - "" - "" - "" ) + rval.append("" + "" + "" + "" + "" + "") for func in funcs: - rval.append( "" ) + rval.append("") # Calculate each field - cc, nc, tt, ct, callers = stats.stats[ func ] + cc, nc, tt, ct, callers = stats.stats[func] # ncalls ncalls = str(nc) if nc != cc: ncalls = ncalls + '/' + str(cc) - rval.append( "" % cgi.escape( ncalls ) ) + rval.append("" % cgi.escape(ncalls)) # tottime - rval.append( "" % tt ) + rval.append("" % tt) # percall if nc == 0: percall = "" else: - percall = "%0.8f" % ( tt / nc ) - rval.append( "" % cgi.escape( percall ) ) + percall = "%0.8f" % (tt / nc) + rval.append("" % cgi.escape(percall)) # cumtime - rval.append( "" % ct ) + rval.append("" % ct) # ctpercall if cc == 0: ctpercall = "" else: - ctpercall = "%0.8f" % ( ct / cc ) - rval.append( "" % cgi.escape( ctpercall ) ) + ctpercall = "%0.8f" % (ct / cc) + rval.append("" % cgi.escape(ctpercall)) # location - rval.append( "" % cgi.escape( func_std_string( func ) ) ) + rval.append("" % cgi.escape(func_std_string(func))) # row complete - rval.append( "" ) - rval.append( "
    ncallstottimepercallcumtimepercallfilename:lineno(function)
    ncallstottimepercallcumtimepercallfilename:lineno(function)
    %s%s%0.8f%0.8f%s%s%0.8f%0.8f%s%s%s%s
    ") + rval.append("") + rval.append("") # Concatenate result - return "".join( rval ) + return "".join(rval) -def get_func_list( stats, sel_list ): +def get_func_list(stats, sel_list): """ Use 'sel_list' to select a list of functions to display. """ @@ -155,12 +155,12 @@ def get_func_list( stats, sel_list ): # Do the selection and accumulate messages select_message = "" for selection in sel_list: - list, select_message = stats.eval_print_amount( selection, list, select_message ) + list, select_message = stats.eval_print_amount(selection, list, select_message) # Return the list of functions selected and the message return list, order_message, select_message -def func_std_string( func_name ): +def func_std_string(func_name): """ Match what old profile produced """ diff --git a/lib/galaxy/web/framework/middleware/remoteuser.py b/lib/galaxy/web/framework/middleware/remoteuser.py index cab1548882ad..c635f9d2d7a2 100644 --- a/lib/galaxy/web/framework/middleware/remoteuser.py +++ b/lib/galaxy/web/framework/middleware/remoteuser.py @@ -39,11 +39,11 @@ """ -class RemoteUser( object ): +class RemoteUser(object): - def __init__( self, app, maildomain=None, display_servers=None, admin_users=None, - single_user=None, remote_user_header=None, remote_user_secret_header=None, - normalize_remote_user_email=False ): + def __init__(self, app, maildomain=None, display_servers=None, admin_users=None, + single_user=None, remote_user_header=None, remote_user_secret_header=None, + normalize_remote_user_email=False): self.app = app self.maildomain = maildomain self.display_servers = display_servers or [] @@ -53,21 +53,21 @@ def __init__( self, app, maildomain=None, display_servers=None, admin_users=None self.config_secret_header = remote_user_secret_header self.normalize_remote_user_email = normalize_remote_user_email - def __call__( self, environ, start_response ): + def __call__(self, environ, start_response): # Allow display servers if self.display_servers and 'REMOTE_ADDR' in environ: try: - host = socket.gethostbyaddr( environ[ 'REMOTE_ADDR' ] )[0] - except( socket.error, socket.herror, socket.gaierror, socket.timeout ): + host = socket.gethostbyaddr(environ['REMOTE_ADDR'])[0] + except(socket.error, socket.herror, socket.gaierror, socket.timeout): # in the event of a lookup failure, deny access host = None if host in self.display_servers: - environ[ self.remote_user_header ] = 'remote_display_server@%s' % ( self.maildomain or 'example.org' ) - return self.app( environ, start_response ) + environ[self.remote_user_header] = 'remote_display_server@%s' % (self.maildomain or 'example.org') + return self.app(environ, start_response) if self.single_user: assert self.remote_user_header not in environ - environ[ self.remote_user_header ] = self.single_user + environ[self.remote_user_header] = self.single_user if environ.get(self.remote_user_header, '').startswith('(null)'): # Throw away garbage headers. @@ -87,8 +87,8 @@ def __call__( self, environ, start_response ): # The API handles its own authentication via keys # Check for API key before checking for header - if path_info.startswith( '/api/' ): - return self.app( environ, start_response ) + if path_info.startswith('/api/'): + return self.app(environ, start_response) # If the secret header is enabled, we expect upstream to send along some key # in HTTP_GX_SECRET, so we'll need to compare that here to the correct value @@ -115,7 +115,7 @@ def __call__( self, environ, start_response ): GX_SECRET header must be set before you may access Galaxy. """ - return self.error( start_response, title, message ) + return self.error(start_response, title, message) if not safe_str_cmp(environ.get('HTTP_GX_SECRET', ''), self.config_secret_header): title = "Access to Galaxy is denied" message = """ @@ -128,12 +128,12 @@ def __call__( self, environ, start_response ): GX_SECRET header must be set before you may access Galaxy. """ - return self.error( start_response, title, message ) + return self.error(start_response, title, message) if environ.get(self.remote_user_header, None): - if not environ[ self.remote_user_header ].count( '@' ): + if not environ[self.remote_user_header].count('@'): if self.maildomain is not None: - environ[ self.remote_user_header ] += '@' + self.maildomain + environ[self.remote_user_header] += '@' + self.maildomain else: title = "Access to Galaxy is denied" message = """ @@ -146,7 +146,7 @@ def __call__( self, environ, start_response ): variable remote_user_maildomain must be set before you may access Galaxy. """ - return self.error( start_response, title, message ) + return self.error(start_response, title, message) user_accessible_paths = ( '/users', '/user/api_key', @@ -180,15 +180,15 @@ def __call__( self, environ, start_response ): pass elif path_info == '/user' or path_info == '/user/': pass # We do allow access to the root user preferences page. - elif path_info.startswith( '/user' ): + elif path_info.startswith('/user'): # Any other endpoint in the user controller is off limits title = "Access to Galaxy user controls is disabled" message = """ User controls are disabled when Galaxy is configured for external authentication. """ - return self.error( start_response, title, message ) - return self.app( environ, start_response ) + return self.error(start_response, title, message) + return self.app(environ, start_response) else: log.debug("Unable to identify user. %s not found" % self.remote_user_header) for k, v in environ.iteritems(): @@ -202,8 +202,8 @@ def __call__( self, environ, start_response ): generally due to a misconfiguration in the upstream server.

    Please contact your local Galaxy administrator. """ - return self.error( start_response, title, message ) + return self.error(start_response, title, message) - def error( self, start_response, title="Access denied", message="Please contact your local Galaxy administrator." ): - start_response( '403 Forbidden', [('Content-type', 'text/html')] ) + def error(self, start_response, title="Access denied", message="Please contact your local Galaxy administrator."): + start_response('403 Forbidden', [('Content-type', 'text/html')]) return [errorpage % (title, message)] diff --git a/lib/galaxy/web/framework/middleware/request_id.py b/lib/galaxy/web/framework/middleware/request_id.py index 1f7500bc7bf8..46eb644f8963 100644 --- a/lib/galaxy/web/framework/middleware/request_id.py +++ b/lib/galaxy/web/framework/middleware/request_id.py @@ -1,14 +1,15 @@ import uuid -class RequestIDMiddleware( object ): +class RequestIDMiddleware(object): """ A WSGI middleware that creates a unique ID for the request and puts it in the environment """ - def __init__( self, app, global_conf=None ): + + def __init__(self, app, global_conf=None): self.app = app - def __call__( self, environ, start_response ): + def __call__(self, environ, start_response): environ['request_id'] = uuid.uuid1().hex - return self.app( environ, start_response ) + return self.app(environ, start_response) diff --git a/lib/galaxy/web/framework/middleware/sentry.py b/lib/galaxy/web/framework/middleware/sentry.py index aec0ee3b4203..0817fc5fbb36 100644 --- a/lib/galaxy/web/framework/middleware/sentry.py +++ b/lib/galaxy/web/framework/middleware/sentry.py @@ -24,13 +24,14 @@ class Sentry(object): A WSGI middleware which will attempt to capture any uncaught exceptions and send them to Sentry. """ + def __init__(self, application, dsn): assert Client is not None, RAVEN_IMPORT_MESSAGE self.application = application self.client = None def postfork_sentry_client(): - self.client = Client( dsn ) + self.client = Client(dsn) register_postfork_function(postfork_sentry_client) @@ -90,7 +91,7 @@ def handle_exception(self, environ): }, # Galaxy: add request id from environment if available extra={ - 'request_id': environ.get( 'request_id', 'Unknown' ) + 'request_id': environ.get('request_id', 'Unknown') } ) # Galaxy: store event_id in environment so we can show it to the user diff --git a/lib/galaxy/web/framework/middleware/static.py b/lib/galaxy/web/framework/middleware/static.py index c68838dee5d7..3b89e45976ef 100644 --- a/lib/galaxy/web/framework/middleware/static.py +++ b/lib/galaxy/web/framework/middleware/static.py @@ -8,20 +8,20 @@ from paste.urlparser import StaticURLParser -class CacheableStaticURLParser( StaticURLParser ): +class CacheableStaticURLParser(StaticURLParser): - def __init__( self, directory, cache_seconds=None ): - StaticURLParser.__init__( self, directory ) + def __init__(self, directory, cache_seconds=None): + StaticURLParser.__init__(self, directory) self.cache_seconds = cache_seconds - def __call__( self, environ, start_response ): + def __call__(self, environ, start_response): path_info = environ.get('PATH_INFO', '') if not path_info: # See if this is a static file hackishly mapped. if os.path.exists(self.directory) and os.path.isfile(self.directory): app = fileapp.FileApp(self.directory) if self.cache_seconds: - app.cache_control( max_age=int( self.cache_seconds ) ) + app.cache_control(max_age=int(self.cache_seconds)) return app(environ, start_response) return self.add_slash(environ, start_response) if path_info == '/': @@ -47,9 +47,9 @@ def __call__( self, environ, start_response ): return [''] # empty body app = fileapp.FileApp(full) if self.cache_seconds: - app.cache_control( max_age=int( self.cache_seconds ) ) + app.cache_control(max_age=int(self.cache_seconds)) return app(environ, start_response) -def make_static( global_conf, document_root, cache_seconds=None ): - return CacheableStaticURLParser( document_root, cache_seconds ) +def make_static(global_conf, document_root, cache_seconds=None): + return CacheableStaticURLParser(document_root, cache_seconds) diff --git a/lib/galaxy/web/framework/middleware/statsd.py b/lib/galaxy/web/framework/middleware/statsd.py index d3ed58082acb..86829e8519a6 100644 --- a/lib/galaxy/web/framework/middleware/statsd.py +++ b/lib/galaxy/web/framework/middleware/statsd.py @@ -25,8 +25,8 @@ def __init__(self, statsd_port, statsd_prefix): if not statsd: - raise ImportError( "Statsd middleware configured, but no statsd python module found. " - "Please install the python statsd module to use this functionality." ) + raise ImportError("Statsd middleware configured, but no statsd python module found. " + "Please install the python statsd module to use this functionality.") self.application = application self.statsd_client = statsd.StatsClient(statsd_host, statsd_port, prefix=statsd_prefix) diff --git a/lib/galaxy/web/framework/middleware/translogger.py b/lib/galaxy/web/framework/middleware/translogger.py index 286f39ba51c3..cf837c1421a8 100644 --- a/lib/galaxy/web/framework/middleware/translogger.py +++ b/lib/galaxy/web/framework/middleware/translogger.py @@ -67,20 +67,20 @@ def replacement_start_response(status, headers, exc_info=None): if name.lower() == 'content-length': bytes = value self.write_log(environ, method, req_uri, start, status, bytes) - return start_response( status, headers, exc_info ) + return start_response(status, headers, exc_info) return self.application(environ, replacement_start_response) def write_log(self, environ, method, req_uri, start, status, bytes): if bytes is None: bytes = '-' if time.daylight: - offset = time.altzone / 60 / 60 * -100 + offset = time.altzone / 60 / 60 * -100 else: - offset = time.timezone / 60 / 60 * -100 + offset = time.timezone / 60 / 60 * -100 if offset >= 0: - offset = "+%0.4d" % (offset) + offset = "+%0.4d" % (offset) elif offset < 0: - offset = "%0.4d" % (offset) + offset = "%0.4d" % (offset) d = { 'REMOTE_ADDR': environ.get('REMOTE_ADDR') or '-', 'REMOTE_USER': environ.get('REMOTE_USER') or '-', diff --git a/lib/galaxy/web/framework/middleware/xforwardedhost.py b/lib/galaxy/web/framework/middleware/xforwardedhost.py index 8ea6fce88dbf..ff5e394c062c 100644 --- a/lib/galaxy/web/framework/middleware/xforwardedhost.py +++ b/lib/galaxy/web/framework/middleware/xforwardedhost.py @@ -1,22 +1,23 @@ -class XForwardedHostMiddleware( object ): +class XForwardedHostMiddleware(object): """ A WSGI middleware that changes the HTTP host header in the WSGI environ based on the X-Forwarded-Host header IF found """ - def __init__( self, app, global_conf=None ): + + def __init__(self, app, global_conf=None): self.app = app - def __call__( self, environ, start_response ): - x_forwarded_host = environ.get( 'HTTP_X_FORWARDED_HOST', None ) + def __call__(self, environ, start_response): + x_forwarded_host = environ.get('HTTP_X_FORWARDED_HOST', None) if x_forwarded_host: - environ[ 'ORGINAL_HTTP_HOST' ] = environ[ 'HTTP_HOST' ] - environ[ 'HTTP_HOST' ] = x_forwarded_host.split(', ', 1)[0] - x_forwarded_for = environ.get( 'HTTP_X_FORWARDED_FOR', None ) + environ['ORGINAL_HTTP_HOST'] = environ['HTTP_HOST'] + environ['HTTP_HOST'] = x_forwarded_host.split(', ', 1)[0] + x_forwarded_for = environ.get('HTTP_X_FORWARDED_FOR', None) if x_forwarded_for: - environ[ 'ORGINAL_REMOTE_ADDR' ] = environ[ 'REMOTE_ADDR' ] - environ[ 'REMOTE_ADDR' ] = x_forwarded_for.split(', ', 1)[0] - x_url_scheme = environ.get( 'HTTP_X_URL_SCHEME', None ) + environ['ORGINAL_REMOTE_ADDR'] = environ['REMOTE_ADDR'] + environ['REMOTE_ADDR'] = x_forwarded_for.split(', ', 1)[0] + x_url_scheme = environ.get('HTTP_X_URL_SCHEME', None) if x_url_scheme: - environ[ 'original_wsgi.url_scheme' ] = environ[ 'wsgi.url_scheme' ] - environ[ 'wsgi.url_scheme' ] = x_url_scheme - return self.app( environ, start_response ) + environ['original_wsgi.url_scheme'] = environ['wsgi.url_scheme'] + environ['wsgi.url_scheme'] = x_url_scheme + return self.app(environ, start_response) diff --git a/lib/galaxy/web/framework/openid_manager.py b/lib/galaxy/web/framework/openid_manager.py index 3856b222ff37..22b8b6d1c4e3 100644 --- a/lib/galaxy/web/framework/openid_manager.py +++ b/lib/galaxy/web/framework/openid_manager.py @@ -14,7 +14,7 @@ except ImportError: oidutil = None - class FakeConsumer( object ): + class FakeConsumer(object): def __getattr__(x, y): return None consumer = FakeConsumer() @@ -23,50 +23,50 @@ def __getattr__(x, y): OPENID_IMPORT_MESSAGE = ('The Python openid package is required to use this ' 'feature, please install it') -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -def oidlog( message, level=0 ): - log.debug( message ) +def oidlog(message, level=0): + log.debug(message) if oidutil is not None: oidutil.log = oidlog -class OpenIDManager( object ): - def __init__( self, cache_path ): +class OpenIDManager(object): + def __init__(self, cache_path): assert oidutil is not None, OPENID_IMPORT_MESSAGE - self.session_path = os.path.join( cache_path, 'session' ) - self.store_path = os.path.join( cache_path, 'store' ) + self.session_path = os.path.join(cache_path, 'session') + self.store_path = os.path.join(cache_path, 'store') for dir in self.session_path, self.store_path: - if not os.path.exists( dir ): - os.makedirs( dir ) - self.store = filestore.FileOpenIDStore( self.store_path ) + if not os.path.exists(dir): + os.makedirs(dir) + self.store = filestore.FileOpenIDStore(self.store_path) - def get_session( self, trans ): - session_file = os.path.join( self.session_path, str( trans.galaxy_session.id ) ) - if not os.path.exists( session_file ): - pickle.dump( dict(), open( session_file, 'w' ) ) - return pickle.load( open( session_file ) ) + def get_session(self, trans): + session_file = os.path.join(self.session_path, str(trans.galaxy_session.id)) + if not os.path.exists(session_file): + pickle.dump(dict(), open(session_file, 'w')) + return pickle.load(open(session_file)) - def persist_session( self, trans, oidconsumer ): - session_file = os.path.join( self.session_path, str( trans.galaxy_session.id ) ) - pickle.dump( oidconsumer.session, open( session_file, 'w' ) ) + def persist_session(self, trans, oidconsumer): + session_file = os.path.join(self.session_path, str(trans.galaxy_session.id)) + pickle.dump(oidconsumer.session, open(session_file, 'w')) - def get_consumer( self, trans ): - return consumer.Consumer( self.get_session( trans ), self.store ) + def get_consumer(self, trans): + return consumer.Consumer(self.get_session(trans), self.store) - def add_sreg( self, trans, request, required=None, optional=None ): + def add_sreg(self, trans, request, required=None, optional=None): if required is None: required = [] if optional is None: optional = [] - sreg_request = sreg.SRegRequest( required=required, optional=optional ) - request.addExtension( sreg_request ) + sreg_request = sreg.SRegRequest(required=required, optional=optional) + request.addExtension(sreg_request) - def get_sreg( self, info ): - return sreg.SRegResponse.fromSuccessResponse( info ) + def get_sreg(self, info): + return sreg.SRegResponse.fromSuccessResponse(info) # so I don't have to expose all of openid.consumer.consumer FAILURE = consumer.FAILURE diff --git a/lib/galaxy/web/framework/webapp.py b/lib/galaxy/web/framework/webapp.py index 0e02d36795a3..786e9c4396fa 100644 --- a/lib/galaxy/web/framework/webapp.py +++ b/lib/galaxy/web/framework/webapp.py @@ -1,42 +1,44 @@ """ """ import datetime +import hashlib import inspect +import logging import os -import hashlib import random import socket import string import time -import urlparse -from Cookie import CookieError from importlib import import_module -from Cheetah.Template import Template -import mako.runtime import mako.lookup -from babel.support import Translations +import mako.runtime from babel import Locale +from babel.support import Translations +from Cheetah.Template import Template from six import string_types +from six.moves.http_cookies import CookieError +from six.moves.urllib.parse import urlparse from sqlalchemy import and_, true -from sqlalchemy.orm.exc import NoResultFound from sqlalchemy.orm import joinedload - -from galaxy.exceptions import MessageException +from sqlalchemy.orm.exc import NoResultFound from galaxy import util -from galaxy.util import asbool -from galaxy.util import safe_str_cmp -from galaxy.util.sanitize_html import sanitize_html - +from galaxy.exceptions import MessageException from galaxy.managers import context -from galaxy.web.framework import url_for -from galaxy.web.framework import base -from galaxy.web.framework import helpers -from galaxy.web.framework import formbuilder +from galaxy.util import ( + asbool, + safe_str_cmp +) +from galaxy.util.sanitize_html import sanitize_html +from galaxy.web.framework import ( + base, + formbuilder, + helpers, + url_for +) -import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) UCSC_SERVERS = ( @@ -59,7 +61,7 @@ ) -class WebApplication( base.WebApplication ): +class WebApplication(base.WebApplication): """ Base WSGI application instantiated for all Galaxy webapps. @@ -70,118 +72,119 @@ class WebApplication( base.WebApplication ): * builds mako template lookups. * generates GalaxyWebTransactions. """ - def __init__( self, galaxy_app, session_cookie='galaxysession', name=None ): + + def __init__(self, galaxy_app, session_cookie='galaxysession', name=None): self.name = name - base.WebApplication.__init__( self ) - self.set_transaction_factory( lambda e: self.transaction_chooser( e, galaxy_app, session_cookie ) ) + base.WebApplication.__init__(self) + self.set_transaction_factory(lambda e: self.transaction_chooser(e, galaxy_app, session_cookie)) # Mako support - self.mako_template_lookup = self.create_mako_template_lookup( galaxy_app, name ) + self.mako_template_lookup = self.create_mako_template_lookup(galaxy_app, name) # Security helper self.security = galaxy_app.security - def create_mako_template_lookup( self, galaxy_app, name ): + def create_mako_template_lookup(self, galaxy_app, name): paths = [] # First look in webapp specific directory if name is not None: - paths.append( os.path.join( galaxy_app.config.template_path, 'webapps', name ) ) + paths.append(os.path.join(galaxy_app.config.template_path, 'webapps', name)) # Then look in root directory - paths.append( galaxy_app.config.template_path ) + paths.append(galaxy_app.config.template_path) # Create TemplateLookup with a small cache return mako.lookup.TemplateLookup(directories=paths, module_directory=galaxy_app.config.template_cache, collection_size=500, - output_encoding='utf-8' ) + output_encoding='utf-8') - def handle_controller_exception( self, e, trans, **kwargs ): - if isinstance( e, MessageException ): + def handle_controller_exception(self, e, trans, **kwargs): + if isinstance(e, MessageException): # In the case of a controller exception, sanitize to make sure # unsafe html input isn't reflected back to the user - return trans.show_message( sanitize_html(e.err_msg), e.type ) + return trans.show_message(sanitize_html(e.err_msg), e.type) - def make_body_iterable( self, trans, body ): - if isinstance( body, formbuilder.FormBuilder ): - body = trans.show_form( body ) - return base.WebApplication.make_body_iterable( self, trans, body ) + def make_body_iterable(self, trans, body): + if isinstance(body, formbuilder.FormBuilder): + body = trans.show_form(body) + return base.WebApplication.make_body_iterable(self, trans, body) - def transaction_chooser( self, environ, galaxy_app, session_cookie ): - return GalaxyWebTransaction( environ, galaxy_app, self, session_cookie ) + def transaction_chooser(self, environ, galaxy_app, session_cookie): + return GalaxyWebTransaction(environ, galaxy_app, self, session_cookie) - def add_ui_controllers( self, package_name, app ): + def add_ui_controllers(self, package_name, app): """ Search for UI controllers in `package_name` and add them to the webapp. """ from galaxy.web.base.controller import BaseUIController from galaxy.web.base.controller import ControllerUnavailable - package = import_module( package_name ) + package = import_module(package_name) controller_dir = package.__path__[0] - for fname in os.listdir( controller_dir ): - if not( fname.startswith( "_" ) ) and fname.endswith( ".py" ): + for fname in os.listdir(controller_dir): + if not(fname.startswith("_")) and fname.endswith(".py"): name = fname[:-3] module_name = package_name + "." + name try: - module = import_module( module_name ) + module = import_module(module_name) except ControllerUnavailable as exc: log.debug("%s could not be loaded: %s" % (module_name, str(exc))) continue # Look for a controller inside the modules - for key in dir( module ): - T = getattr( module, key ) - if inspect.isclass( T ) and T is not BaseUIController and issubclass( T, BaseUIController ): - controller = self._instantiate_controller( T, app ) - self.add_ui_controller( name, controller ) + for key in dir(module): + T = getattr(module, key) + if inspect.isclass(T) and T is not BaseUIController and issubclass(T, BaseUIController): + controller = self._instantiate_controller(T, app) + self.add_ui_controller(name, controller) - def add_api_controllers( self, package_name, app ): + def add_api_controllers(self, package_name, app): """ Search for UI controllers in `package_name` and add them to the webapp. """ from galaxy.web.base.controller import BaseAPIController from galaxy.web.base.controller import ControllerUnavailable - package = import_module( package_name ) + package = import_module(package_name) controller_dir = package.__path__[0] - for fname in os.listdir( controller_dir ): - if not( fname.startswith( "_" ) ) and fname.endswith( ".py" ): + for fname in os.listdir(controller_dir): + if not(fname.startswith("_")) and fname.endswith(".py"): name = fname[:-3] module_name = package_name + "." + name try: - module = import_module( module_name ) + module = import_module(module_name) except ControllerUnavailable as exc: log.debug("%s could not be loaded: %s" % (module_name, str(exc))) continue - for key in dir( module ): - T = getattr( module, key ) + for key in dir(module): + T = getattr(module, key) # Exclude classes such as BaseAPIController and BaseTagItemsController - if inspect.isclass( T ) and not key.startswith("Base") and issubclass( T, BaseAPIController ): + if inspect.isclass(T) and not key.startswith("Base") and issubclass(T, BaseAPIController): # By default use module_name, but allow controller to override name - controller_name = getattr( T, "controller_name", name ) - controller = self._instantiate_controller( T, app ) - self.add_api_controller( controller_name, controller ) + controller_name = getattr(T, "controller_name", name) + controller = self._instantiate_controller(T, app) + self.add_api_controller(controller_name, controller) - def _instantiate_controller( self, T, app ): + def _instantiate_controller(self, T, app): """ Extension point, allow apps to contstruct controllers differently, really just used to stub out actual controllers for routes testing. """ - return T( app ) + return T(app) -class GalaxyWebTransaction( base.DefaultWebTransaction, - context.ProvidesAppContext, context.ProvidesUserContext, context.ProvidesHistoryContext ): +class GalaxyWebTransaction(base.DefaultWebTransaction, + context.ProvidesAppContext, context.ProvidesUserContext, context.ProvidesHistoryContext): """ Encapsulates web transaction specific state for the Galaxy application (specifically the user's "cookie" session and history) """ - def __init__( self, environ, app, webapp, session_cookie=None): + def __init__(self, environ, app, webapp, session_cookie=None): self.app = app self.webapp = webapp self.security = webapp.security - base.DefaultWebTransaction.__init__( self, environ ) + base.DefaultWebTransaction.__init__(self, environ) self.setup_i18n() self.expunge_all() config = self.app.config - self.debug = asbool( config.get( 'debug', False ) ) - x_frame_options = getattr( config, 'x_frame_options', None ) + self.debug = asbool(config.get('debug', False)) + x_frame_options = getattr(config, 'x_frame_options', None) if x_frame_options: self.response.headers['X-Frame-Options'] = x_frame_options # Flag indicating whether we are in workflow building mode (means @@ -203,20 +206,20 @@ def __init__( self, environ, app, webapp, session_cookie=None): # If not, check for an active session but do not create one. # If an error message is set here, it's sent back using # trans.show_error in the response -- in expose_api. - self.error_message = self._authenticate_api( session_cookie ) + self.error_message = self._authenticate_api(session_cookie) elif self.app.name == "reports": self.galaxy_session = None else: # This is a web request, get or create session. - self._ensure_valid_session( session_cookie ) + self._ensure_valid_session(session_cookie) if self.galaxy_session: # When we've authenticated by session, we have to check the # following. # Prevent deleted users from accessing Galaxy if config.use_remote_user and self.galaxy_session.user.deleted: - self.response.send_redirect( url_for( '/static/user_disabled.html' ) ) + self.response.send_redirect(url_for('/static/user_disabled.html')) if config.require_login: - self._ensure_logged_in_user( environ, session_cookie ) + self._ensure_logged_in_user(environ, session_cookie) if config.session_duration: # TODO DBTODO All ajax calls from the client need to go through # a single point of control where we can do things like @@ -242,33 +245,33 @@ def __init__( self, environ, app, webapp, session_cookie=None): self.user = None self.galaxy_session = None else: - self.response.send_redirect( url_for( controller='user', + self.response.send_redirect(url_for(controller='user', action='login', message="You have been logged out due to inactivity. Please log in again to continue using Galaxy.", status='info', - use_panels=True ) ) + use_panels=True)) else: self.galaxy_session.last_action = now self.sa_session.add(self.galaxy_session) self.sa_session.flush() - def setup_i18n( self ): + def setup_i18n(self): locales = [] if 'HTTP_ACCEPT_LANGUAGE' in self.environ: # locales looks something like: ['en', 'en-us;q=0.7', 'ja;q=0.3'] - client_locales = self.environ['HTTP_ACCEPT_LANGUAGE'].split( ',' ) + client_locales = self.environ['HTTP_ACCEPT_LANGUAGE'].split(',') for locale in client_locales: try: - locales.append( Locale.parse( locale.split( ';' )[0].strip(), sep='-' ).language ) + locales.append(Locale.parse(locale.split(';')[0].strip(), sep='-').language) except Exception as e: - log.debug( "Error parsing locale '%s'. %s: %s", locale, type( e ), e ) + log.debug("Error parsing locale '%s'. %s: %s", locale, type(e), e) if not locales: # Default to English locales = 'en' - t = Translations.load( dirname='locale', locales=locales, domain='ginga' ) - self.template_context.update( dict( _=t.ugettext, n_=t.ugettext, N_=t.ungettext ) ) + t = Translations.load(dirname='locale', locales=locales, domain='ginga') + self.template_context.update(dict(_=t.ugettext, n_=t.ugettext, N_=t.ungettext)) - def set_cors_headers( self ): + def set_cors_headers(self): """Allow CORS requests if configured to do so by echoing back the request's 'Origin' header (if any) as the response header 'Access-Control-Allow-Origin' """ @@ -277,59 +280,59 @@ def set_cors_headers( self ): # ALLOWED_METHODS = ( 'POST', 'PUT' ) # do not set any access control headers if not configured for it (common case) - if not self.app.config.get( 'allowed_origin_hostnames', None ): + if not self.app.config.get('allowed_origin_hostnames', None): return # do not set any access control headers if there's no origin header on the request - origin_header = self.request.headers.get( "Origin", None ) + origin_header = self.request.headers.get("Origin", None) if not origin_header: return # singular match - def matches_allowed_origin( origin, allowed_origin ): - if isinstance( allowed_origin, string_types ): + def matches_allowed_origin(origin, allowed_origin): + if isinstance(allowed_origin, string_types): return origin == allowed_origin - match = allowed_origin.match( origin ) + match = allowed_origin.match(origin) return match and match.group() == origin # check for '*' or compare to list of allowed - def is_allowed_origin( origin ): + def is_allowed_origin(origin): # localhost uses no origin header (== null) if not origin: return False for allowed_origin in self.app.config.allowed_origin_hostnames: - if allowed_origin == '*' or matches_allowed_origin( origin, allowed_origin ): + if allowed_origin == '*' or matches_allowed_origin(origin, allowed_origin): return True return False # boil origin header down to hostname - origin = urlparse.urlparse( origin_header ).hostname + origin = urlparse(origin_header).hostname # check against the list of allowed strings/regexp hostnames, echo original if cleared - if is_allowed_origin( origin ): - self.response.headers[ 'Access-Control-Allow-Origin' ] = origin_header + if is_allowed_origin(origin): + self.response.headers['Access-Control-Allow-Origin'] = origin_header # TODO: see the to do on ALLOWED_METHODS above # self.response.headers[ 'Access-Control-Allow-Methods' ] = ', '.join( ALLOWED_METHODS ) # NOTE: raising some errors (such as httpexceptions), will remove the header # (e.g. client will get both cors error and 404 inside that) - def get_user( self ): + def get_user(self): """Return the current user if logged in or None.""" if self.galaxy_session: return self.galaxy_session.user else: return self.__user - def set_user( self, user ): + def set_user(self, user): """Set the current user.""" if self.galaxy_session: self.galaxy_session.user = user - self.sa_session.add( self.galaxy_session ) + self.sa_session.add(self.galaxy_session) self.sa_session.flush() self.__user = user - user = property( get_user, set_user ) + user = property(get_user, set_user) - def get_cookie( self, name='galaxysession' ): + def get_cookie(self, name='galaxysession'): """Convenience method for getting a session cookie""" try: # If we've changed the cookie during the request return the new value @@ -340,40 +343,40 @@ def get_cookie( self, name='galaxysession' ): except: return None - def set_cookie( self, value, name='galaxysession', path='/', age=90, version='1' ): + def set_cookie(self, value, name='galaxysession', path='/', age=90, version='1'): """Convenience method for setting a session cookie""" # The galaxysession cookie value must be a high entropy 128 bit random number encrypted # using a server secret key. Any other value is invalid and could pose security issues. self.response.cookies[name] = value self.response.cookies[name]['path'] = path self.response.cookies[name]['max-age'] = 3600 * 24 * age # 90 days - tstamp = time.localtime( time.time() + 3600 * 24 * age ) - self.response.cookies[name]['expires'] = time.strftime( '%a, %d-%b-%Y %H:%M:%S GMT', tstamp ) + tstamp = time.localtime(time.time() + 3600 * 24 * age) + self.response.cookies[name]['expires'] = time.strftime('%a, %d-%b-%Y %H:%M:%S GMT', tstamp) self.response.cookies[name]['version'] = version - https = self.request.environ[ "wsgi.url_scheme" ] == "https" + https = self.request.environ["wsgi.url_scheme"] == "https" if https: self.response.cookies[name]['secure'] = True try: self.response.cookies[name]['httponly'] = True except CookieError as e: - log.warning( "Error setting httponly attribute in cookie '%s': %s" % ( name, e ) ) + log.warning("Error setting httponly attribute in cookie '%s': %s" % (name, e)) - def _authenticate_api( self, session_cookie ): + def _authenticate_api(self, session_cookie): """ Authenticate for the API via key or session (if available). """ - api_key = self.request.params.get('key', None) or self.request.headers.get( 'x-api-key', None ) - secure_id = self.get_cookie( name=session_cookie ) + api_key = self.request.params.get('key', None) or self.request.headers.get('x-api-key', None) + secure_id = self.get_cookie(name=session_cookie) api_key_supplied = self.environ.get('is_api_request', False) and api_key - if api_key_supplied and self._check_master_api_key( api_key ): + if api_key_supplied and self._check_master_api_key(api_key): self.api_inherit_admin = True - log.info( "Session authenticated using Galaxy master api key" ) + log.info("Session authenticated using Galaxy master api key") self.user = None self.galaxy_session = None elif api_key_supplied: # Sessionless API transaction, we just need to associate a user. try: - provided_key = self.sa_session.query( self.app.model.APIKeys ).filter( self.app.model.APIKeys.table.c.key == api_key ).one() + provided_key = self.sa_session.query(self.app.model.APIKeys).filter(self.app.model.APIKeys.table.c.key == api_key).one() except NoResultFound: return 'Provided API key is not valid.' if provided_key.user.deleted: @@ -381,13 +384,13 @@ def _authenticate_api( self, session_cookie ): newest_key = provided_key.user.api_keys[0] if newest_key.key != provided_key.key: return 'Provided API key has expired.' - self.set_user( provided_key.user ) + self.set_user(provided_key.user) elif secure_id: # API authentication via active session # Associate user using existing session # This will throw an exception under remote auth with anon users. try: - self._ensure_valid_session( session_cookie ) + self._ensure_valid_session(session_cookie) except Exception: log.exception("Exception during Session-based API authentication, this was most likely an attempt to use an anonymous cookie under remote authentication (so, no user), which we don't support.") self.user = None @@ -397,16 +400,16 @@ def _authenticate_api( self, session_cookie ): self.user = None self.galaxy_session = None - def _check_master_api_key( self, api_key ): - master_api_key = getattr( self.app.config, 'master_api_key', None ) + def _check_master_api_key(self, api_key): + master_api_key = getattr(self.app.config, 'master_api_key', None) if not master_api_key: return False # Hash keys to make them the same size, so we can do safe comparison. - master_hash = hashlib.sha256( master_api_key ).hexdigest() - provided_hash = hashlib.sha256( api_key ).hexdigest() - return safe_str_cmp( master_hash, provided_hash ) + master_hash = hashlib.sha256(master_api_key).hexdigest() + provided_hash = hashlib.sha256(api_key).hexdigest() + return safe_str_cmp(master_hash, provided_hash) - def _ensure_valid_session( self, session_cookie, create=True): + def _ensure_valid_session(self, session_cookie, create=True): """ Ensure that a valid Galaxy session exists and is available as trans.session (part of initialization) @@ -415,7 +418,7 @@ def _ensure_valid_session( self, session_cookie, create=True): removed as of 31 Oct 2008. """ # Try to load an existing session - secure_id = self.get_cookie( name=session_cookie ) + secure_id = self.get_cookie(name=session_cookie) galaxy_session = None prev_galaxy_session = None user_for_new_session = None @@ -425,27 +428,27 @@ def _ensure_valid_session( self, session_cookie, create=True): galaxy_session_requires_flush = False if secure_id: # Decode the cookie value to get the session_key - session_key = self.security.decode_guid( secure_id ) + session_key = self.security.decode_guid(secure_id) try: # Make sure we have a valid UTF-8 string - session_key = session_key.encode( 'utf8' ) + session_key = session_key.encode('utf8') except UnicodeDecodeError: # We'll end up creating a new galaxy_session session_key = None if session_key: # Retrieve the galaxy_session id via the unique session_key - galaxy_session = self.sa_session.query( self.app.model.GalaxySession ) \ - .filter( and_( self.app.model.GalaxySession.table.c.session_key == session_key, - self.app.model.GalaxySession.table.c.is_valid == true() ) ).options( joinedload( "user" ) ).first() + galaxy_session = self.sa_session.query(self.app.model.GalaxySession) \ + .filter(and_(self.app.model.GalaxySession.table.c.session_key == session_key, + self.app.model.GalaxySession.table.c.is_valid == true())).options(joinedload("user")).first() # If remote user is in use it can invalidate the session and in some # cases won't have a cookie set above, so we need to to check some # things now. if self.app.config.use_remote_user: - remote_user_email = self.environ.get( self.app.config.remote_user_header, None) + remote_user_email = self.environ.get(self.app.config.remote_user_header, None) if galaxy_session: if remote_user_email and galaxy_session.user is None: # No user, associate - galaxy_session.user = self.get_or_create_remote_user( remote_user_email ) + galaxy_session.user = self.get_or_create_remote_user(remote_user_email) galaxy_session_requires_flush = True elif (remote_user_email and (galaxy_session.user.email != remote_user_email) and @@ -455,12 +458,12 @@ def _ensure_valid_session( self, session_cookie, create=True): # remote user, and the currently set remote_user is not a # potentially impersonating admin. invalidate_existing_session = True - user_for_new_session = self.get_or_create_remote_user( remote_user_email ) - log.warning( "User logged in as '%s' externally, but has a cookie as '%s' invalidating session", - remote_user_email, galaxy_session.user.email ) + user_for_new_session = self.get_or_create_remote_user(remote_user_email) + log.warning("User logged in as '%s' externally, but has a cookie as '%s' invalidating session", + remote_user_email, galaxy_session.user.email) elif remote_user_email: # No session exists, get/create user for new session - user_for_new_session = self.get_or_create_remote_user( remote_user_email ) + user_for_new_session = self.get_or_create_remote_user(remote_user_email) if ((galaxy_session and galaxy_session.user is None) and user_for_new_session is None): raise Exception("Remote Authentication Failure - user is unknown and/or not supplied.") else: @@ -468,11 +471,11 @@ def _ensure_valid_session( self, session_cookie, create=True): # Remote user support is not enabled, but there is an existing # session with an external user, invalidate invalidate_existing_session = True - log.warning( "User '%s' is an external user with an existing session, invalidating session since external auth is disabled", - galaxy_session.user.email ) + log.warning("User '%s' is an external user with an existing session, invalidating session since external auth is disabled", + galaxy_session.user.email) elif galaxy_session is not None and galaxy_session.user is not None and galaxy_session.user.deleted: invalidate_existing_session = True - log.warning( "User '%s' is marked deleted, invalidating session" % galaxy_session.user.email ) + log.warning("User '%s' is marked deleted, invalidating session" % galaxy_session.user.email) # Do we need to invalidate the session for some reason? if invalidate_existing_session: prev_galaxy_session = galaxy_session @@ -480,26 +483,26 @@ def _ensure_valid_session( self, session_cookie, create=True): galaxy_session = None # No relevant cookies, or couldn't find, or invalid, so create a new session if galaxy_session is None: - galaxy_session = self.__create_new_session( prev_galaxy_session, user_for_new_session ) + galaxy_session = self.__create_new_session(prev_galaxy_session, user_for_new_session) galaxy_session_requires_flush = True self.galaxy_session = galaxy_session - self.__update_session_cookie( name=session_cookie ) + self.__update_session_cookie(name=session_cookie) else: self.galaxy_session = galaxy_session # Do we need to flush the session? if galaxy_session_requires_flush: - self.sa_session.add( galaxy_session ) + self.sa_session.add(galaxy_session) # FIXME: If prev_session is a proper relation this would not # be needed. if prev_galaxy_session: - self.sa_session.add( prev_galaxy_session ) + self.sa_session.add(prev_galaxy_session) self.sa_session.flush() # If the old session was invalid, get a new (or existing default, # unused) history with our new session if invalidate_existing_session: self.get_or_create_default_history() - def _ensure_logged_in_user( self, environ, session_cookie ): + def _ensure_logged_in_user(self, environ, session_cookie): # The value of session_cookie can be one of # 'galaxysession' or 'galaxycommunitysession' # Currently this method does nothing unless session_cookie is 'galaxysession' @@ -509,53 +512,53 @@ def _ensure_logged_in_user( self, environ, session_cookie ): allowed_paths = [ # client app route # TODO: might be better as '/:username/login', '/:username/logout' - url_for( controller='root', action='login' ), + url_for(controller='root', action='login'), # mako app routes - url_for( controller='user', action='login' ), - url_for( controller='user', action='logout' ), - url_for( controller='user', action='reset_password' ), - url_for( controller='user', action='change_password' ), + url_for(controller='user', action='login'), + url_for(controller='user', action='logout'), + url_for(controller='user', action='reset_password'), + url_for(controller='user', action='change_password'), # required to log in w/ openid - url_for( controller='user', action='openid_auth' ), - url_for( controller='user', action='openid_process' ), - url_for( controller='user', action='openid_associate' ), + url_for(controller='user', action='openid_auth'), + url_for(controller='user', action='openid_process'), + url_for(controller='user', action='openid_associate'), # TODO: do any of these still need to bypass require login? - url_for( controller='user', action='api_keys' ), - url_for( controller='user', action='create' ), - url_for( controller='user', action='index' ), - url_for( controller='user', action='manage_user_info' ), - url_for( controller='user', action='set_default_permissions' ), + url_for(controller='user', action='api_keys'), + url_for(controller='user', action='create'), + url_for(controller='user', action='index'), + url_for(controller='user', action='manage_user_info'), + url_for(controller='user', action='set_default_permissions'), ] # append the welcome url to allowed paths if we'll show it at the login screen if self.app.config.show_welcome_with_login: - allowed_paths.append( url_for( controller='root', action='welcome' ) ) + allowed_paths.append(url_for(controller='root', action='welcome')) # prevent redirect when UCSC server attempts to get dataset contents as 'anon' user - display_as = url_for( controller='root', action='display_as' ) + display_as = url_for(controller='root', action='display_as') if self.app.datatypes_registry.get_display_sites('ucsc') and self.request.path == display_as: try: - host = socket.gethostbyaddr( self.environ[ 'REMOTE_ADDR' ] )[0] - except( socket.error, socket.herror, socket.gaierror, socket.timeout ): + host = socket.gethostbyaddr(self.environ['REMOTE_ADDR'])[0] + except(socket.error, socket.herror, socket.gaierror, socket.timeout): host = None if host in UCSC_SERVERS: return # prevent redirect for external, enabled display applications getting dataset contents - external_display_path = url_for( controller='', action='display_application' ) - if self.request.path.startswith( external_display_path ): - request_path_split = self.request.path.split( '/' ) + external_display_path = url_for(controller='', action='display_application') + if self.request.path.startswith(external_display_path): + request_path_split = self.request.path.split('/') try: - if (self.app.datatypes_registry.display_applications.get( request_path_split[-5] ) and - request_path_split[-4] in self.app.datatypes_registry.display_applications.get( request_path_split[-5] ).links and + if (self.app.datatypes_registry.display_applications.get(request_path_split[-5]) and + request_path_split[-4] in self.app.datatypes_registry.display_applications.get(request_path_split[-5]).links and request_path_split[-3] != 'None'): return except IndexError: pass # redirect to root if the path is not in the list above if self.request.path not in allowed_paths: - login_url = url_for( controller='root', action='login', redirect=self.request.path ) - self.response.send_redirect( login_url ) + login_url = url_for(controller='root', action='login', redirect=self.request.path) + self.response.send_redirect(login_url) - def __create_new_session( self, prev_galaxy_session=None, user_for_new_session=None ): + def __create_new_session(self, prev_galaxy_session=None, user_for_new_session=None): """ Create a new GalaxySession for this request, possibly with a connection to a previous session (in `prev_galaxy_session`) and an existing user @@ -569,7 +572,7 @@ def __create_new_session( self, prev_galaxy_session=None, user_for_new_session=N is_valid=True, remote_host=self.request.remote_host, remote_addr=self.request.remote_addr, - referer=self.request.headers.get( 'Referer', None ) ) + referer=self.request.headers.get('Referer', None)) if prev_galaxy_session: # Invalidated an existing session for some reason, keep track galaxy_session.prev_session_id = prev_galaxy_session.id @@ -578,58 +581,58 @@ def __create_new_session( self, prev_galaxy_session=None, user_for_new_session=N galaxy_session.user = user_for_new_session return galaxy_session - def get_or_create_remote_user( self, remote_user_email ): + def get_or_create_remote_user(self, remote_user_email): """ Create a remote user with the email remote_user_email and return it """ if not self.app.config.use_remote_user: return None - if getattr( self.app.config, "normalize_remote_user_email", False ): + if getattr(self.app.config, "normalize_remote_user_email", False): remote_user_email = remote_user_email.lower() - user = self.sa_session.query( self.app.model.User).filter( self.app.model.User.table.c.email == remote_user_email ).first() + user = self.sa_session.query(self.app.model.User).filter(self.app.model.User.table.c.email == remote_user_email).first() if user: # GVK: June 29, 2009 - This is to correct the behavior of a previous bug where a private # role and default user / history permissions were not set for remote users. When a # remote user authenticates, we'll look for this information, and if missing, create it. - if not self.app.security_agent.get_private_user_role( user ): - self.app.security_agent.create_private_user_role( user ) + if not self.app.security_agent.get_private_user_role(user): + self.app.security_agent.create_private_user_role(user) if 'webapp' not in self.environ or self.environ['webapp'] != 'tool_shed': if not user.default_permissions: - self.app.security_agent.user_set_default_permissions( user ) - self.app.security_agent.user_set_default_permissions( user, history=True, dataset=True ) + self.app.security_agent.user_set_default_permissions(user) + self.app.security_agent.user_set_default_permissions(user, history=True, dataset=True) elif user is None: - username = remote_user_email.split( '@', 1 )[0].lower() + username = remote_user_email.split('@', 1)[0].lower() random.seed() - user = self.app.model.User( email=remote_user_email ) - user.set_password_cleartext( ''.join( random.sample( string.letters + string.digits, 12 ) ) ) + user = self.app.model.User(email=remote_user_email) + user.set_password_cleartext(''.join(random.sample(string.ascii_letters + string.digits, 12))) user.external = True # Replace invalid characters in the username - for char in filter( lambda x: x not in string.ascii_lowercase + string.digits + '-', username ): - username = username.replace( char, '-' ) + for char in [x for x in username if x not in string.ascii_lowercase + string.digits + '-']: + username = username.replace(char, '-') # Find a unique username - user can change it later - if self.sa_session.query( self.app.model.User ).filter_by( username=username ).first(): + if self.sa_session.query(self.app.model.User).filter_by(username=username).first(): i = 1 - while self.sa_session.query( self.app.model.User ).filter_by( username=(username + '-' + str(i) ) ).first(): + while self.sa_session.query(self.app.model.User).filter_by(username=(username + '-' + str(i))).first(): i += 1 username += '-' + str(i) user.username = username - self.sa_session.add( user ) + self.sa_session.add(user) self.sa_session.flush() - self.app.security_agent.create_private_user_role( user ) + self.app.security_agent.create_private_user_role(user) # We set default user permissions, before we log in and set the default history permissions if 'webapp' not in self.environ or self.environ['webapp'] != 'tool_shed': - self.app.security_agent.user_set_default_permissions( user ) + self.app.security_agent.user_set_default_permissions(user) # self.log_event( "Automatically created account '%s'", user.email ) return user - def __update_session_cookie( self, name='galaxysession' ): + def __update_session_cookie(self, name='galaxysession'): """ Update the session cookie to match the current session. """ - self.set_cookie( self.security.encode_guid(self.galaxy_session.session_key ), - name=name, path=self.app.config.cookie_path ) + self.set_cookie(self.security.encode_guid(self.galaxy_session.session_key), + name=name, path=self.app.config.cookie_path) - def handle_user_login( self, user ): + def handle_user_login(self, user): """ Login a new user (possibly newly created) @@ -643,7 +646,7 @@ def handle_user_login( self, user ): prev_galaxy_session = self.galaxy_session prev_galaxy_session.is_valid = False # Define a new current_session - self.galaxy_session = self.__create_new_session( prev_galaxy_session, user ) + self.galaxy_session = self.__create_new_session(prev_galaxy_session, user) if self.webapp.name == 'galaxy': cookie_name = 'galaxysession' # Associated the current user's last accessed history (if exists) with their new session @@ -672,24 +675,24 @@ def handle_user_login( self, user ): users_last_session.current_history.deleted): history = users_last_session.current_history elif not history: - history = self.get_history( create=True, most_recent=True ) + history = self.get_history(create=True, most_recent=True) if history not in self.galaxy_session.histories: - self.galaxy_session.add_history( history ) + self.galaxy_session.add_history(history) if history.user is None: history.user = user self.galaxy_session.current_history = history if not last_accessed: # Only set default history permissions if current history is not from a previous session - self.app.security_agent.history_set_default_permissions( history, dataset=True, bypass_manage_permission=True ) - self.sa_session.add_all( ( prev_galaxy_session, self.galaxy_session, history ) ) + self.app.security_agent.history_set_default_permissions(history, dataset=True, bypass_manage_permission=True) + self.sa_session.add_all((prev_galaxy_session, self.galaxy_session, history)) else: cookie_name = 'galaxycommunitysession' - self.sa_session.add_all( ( prev_galaxy_session, self.galaxy_session ) ) + self.sa_session.add_all((prev_galaxy_session, self.galaxy_session)) self.sa_session.flush() # This method is not called from the Galaxy reports, so the cookie will always be galaxysession - self.__update_session_cookie( name=cookie_name ) + self.__update_session_cookie(name=cookie_name) - def handle_user_logout( self, logout_all=False ): + def handle_user_logout(self, logout_all=False): """ Logout the current user: - invalidate the current session @@ -697,30 +700,30 @@ def handle_user_logout( self, logout_all=False ): """ prev_galaxy_session = self.galaxy_session prev_galaxy_session.is_valid = False - self.galaxy_session = self.__create_new_session( prev_galaxy_session ) - self.sa_session.add_all( ( prev_galaxy_session, self.galaxy_session ) ) + self.galaxy_session = self.__create_new_session(prev_galaxy_session) + self.sa_session.add_all((prev_galaxy_session, self.galaxy_session)) galaxy_user_id = prev_galaxy_session.user_id if logout_all and galaxy_user_id is not None: - for other_galaxy_session in ( self.sa_session.query(self.app.model.GalaxySession) - .filter( and_( self.app.model.GalaxySession.table.c.user_id == galaxy_user_id, - self.app.model.GalaxySession.table.c.is_valid == true(), - self.app.model.GalaxySession.table.c.id != prev_galaxy_session.id ) ) ): + for other_galaxy_session in (self.sa_session.query(self.app.model.GalaxySession) + .filter(and_(self.app.model.GalaxySession.table.c.user_id == galaxy_user_id, + self.app.model.GalaxySession.table.c.is_valid == true(), + self.app.model.GalaxySession.table.c.id != prev_galaxy_session.id))): other_galaxy_session.is_valid = False - self.sa_session.add( other_galaxy_session ) + self.sa_session.add(other_galaxy_session) self.sa_session.flush() if self.webapp.name == 'galaxy': # This method is not called from the Galaxy reports, so the cookie will always be galaxysession - self.__update_session_cookie( name='galaxysession' ) + self.__update_session_cookie(name='galaxysession') elif self.webapp.name == 'tool_shed': - self.__update_session_cookie( name='galaxycommunitysession' ) + self.__update_session_cookie(name='galaxycommunitysession') - def get_galaxy_session( self ): + def get_galaxy_session(self): """ Return the current galaxy session """ return self.galaxy_session - def get_history( self, create=False, most_recent=False ): + def get_history(self, create=False, most_recent=False): """ Load the current history. - If that isn't available, we find the most recently updated history. @@ -730,23 +733,23 @@ def get_history( self, create=False, most_recent=False ): """ history = None if self.galaxy_session: - if hasattr( self.galaxy_session, 'current_history' ): + if hasattr(self.galaxy_session, 'current_history'): history = self.galaxy_session.current_history if not history and most_recent: history = self.get_most_recent_history() - if not history and util.string_as_bool( create ): + if not history and util.string_as_bool(create): history = self.get_or_create_default_history() return history - def set_history( self, history ): + def set_history(self, history): if history and not history.deleted: self.galaxy_session.current_history = history - self.sa_session.add( self.galaxy_session ) + self.sa_session.add(self.galaxy_session) self.sa_session.flush() - history = property( get_history, set_history ) + history = property(get_history, set_history) - def get_or_create_default_history( self ): + def get_or_create_default_history(self): """ Gets or creates a default history and associates it with the current session. @@ -759,13 +762,13 @@ def get_or_create_default_history( self ): # Look for default history that (a) has default name + is not deleted and # (b) has no datasets. If suitable history found, use it; otherwise, create # new history. - unnamed_histories = self.sa_session.query( self.app.model.History ).filter_by( + unnamed_histories = self.sa_session.query(self.app.model.History).filter_by( user=self.galaxy_session.user, name=self.app.model.History.default_name, - deleted=False ) + deleted=False) default_history = None for history in unnamed_histories: - if len( history.datasets ) == 0: + if len(history.datasets) == 0: # Found suitable default history. default_history = history break @@ -773,13 +776,13 @@ def get_or_create_default_history( self ): # Set or create hsitory. if default_history: history = default_history - self.set_history( history ) + self.set_history(history) else: history = self.new_history() return history - def get_most_recent_history( self ): + def get_most_recent_history(self): """ Gets the most recently updated history. """ @@ -788,15 +791,15 @@ def get_most_recent_history( self ): if not self.galaxy_session.user: return None try: - recent_history = self.sa_session.query( self.app.model.History ).filter_by( + recent_history = self.sa_session.query(self.app.model.History).filter_by( user=self.galaxy_session.user, - deleted=False ).order_by(self.app.model.History.update_time.desc()).first() + deleted=False).order_by(self.app.model.History.update_time.desc()).first() except NoResultFound: return None self.set_history(recent_history) return recent_history - def new_history( self, name=None ): + def new_history(self, name=None): """ Create a new history and associate it with the current session and its associated user (if set). @@ -806,7 +809,7 @@ def new_history( self, name=None ): if name: history.name = name # Associate with session - history.add_galaxy_session( self.galaxy_session ) + history.add_galaxy_session(self.galaxy_session) # Make it the session's current history self.galaxy_session.current_history = history # Associate with user @@ -815,17 +818,17 @@ def new_history( self, name=None ): # Track genome_build with history history.genome_build = self.app.genome_builds.default_value # Set the user's default history permissions - self.app.security_agent.history_set_default_permissions( history ) + self.app.security_agent.history_set_default_permissions(history) # Save - self.sa_session.add_all( ( self.galaxy_session, history ) ) + self.sa_session.add_all((self.galaxy_session, history)) self.sa_session.flush() return history @base.lazy_property - def template_context( self ): + def template_context(self): return dict() - def set_message( self, message, type=None ): + def set_message(self, message, type=None): """ Convenience method for setting the 'message' and 'message_type' element of the template context. @@ -834,14 +837,14 @@ def set_message( self, message, type=None ): if type: self.template_context['status'] = type - def get_message( self ): + def get_message(self): """ Convenience method for getting the 'message' element of the template context. """ return self.template_context['message'] - def show_message( self, message, type='info', refresh_frames=[], cont=None, use_panels=False, active_view="" ): + def show_message(self, message, type='info', refresh_frames=[], cont=None, use_panels=False, active_view=""): """ Convenience method for displaying a simple page with a single message. @@ -851,34 +854,56 @@ def show_message( self, message, type='info', refresh_frames=[], cont=None, use_ `refresh_frames`: names of frames in the interface that should be refreshed when the message is displayed """ - return self.fill_template( "message.mako", status=type, message=message, refresh_frames=refresh_frames, cont=cont, use_panels=use_panels, active_view=active_view ) + return self.fill_template("message.mako", status=type, message=message, refresh_frames=refresh_frames, cont=cont, use_panels=use_panels, active_view=active_view) - def show_error_message( self, message, refresh_frames=[], use_panels=False, active_view="" ): + def show_error_message(self, message, refresh_frames=[], use_panels=False, active_view=""): """ Convenience method for displaying an error message. See `show_message`. """ - return self.show_message( message, 'error', refresh_frames, use_panels=use_panels, active_view=active_view ) + return self.show_message(message, 'error', refresh_frames, use_panels=use_panels, active_view=active_view) - def show_ok_message( self, message, refresh_frames=[], use_panels=False, active_view="" ): + def show_ok_message(self, message, refresh_frames=[], use_panels=False, active_view=""): """ Convenience method for displaying an ok message. See `show_message`. """ - return self.show_message( message, 'done', refresh_frames, use_panels=use_panels, active_view=active_view ) + return self.show_message(message, 'done', refresh_frames, use_panels=use_panels, active_view=active_view) - def show_warn_message( self, message, refresh_frames=[], use_panels=False, active_view="" ): + def show_warn_message(self, message, refresh_frames=[], use_panels=False, active_view=""): """ Convenience method for displaying an warn message. See `show_message`. """ - return self.show_message( message, 'warning', refresh_frames, use_panels=use_panels, active_view=active_view ) + return self.show_message(message, 'warning', refresh_frames, use_panels=use_panels, active_view=active_view) - def show_form( self, form, header=None, template="form.mako", use_panels=False, active_view="" ): + def show_form(self, form, header=None, template="form.mako", use_panels=False, active_view=""): """ Convenience method for displaying a simple page with a single HTML form. """ - return self.fill_template( template, form=form, header=header, - use_panels=( form.use_panels or use_panels ), - active_view=active_view ) + return self.fill_template(template, form=form, header=header, + use_panels=(form.use_panels or use_panels), + active_view=active_view) + + @property + def session_csrf_token(self): + token = '' + if self.galaxy_session: + token = self.security.encode_id( + self.galaxy_session.id, kind="session_csrf_token" + ) + return token + + def check_csrf_token(self): + session_csrf_token = self.request.params.get("session_csrf_token", None) + problem = False + if not session_csrf_token: + log.warning("No session_csrf_token set, denying request.") + problem = True + elif session_csrf_token != self.session_csrf_token: + log.warning("Wrong session token found, denying request.") + problem = True + + if problem: + return self.show_warn_message("Failed to authorize action.") def fill_template(self, filename, **kwargs): """ @@ -887,39 +912,39 @@ def fill_template(self, filename, **kwargs): # call get_user so we can invalidate sessions from external users, # if external auth has been disabled. self.get_user() - if filename.endswith( ".mako" ): - return self.fill_template_mako( filename, **kwargs ) + if filename.endswith(".mako"): + return self.fill_template_mako(filename, **kwargs) else: - template = Template( file=os.path.join(self.app.config.template_path, filename), - searchList=[kwargs, self.template_context, dict(caller=self, t=self, h=helpers, util=util, request=self.request, response=self.response, app=self.app)] ) - return str( template ) + template = Template(file=os.path.join(self.app.config.template_path, filename), + searchList=[kwargs, self.template_context, dict(caller=self, t=self, h=helpers, util=util, request=self.request, response=self.response, app=self.app)]) + return str(template) - def fill_template_mako( self, filename, template_lookup=None, **kwargs ): + def fill_template_mako(self, filename, template_lookup=None, **kwargs): template_lookup = template_lookup or self.webapp.mako_template_lookup - template = template_lookup.get_template( filename ) + template = template_lookup.get_template(filename) template.output_encoding = 'utf-8' - data = dict( caller=self, t=self, trans=self, h=helpers, util=util, request=self.request, response=self.response, app=self.app ) - data.update( self.template_context ) - data.update( kwargs ) - return template.render( **data ) + data = dict(caller=self, t=self, trans=self, h=helpers, util=util, request=self.request, response=self.response, app=self.app) + data.update(self.template_context) + data.update(kwargs) + return template.render(**data) - def stream_template_mako( self, filename, **kwargs ): - template = self.webapp.mako_template_lookup.get_template( filename ) + def stream_template_mako(self, filename, **kwargs): + template = self.webapp.mako_template_lookup.get_template(filename) template.output_encoding = 'utf-8' - data = dict( caller=self, t=self, trans=self, h=helpers, util=util, request=self.request, response=self.response, app=self.app ) - data.update( self.template_context ) - data.update( kwargs ) + data = dict(caller=self, t=self, trans=self, h=helpers, util=util, request=self.request, response=self.response, app=self.app) + data.update(self.template_context) + data.update(kwargs) - def render( environ, start_response ): - response_write = start_response( self.response.wsgi_status(), self.response.wsgi_headeritems() ) + def render(environ, start_response): + response_write = start_response(self.response.wsgi_status(), self.response.wsgi_headeritems()) - class StreamBuffer( object ): - def write( self, d ): - response_write( d.encode( 'utf-8' ) ) + class StreamBuffer(object): + def write(self, d): + response_write(d.encode('utf-8')) buffer = StreamBuffer() - context = mako.runtime.Context( buffer, **data ) - template.render_context( context ) + context = mako.runtime.Context(buffer, **data) + template.render_context(context) return [] return render @@ -927,12 +952,30 @@ def fill_template_string(self, template_string, context=None, **kwargs): """ Fill in a template, putting any keyword arguments on the context. """ - template = Template( source=template_string, - searchList=[context or kwargs, dict(caller=self)] ) + template = Template(source=template_string, + searchList=[context or kwargs, dict(caller=self)]) return str(template) -def build_url_map( app, global_conf, local_conf ): +def build_native_uwsgi_app(paste_factory, config_section): + """uwsgi can load paste factories with --ini-paste, but this builds non-paste uwsgi apps. + + In particular these are useful with --yaml or --json for config.""" + import uwsgi + uwsgi_opt = uwsgi.opt + config_file = uwsgi_opt.get("yaml") or uwsgi_opt.get("json") + if not config_file: + # Probably loaded via --ini-paste - expect paste app. + return None + + uwsgi_app = paste_factory(uwsgi.opt, load_app_kwds={ + "config_file": config_file, + "config_section": config_section, + }) + return uwsgi_app + + +def build_url_map(app, global_conf, local_conf): from paste.urlmap import URLMap from galaxy.web.framework.middleware.static import CacheableStaticURLParser as Static urlmap = URLMap() @@ -940,16 +983,16 @@ def build_url_map( app, global_conf, local_conf ): conf = global_conf.copy() conf.update(local_conf) # Get cache time in seconds - cache_time = conf.get( "static_cache_time", None ) + cache_time = conf.get("static_cache_time", None) if cache_time is not None: - cache_time = int( cache_time ) + cache_time = int(cache_time) # Send to dynamic app by default urlmap["/"] = app # Define static mappings from config - urlmap["/static"] = Static( conf.get( "static_dir", "./static/" ), cache_time ) - urlmap["/images"] = Static( conf.get( "static_images_dir", "./static/images" ), cache_time ) - urlmap["/static/scripts"] = Static( conf.get( "static_scripts_dir", "./static/scripts/" ), cache_time ) - urlmap["/static/style"] = Static( conf.get( "static_style_dir", "./static/style/blue" ), cache_time ) - urlmap["/favicon.ico"] = Static( conf.get( "static_favicon_dir", "./static/favicon.ico" ), cache_time ) - urlmap["/robots.txt"] = Static( conf.get( "static_robots_txt", "./static/robots.txt" ), cache_time ) + urlmap["/static"] = Static(conf.get("static_dir", "./static/"), cache_time) + urlmap["/images"] = Static(conf.get("static_images_dir", "./static/images"), cache_time) + urlmap["/static/scripts"] = Static(conf.get("static_scripts_dir", "./static/scripts/"), cache_time) + urlmap["/static/style"] = Static(conf.get("static_style_dir", "./static/style/blue"), cache_time) + urlmap["/favicon.ico"] = Static(conf.get("static_favicon_dir", "./static/favicon.ico"), cache_time) + urlmap["/robots.txt"] = Static(conf.get("static_robots_txt", "./static/robots.txt"), cache_time) return urlmap, cache_time diff --git a/lib/galaxy/web/params.py b/lib/galaxy/web/params.py index b141f40cd465..df59556ef02b 100644 --- a/lib/galaxy/web/params.py +++ b/lib/galaxy/web/params.py @@ -4,28 +4,28 @@ from galaxy import util -class BaseParamParser( object ): - def get_params( self, kwargs ): - params = util.Params( kwargs ) +class BaseParamParser(object): + def get_params(self, kwargs): + params = util.Params(kwargs) # set defaults if unset - updates = dict( webapp=params.get( 'webapp', 'galaxy' ), - message=util.restore_text( params.get( 'message', '' ) ), - status=util.restore_text( params.get( 'status', 'done' ) ) ) - params.update( updates ) + updates = dict(webapp=params.get('webapp', 'galaxy'), + message=util.restore_text(params.get('message', '')), + status=util.restore_text(params.get('status', 'done'))) + params.update(updates) return params -class QuotaParamParser( BaseParamParser ): - def get_quota_params( self, kwargs ): - params = self.get_params( kwargs ) - updates = dict( name=util.restore_text( params.get( 'name', '' ) ), - description=util.restore_text( params.get( 'description', '' ) ), - amount=util.restore_text( params.get( 'amount', '' ).strip() ), - operation=params.get( 'operation', '' ), - default=params.get( 'default', '' ), - in_users=util.listify( params.get( 'in_users', [] ) ), - out_users=util.listify( params.get( 'out_users', [] ) ), - in_groups=util.listify( params.get( 'in_groups', [] ) ), - out_groups=util.listify( params.get( 'out_groups', [] ) ) ) - params.update( updates ) +class QuotaParamParser(BaseParamParser): + def get_quota_params(self, kwargs): + params = self.get_params(kwargs) + updates = dict(name=util.restore_text(params.get('name', '')), + description=util.restore_text(params.get('description', '')), + amount=util.restore_text(params.get('amount', '').strip()), + operation=params.get('operation', ''), + default=params.get('default', ''), + in_users=util.listify(params.get('in_users', [])), + out_users=util.listify(params.get('out_users', [])), + in_groups=util.listify(params.get('in_groups', [])), + out_groups=util.listify(params.get('out_groups', []))) + params.update(updates) return params diff --git a/lib/galaxy/web/proxy/__init__.py b/lib/galaxy/web/proxy/__init__.py index 40485506e2fa..6ff3ee3399f7 100644 --- a/lib/galaxy/web/proxy/__init__.py +++ b/lib/galaxy/web/proxy/__init__.py @@ -11,7 +11,7 @@ import urllib2 import time -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) DEFAULT_PROXY_TO_HOST = "localhost" @@ -21,7 +21,7 @@ class ProxyManager(object): - def __init__( self, config ): + def __init__(self, config): for option in ["manage_dynamic_proxy", "dynamic_proxy_bind_port", "dynamic_proxy_bind_ip", "dynamic_proxy_debug", "dynamic_proxy_external_proxy", "dynamic_proxy_prefix", @@ -32,10 +32,10 @@ def __init__( self, config ): "dynamic_proxy_golang_docker_address", "dynamic_proxy_golang_api_key"]: - setattr( self, option, getattr( config, option ) ) + setattr(self, option, getattr(config, option)) if self.manage_dynamic_proxy: - self.lazy_process = self.__setup_lazy_process( config ) + self.lazy_process = self.__setup_lazy_process(config) else: self.lazy_process = NoOpLazyProcess() @@ -44,10 +44,10 @@ def __init__( self, config ): self.proxy_ipc = proxy_ipc(config) - def shutdown( self ): + def shutdown(self): self.lazy_process.shutdown() - def setup_proxy( self, trans, host=DEFAULT_PROXY_TO_HOST, port=None, proxy_prefix="", route_name="", container_ids=None, container_interface=None ): + def setup_proxy(self, trans, host=DEFAULT_PROXY_TO_HOST, port=None, proxy_prefix="", route_name="", container_ids=None, container_interface=None): if self.manage_dynamic_proxy: log.info("Attempting to start dynamic proxy process") log.debug("Cmd: " + ' '.join(self.lazy_process.command_and_args)) @@ -81,11 +81,11 @@ def setup_proxy( self, trans, host=DEFAULT_PROXY_TO_HOST, port=None, proxy_prefi 'proxied_host': proxy_requests.host, } - def query_proxy( self, trans ): + def query_proxy(self, trans): authentication = AuthenticationToken(trans) return self.proxy_ipc.fetch_requests(authentication) - def __setup_lazy_process( self, config ): + def __setup_lazy_process(self, config): launcher = self.proxy_launcher() command = launcher.launch_proxy_command(config) return LazyProcess(command) @@ -117,9 +117,9 @@ def launch_proxy_command(self, config): if config.dynamic_proxy_debug: args.append("--verbose") - parent_directory = os.path.dirname( __file__ ) - path_to_application = os.path.join( parent_directory, "js", "lib", "main.js" ) - command = [ path_to_application ] + args + parent_directory = os.path.dirname(__file__) + path_to_application = os.path.join(parent_directory, "js", "lib", "main.js") + command = [path_to_application] + args return command @@ -152,7 +152,7 @@ class AuthenticationToken(object): def __init__(self, trans): self.cookie_name = SECURE_COOKIE - self.cookie_value = trans.get_cookie( self.cookie_name ) + self.cookie_value = trans.get_cookie(self.cookie_name) class ProxyRequests(object): @@ -194,19 +194,19 @@ def __init__(self, proxy_session_map): def handle_requests(self, authentication, proxy_requests, route_name, container_ids, container_interface): key = authentication.cookie_value - with FileLock( self.proxy_session_map ): - if not os.path.exists( self.proxy_session_map ): - open( self.proxy_session_map, "w" ).write( "{}" ) - json_data = open( self.proxy_session_map, "r" ).read() - session_map = json.loads( json_data ) - session_map[ key ] = { + with FileLock(self.proxy_session_map): + if not os.path.exists(self.proxy_session_map): + open(self.proxy_session_map, "w").write("{}") + json_data = open(self.proxy_session_map, "r").read() + session_map = json.loads(json_data) + session_map[key] = { 'host': proxy_requests.host, 'port': proxy_requests.port, 'container_ids': container_ids, 'container_interface': container_interface, } - new_json_data = json.dumps( session_map ) - open( self.proxy_session_map, "w" ).write( new_json_data ) + new_json_data = json.dumps(session_map) + open(self.proxy_session_map, "w").write(new_json_data) def fetch_requests(self, authentication): key = authentication.cookie_value @@ -232,7 +232,7 @@ def __init__(self, proxy_session_map): def handle_requests(self, authentication, proxy_requests, route_name, container_ids, container_interface): key = authentication.cookie_value - with FileLock( self.proxy_session_map ): + with FileLock(self.proxy_session_map): conn = sqlite.connect(self.proxy_session_map) try: c = conn.cursor() @@ -263,7 +263,7 @@ def handle_requests(self, authentication, proxy_requests, route_name, container_ def fetch_requests(self, authentication): key = authentication.cookie_value - with FileLock( self.proxy_session_map): + with FileLock(self.proxy_session_map): conn = sqlite.connect(self.proxy_session_map) try: c = conn.cursor() @@ -296,7 +296,7 @@ def handle_requests(self, authentication, proxy_requests, route_name, container_ """ values = { 'FrontendPath': route_name, - 'BackendAddr': "%s:%s" % ( proxy_requests.host, proxy_requests.port ), + 'BackendAddr': "%s:%s" % (proxy_requests.host, proxy_requests.port), 'AuthorizedCookie': authentication.cookie_value, 'ContainerIds': container_ids, } diff --git a/lib/galaxy/web/proxy/js/lib/proxy.js b/lib/galaxy/web/proxy/js/lib/proxy.js index 95ebd8ed9ccd..1857e2632e1e 100644 --- a/lib/galaxy/web/proxy/js/lib/proxy.js +++ b/lib/galaxy/web/proxy/js/lib/proxy.js @@ -70,7 +70,7 @@ DynamicProxy.prototype.targetForRequest = function(request) { }; DynamicProxy.prototype.findSession = function(request) { - var sessionCookie = this.sessionCookie; + var sessionCookie = this.sessionCookie, rc = request.headers.cookie; if(!rc) { return null; diff --git a/lib/galaxy/web/security/__init__.py b/lib/galaxy/web/security/__init__.py index 4a2ed331a97a..9689a51dfd5f 100644 --- a/lib/galaxy/web/security/__init__.py +++ b/lib/galaxy/web/security/__init__.py @@ -9,122 +9,122 @@ from Crypto.Util.randpool import RandomPool from Crypto.Util import number -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -if os.path.exists( "/dev/urandom" ): +if os.path.exists("/dev/urandom"): # We have urandom, use it as the source of random data - random_fd = os.open( "/dev/urandom", os.O_RDONLY ) + random_fd = os.open("/dev/urandom", os.O_RDONLY) - def get_random_bytes( nbytes ): - value = os.read( random_fd, nbytes ) + def get_random_bytes(nbytes): + value = os.read(random_fd, nbytes) # Normally we should get as much as we need - if len( value ) == nbytes: - return value.encode( "hex" ) + if len(value) == nbytes: + return value.encode("hex") # If we don't, keep reading (this is slow and should never happen) - while len( value ) < nbytes: - value += os.read( random_fd, nbytes - len( value ) ) - return value.encode( "hex" ) + while len(value) < nbytes: + value += os.read(random_fd, nbytes - len(value)) + return value.encode("hex") else: - def get_random_bytes( nbytes ): + def get_random_bytes(nbytes): nbits = nbytes * 8 - random_pool = RandomPool( 1064 ) + random_pool = RandomPool(1064) while random_pool.entropy < nbits: random_pool.add_event() random_pool.stir() - return str( number.getRandomNumber( nbits, random_pool.get_bytes ) ) + return str(number.getRandomNumber(nbits, random_pool.get_bytes)) -class SecurityHelper( object ): +class SecurityHelper(object): - def __init__( self, **config ): + def __init__(self, **config): self.id_secret = config['id_secret'] - self.id_cipher = Blowfish.new( self.id_secret ) + self.id_cipher = Blowfish.new(self.id_secret) - per_kind_id_secret_base = config.get( 'per_kind_id_secret_base', self.id_secret ) - self.id_ciphers_for_kind = _cipher_cache( per_kind_id_secret_base ) + per_kind_id_secret_base = config.get('per_kind_id_secret_base', self.id_secret) + self.id_ciphers_for_kind = _cipher_cache(per_kind_id_secret_base) - def encode_id( self, obj_id, kind=None ): + def encode_id(self, obj_id, kind=None): if obj_id is None: raise galaxy.exceptions.MalformedId("Attempted to encode None id") - id_cipher = self.__id_cipher( kind ) + id_cipher = self.__id_cipher(kind) # Convert to string - s = str( obj_id ) + s = str(obj_id) # Pad to a multiple of 8 with leading "!" - s = ( "!" * ( 8 - len(s) % 8 ) ) + s + s = ("!" * (8 - len(s) % 8)) + s # Encrypt - return id_cipher.encrypt( s ).encode( 'hex' ) + return id_cipher.encrypt(s).encode('hex') - def encode_dict_ids( self, a_dict, kind=None, skip_startswith=None ): + def encode_dict_ids(self, a_dict, kind=None, skip_startswith=None): """ Encode all ids in dictionary. Ids are identified by (a) an 'id' key or (b) a key that ends with '_id' """ for key, val in a_dict.items(): - if key == 'id' or key.endswith('_id') and ( skip_startswith is None or not key.startswith( skip_startswith ) ): - a_dict[ key ] = self.encode_id( val, kind=kind ) + if key == 'id' or key.endswith('_id') and (skip_startswith is None or not key.startswith(skip_startswith)): + a_dict[key] = self.encode_id(val, kind=kind) return a_dict - def encode_all_ids( self, rval, recursive=False ): + def encode_all_ids(self, rval, recursive=False): """ Encodes all integer values in the dict rval whose keys are 'id' or end with '_id' excluding `tool_id` which are consumed and produced as is via the API. """ - if not isinstance( rval, dict ): + if not isinstance(rval, dict): return rval for k, v in rval.items(): - if ( k == 'id' or k.endswith( '_id' ) ) and v is not None and k not in [ 'tool_id', 'external_id' ]: + if (k == 'id' or k.endswith('_id')) and v is not None and k not in ['tool_id', 'external_id']: try: - rval[ k ] = self.encode_id( v ) + rval[k] = self.encode_id(v) except Exception: pass # probably already encoded - if ( k.endswith( "_ids" ) and isinstance( v, list ) ): + if (k.endswith("_ids") and isinstance(v, list)): try: o = [] for i in v: - o.append( self.encode_id( i ) ) - rval[ k ] = o + o.append(self.encode_id(i)) + rval[k] = o except Exception: pass else: - if recursive and isinstance( v, dict ): - rval[ k ] = self.encode_all_ids( v, recursive ) - elif recursive and isinstance( v, list ): - rval[ k ] = map( lambda el: self.encode_all_ids( el, True), v ) + if recursive and isinstance(v, dict): + rval[k] = self.encode_all_ids(v, recursive) + elif recursive and isinstance(v, list): + rval[k] = map(lambda el: self.encode_all_ids(el, True), v) return rval - def decode_id( self, obj_id, kind=None ): - id_cipher = self.__id_cipher( kind ) - return int( id_cipher.decrypt( obj_id.decode( 'hex' ) ).lstrip( "!" ) ) + def decode_id(self, obj_id, kind=None): + id_cipher = self.__id_cipher(kind) + return int(id_cipher.decrypt(obj_id.decode('hex')).lstrip("!")) - def encode_guid( self, session_key ): + def encode_guid(self, session_key): # Session keys are strings # Pad to a multiple of 8 with leading "!" - s = ( "!" * ( 8 - len( session_key ) % 8 ) ) + session_key + s = ("!" * (8 - len(session_key) % 8)) + session_key # Encrypt - return self.id_cipher.encrypt( s ).encode( 'hex' ) + return self.id_cipher.encrypt(s).encode('hex') - def decode_guid( self, session_key ): + def decode_guid(self, session_key): # Session keys are strings - return self.id_cipher.decrypt( session_key.decode( 'hex' ) ).lstrip( "!" ) + return self.id_cipher.decrypt(session_key.decode('hex')).lstrip("!") - def get_new_guid( self ): + def get_new_guid(self): # Generate a unique, high entropy 128 bit random number - return get_random_bytes( 16 ) + return get_random_bytes(16) - def __id_cipher( self, kind ): + def __id_cipher(self, kind): if not kind: id_cipher = self.id_cipher else: - id_cipher = self.id_ciphers_for_kind[ kind ] + id_cipher = self.id_ciphers_for_kind[kind] return id_cipher -class _cipher_cache( collections.defaultdict ): +class _cipher_cache(collections.defaultdict): - def __init__( self, secret_base ): + def __init__(self, secret_base): self.secret_base = secret_base - def __missing__( self, key ): - return Blowfish.new( self.secret_base + "__" + key ) + def __missing__(self, key): + return Blowfish.new(self.secret_base + "__" + key) diff --git a/lib/galaxy/web/stack/__init__.py b/lib/galaxy/web/stack/__init__.py index d843822e5982..3b7689be4d54 100644 --- a/lib/galaxy/web/stack/__init__.py +++ b/lib/galaxy/web/stack/__init__.py @@ -102,5 +102,5 @@ def register_postfork_function(f, *args, **kwargs): @uwsgi_postfork def _do_postfork(): - for f, args, kwargs in [ t for t in UWSGIApplicationStack.postfork_functions ]: + for f, args, kwargs in [t for t in UWSGIApplicationStack.postfork_functions]: f(*args, **kwargs) diff --git a/lib/galaxy/webapps/__init__.py b/lib/galaxy/webapps/__init__.py index 7bcb5ec735f3..988de1719ee1 100644 --- a/lib/galaxy/webapps/__init__.py +++ b/lib/galaxy/webapps/__init__.py @@ -1,3 +1,3 @@ """Galaxy webapps root package -- this is a namespace package.""" -__import__( "pkg_resources" ).declare_namespace( __name__ ) +__import__("pkg_resources").declare_namespace(__name__) diff --git a/lib/galaxy/webapps/config_manage.py b/lib/galaxy/webapps/config_manage.py new file mode 100644 index 000000000000..07d0bc036586 --- /dev/null +++ b/lib/galaxy/webapps/config_manage.py @@ -0,0 +1,710 @@ +from __future__ import absolute_import +from __future__ import print_function + +import argparse +from collections import namedtuple +from collections import OrderedDict +import copy +import os +import shutil +import string +import sys +import tempfile +from textwrap import TextWrapper +import urllib2 + +import six +from six import StringIO + +import yaml + +try: + from pykwalify.core import Core +except ImportError: + Core = None + +if __name__ == '__main__': + sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir))) + +from galaxy.util.properties import nice_config_parser +from galaxy.util import safe_makedirs + +DESCRIPTION = "Convert configuration files." + +APP_DESCRIPTION = """Application to target for operation (i.e. galaxy, tool_shed, or reports))""" +DRY_RUN_DESCRIPTION = """If this action modifies files, just print what would be the result and continue.""" +UNKNOWN_OPTION_MESSAGE = "Option [%s] not found in schema - either it is invalid or the Galaxy team hasn't documented it. If invalid, you should manually remove it. If the option is valid but undocumented, please file an issue with the Galaxy team." +USING_SAMPLE_MESSAGE = "Path [%s] not a file, using sample." +EXTRA_SERVER_MESSAGE = "Additional server section after [%s] encountered [%s], will be ignored." +MISSING_FILTER_TYPE_MESSAGE = "Missing filter type for section [%s], it will be ignored." +UNHANDLED_FILTER_TYPE_MESSAGE = "Unhandled filter type encountered [%s] for section [%s]." +NO_APP_MAIN_MESSAGE = "No app:main section found, using application defaults throughout." +YAML_COMMENT_WRAPPER = TextWrapper(initial_indent="# ", subsequent_indent="# ") +RST_DESCRIPTION_WRAPPER = TextWrapper(initial_indent=" ", subsequent_indent=" ") +UWSGI_SCHEMA_PATH = "lib/galaxy/webapps/uwsgi_schema.yml" + +App = namedtuple( + "App", + ["config_paths", "default_port", "expected_app_factories", "destination", "schema_path", "uwsgi_module"] +) + +UWSGI_OPTIONS = OrderedDict([ + ('http', { + 'desc': """The address and port on which to listen. By default, only listen to localhost ($app_name will not be accessible over the network). Use '0.0.0.0' to listen on all available network interfaces.""", + 'default': '127.0.0.1:$default_port', + 'type': 'str', + }), + ('threads', { + 'default': 8, + 'type': 'int', + }), + # ('route-uri', { + # 'default': '^/proxy/ goto:proxy' + # }), + # ('route', { + # 'default': '.* last:' + # }), + # ('route-label', { + # 'default': 'proxy' + # }), + # ('route-run', { + # 'default': 'rpcvar:TARGET_HOST galaxy_dynamic_proxy_mapper ${HTTP_HOST} ${cookie[galaxysession]}' + # }), + # ('route-run', { + # 'default': "['log:Proxy ${HTTP_HOST} to ${TARGET_HOST}', 'httpdumb:${TARGET_HOST}']", + # }), + ('http-raw-body', { + 'default': 'True' + }), + ('offload-threads', { + 'default': '8', + }), + ('module', { + 'default': '$uwsgi_module', + 'type': 'str', + }) +]) + +DROP_OPTION_VALUE = object() + + +class _OptionAction(object): + + def converted(self, args, app_desc, key, value): + pass + + def lint(self, args, app_desc, key, value): + pass + + +class _DeprecatedAndDroppedAction(_OptionAction): + + def converted(self, args, app_desc, key, value): + print("Option [%s] has been deprecated and dropped. It is not included in converted configuration." % key) + return DROP_OPTION_VALUE + + def lint(self, args, app_desc, key, value): + print("Option [%s] has been deprecated. Option should be dropped without replacement." % key) + + +class _PasteAppFactoryAction(_OptionAction): + + def converted(self, args, app_desc, key, value): + if value not in app_desc.expected_app_factories: + raise Exception("Ending convert process - unknown paste factory encountered [%s]" % value) + return DROP_OPTION_VALUE + + def lint(self, args, app_desc, key, value): + if value not in app_desc.expected_app_factories: + print("Problem - unknown paste app factory encountered [%s]" % value) + + +class _ProductionNotReady(_OptionAction): + + def __init__(self, unsafe_value): + self.unsafe_value = unsafe_value + + def lint(self, args, app_desc, key, value): + if str(value).lower() == str(self.unsafe_value).lower(): + template = "Problem - option [%s] should not be set to [%s] in production environments - it is unsafe." + message = template % (key, value) + print(message) + + +class _HandleFilterWithAction(_OptionAction): + + def converted(self, args, app_desc, key, value): + print("filter-with converted to prefixed module load of uwsgi module, dropping from converted configuration") + return DROP_OPTION_VALUE + + +OPTION_ACTIONS = { + 'use_beaker_session': _DeprecatedAndDroppedAction(), + 'session_type': _DeprecatedAndDroppedAction(), + 'session_data_dir': _DeprecatedAndDroppedAction(), + 'session_key': _DeprecatedAndDroppedAction(), + 'session_secret': _DeprecatedAndDroppedAction(), + 'paste.app_factory': _PasteAppFactoryAction(), + 'filter-with': _HandleFilterWithAction(), + 'debug': _ProductionNotReady(True), + 'serve_xss_vulnerable_mimetypes': _ProductionNotReady(True), + 'use_printdebug': _ProductionNotReady(True), + 'use_interactive': _ProductionNotReady(True), + 'id_secret': _ProductionNotReady('USING THE DEFAULT IS NOT SECURE!'), + 'master_api_key': _ProductionNotReady('changethis'), +} + + +def _app_name(self): + return os.path.splitext(os.path.basename(self.destination))[0] + + +def _sample_destination(self): + return self.destination + ".sample" + + +def _schema(self): + return AppSchema(self) + + +App.app_name = property(_app_name) +App.sample_destination = property(_sample_destination) +App.schema = property(_schema) + +OptionValue = namedtuple("OptionValue", ["name", "value", "option"]) + + +UNKNOWN_OPTION = { + "type": "str", + "required": False, + "unknown_option": True, + "desc": "Unknown option, may want to remove or report to Galaxy team." +} + +OPTION_DEFAULTS = { + "type": "str", + "unknown_option": False, + "default": None, + "desc": None, +} + + +class Schema(object): + + def __init__(self, mapping): + self.app_schema = mapping + + def get_app_option(self, name): + try: + raw_option = self.app_schema[name] + except KeyError: + raw_option = UNKNOWN_OPTION + option = OPTION_DEFAULTS.copy() + option.update(raw_option) + return option + + +class AppSchema(Schema): + + def __init__(self, app_desc): + schema_path = app_desc.schema_path + app_name = app_desc.app_name + with open(schema_path, "r") as f: + config_all = _ordered_load(f) + self.raw_schema = config_all + app_schema = config_all["mapping"][app_name] + super(AppSchema, self).__init__(app_schema["mapping"]) + + def get_app_option(self, name): + try: + raw_option = self.app_schema[name] + except KeyError: + raw_option = UNKNOWN_OPTION + option = OPTION_DEFAULTS.copy() + option.update(raw_option) + return option + + +GALAXY_APP = App( + ["universe_wsgi.ini", "config/galaxy.ini"], + "8080", + ["galaxy.web.buildapp:app_factory"], # TODO: Galaxy could call factory a few different things and they'd all be fine. + "config/galaxy.yml", + "lib/galaxy/webapps/galaxy/config_schema.yml", + 'galaxy.webapps.galaxy.buildapp:uwsgi_app()', +) +SHED_APP = App( + ["tool_shed_wsgi.ini", "config/tool_shed.ini"], + "9009", + ["galaxy.webapps.tool_shed.buildapp:app_factory"], + "config/tool_shed.yml", + "lib/galaxy/webapps/tool_shed/config_schema.yml", + 'galaxy.webapps.tool_shed.buildapp:uwsgi_app()', +) +REPORTS_APP = App( + ["reports_wsgi.ini", "config/reports.ini"], + "9001", + ["galaxy.webapps.reports.buildapp:app_factory"], + "config/reports.yml", + "lib/galaxy/webapps/reports/config_schema.yml", + 'galaxy.webapps.reports.buildapp:uwsgi_app()', +) +APPS = {"galaxy": GALAXY_APP, "tool_shed": SHED_APP, "reports": REPORTS_APP} + + +def main(argv=None): + """Entry point for conversion process.""" + if argv is None: + argv = sys.argv[1:] + args = _arg_parser().parse_args(argv) + app_name = args.app + app_desc = APPS.get(app_name, None) + action = args.action + action_func = ACTIONS[action] + action_func(args, app_desc) + + +def _arg_parser(): + parser = argparse.ArgumentParser(description=DESCRIPTION) + parser.add_argument('action', metavar='ACTION', type=str, + choices=ACTIONS.keys(), + help='action to perform') + parser.add_argument('app', metavar='APP', type=str, nargs="?", + help=APP_DESCRIPTION) + parser.add_argument('--add-comments', default=False, action="store_true") + parser.add_argument('--dry-run', default=False, action="store_true", + help=DRY_RUN_DESCRIPTION) + parser.add_argument('--galaxy_root', default=".", type=str) + return parser + + +def _to_rst(args, app_desc, heading_level="~"): + rst = StringIO() + schema = app_desc.schema + for key, value in schema.app_schema.items(): + default = None if "default" not in value else value["default"] + option = schema.get_app_option(key) + option_value = OptionValue(key, default, option) + _write_option_rst(args, rst, key, heading_level, option_value) + print(rst.getvalue()) + + +def _write_option_rst(args, rst, key, heading_level, option_value): + title = "``%s``" % key + heading = heading_level * len(title) + rst.write("%s\n%s\n%s\n\n" % (heading, title, heading)) + option, value = _parse_option_value(option_value) + desc = option["desc"] + rst.write(":Description:\n") + rst.write("\n".join(RST_DESCRIPTION_WRAPPER.wrap(desc))) + rst.write("\n") + type = option.get("type", None) + default = option.get("default", "*null*") + rst.write(":Default: %s\n" % default) + if type: + rst.write(":Type: %s\n" % type) + rst.write("\n\n") + + +def _build_uwsgi_schema(args, app_desc): + req = urllib2.Request('https://raw.githubusercontent.com/unbit/uwsgi-docs/master/Options.rst') + response = urllib2.urlopen(req) + rst_options = response.read() + last_line = None + current_opt = None + + options = OrderedDict({}) + option = None + for line in rst_options.splitlines(): + line = line.strip() + dots = "*" * len(line) + if line and (line == dots): + current_opt = last_line + option = { + 'type': 'any', + } + options[current_opt] = option + + if line.startswith("``parser``"): + parser = line.split(":", 1)[1].strip() + if parser == "uwsgi_opt_set_int": + option["type"] = "int" + # TODO: disptch on parser... + elif line.startswith("``help``"): + option["desc"] = line.split(":", 1)[1] + + last_line = line + schema = { + "type": "map", + "desc": "uwsgi definition, see http://uwsgi-docs.readthedocs.io/en/latest/Options.html", + "mapping": options + } + path = os.path.join(args.galaxy_root, UWSGI_SCHEMA_PATH) + contents = _ordered_dump(schema) + _write_to_file(args, contents, path) + + +def _find_config(args, app_desc): + path = os.path.join(args.galaxy_root, app_desc.destination) + if not os.path.exists(path): + path = None + + for possible_ini_config_rel in app_desc.config_paths: + possible_ini_config = os.path.join(args.galaxy_root, possible_ini_config_rel) + if os.path.exists(possible_ini_config): + path = possible_ini_config + + if path is None: + _warn(USING_SAMPLE_MESSAGE % path) + path = os.path.join(args.galaxy_root, app_desc.sample_destination) + + return path + + +def _find_app_options(app_desc, path): + """Load app (as opposed to server) options from specified path. + + Supplied ``path`` may be either YAML or ini file. + """ + if _is_ini(path): + p = nice_config_parser(path) + app_items = _find_app_options_from_config_parser(p) + else: + raw_config = _order_load_path(path) + app_items = raw_config.get(app_desc.app_name, None) or {} + return app_items + + +def _find_app_options_from_config_parser(p): + if not p.has_section("app:main"): + _warn(NO_APP_MAIN_MESSAGE) + app_items = OrderedDict() + else: + app_items = OrderedDict(p.items("app:main")) + + return app_items + + +def _lint(args, app_desc): + path = _find_config(args, app_desc) + if not os.path.exists(path): + raise Exception("Expected configuration file [%s] not found." % path) + app_items = _find_app_options(app_desc, path) + for key, value in app_items.items(): + option_action = OPTION_ACTIONS.get(key) + if option_action is not None: + option_action.lint(args, app_desc, key, value) + + +def _validate(args, app_desc): + path = _find_config(args, app_desc) + with open(path, "r") as f: + # Allow empty mapping (not allowed by pykawlify) + raw_config = _order_load_path(f) + if raw_config.get(app_desc.app_name, None) is None: + raw_config[app_desc.app_name] = {} + config_p = tempfile.NamedTemporaryFile(delete=False, suffix=".yml") + _ordered_dump(raw_config, config_p) + config_p.flush() + path = config_p.name + + fp = tempfile.NamedTemporaryFile(delete=False, suffix=".yml") + _ordered_dump(app_desc.schema.raw_schema, fp) + fp.flush() + name = fp.name + if Core is None: + raise Exception("Cannot validate file, pykwalify is not installed.") + c = Core( + source_file=path, + schema_files=[name], + ) + c.validate() + + +class PrefixFilter(object): + + def __init__(self, name, prefix): + self.name = name + self.prefix = prefix + + +class GzipFilter(object): + + def __init__(self, name): + self.name = name + + +def _run_conversion(args, app_desc): + ini_config = _find_config(args, app_desc) + if ini_config and not _is_ini(ini_config): + _warn("Cannot convert YAML file %s, this option is only for ini config files." % ini_config) + sys.exit(1) + elif not ini_config: + _warn("Failed to find a config to convert - exiting without changes.") + sys.exit(1) + + p = nice_config_parser(ini_config) + server_section = None + filters = {} + for section in p.sections(): + if section.startswith("server:"): + if server_section: + _warn(EXTRA_SERVER_MESSAGE % (server_section, section)) + else: + server_section = section + + if section.startswith("filter:"): + filter_name = section[len("filter:"):] + filter_type = p.get(section, "use") + if filter_type is None: + MISSING_FILTER_TYPE_MESSAGE + message = EXTRA_SERVER_MESSAGE % section + _warn(message) + continue + + if filter_type == "egg:PasteDeploy#prefix": + prefix = p.get(section, "prefix") + filters[filter_name] = PrefixFilter(filter_name, prefix) + elif filter_type == "egg:Paste#gzip": + filters[filter_name] = GzipFilter(filter_name) + else: + message = UNHANDLED_FILTER_TYPE_MESSAGE % (filter_type, section) + _warn(message) + continue + + if not server_section: + _warn("No server section found, using default uwsgi server definition.") + server_config = OrderedDict() + else: + server_config = OrderedDict(p.items(server_section)) + + app_items = _find_app_options_from_config_parser(p) + applied_filters = [] + if filters: + for key, value in app_items.items(): + if key == "filter-with": + if value in filters: + applied_filters.append(filters[value]) + else: + _warn("Unknown filter found [%s], exiting..." % value) + sys.exit(1) + + uwsgi_dict = _server_paste_to_uwsgi(app_desc, server_config, applied_filters) + + app_dict = OrderedDict({}) + schema = app_desc.schema + for key, value in app_items.items(): + if key in ["__file__", "here"]: + continue + + if key in OPTION_ACTIONS: + option_action = OPTION_ACTIONS.get(key) + value = option_action.converted(args, app_desc, key, value) + + if value is DROP_OPTION_VALUE: + continue + + option = schema.get_app_option(key) + if option["unknown_option"]: + _warn(UNKNOWN_OPTION_MESSAGE % key) + + option_value = OptionValue(key, value, option) + app_dict[key] = option_value + + f = StringIO() + _write_section(args, f, "uwsgi", uwsgi_dict) + _write_section(args, f, app_desc.app_name, app_dict) + destination = os.path.join(args.galaxy_root, app_desc.destination) + _replace_file(args, f, app_desc, ini_config, destination) + + +def _is_ini(path): + return path.endswith(".ini") or path.endswith(".ini.sample") + + +def _replace_file(args, f, app_desc, from_path, to_path): + _write_to_file(args, f, to_path) + backup_path = "%s.backup" % from_path + print("Moving [%s] to [%s]" % (from_path, backup_path)) + if args.dry_run: + print("... skipping because --dry-run is enabled.") + else: + shutil.move(from_path, backup_path) + + +def _build_sample_yaml(args, app_desc): + schema = app_desc.schema + f = StringIO() + options = copy.deepcopy(UWSGI_OPTIONS) + for key, value in options.items(): + for field in ["desc", "default"]: + if field not in value: + continue + field_value = value[field] + if not isinstance(field_value, six.string_types): + continue + + new_field_value = string.Template(field_value).safe_substitute(**{ + 'default_port': str(app_desc.default_port), + 'app_name': app_desc.app_name, + 'uwsgi_module': app_desc.uwsgi_module, + }) + value[field] = new_field_value + _write_sample_section(args, f, 'uwsgi', Schema(options), as_comment=False) + _write_sample_section(args, f, app_desc.app_name, schema) + destination = os.path.join(args.galaxy_root, app_desc.sample_destination) + _write_to_file(args, f, destination) + + +def _write_to_file(args, f, path): + dry_run = args.dry_run + if hasattr(f, "getvalue"): + contents = f.getvalue() + else: + contents = f + contents_indented = "\n".join([" |%s" % l for l in contents.splitlines()]) + print("Writing the file contents:\n%s\nto %s" % (contents_indented, path)) + if dry_run: + print("... skipping because --dry-run is enabled.") + else: + safe_makedirs(os.path.dirname(path)) + with open(path, "w") as to_f: + to_f.write(contents) + + +def _order_load_path(path): + """Load (with ``_ordered_load``) on specified path (a YAML file).""" + with open(path, "r") as f: + # Allow empty mapping (not allowed by pykawlify) + raw_config = _ordered_load(f) + return raw_config + + +def _write_sample_section(args, f, section_header, schema, as_comment=True): + _write_header(f, section_header) + for key, value in schema.app_schema.items(): + default = None if "default" not in value else value["default"] + option = schema.get_app_option(key) + option_value = OptionValue(key, default, option) + _write_option(args, f, key, option_value, as_comment=as_comment) + + +def _write_section(args, f, section_header, section_dict): + _write_header(f, section_header) + for key, option_value in section_dict.items(): + _write_option(args, f, key, option_value) + + +def _write_header(f, section_header): + f.write("%s:\n\n" % section_header) + + +def _write_option(args, f, key, option_value, as_comment=False): + option, value = _parse_option_value(option_value) + desc = option["desc"] + comment = "" + if desc and args.add_comments: + comment = "\n".join(YAML_COMMENT_WRAPPER.wrap(desc)) + comment += "\n" + as_comment_str = "#" if as_comment else "" + key_val_str = yaml.dump({key: value}).lstrip("{").rstrip("\n}") + lines = "%s%s%s" % (comment, as_comment_str, key_val_str) + lines_idented = "\n".join([(" %s" % l) for l in lines.split("\n")]) + f.write("%s\n\n" % lines_idented) + + +def _parse_option_value(option_value): + if isinstance(option_value, OptionValue): + option = option_value.option + value = option_value.value + else: + value = option_value + option = OPTION_DEFAULTS + return option, value + + +def _server_paste_to_uwsgi(app_desc, server_config, applied_filters): + uwsgi_dict = OrderedDict() + port = server_config.get("port", app_desc.default_port) + host = server_config.get("host", "127.0.0.1") + + if server_config.get("use", "egg:Paste#http") != "egg:Paste#http": + raise Exception("Unhandled paste server 'use' value [%s], file must be manually migrate.") + + uwsgi_dict["http"] = "%s:%s" % (host, port) + # default changing from 10 to 8 + uwsgi_dict["threads"] = int(server_config.get("threadpool_workers", 8)) + # required for static... + uwsgi_dict["http-raw-body"] = True + uwsgi_dict["offload-threads"] = 8 + + # Handle paste filters during conversion. + prefix = None + for applied_filter in applied_filters: + if isinstance(applied_filter, PrefixFilter): + prefix = applied_filter.prefix + break + elif isinstance(applied_filter, GzipFilter): + uwsgi_dict["http-auto-gzip"] = True + + if prefix: + uwsgi_dict["mount"] = "%s=%s" % (prefix, app_desc.uwsgi_module) + uwsgi_dict["manage-script-name"] = True + else: + uwsgi_dict["module"] = app_desc.uwsgi_module + return uwsgi_dict + + +def _warn(message): + print("WARNING: %s" % message) + + +def _ordered_load(stream): + + class OrderedLoader(yaml.Loader): + + def __init__(self, stream): + self._root = os.path.split(stream.name)[0] + super(OrderedLoader, self).__init__(stream) + + def include(self, node): + filename = os.path.join(self._root, self.construct_scalar(node)) + with open(filename, 'r') as f: + return yaml.load(f, OrderedLoader) + + def construct_mapping(loader, node): + loader.flatten_mapping(node) + return OrderedDict(loader.construct_pairs(node)) + + OrderedLoader.add_constructor( + yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, + construct_mapping) + OrderedLoader.add_constructor('!include', OrderedLoader.include) + + return yaml.load(stream, OrderedLoader) + + +def _ordered_dump(data, stream=None, Dumper=yaml.Dumper, **kwds): + class OrderedDumper(Dumper): + pass + + def _dict_representer(dumper, data): + return dumper.represent_mapping( + yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, + data.items()) + OrderedDumper.add_representer(OrderedDict, _dict_representer) + return yaml.dump(data, stream, OrderedDumper, **kwds) + + +ACTIONS = { + "convert": _run_conversion, + "build_sample_yaml": _build_sample_yaml, + "validate": _validate, + "lint": _lint, + "build_uwsgi_yaml": _build_uwsgi_schema, + "build_rst": _to_rst, +} + + +if __name__ == '__main__': + main() diff --git a/lib/galaxy/webapps/galaxy/api/annotations.py b/lib/galaxy/webapps/galaxy/api/annotations.py index 8a96506ad453..66851e0ad062 100644 --- a/lib/galaxy/webapps/galaxy/api/annotations.py +++ b/lib/galaxy/webapps/galaxy/api/annotations.py @@ -13,20 +13,20 @@ from galaxy.util import sanitize_html import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class BaseAnnotationsController( BaseAPIController, UsesStoredWorkflowMixin, UsesAnnotations ): +class BaseAnnotationsController(BaseAPIController, UsesStoredWorkflowMixin, UsesAnnotations): @expose_api - def index( self, trans, **kwd ): + def index(self, trans, **kwd): idnum = kwd[self.tagged_item_id] item = self._get_item_from_id(trans, idnum) if item is not None: - return self.get_item_annotation_str( trans.sa_session, trans.get_user(), item ) + return self.get_item_annotation_str(trans.sa_session, trans.get_user(), item) @expose_api - def create( self, trans, payload, **kwd ): + def create(self, trans, payload, **kwd): if "text" not in payload: return "" idnum = kwd[self.tagged_item_id] @@ -34,22 +34,22 @@ def create( self, trans, payload, **kwd ): if item is not None: new_annotation = payload.get("text") # TODO: sanitize on display not entry - new_annotation = sanitize_html.sanitize_html( new_annotation, 'utf-8', 'text/html' ) + new_annotation = sanitize_html.sanitize_html(new_annotation, 'utf-8', 'text/html') - self.add_item_annotation( trans.sa_session, trans.get_user(), item, new_annotation ) + self.add_item_annotation(trans.sa_session, trans.get_user(), item, new_annotation) trans.sa_session.flush() return new_annotation return "" @expose_api - def delete( self, trans, **kwd ): + def delete(self, trans, **kwd): idnum = kwd[self.tagged_item_id] item = self._get_item_from_id(trans, idnum) if item is not None: - return self.delete_item_annotation( trans.sa_session, trans.get_user(), item ) + return self.delete_item_annotation(trans.sa_session, trans.get_user(), item) @expose_api - def undelete( self, trans, **kwd ): + def undelete(self, trans, **kwd): raise exceptions.NotImplemented() @@ -57,35 +57,35 @@ class HistoryAnnotationsController(BaseAnnotationsController): controller_name = "history_annotations" tagged_item_id = "history_id" - def __init__( self, app ): - super( HistoryAnnotationsController, self ).__init__( app ) - self.history_manager = managers.histories.HistoryManager( app ) + def __init__(self, app): + super(HistoryAnnotationsController, self).__init__(app) + self.history_manager = managers.histories.HistoryManager(app) def _get_item_from_id(self, trans, idstr): - decoded_idstr = self.decode_id( idstr ) - history = self.history_manager.get_accessible( decoded_idstr, trans.user, current_history=trans.history ) + decoded_idstr = self.decode_id(idstr) + history = self.history_manager.get_accessible(decoded_idstr, trans.user, current_history=trans.history) return history -class HistoryContentAnnotationsController( BaseAnnotationsController ): +class HistoryContentAnnotationsController(BaseAnnotationsController): controller_name = "history_content_annotations" tagged_item_id = "history_content_id" - def __init__( self, app ): - super( HistoryContentAnnotationsController, self ).__init__( app ) - self.hda_manager = managers.hdas.HDAManager( app ) + def __init__(self, app): + super(HistoryContentAnnotationsController, self).__init__(app) + self.hda_manager = managers.hdas.HDAManager(app) def _get_item_from_id(self, trans, idstr): - decoded_idstr = self.decode_id( idstr ) - hda = self.hda_manager.get_accessible( decoded_idstr, trans.user ) - hda = self.hda_manager.error_if_uploading( hda ) + decoded_idstr = self.decode_id(idstr) + hda = self.hda_manager.get_accessible(decoded_idstr, trans.user) + hda = self.hda_manager.error_if_uploading(hda) return hda -class WorkflowAnnotationsController( BaseAnnotationsController ): +class WorkflowAnnotationsController(BaseAnnotationsController): controller_name = "workflow_annotations" tagged_item_id = "workflow_id" def _get_item_from_id(self, trans, idstr): - hda = self.get_stored_workflow( trans, idstr ) + hda = self.get_stored_workflow(trans, idstr) return hda diff --git a/lib/galaxy/webapps/galaxy/api/authenticate.py b/lib/galaxy/webapps/galaxy/api/authenticate.py index f07b40f8a96d..3f24607b1bd0 100644 --- a/lib/galaxy/webapps/galaxy/api/authenticate.py +++ b/lib/galaxy/webapps/galaxy/api/authenticate.py @@ -20,17 +20,17 @@ from galaxy.web.base.controller import BaseAPIController import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class AuthenticationController( BaseAPIController ): +class AuthenticationController(BaseAPIController): - def __init__( self, app ): - super( AuthenticationController, self ).__init__( app ) - self.api_keys_manager = api_keys.ApiKeyManager( app ) + def __init__(self, app): + super(AuthenticationController, self).__init__(app) + self.api_keys_manager = api_keys.ApiKeyManager(app) @expose_api_anonymous_and_sessionless - def get_api_key( self, trans, **kwd ): + def get_api_key(self, trans, **kwd): """ def get_api_key( self, trans, **kwd ) * GET /api/authenticate/baseauth @@ -41,25 +41,25 @@ def get_api_key( self, trans, **kwd ) :raises: ObjectNotFound, HTTPBadRequest """ - email, password = self._decode_baseauth( trans.environ.get( 'HTTP_AUTHORIZATION' ) ) + email, password = self._decode_baseauth(trans.environ.get('HTTP_AUTHORIZATION')) - user = trans.sa_session.query( trans.app.model.User ).filter( trans.app.model.User.table.c.email == email ).all() + user = trans.sa_session.query(trans.app.model.User).filter(trans.app.model.User.table.c.email == email).all() - if len( user ) == 0: - raise exceptions.ObjectNotFound( 'The user does not exist.' ) - elif len( user ) > 1: + if len(user) == 0: + raise exceptions.ObjectNotFound('The user does not exist.') + elif len(user) > 1: # DB is inconsistent and we have more users with the same email. - raise exceptions.InconsistentDatabase( 'An error occured, please contact your administrator.' ) + raise exceptions.InconsistentDatabase('An error occured, please contact your administrator.') else: user = user[0] is_valid_user = self.app.auth_manager.check_password(user, password) if is_valid_user: - key = self.api_keys_manager.get_or_create_api_key( user ) - return dict( api_key=key ) + key = self.api_keys_manager.get_or_create_api_key(user) + return dict(api_key=key) else: - raise exceptions.AuthenticationFailed( 'Invalid password.' ) + raise exceptions.AuthenticationFailed('Invalid password.') - def _decode_baseauth( self, encoded_str ): + def _decode_baseauth(self, encoded_str): """ Decode an encrypted HTTP basic authentication string. Returns a tuple of the form (email, password), and raises a HTTPBadRequest exception if @@ -75,23 +75,23 @@ def _decode_baseauth( self, encoded_str ): :raises: HTTPBadRequest """ - split = encoded_str.strip().split( ' ' ) + split = encoded_str.strip().split(' ') # If split is only one element, try to decode the email and password # directly. - if len( split ) == 1: + if len(split) == 1: try: - email, password = b64decode( split[ 0 ] ).split( ':' ) + email, password = b64decode(split[0]).split(':') except: raise exceptions.ActionInputError() # If there are only two elements, check the first and ensure it says # 'basic' so that we know we're about to decode the right thing. If not, # bail out. - elif len( split ) == 2: - if split[ 0 ].strip().lower() == 'basic': + elif len(split) == 2: + if split[0].strip().lower() == 'basic': try: - email, password = b64decode( split[ 1 ] ).split( ':' ) + email, password = b64decode(split[1]).split(':') except: raise exceptions.ActionInputError() else: @@ -102,4 +102,4 @@ def _decode_baseauth( self, encoded_str ): else: raise exceptions.ActionInputError() - return unquote( email ), unquote( password ) + return unquote(email), unquote(password) diff --git a/lib/galaxy/webapps/galaxy/api/configuration.py b/lib/galaxy/webapps/galaxy/api/configuration.py index fb127414ff58..a6e498b94d41 100644 --- a/lib/galaxy/webapps/galaxy/api/configuration.py +++ b/lib/galaxy/webapps/galaxy/api/configuration.py @@ -15,19 +15,19 @@ import os import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class ConfigurationController( BaseAPIController ): +class ConfigurationController(BaseAPIController): - def __init__( self, app ): - super( ConfigurationController, self ).__init__( app ) - self.config_serializer = configuration.ConfigSerializer( app ) - self.admin_config_serializer = configuration.AdminConfigSerializer( app ) - self.user_manager = users.UserManager( app ) + def __init__(self, app): + super(ConfigurationController, self).__init__(app) + self.config_serializer = configuration.ConfigSerializer(app) + self.admin_config_serializer = configuration.AdminConfigSerializer(app) + self.user_manager = users.UserManager(app) @expose_api_anonymous - def whoami( self, trans, **kwd ): + def whoami(self, trans, **kwd): """ GET /api/whoami Return information about the current authenticated user. @@ -35,11 +35,11 @@ def whoami( self, trans, **kwd ): :returns: dictionary with user information :rtype: dict """ - current_user = self.user_manager.current_user( trans ) + current_user = self.user_manager.current_user(trans) return current_user.to_dict() @expose_api_anonymous_and_sessionless - def index( self, trans, **kwd ): + def index(self, trans, **kwd): """ GET /api/configuration Return an object containing exposable configuration settings. @@ -47,11 +47,11 @@ def index( self, trans, **kwd ): Note: a more complete list is returned if the user is an admin. """ is_admin = trans.user_is_admin() - serialization_params = self._parse_serialization_params( kwd, 'all' ) - return self.get_config_dict( trans, is_admin, **serialization_params ) + serialization_params = self._parse_serialization_params(kwd, 'all') + return self.get_config_dict(trans, is_admin, **serialization_params) @expose_api_anonymous_and_sessionless - def version( self, trans, **kwds ): + def version(self, trans, **kwds): """ GET /api/version Return a description of the major version of Galaxy (e.g. 15.03). @@ -68,7 +68,7 @@ def version( self, trans, **kwds ): pass return {"version_major": self.app.config.version_major, "extra": extra} - def get_config_dict( self, trans, return_admin=False, view=None, keys=None, default_view='all' ): + def get_config_dict(self, trans, return_admin=False, view=None, keys=None, default_view='all'): """ Return a dictionary with (a subset of) current Galaxy settings. @@ -81,7 +81,7 @@ def get_config_dict( self, trans, return_admin=False, view=None, keys=None, defa # TODO: this should probably just be under a different route: 'admin/configuration' serializer = self.admin_config_serializer - serialized = serializer.serialize_to_view( self.app.config, view=view, keys=keys, default_view=default_view ) + serialized = serializer.serialize_to_view(self.app.config, view=view, keys=keys, default_view=default_view) return serialized @expose_api @@ -95,7 +95,7 @@ def dynamic_tool_confs(self, trans): def tool_lineages(self, trans): rval = [] for id, tool in self.app.toolbox.tools(): - if hasattr( tool, 'lineage' ): + if hasattr(tool, 'lineage'): lineage_dict = tool.lineage.to_dict() else: lineage_dict = None diff --git a/lib/galaxy/webapps/galaxy/api/dataset_collections.py b/lib/galaxy/webapps/galaxy/api/dataset_collections.py index 9d25976dffdd..5aeec294f693 100644 --- a/lib/galaxy/webapps/galaxy/api/dataset_collections.py +++ b/lib/galaxy/webapps/galaxy/api/dataset_collections.py @@ -7,7 +7,7 @@ from galaxy.managers.collections_util import api_payload_to_create_params, dictify_dataset_collection_instance from logging import getLogger -log = getLogger( __name__ ) +log = getLogger(__name__) class DatasetCollectionsController( @@ -15,17 +15,17 @@ class DatasetCollectionsController( UsesLibraryMixinItems, ): - def __init__( self, app ): - super( DatasetCollectionsController, self ).__init__( app ) - self.history_manager = managers.histories.HistoryManager( app ) + def __init__(self, app): + super(DatasetCollectionsController, self).__init__(app) + self.history_manager = managers.histories.HistoryManager(app) @expose_api - def index( self, trans, **kwd ): + def index(self, trans, **kwd): trans.response.status = 501 return 'not implemented' @expose_api - def create( self, trans, payload, **kwd ): + def create(self, trans, payload, **kwd): """ * POST /api/dataset_collections: create a new dataset collection instance. @@ -40,28 +40,28 @@ def create( self, trans, payload, **kwd ): :returns: element view of new dataset collection """ # TODO: Error handling... - create_params = api_payload_to_create_params( payload ) - instance_type = payload.pop( "instance_type", "history" ) + create_params = api_payload_to_create_params(payload) + instance_type = payload.pop("instance_type", "history") if instance_type == "history": - history_id = payload.get( 'history_id' ) - history_id = self.decode_id( history_id ) - history = self.history_manager.get_owned( history_id, trans.user, current_history=trans.history ) - create_params[ "parent" ] = history + history_id = payload.get('history_id') + history_id = self.decode_id(history_id) + history = self.history_manager.get_owned(history_id, trans.user, current_history=trans.history) + create_params["parent"] = history elif instance_type == "library": - folder_id = payload.get( 'folder_id' ) - library_folder = self.get_library_folder( trans, folder_id, check_accessible=True ) - self.check_user_can_add_to_library_item( trans, library_folder, check_accessible=False ) - create_params[ "parent" ] = library_folder + folder_id = payload.get('folder_id') + library_folder = self.get_library_folder(trans, folder_id, check_accessible=True) + self.check_user_can_add_to_library_item(trans, library_folder, check_accessible=False) + create_params["parent"] = library_folder else: trans.status = 501 return - dataset_collection_instance = self.__service( trans ).create( trans=trans, **create_params ) - return dictify_dataset_collection_instance( dataset_collection_instance, - security=trans.security, parent=create_params[ "parent" ] ) + dataset_collection_instance = self.__service(trans).create(trans=trans, **create_params) + return dictify_dataset_collection_instance(dataset_collection_instance, + security=trans.security, parent=create_params["parent"]) @expose_api - def show( self, trans, instance_type, id, **kwds ): - dataset_collection_instance = self.__service( trans ).get_dataset_collection_instance( + def show(self, trans, instance_type, id, **kwds): + dataset_collection_instance = self.__service(trans).get_dataset_collection_instance( trans, id=id, instance_type=instance_type, @@ -80,6 +80,6 @@ def show( self, trans, instance_type, id, **kwds ): view='element' ) - def __service( self, trans ): + def __service(self, trans): service = trans.app.dataset_collections_service return service diff --git a/lib/galaxy/webapps/galaxy/api/datasets.py b/lib/galaxy/webapps/galaxy/api/datasets.py index b58ce404d258..81e6889337cb 100644 --- a/lib/galaxy/webapps/galaxy/api/datasets.py +++ b/lib/galaxy/webapps/galaxy/api/datasets.py @@ -19,25 +19,25 @@ from galaxy import managers import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class DatasetsController( BaseAPIController, UsesVisualizationMixin ): +class DatasetsController(BaseAPIController, UsesVisualizationMixin): - def __init__( self, app ): - super( DatasetsController, self ).__init__( app ) - self.hda_manager = managers.hdas.HDAManager( app ) - self.hda_serializer = managers.hdas.HDASerializer( self.app ) + def __init__(self, app): + super(DatasetsController, self).__init__(app) + self.hda_manager = managers.hdas.HDAManager(app) + self.hda_serializer = managers.hdas.HDASerializer(self.app) - def _parse_serialization_params( self, kwd, default_view ): - view = kwd.get( 'view', None ) - keys = kwd.get( 'keys' ) - if isinstance( keys, string_types ): - keys = keys.split( ',' ) - return dict( view=view, keys=keys, default_view=default_view ) + def _parse_serialization_params(self, kwd, default_view): + view = kwd.get('view', None) + keys = kwd.get('keys') + if isinstance(keys, string_types): + keys = keys.split(',') + return dict(view=view, keys=keys, default_view=default_view) @web.expose_api - def index( self, trans, **kwd ): + def index(self, trans, **kwd): """ GET /api/datasets Lists datasets. @@ -46,104 +46,104 @@ def index( self, trans, **kwd ): return 'not implemented' @web.expose_api_anonymous - def show( self, trans, id, hda_ldda='hda', data_type=None, provider=None, **kwd ): + def show(self, trans, id, hda_ldda='hda', data_type=None, provider=None, **kwd): """ GET /api/datasets/{encoded_dataset_id} Displays information about and/or content of a dataset. """ # Get dataset. try: - dataset = self.get_hda_or_ldda( trans, hda_ldda=hda_ldda, dataset_id=id ) + dataset = self.get_hda_or_ldda(trans, hda_ldda=hda_ldda, dataset_id=id) except Exception as e: - return str( e ) + return str(e) # Use data type to return particular type of data. try: if data_type == 'state': - rval = self._dataset_state( trans, dataset ) + rval = self._dataset_state(trans, dataset) elif data_type == 'converted_datasets_state': - rval = self._converted_datasets_state( trans, dataset, kwd.get( 'chrom', None ), - is_true( kwd.get( 'retry', False ) ) ) + rval = self._converted_datasets_state(trans, dataset, kwd.get('chrom', None), + is_true(kwd.get('retry', False))) elif data_type == 'data': - rval = self._data( trans, dataset, **kwd ) + rval = self._data(trans, dataset, **kwd) elif data_type == 'features': - rval = self._search_features( trans, dataset, kwd.get( 'query' ) ) + rval = self._search_features(trans, dataset, kwd.get('query')) elif data_type == 'raw_data': - rval = self._raw_data( trans, dataset, provider, **kwd ) + rval = self._raw_data(trans, dataset, provider, **kwd) elif data_type == 'track_config': - rval = self.get_new_track_config( trans, dataset ) + rval = self.get_new_track_config(trans, dataset) elif data_type == 'genome_data': - rval = self._get_genome_data( trans, dataset, kwd.get('dbkey', None) ) + rval = self._get_genome_data(trans, dataset, kwd.get('dbkey', None)) else: # Default: return dataset as dict. if hda_ldda == 'hda': - return self.hda_serializer.serialize_to_view( dataset, - view=kwd.get( 'view', 'detailed' ), user=trans.user, trans=trans ) + return self.hda_serializer.serialize_to_view(dataset, + view=kwd.get('view', 'detailed'), user=trans.user, trans=trans) else: rval = dataset.to_dict() except Exception as e: - rval = "Error in dataset API at listing contents: " + str( e ) - log.error( rval + ": %s" % str(e), exc_info=True ) + rval = "Error in dataset API at listing contents: " + str(e) + log.error(rval + ": %s" % str(e), exc_info=True) trans.response.status = 500 return rval - def _dataset_state( self, trans, dataset, **kwargs ): + def _dataset_state(self, trans, dataset, **kwargs): """ Returns state of dataset. """ - msg = self.hda_manager.data_conversion_status( dataset ) + msg = self.hda_manager.data_conversion_status(dataset) if not msg: msg = dataset.conversion_messages.DATA return msg - def _converted_datasets_state( self, trans, dataset, chrom=None, retry=False ): + def _converted_datasets_state(self, trans, dataset, chrom=None, retry=False): """ Init-like method that returns state of dataset's converted datasets. Returns valid chroms for that dataset as well. """ - msg = self.hda_manager.data_conversion_status( dataset ) + msg = self.hda_manager.data_conversion_status(dataset) if msg: return msg # Get datasources and check for messages (which indicate errors). Retry if flag is set. - data_sources = dataset.get_datasources( trans ) - messages_list = [ data_source_dict[ 'message' ] for data_source_dict in data_sources.values() ] - msg = self._get_highest_priority_msg( messages_list ) + data_sources = dataset.get_datasources(trans) + messages_list = [data_source_dict['message'] for data_source_dict in data_sources.values()] + msg = self._get_highest_priority_msg(messages_list) if msg: if retry: # Clear datasources and then try again. dataset.clear_associated_files() - return self._converted_datasets_state( trans, dataset, chrom ) + return self._converted_datasets_state(trans, dataset, chrom) else: return msg # If there is a chrom, check for data on the chrom. if chrom: - data_provider = trans.app.data_provider_registry.get_data_provider( trans, - original_dataset=dataset, source='index' ) - if not data_provider.has_data( chrom ): + data_provider = trans.app.data_provider_registry.get_data_provider(trans, + original_dataset=dataset, source='index') + if not data_provider.has_data(chrom): return dataset.conversion_messages.NO_DATA # Have data if we get here - return { "status": dataset.conversion_messages.DATA, "valid_chroms": None } + return {"status": dataset.conversion_messages.DATA, "valid_chroms": None} - def _search_features( self, trans, dataset, query ): + def _search_features(self, trans, dataset, query): """ Returns features, locations in dataset that match query. Format is a list of features; each feature is a list itself: [name, location] """ - if dataset.can_convert_to( "fli" ): - converted_dataset = dataset.get_converted_dataset( trans, "fli" ) + if dataset.can_convert_to("fli"): + converted_dataset = dataset.get_converted_dataset(trans, "fli") if converted_dataset: - data_provider = FeatureLocationIndexDataProvider( converted_dataset=converted_dataset ) + data_provider = FeatureLocationIndexDataProvider(converted_dataset=converted_dataset) if data_provider: - return data_provider.get_data( query ) + return data_provider.get_data(query) return [] - def _data( self, trans, dataset, chrom, low, high, start_val=0, max_vals=None, **kwargs ): + def _data(self, trans, dataset, chrom, low, high, start_val=0, max_vals=None, **kwargs): """ Provides a block of data from a dataset. """ @@ -152,27 +152,27 @@ def _data( self, trans, dataset, chrom, low, high, start_val=0, max_vals=None, * return dataset.conversion_messages.NO_DATA # Dataset check. - msg = self.hda_manager.data_conversion_status( dataset ) + msg = self.hda_manager.data_conversion_status(dataset) if msg: return msg # Get datasources and check for messages. - data_sources = dataset.get_datasources( trans ) - messages_list = [ data_source_dict[ 'message' ] for data_source_dict in data_sources.values() ] - return_message = self._get_highest_priority_msg( messages_list ) + data_sources = dataset.get_datasources(trans) + messages_list = [data_source_dict['message'] for data_source_dict in data_sources.values()] + return_message = self._get_highest_priority_msg(messages_list) if return_message: return return_message extra_info = None - mode = kwargs.get( "mode", "Auto" ) + mode = kwargs.get("mode", "Auto") data_provider_registry = trans.app.data_provider_registry indexer = None # Coverage mode uses index data. if mode == "Coverage": # Get summary using minimal cutoffs. - indexer = data_provider_registry.get_data_provider( trans, original_dataset=dataset, source='index' ) - return indexer.get_data( chrom, low, high, **kwargs ) + indexer = data_provider_registry.get_data_provider(trans, original_dataset=dataset, source='index') + return indexer.get_data(chrom, low, high, **kwargs) # TODO: # (1) add logic back in for no_detail @@ -181,33 +181,33 @@ def _data( self, trans, dataset, chrom, low, high, start_val=0, max_vals=None, * # If mode is Auto, need to determine what type of data to return. if mode == "Auto": # Get stats from indexer. - indexer = data_provider_registry.get_data_provider( trans, original_dataset=dataset, source='index' ) - stats = indexer.get_data( chrom, low, high, stats=True ) + indexer = data_provider_registry.get_data_provider(trans, original_dataset=dataset, source='index') + stats = indexer.get_data(chrom, low, high, stats=True) # If stats were requested, return them. if 'stats' in kwargs: - if stats[ 'data' ][ 'max' ] == 0: - return { 'dataset_type': indexer.dataset_type, 'data': None } + if stats['data']['max'] == 0: + return {'dataset_type': indexer.dataset_type, 'data': None} else: return stats # Stats provides features/base and resolution is bases/pixel, so # multiplying them yields features/pixel. - features_per_pixel = stats[ 'data' ][ 'max' ] * float( kwargs[ 'resolution' ] ) + features_per_pixel = stats['data']['max'] * float(kwargs['resolution']) # Use heuristic based on features/pixel and region size to determine whether to # return coverage data. When zoomed out and region is large, features/pixel # is determining factor. However, when sufficiently zoomed in and region is # small, coverage data is no longer provided. - if int( high ) - int( low ) > 50000 and features_per_pixel > 1000: - return indexer.get_data( chrom, low, high ) + if int(high) - int(low) > 50000 and features_per_pixel > 1000: + return indexer.get_data(chrom, low, high) # # Provide individual data points. # # Get data provider. - data_provider = data_provider_registry.get_data_provider( trans, original_dataset=dataset, source='data' ) + data_provider = data_provider_registry.get_data_provider(trans, original_dataset=dataset, source='data') # Allow max_vals top be data provider set if not passed if max_vals is None: @@ -216,36 +216,36 @@ def _data( self, trans, dataset, chrom, low, high, start_val=0, max_vals=None, * # Get reference sequence and mean depth for region; these is used by providers for aligned reads. region = None mean_depth = None - if isinstance( data_provider, (SamDataProvider, BamDataProvider ) ): + if isinstance(data_provider, (SamDataProvider, BamDataProvider)): # Get reference sequence. if dataset.dbkey: # FIXME: increase region 1M each way to provide sequence for # spliced/gapped reads. Probably should provide refseq object # directly to data provider. - region = self.app.genomes.reference( trans, dbkey=dataset.dbkey, chrom=chrom, - low=( max( 0, int( low ) - 1000000 ) ), - high=( int( high ) + 1000000 ) ) + region = self.app.genomes.reference(trans, dbkey=dataset.dbkey, chrom=chrom, + low=(max(0, int(low) - 1000000)), + high=(int(high) + 1000000)) # Get mean depth. if not indexer: - indexer = data_provider_registry.get_data_provider( trans, original_dataset=dataset, source='index' ) - stats = indexer.get_data( chrom, low, high, stats=True ) - mean_depth = stats[ 'data' ][ 'mean' ] + indexer = data_provider_registry.get_data_provider(trans, original_dataset=dataset, source='index') + stats = indexer.get_data(chrom, low, high, stats=True) + mean_depth = stats['data']['mean'] # Get and return data from data_provider. - result = data_provider.get_data( chrom, int( low ), int( high ), int( start_val ), int( max_vals ), - ref_seq=region, mean_depth=mean_depth, **kwargs ) - result.update( { 'dataset_type': data_provider.dataset_type, 'extra_info': extra_info } ) + result = data_provider.get_data(chrom, int(low), int(high), int(start_val), int(max_vals), + ref_seq=region, mean_depth=mean_depth, **kwargs) + result.update({'dataset_type': data_provider.dataset_type, 'extra_info': extra_info}) return result - def _raw_data( self, trans, dataset, provider=None, **kwargs ): + def _raw_data(self, trans, dataset, provider=None, **kwargs): """ Uses original (raw) dataset to return data. This method is useful when the dataset is not yet indexed and hence using data would be slow because indexes need to be created. """ # Dataset check. - msg = self.hda_manager.data_conversion_status( dataset ) + msg = self.hda_manager.data_conversion_status(dataset) if msg: return msg @@ -255,30 +255,30 @@ def _raw_data( self, trans, dataset, provider=None, **kwargs ): # pulling from the original providers if possible, then the new providers if provider: if provider in registry.dataset_type_name_to_data_provider: - data_provider = registry.dataset_type_name_to_data_provider[ provider ]( dataset ) + data_provider = registry.dataset_type_name_to_data_provider[provider](dataset) - elif dataset.datatype.has_dataprovider( provider ): - kwargs = dataset.datatype.dataproviders[ provider ].parse_query_string_settings( kwargs ) + elif dataset.datatype.has_dataprovider(provider): + kwargs = dataset.datatype.dataproviders[provider].parse_query_string_settings(kwargs) # use dictionary to allow more than the data itself to be returned (data totals, other meta, etc.) return { - 'data': list( dataset.datatype.dataprovider( dataset, provider, **kwargs ) ) + 'data': list(dataset.datatype.dataprovider(dataset, provider, **kwargs)) } else: - raise dataproviders.exceptions.NoProviderAvailable( dataset.datatype, provider ) + raise dataproviders.exceptions.NoProviderAvailable(dataset.datatype, provider) # no provider name: look up by datatype else: - data_provider = registry.get_data_provider( trans, raw=True, original_dataset=dataset ) + data_provider = registry.get_data_provider(trans, raw=True, original_dataset=dataset) # Return data. - data = data_provider.get_data( **kwargs ) + data = data_provider.get_data(**kwargs) return data @web.expose_api_raw_anonymous - def display( self, trans, history_content_id, history_id, - preview=False, filename=None, to_ext=None, raw=False, **kwd ): + def display(self, trans, history_content_id, history_id, + preview=False, filename=None, to_ext=None, raw=False, **kwd): """ GET /api/histories/{encoded_history_id}/contents/{encoded_content_id}/display Displays history content (dataset). @@ -287,56 +287,56 @@ def display( self, trans, history_content_id, history_id, some point in the future without warning. Generally, data should be processed by its datatype prior to display (the defult if raw is unspecified or explicitly false. """ - decoded_content_id = self.decode_id( history_content_id ) - raw = util.string_as_bool_or_none( raw ) + decoded_content_id = self.decode_id(history_content_id) + raw = util.string_as_bool_or_none(raw) rval = '' try: - hda = self.hda_manager.get_accessible( decoded_content_id, trans.user ) + hda = self.hda_manager.get_accessible(decoded_content_id, trans.user) if raw: if filename and filename != 'index': - file_path = trans.app.object_store.get_filename( hda.dataset, - extra_dir=( 'dataset_%s_files' % hda.dataset.id ), - alt_name=filename) + file_path = trans.app.object_store.get_filename(hda.dataset, + extra_dir=('dataset_%s_files' % hda.dataset.id), + alt_name=filename) else: file_path = hda.file_name - rval = open( file_path ) + rval = open(file_path) else: display_kwd = kwd.copy() if 'key' in display_kwd: del display_kwd["key"] - rval = hda.datatype.display_data( trans, hda, preview, filename, to_ext, **display_kwd ) + rval = hda.datatype.display_data(trans, hda, preview, filename, to_ext, **display_kwd) except Exception as exception: - log.error( "Error getting display data for dataset (%s) from history (%s): %s", - history_content_id, history_id, str( exception ), exc_info=True ) + log.error("Error getting display data for dataset (%s) from history (%s): %s", + history_content_id, history_id, str(exception), exc_info=True) trans.response.status = 500 - rval = ( "Could not get display data for dataset: " + str( exception ) ) + rval = ("Could not get display data for dataset: " + str(exception)) return rval @web.expose_api_raw_anonymous - def get_metadata_file( self, trans, history_content_id, history_id, metadata_file=None, **kwd ): - decoded_content_id = self.decode_id( history_content_id ) + def get_metadata_file(self, trans, history_content_id, history_id, metadata_file=None, **kwd): + decoded_content_id = self.decode_id(history_content_id) rval = '' try: - hda = self.hda_manager.get_accessible( decoded_content_id, trans.user ) + hda = self.hda_manager.get_accessible(decoded_content_id, trans.user) file_ext = hda.metadata.spec.get(metadata_file).get("file_ext", metadata_file) fname = ''.join(c in util.FILENAME_VALID_CHARS and c or '_' for c in hda.name)[0:150] trans.response.headers["Content-Type"] = "application/octet-stream" trans.response.headers["Content-Disposition"] = 'attachment; filename="Galaxy%s-[%s].%s"' % (hda.hid, fname, file_ext) return open(hda.metadata.get(metadata_file).file_name) except Exception as exception: - log.error( "Error getting metadata_file (%s) for dataset (%s) from history (%s): %s", - metadata_file, history_content_id, history_id, str( exception ), exc_info=True ) + log.error("Error getting metadata_file (%s) for dataset (%s) from history (%s): %s", + metadata_file, history_content_id, history_id, str(exception), exc_info=True) trans.response.status = 500 - rval = ( "Could not get display data for dataset: " + str( exception ) ) + rval = ("Could not get display data for dataset: " + str(exception)) return rval @web._future_expose_api_anonymous - def converted( self, trans, dataset_id, ext, **kwargs ): + def converted(self, trans, dataset_id, ext, **kwargs): """ converted( self, trans, dataset_id, ext, **kwargs ) * GET /api/datasets/{dataset_id}/converted/{ext} @@ -362,21 +362,21 @@ def converted( self, trans, dataset_id, ext, **kwargs ): :returns: dictionary containing detailed HDA information or (if `ext` is None) an extension->dataset_id map """ - decoded_id = self.decode_id( dataset_id ) - hda = self.hda_manager.get_accessible( decoded_id, trans.user ) + decoded_id = self.decode_id(dataset_id) + hda = self.hda_manager.get_accessible(decoded_id, trans.user) if ext: - converted = self._get_or_create_converted( trans, hda, ext, **kwargs ) - return self.hda_serializer.serialize_to_view( converted, - user=trans.user, trans=trans, **self._parse_serialization_params( kwargs, 'detailed' ) ) + converted = self._get_or_create_converted(trans, hda, ext, **kwargs) + return self.hda_serializer.serialize_to_view(converted, + user=trans.user, trans=trans, **self._parse_serialization_params(kwargs, 'detailed')) - return self.hda_serializer.serialize_converted_datasets( hda, 'converted' ) + return self.hda_serializer.serialize_converted_datasets(hda, 'converted') - def _get_or_create_converted( self, trans, original, target_ext, **kwargs ): + def _get_or_create_converted(self, trans, original, target_ext, **kwargs): try: - original.get_converted_dataset( trans, target_ext ) - converted = original.get_converted_files_by_type( target_ext ) + original.get_converted_dataset(trans, target_ext) + converted = original.get_converted_files_by_type(target_ext) return converted except model.NoConverterException: - exc_data = dict( source=original.ext, target=target_ext, available=original.get_converter_types().keys() ) - raise galaxy_exceptions.RequestParameterInvalidException( 'Conversion not possible', **exc_data ) + exc_data = dict(source=original.ext, target=target_ext, available=original.get_converter_types().keys()) + raise galaxy_exceptions.RequestParameterInvalidException('Conversion not possible', **exc_data) diff --git a/lib/galaxy/webapps/galaxy/api/datatypes.py b/lib/galaxy/webapps/galaxy/api/datatypes.py index ec0f6fab8ce0..0087f77c10b7 100644 --- a/lib/galaxy/webapps/galaxy/api/datatypes.py +++ b/lib/galaxy/webapps/galaxy/api/datatypes.py @@ -9,26 +9,26 @@ from galaxy.datatypes.data import Data import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class DatatypesController( BaseAPIController ): +class DatatypesController(BaseAPIController): @expose_api_anonymous_and_sessionless - def index( self, trans, **kwd ): + def index(self, trans, **kwd): """ GET /api/datatypes Return an object containing upload datatypes. """ datatypes_registry = self._datatypes_registry try: - extension_only = asbool( kwd.get( 'extension_only', True ) ) - upload_only = asbool( kwd.get( 'upload_only', True ) ) + extension_only = asbool(kwd.get('extension_only', True)) + upload_only = asbool(kwd.get('upload_only', True)) if extension_only: if upload_only: return datatypes_registry.upload_file_formats else: - return [ ext for ext in datatypes_registry.datatypes_by_extension ] + return [ext for ext in datatypes_registry.datatypes_by_extension] else: rval = [] for elem in datatypes_registry.datatype_elems: @@ -40,20 +40,20 @@ def index( self, trans, **kwd ): dictionary[key] = elem.get(key) extension = elem.get('extension') if extension in datatypes_registry.datatypes_by_extension: - composite_files = datatypes_registry.datatypes_by_extension[ extension ].composite_files + composite_files = datatypes_registry.datatypes_by_extension[extension].composite_files if composite_files: dictionary['composite_files'] = [_.dict() for _ in composite_files.itervalues()] rval.append(dictionary) return rval except Exception as exception: - log.error( 'could not get datatypes: %s', str( exception ), exc_info=True ) - if not isinstance( exception, exceptions.MessageException ): - raise exceptions.InternalServerError( str( exception ) ) + log.error('could not get datatypes: %s', str(exception), exc_info=True) + if not isinstance(exception, exceptions.MessageException): + raise exceptions.InternalServerError(str(exception)) else: raise @expose_api_anonymous_and_sessionless - def mapping( self, trans, **kwd ): + def mapping(self, trans, **kwd): ''' GET /api/datatypes/mapping Return a dictionary of class to class mappings. @@ -64,30 +64,30 @@ def mapping( self, trans, **kwd ): for k, v in self._datatypes_registry.datatypes_by_extension.iteritems(): c = v.__class__ ext_to_class_name[k] = c.__module__ + "." + c.__name__ - classes.append( c ) + classes.append(c) class_to_classes = dict() - def visit_bases( types, cls ): + def visit_bases(types, cls): for base in cls.__bases__: - if issubclass( base, Data ): - types.add( base.__module__ + "." + base.__name__ ) - visit_bases( types, base ) + if issubclass(base, Data): + types.add(base.__module__ + "." + base.__name__) + visit_bases(types, base) for c in classes: n = c.__module__ + "." + c.__name__ - types = set( [ n ] ) - visit_bases( types, c ) - class_to_classes[ n ] = dict( ( t, True ) for t in types ) - return dict( ext_to_class_name=ext_to_class_name, class_to_classes=class_to_classes ) + types = set([n]) + visit_bases(types, c) + class_to_classes[n] = dict((t, True) for t in types) + return dict(ext_to_class_name=ext_to_class_name, class_to_classes=class_to_classes) except Exception as exception: - log.error( 'could not get datatype mapping: %s', str( exception ), exc_info=True ) - if not isinstance( exception, exceptions.MessageException ): - raise exceptions.InternalServerError( str( exception ) ) + log.error('could not get datatype mapping: %s', str(exception), exc_info=True) + if not isinstance(exception, exceptions.MessageException): + raise exceptions.InternalServerError(str(exception)) else: raise @expose_api_anonymous_and_sessionless - def sniffers( self, trans, **kwd ): + def sniffers(self, trans, **kwd): ''' GET /api/datatypes/sniffers Return a list of sniffers. @@ -95,38 +95,38 @@ def sniffers( self, trans, **kwd ): try: rval = [] for sniffer_elem in self._datatypes_registry.sniffer_elems: - datatype = sniffer_elem.get( 'type' ) + datatype = sniffer_elem.get('type') if datatype is not None: - rval.append( datatype ) + rval.append(datatype) return rval except Exception as exception: - log.error( 'could not get datatypes: %s', str( exception ), exc_info=True ) - if not isinstance( exception, exceptions.MessageException ): - raise exceptions.InternalServerError( str( exception ) ) + log.error('could not get datatypes: %s', str(exception), exc_info=True) + if not isinstance(exception, exceptions.MessageException): + raise exceptions.InternalServerError(str(exception)) else: raise @expose_api_anonymous_and_sessionless - def converters( self, trans, **kwd ): + def converters(self, trans, **kwd): converters = [] for (source_type, targets) in self._datatypes_registry.datatype_converters.iteritems(): for target_type in targets: - converters.append( { + converters.append({ 'source': source_type, 'target': target_type, - 'tool_id': targets[ target_type ].id, - } ) + 'tool_id': targets[target_type].id, + }) return converters @expose_api_anonymous_and_sessionless - def edam_formats( self, trans, **kwds ): + def edam_formats(self, trans, **kwds): return self._datatypes_registry.edam_formats @expose_api_anonymous_and_sessionless - def edam_data( self, trans, **kwds ): + def edam_data(self, trans, **kwds): return self._datatypes_registry.edam_data @property - def _datatypes_registry( self ): + def _datatypes_registry(self): return self.app.datatypes_registry diff --git a/lib/galaxy/webapps/galaxy/api/extended_metadata.py b/lib/galaxy/webapps/galaxy/api/extended_metadata.py index 7c016bff4a15..b9c5e43772dd 100644 --- a/lib/galaxy/webapps/galaxy/api/extended_metadata.py +++ b/lib/galaxy/webapps/galaxy/api/extended_metadata.py @@ -7,22 +7,22 @@ from galaxy.web.base.controller import BaseAPIController, UsesLibraryMixinItems, UsesStoredWorkflowMixin, UsesExtendedMetadataMixin, HTTPNotImplemented -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class BaseExtendedMetadataController( BaseAPIController, UsesExtendedMetadataMixin, UsesLibraryMixinItems, UsesStoredWorkflowMixin ): +class BaseExtendedMetadataController(BaseAPIController, UsesExtendedMetadataMixin, UsesLibraryMixinItems, UsesStoredWorkflowMixin): @web.expose_api - def index( self, trans, **kwd ): + def index(self, trans, **kwd): idnum = kwd[self.exmeta_item_id] item = self._get_item_from_id(trans, idnum, check_writable=False) if item is not None: - ex_meta = self.get_item_extended_metadata_obj( trans, item ) + ex_meta = self.get_item_extended_metadata_obj(trans, item) if ex_meta is not None: return ex_meta.data @web.expose_api - def create( self, trans, payload, **kwd ): + def create(self, trans, payload, **kwd): idnum = kwd[self.exmeta_item_id] item = self._get_item_from_id(trans, idnum, check_writable=True) if item is not None: @@ -34,7 +34,7 @@ def create( self, trans, payload, **kwd ): self.set_item_extended_metadata_obj(trans, item, ex_obj) @web.expose_api - def delete( self, trans, **kwd ): + def delete(self, trans, **kwd): idnum = kwd[self.tagged_item_id] item = self._get_item_from_id(trans, idnum, check_writable=True) if item is not None: @@ -44,7 +44,7 @@ def delete( self, trans, **kwd ): self.delete_extended_metadata(trans, ex_obj) @web.expose_api - def undelete( self, trans, **kwd ): + def undelete(self, trans, **kwd): raise HTTPNotImplemented() @@ -54,12 +54,12 @@ class LibraryDatasetExtendMetadataController(BaseExtendedMetadataController): def _get_item_from_id(self, trans, idstr, check_writable=True): if check_writable: - item = self.get_library_dataset_dataset_association( trans, idstr) - if trans.app.security_agent.can_modify_library_item( trans.get_current_user_roles(), item ): + item = self.get_library_dataset_dataset_association(trans, idstr) + if trans.app.security_agent.can_modify_library_item(trans.get_current_user_roles(), item): return item else: - item = self.get_library_dataset_dataset_association( trans, idstr) - if trans.app.security_agent.can_access_library_item( trans.get_current_user_roles(), item, trans.user ): + item = self.get_library_dataset_dataset_association(trans, idstr) + if trans.app.security_agent.can_access_library_item(trans.get_current_user_roles(), item, trans.user): return item return None @@ -68,14 +68,14 @@ class HistoryDatasetExtendMetadataController(BaseExtendedMetadataController): controller_name = "history_dataset_extended_metadata" exmeta_item_id = "history_content_id" - def __init__( self, app ): - super( HistoryDatasetExtendMetadataController, self ).__init__( app ) - self.hda_manager = managers.hdas.HDAManager( app ) + def __init__(self, app): + super(HistoryDatasetExtendMetadataController, self).__init__(app) + self.hda_manager = managers.hdas.HDAManager(app) def _get_item_from_id(self, trans, idstr, check_writable=True): - decoded_idstr = self.decode_id( idstr ) + decoded_idstr = self.decode_id(idstr) if check_writable: - return self.hda_manager.get_owned( decoded_idstr, trans.user, current_history=trans.history ) + return self.hda_manager.get_owned(decoded_idstr, trans.user, current_history=trans.history) else: - hda = self.hda_manager.get_accessible( decoded_idstr, trans.user ) - return self.hda_manager.error_if_uploading( hda ) + hda = self.hda_manager.get_accessible(decoded_idstr, trans.user) + return self.hda_manager.error_if_uploading(hda) diff --git a/lib/galaxy/webapps/galaxy/api/folder_contents.py b/lib/galaxy/webapps/galaxy/api/folder_contents.py index 619dadda53f3..3ff8c2e0629b 100644 --- a/lib/galaxy/webapps/galaxy/api/folder_contents.py +++ b/lib/galaxy/webapps/galaxy/api/folder_contents.py @@ -10,21 +10,21 @@ from galaxy.web.base.controller import BaseAPIController, UsesLibraryMixin, UsesLibraryMixinItems import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class FolderContentsController( BaseAPIController, UsesLibraryMixin, UsesLibraryMixinItems ): +class FolderContentsController(BaseAPIController, UsesLibraryMixin, UsesLibraryMixinItems): """ Class controls retrieval, creation and updating of folder contents. """ - def __init__( self, app ): - super( FolderContentsController, self ).__init__( app ) + def __init__(self, app): + super(FolderContentsController, self).__init__(app) self.folder_manager = folders.FolderManager() - self.hda_manager = managers.hdas.HDAManager( app ) + self.hda_manager = managers.hdas.HDAManager(app) @expose_api_anonymous - def index( self, trans, folder_id, **kwd ): + def index(self, trans, folder_id, **kwd): """ GET /api/folders/{encoded_folder_id}/contents @@ -49,104 +49,102 @@ def index( self, trans, folder_id, **kwd ): InternalServerError """ is_admin = trans.user_is_admin() - deleted = kwd.get( 'include_deleted', 'missing' ) + deleted = kwd.get('include_deleted', 'missing') current_user_roles = trans.get_current_user_roles() try: - deleted = util.asbool( deleted ) + deleted = util.asbool(deleted) except ValueError: deleted = False - decoded_folder_id = self.folder_manager.cut_and_decode( trans, folder_id ) - folder = self.folder_manager.get( trans, decoded_folder_id ) + decoded_folder_id = self.folder_manager.cut_and_decode(trans, folder_id) + folder = self.folder_manager.get(trans, decoded_folder_id) # Special level of security on top of libraries. - if trans.app.security_agent.can_access_library( current_user_roles, folder.parent_library ) or is_admin: + if trans.app.security_agent.can_access_library(current_user_roles, folder.parent_library) or is_admin: pass else: if trans.user: - log.warning( "SECURITY: User (id: %s) without proper access rights is trying to load folder with ID of %s" % ( trans.user.id, decoded_folder_id ) ) + log.warning("SECURITY: User (id: %s) without proper access rights is trying to load folder with ID of %s" % (trans.user.id, decoded_folder_id)) else: - log.warning( "SECURITY: Anonymous user is trying to load restricted folder with ID of %s" % ( decoded_folder_id ) ) - raise exceptions.ObjectNotFound( 'Folder with the id provided ( %s ) was not found' % str( folder_id ) ) + log.warning("SECURITY: Anonymous user is trying to load restricted folder with ID of %s" % (decoded_folder_id)) + raise exceptions.ObjectNotFound('Folder with the id provided ( %s ) was not found' % str(folder_id)) folder_contents = [] update_time = '' create_time = '' # Go through every accessible item (folders, datasets) in the folder and include its metadata. - for content_item in self._load_folder_contents( trans, folder, deleted ): + for content_item in self._load_folder_contents(trans, folder, deleted): return_item = {} - encoded_id = trans.security.encode_id( content_item.id ) - update_time = content_item.update_time.strftime( "%Y-%m-%d %I:%M %p" ) - create_time = content_item.create_time.strftime( "%Y-%m-%d %I:%M %p" ) + encoded_id = trans.security.encode_id(content_item.id) + update_time = content_item.update_time.strftime("%Y-%m-%d %I:%M %p") + create_time = content_item.create_time.strftime("%Y-%m-%d %I:%M %p") if content_item.api_type == 'folder': encoded_id = 'F' + encoded_id - can_modify = is_admin or ( trans.user and trans.app.security_agent.can_modify_library_item( current_user_roles, folder ) ) - can_manage = is_admin or ( trans.user and trans.app.security_agent.can_manage_library_item( current_user_roles, folder ) ) - return_item.update( dict( can_modify=can_modify, can_manage=can_manage ) ) + can_modify = is_admin or (trans.user and trans.app.security_agent.can_modify_library_item(current_user_roles, folder)) + can_manage = is_admin or (trans.user and trans.app.security_agent.can_manage_library_item(current_user_roles, folder)) + return_item.update(dict(can_modify=can_modify, can_manage=can_manage)) if content_item.description: - return_item.update( dict( description=content_item.description ) ) + return_item.update(dict(description=content_item.description)) if content_item.api_type == 'file': # Is the dataset public or private? # When both are False the dataset is 'restricted' # Access rights are checked on the dataset level, not on the ld or ldda level to maintain consistency - is_unrestricted = trans.app.security_agent.dataset_is_public( content_item.library_dataset_dataset_association.dataset ) - if trans.user and trans.app.security_agent.dataset_is_private_to_user( trans, content_item ): + is_unrestricted = trans.app.security_agent.dataset_is_public(content_item.library_dataset_dataset_association.dataset) + if trans.user and trans.app.security_agent.dataset_is_private_to_user(trans, content_item): is_private = True else: is_private = False # Can user manage the permissions on the dataset? - can_manage = is_admin or (trans.user and trans.app.security_agent.can_manage_dataset( current_user_roles, content_item.library_dataset_dataset_association.dataset ) ) + can_manage = is_admin or (trans.user and trans.app.security_agent.can_manage_dataset(current_user_roles, content_item.library_dataset_dataset_association.dataset)) - nice_size = util.nice_size( int( content_item.library_dataset_dataset_association.get_size() ) ) + nice_size = util.nice_size(int(content_item.library_dataset_dataset_association.get_size())) library_dataset_dict = content_item.to_dict() - return_item.update( dict( file_ext=library_dataset_dict[ 'file_ext' ], - date_uploaded=library_dataset_dict[ 'date_uploaded' ], - is_unrestricted=is_unrestricted, - is_private=is_private, - can_manage=can_manage, - file_size=nice_size - ) ) + return_item.update(dict(file_ext=library_dataset_dict['file_ext'], + date_uploaded=library_dataset_dict['date_uploaded'], + is_unrestricted=is_unrestricted, + is_private=is_private, + can_manage=can_manage, + file_size=nice_size)) if content_item.library_dataset_dataset_association.message: - return_item.update( dict( message=content_item.library_dataset_dataset_association.message ) ) + return_item.update(dict(message=content_item.library_dataset_dataset_association.message)) # For every item include the default metadata - return_item.update( dict( id=encoded_id, - type=content_item.api_type, - name=content_item.name, - update_time=update_time, - create_time=create_time, - deleted=content_item.deleted - ) ) - folder_contents.append( return_item ) + return_item.update(dict(id=encoded_id, + type=content_item.api_type, + name=content_item.name, + update_time=update_time, + create_time=create_time, + deleted=content_item.deleted)) + folder_contents.append(return_item) # Return the reversed path so it starts with the library node. - full_path = self.build_path( trans, folder )[ ::-1 ] + full_path = self.build_path(trans, folder)[::-1] # Check whether user can add items to the current folder - can_add_library_item = is_admin or trans.app.security_agent.can_add_library_item( current_user_roles, folder ) + can_add_library_item = is_admin or trans.app.security_agent.can_add_library_item(current_user_roles, folder) # Check whether user can modify the current folder - can_modify_folder = is_admin or trans.app.security_agent.can_modify_library_item( current_user_roles, folder ) + can_modify_folder = is_admin or trans.app.security_agent.can_modify_library_item(current_user_roles, folder) parent_library_id = None if folder.parent_library is not None: - parent_library_id = trans.security.encode_id( folder.parent_library.id ) - - metadata = dict( full_path=full_path, - can_add_library_item=can_add_library_item, - can_modify_folder=can_modify_folder, - folder_name=folder.name, - folder_description=folder.description, - parent_library_id=parent_library_id ) - folder_container = dict( metadata=metadata, folder_contents=folder_contents ) + parent_library_id = trans.security.encode_id(folder.parent_library.id) + + metadata = dict(full_path=full_path, + can_add_library_item=can_add_library_item, + can_modify_folder=can_modify_folder, + folder_name=folder.name, + folder_description=folder.description, + parent_library_id=parent_library_id) + folder_container = dict(metadata=metadata, folder_contents=folder_contents) return folder_container - def build_path( self, trans, folder ): + def build_path(self, trans, folder): """ Search the path upwards recursively and load the whole route of names and ids for breadcrumb building purposes. @@ -160,15 +158,15 @@ def build_path( self, trans, folder ): path_to_root = [] # We are almost in root if folder.parent_id is None: - path_to_root.append( ( 'F' + trans.security.encode_id( folder.id ), folder.name ) ) + path_to_root.append(('F' + trans.security.encode_id(folder.id), folder.name)) else: # We add the current folder and traverse up one folder. - path_to_root.append( ( 'F' + trans.security.encode_id( folder.id ), folder.name ) ) - upper_folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( folder.parent_id ) - path_to_root.extend( self.build_path( trans, upper_folder ) ) + path_to_root.append(('F' + trans.security.encode_id(folder.id), folder.name)) + upper_folder = trans.sa_session.query(trans.app.model.LibraryFolder).get(folder.parent_id) + path_to_root.extend(self.build_path(trans, upper_folder)) return path_to_root - def _load_folder_contents( self, trans, folder, include_deleted ): + def _load_folder_contents(self, trans, folder, include_deleted): """ Loads all contents of the folder (folders and data sets) but only in the first level. Include deleted if the flag is set and if the @@ -193,18 +191,18 @@ def _load_folder_contents( self, trans, folder, include_deleted ): if is_admin: # Admins can see all deleted folders. subfolder.api_type = 'folder' - content_items.append( subfolder ) + content_items.append(subfolder) else: # Users with MODIFY permissions can see deleted folders. - can_modify = trans.app.security_agent.can_modify_library_item( current_user_roles, subfolder ) + can_modify = trans.app.security_agent.can_modify_library_item(current_user_roles, subfolder) if can_modify: subfolder.api_type = 'folder' - content_items.append( subfolder ) + content_items.append(subfolder) else: # Undeleted folders are non-restricted for now. The contents are not. # TODO decide on restrictions subfolder.api_type = 'folder' - content_items.append( subfolder ) + content_items.append(subfolder) # if is_admin: # subfolder.api_type = 'folder' # content_items.append( subfolder ) @@ -220,27 +218,27 @@ def _load_folder_contents( self, trans, folder, include_deleted ): if is_admin: # Admins can see all deleted datasets. dataset.api_type = 'file' - content_items.append( dataset ) + content_items.append(dataset) else: # Users with MODIFY permissions on the item can see the deleted item. - can_modify = trans.app.security_agent.can_modify_library_item( current_user_roles, dataset ) + can_modify = trans.app.security_agent.can_modify_library_item(current_user_roles, dataset) if can_modify: dataset.api_type = 'file' - content_items.append( dataset ) + content_items.append(dataset) else: if is_admin: dataset.api_type = 'file' - content_items.append( dataset ) + content_items.append(dataset) else: - can_access = trans.app.security_agent.can_access_dataset( current_user_roles, dataset.library_dataset_dataset_association.dataset ) + can_access = trans.app.security_agent.can_access_dataset(current_user_roles, dataset.library_dataset_dataset_association.dataset) if can_access: dataset.api_type = 'file' - content_items.append( dataset ) + content_items.append(dataset) return content_items @expose_api - def create( self, trans, encoded_folder_id, payload, **kwd ): + def create(self, trans, encoded_folder_id, payload, **kwd): """ * POST /api/folders/{encoded_id}/contents create a new library file from an HDA @@ -266,28 +264,28 @@ def create( self, trans, encoded_folder_id, payload, **kwd ): InsufficientPermissionsException, ItemAccessibilityException, InternalServerError """ - encoded_folder_id_16 = self.__decode_library_content_id( trans, encoded_folder_id ) - from_hda_id = payload.pop( 'from_hda_id', None ) - from_hdca_id = payload.pop( 'from_hdca_id', None ) - ldda_message = payload.pop( 'ldda_message', '' ) + encoded_folder_id_16 = self.__decode_library_content_id(trans, encoded_folder_id) + from_hda_id = payload.pop('from_hda_id', None) + from_hdca_id = payload.pop('from_hdca_id', None) + ldda_message = payload.pop('ldda_message', '') if ldda_message: - ldda_message = util.sanitize_html.sanitize_html( ldda_message, 'utf-8' ) + ldda_message = util.sanitize_html.sanitize_html(ldda_message, 'utf-8') try: if from_hda_id: - decoded_hda_id = self.decode_id( from_hda_id ) - return self._copy_hda_to_library_folder( trans, self.hda_manager, decoded_hda_id, encoded_folder_id_16, ldda_message ) + decoded_hda_id = self.decode_id(from_hda_id) + return self._copy_hda_to_library_folder(trans, self.hda_manager, decoded_hda_id, encoded_folder_id_16, ldda_message) if from_hdca_id: - decoded_hdca_id = self.decode_id( from_hdca_id ) - return self._copy_hdca_to_library_folder( trans, self.hda_manager, decoded_hdca_id, encoded_folder_id_16, ldda_message ) + decoded_hdca_id = self.decode_id(from_hdca_id) + return self._copy_hdca_to_library_folder(trans, self.hda_manager, decoded_hdca_id, encoded_folder_id_16, ldda_message) except Exception as exc: # TODO handle exceptions better within the mixins - if 'not accessible to the current user' in str( exc ) or 'You are not allowed to access this dataset' in str( exc ): - raise exceptions.ItemAccessibilityException( 'You do not have access to the requested item' ) + if 'not accessible to the current user' in str(exc) or 'You are not allowed to access this dataset' in str(exc): + raise exceptions.ItemAccessibilityException('You do not have access to the requested item') else: - log.exception( exc ) + log.exception(exc) raise exc - def __decode_library_content_id( self, trans, encoded_folder_id ): + def __decode_library_content_id(self, trans, encoded_folder_id): """ Identifies whether the id provided is properly encoded LibraryFolder. @@ -300,21 +298,21 @@ def __decode_library_content_id( self, trans, encoded_folder_id ): :raises: MalformedId """ - if ( ( len( encoded_folder_id ) % 16 == 1 ) and encoded_folder_id.startswith( 'F' ) ): - return encoded_folder_id[ 1: ] + if ((len(encoded_folder_id) % 16 == 1) and encoded_folder_id.startswith('F')): + return encoded_folder_id[1:] else: - raise exceptions.MalformedId( 'Malformed folder id ( %s ) specified, unable to decode.' % str( encoded_folder_id ) ) + raise exceptions.MalformedId('Malformed folder id ( %s ) specified, unable to decode.' % str(encoded_folder_id)) @expose_api - def show( self, trans, id, library_id, **kwd ): + def show(self, trans, id, library_id, **kwd): """ GET /api/folders/{encoded_folder_id}/ """ - raise exceptions.NotImplemented( 'Showing the library folder content is not implemented here.' ) + raise exceptions.NotImplemented('Showing the library folder content is not implemented here.') @expose_api - def update( self, trans, id, library_id, payload, **kwd ): + def update(self, trans, id, library_id, payload, **kwd): """ PUT /api/folders/{encoded_folder_id}/contents """ - raise exceptions.NotImplemented( 'Updating the library folder content is not implemented here.' ) + raise exceptions.NotImplemented('Updating the library folder content is not implemented here.') diff --git a/lib/galaxy/webapps/galaxy/api/folders.py b/lib/galaxy/webapps/galaxy/api/folders.py index 21a019c36607..e638d7119be3 100644 --- a/lib/galaxy/webapps/galaxy/api/folders.py +++ b/lib/galaxy/webapps/galaxy/api/folders.py @@ -8,27 +8,27 @@ from galaxy.web.base.controller import BaseAPIController, UsesLibraryMixin, UsesLibraryMixinItems import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class FoldersController( BaseAPIController, UsesLibraryMixin, UsesLibraryMixinItems ): +class FoldersController(BaseAPIController, UsesLibraryMixin, UsesLibraryMixinItems): - def __init__( self, app ): - super( FoldersController, self ).__init__( app ) + def __init__(self, app): + super(FoldersController, self).__init__(app) self.folder_manager = folders.FolderManager() - self.role_manager = roles.RoleManager( app ) + self.role_manager = roles.RoleManager(app) @expose_api - def index( self, trans, **kwd ): + def index(self, trans, **kwd): """ *GET /api/folders/ This would normally display a list of folders. However, that would be across multiple libraries, so it's not implemented. """ - raise exceptions.NotImplemented( 'Listing all accessible library folders is not implemented.' ) + raise exceptions.NotImplemented('Listing all accessible library folders is not implemented.') @expose_api - def show( self, trans, id, **kwd ): + def show(self, trans, id, **kwd): """ show( self, trans, id, **kwd ) *GET /api/folders/{encoded_folder_id} @@ -41,13 +41,13 @@ def show( self, trans, id, **kwd ): :returns: dictionary including details of the folder :rtype: dict """ - folder_id = self.folder_manager.cut_and_decode( trans, id ) - folder = self.folder_manager.get( trans, folder_id, check_manageable=False, check_accessible=True ) - return_dict = self.folder_manager.get_folder_dict( trans, folder ) + folder_id = self.folder_manager.cut_and_decode(trans, id) + folder = self.folder_manager.get(trans, folder_id, check_manageable=False, check_accessible=True) + return_dict = self.folder_manager.get_folder_dict(trans, folder) return return_dict @expose_api - def create( self, trans, encoded_parent_folder_id, payload=None, **kwd ): + def create(self, trans, encoded_parent_folder_id, payload=None, **kwd): """ *POST /api/folders/{encoded_parent_folder_id} Create a new folder object underneath the one specified in the parameters. @@ -66,17 +66,17 @@ def create( self, trans, encoded_parent_folder_id, payload=None, **kwd ): """ if payload: kwd.update(payload) - name = kwd.get( 'name', None ) + name = kwd.get('name', None) if name is None: - raise exceptions.RequestParameterMissingException( "Missing required parameter 'name'." ) - description = kwd.get( 'description', '' ) - decoded_parent_folder_id = self.folder_manager.cut_and_decode( trans, encoded_parent_folder_id ) - parent_folder = self.folder_manager.get( trans, decoded_parent_folder_id ) - new_folder = self.folder_manager.create( trans, parent_folder.id, name, description ) - return self.folder_manager.get_folder_dict( trans, new_folder ) + raise exceptions.RequestParameterMissingException("Missing required parameter 'name'.") + description = kwd.get('description', '') + decoded_parent_folder_id = self.folder_manager.cut_and_decode(trans, encoded_parent_folder_id) + parent_folder = self.folder_manager.get(trans, decoded_parent_folder_id) + new_folder = self.folder_manager.create(trans, parent_folder.id, name, description) + return self.folder_manager.get_folder_dict(trans, new_folder) @expose_api - def get_permissions( self, trans, encoded_folder_id, **kwd ): + def get_permissions(self, trans, encoded_folder_id, **kwd): """ * GET /api/folders/{id}/permissions @@ -95,39 +95,39 @@ def get_permissions( self, trans, encoded_folder_id, **kwd ): """ current_user_roles = trans.get_current_user_roles() is_admin = trans.user_is_admin() - decoded_folder_id = self.folder_manager.cut_and_decode( trans, encoded_folder_id ) - folder = self.folder_manager.get( trans, decoded_folder_id ) + decoded_folder_id = self.folder_manager.cut_and_decode(trans, encoded_folder_id) + folder = self.folder_manager.get(trans, decoded_folder_id) - if not ( is_admin or trans.app.security_agent.can_manage_library_item( current_user_roles, folder ) ): - raise exceptions.InsufficientPermissionsException( 'You do not have proper permission to access permissions of this folder.' ) + if not (is_admin or trans.app.security_agent.can_manage_library_item(current_user_roles, folder)): + raise exceptions.InsufficientPermissionsException('You do not have proper permission to access permissions of this folder.') - scope = kwd.get( 'scope', None ) + scope = kwd.get('scope', None) if scope == 'current' or scope is None: - return self.folder_manager.get_current_roles( trans, folder ) + return self.folder_manager.get_current_roles(trans, folder) # Return roles that are available to select. elif scope == 'available': - page = kwd.get( 'page', None ) + page = kwd.get('page', None) if page is not None: - page = int( page ) + page = int(page) else: page = 1 - page_limit = kwd.get( 'page_limit', None ) + page_limit = kwd.get('page_limit', None) if page_limit is not None: - page_limit = int( page_limit ) + page_limit = int(page_limit) else: page_limit = 10 - query = kwd.get( 'q', None ) - roles, total_roles = trans.app.security_agent.get_valid_roles( trans, folder, query, page, page_limit ) + query = kwd.get('q', None) + roles, total_roles = trans.app.security_agent.get_valid_roles(trans, folder, query, page, page_limit) return_roles = [] for role in roles: - role_id = trans.security.encode_id( role.id ) - return_roles.append( dict( id=role_id, name=role.name, type=role.type ) ) - return dict( roles=return_roles, page=page, page_limit=page_limit, total=total_roles ) + role_id = trans.security.encode_id(role.id) + return_roles.append(dict(id=role_id, name=role.name, type=role.type)) + return dict(roles=return_roles, page=page, page_limit=page_limit, total=total_roles) else: - raise exceptions.RequestParameterInvalidException( "The value of 'scope' parameter is invalid. Alllowed values: current, available" ) + raise exceptions.RequestParameterInvalidException("The value of 'scope' parameter is invalid. Alllowed values: current, available") @expose_api - def set_permissions( self, trans, encoded_folder_id, payload=None, **kwd ): + def set_permissions(self, trans, encoded_folder_id, payload=None, **kwd): """ *POST /api/folders/{encoded_folder_id}/permissions Set permissions of the given folder to the given role ids. @@ -152,74 +152,74 @@ def set_permissions( self, trans, encoded_folder_id, payload=None, **kwd ): kwd.update(payload) is_admin = trans.user_is_admin() current_user_roles = trans.get_current_user_roles() - decoded_folder_id = self.folder_manager.cut_and_decode( trans, encoded_folder_id ) - folder = self.folder_manager.get( trans, decoded_folder_id ) - if not ( is_admin or trans.app.security_agent.can_manage_library_item( current_user_roles, folder ) ): - raise exceptions.InsufficientPermissionsException( 'You do not have proper permission to modify permissions of this folder.' ) + decoded_folder_id = self.folder_manager.cut_and_decode(trans, encoded_folder_id) + folder = self.folder_manager.get(trans, decoded_folder_id) + if not (is_admin or trans.app.security_agent.can_manage_library_item(current_user_roles, folder)): + raise exceptions.InsufficientPermissionsException('You do not have proper permission to modify permissions of this folder.') - new_add_roles_ids = util.listify( kwd.get( 'add_ids[]', None ) ) - new_manage_roles_ids = util.listify( kwd.get( 'manage_ids[]', None ) ) - new_modify_roles_ids = util.listify( kwd.get( 'modify_ids[]', None ) ) + new_add_roles_ids = util.listify(kwd.get('add_ids[]', None)) + new_manage_roles_ids = util.listify(kwd.get('manage_ids[]', None)) + new_modify_roles_ids = util.listify(kwd.get('modify_ids[]', None)) - action = kwd.get( 'action', None ) + action = kwd.get('action', None) if action is None: - raise exceptions.RequestParameterMissingException( 'The mandatory parameter "action" is missing.' ) + raise exceptions.RequestParameterMissingException('The mandatory parameter "action" is missing.') elif action == 'set_permissions': # ADD TO LIBRARY ROLES valid_add_roles = [] invalid_add_roles_names = [] for role_id in new_add_roles_ids: - role = self.role_manager.get( trans, self.__decode_id( trans, role_id, 'role' ) ) + role = self.role_manager.get(trans, self.__decode_id(trans, role_id, 'role')) # Check whether role is in the set of allowed roles - valid_roles, total_roles = trans.app.security_agent.get_valid_roles( trans, folder ) + valid_roles, total_roles = trans.app.security_agent.get_valid_roles(trans, folder) if role in valid_roles: - valid_add_roles.append( role ) + valid_add_roles.append(role) else: - invalid_add_roles_names.append( role_id ) - if len( invalid_add_roles_names ) > 0: - log.warning( "The following roles could not be added to the add library item permission: " + str( invalid_add_roles_names ) ) + invalid_add_roles_names.append(role_id) + if len(invalid_add_roles_names) > 0: + log.warning("The following roles could not be added to the add library item permission: " + str(invalid_add_roles_names)) # MANAGE FOLDER ROLES valid_manage_roles = [] invalid_manage_roles_names = [] for role_id in new_manage_roles_ids: - role = self.role_manager.get( trans, self.__decode_id( trans, role_id, 'role' ) ) + role = self.role_manager.get(trans, self.__decode_id(trans, role_id, 'role')) # Check whether role is in the set of allowed roles - valid_roles, total_roles = trans.app.security_agent.get_valid_roles( trans, folder ) + valid_roles, total_roles = trans.app.security_agent.get_valid_roles(trans, folder) if role in valid_roles: - valid_manage_roles.append( role ) + valid_manage_roles.append(role) else: - invalid_manage_roles_names.append( role_id ) - if len( invalid_manage_roles_names ) > 0: - log.warning( "The following roles could not be added to the manage folder permission: " + str( invalid_manage_roles_names ) ) + invalid_manage_roles_names.append(role_id) + if len(invalid_manage_roles_names) > 0: + log.warning("The following roles could not be added to the manage folder permission: " + str(invalid_manage_roles_names)) # MODIFY FOLDER ROLES valid_modify_roles = [] invalid_modify_roles_names = [] for role_id in new_modify_roles_ids: - role = self.role_manager.get( trans, self.__decode_id( trans, role_id, 'role' ) ) + role = self.role_manager.get(trans, self.__decode_id(trans, role_id, 'role')) # Check whether role is in the set of allowed roles - valid_roles, total_roles = trans.app.security_agent.get_valid_roles( trans, folder ) + valid_roles, total_roles = trans.app.security_agent.get_valid_roles(trans, folder) if role in valid_roles: - valid_modify_roles.append( role ) + valid_modify_roles.append(role) else: - invalid_modify_roles_names.append( role_id ) - if len( invalid_modify_roles_names ) > 0: - log.warning( "The following roles could not be added to the modify folder permission: " + str( invalid_modify_roles_names ) ) + invalid_modify_roles_names.append(role_id) + if len(invalid_modify_roles_names) > 0: + log.warning("The following roles could not be added to the modify folder permission: " + str(invalid_modify_roles_names)) - permissions = { trans.app.security_agent.permitted_actions.LIBRARY_ADD: valid_add_roles } - permissions.update( { trans.app.security_agent.permitted_actions.LIBRARY_MANAGE: valid_manage_roles } ) - permissions.update( { trans.app.security_agent.permitted_actions.LIBRARY_MODIFY: valid_modify_roles } ) + permissions = {trans.app.security_agent.permitted_actions.LIBRARY_ADD: valid_add_roles} + permissions.update({trans.app.security_agent.permitted_actions.LIBRARY_MANAGE: valid_manage_roles}) + permissions.update({trans.app.security_agent.permitted_actions.LIBRARY_MODIFY: valid_modify_roles}) - trans.app.security_agent.set_all_library_permissions( trans, folder, permissions ) + trans.app.security_agent.set_all_library_permissions(trans, folder, permissions) else: - raise exceptions.RequestParameterInvalidException( 'The mandatory parameter "action" has an invalid value.' - 'Allowed values are: "set_permissions"' ) - return self.folder_manager.get_current_roles( trans, folder ) + raise exceptions.RequestParameterInvalidException('The mandatory parameter "action" has an invalid value.' + 'Allowed values are: "set_permissions"') + return self.folder_manager.get_current_roles(trans, folder) @expose_api - def delete( self, trans, encoded_folder_id, **kwd ): + def delete(self, trans, encoded_folder_id, **kwd): """ delete( self, trans, encoded_folder_id, **kwd ) * DELETE /api/folders/{encoded_folder_id} @@ -237,14 +237,14 @@ def delete( self, trans, encoded_folder_id, **kwd ): :rtype: dictionary """ - folder = self.folder_manager.get( trans, self.folder_manager.cut_and_decode( trans, encoded_folder_id ), True ) - undelete = util.string_as_bool( kwd.get( 'undelete', False ) ) - folder = self.folder_manager.delete( trans, folder, undelete ) - folder_dict = self.folder_manager.get_folder_dict( trans, folder ) + folder = self.folder_manager.get(trans, self.folder_manager.cut_and_decode(trans, encoded_folder_id), True) + undelete = util.string_as_bool(kwd.get('undelete', False)) + folder = self.folder_manager.delete(trans, folder, undelete) + folder_dict = self.folder_manager.get_folder_dict(trans, folder) return folder_dict @expose_api - def update( self, trans, encoded_folder_id, payload=None, **kwd ): + def update(self, trans, encoded_folder_id, payload=None, **kwd): """ * PATCH /api/folders/{encoded_folder_id} Update the folder defined by an ``encoded_folder_id`` with the data in the payload. @@ -261,19 +261,19 @@ def update( self, trans, encoded_folder_id, payload=None, **kwd ): :rtype: dict :raises: RequestParameterMissingException """ - decoded_folder_id = self.folder_manager.cut_and_decode( trans, encoded_folder_id ) - folder = self.folder_manager.get( trans, decoded_folder_id ) + decoded_folder_id = self.folder_manager.cut_and_decode(trans, encoded_folder_id) + folder = self.folder_manager.get(trans, decoded_folder_id) if payload: kwd.update(payload) - name = kwd.get( 'name', None ) + name = kwd.get('name', None) if not name: - raise exceptions.RequestParameterMissingException( "Parameter 'name' of library folder is required. You cannot remove it." ) - description = kwd.get( 'description', None ) - updated_folder = self.folder_manager.update( trans, folder, name, description ) - folder_dict = self.folder_manager.get_folder_dict( trans, updated_folder ) + raise exceptions.RequestParameterMissingException("Parameter 'name' of library folder is required. You cannot remove it.") + description = kwd.get('description', None) + updated_folder = self.folder_manager.update(trans, folder, name, description) + folder_dict = self.folder_manager.get_folder_dict(trans, updated_folder) return folder_dict - def __decode_id( self, trans, encoded_id, object_name=None ): + def __decode_id(self, trans, encoded_id, object_name=None): """ Try to decode the id. @@ -281,8 +281,8 @@ def __decode_id( self, trans, encoded_id, object_name=None ): :type object_name: str """ try: - return trans.security.decode_id( encoded_id ) + return trans.security.decode_id(encoded_id) except TypeError: - raise exceptions.MalformedId( 'Malformed %s id specified, unable to decode.' % object_name if object_name is not None else '' ) + raise exceptions.MalformedId('Malformed %s id specified, unable to decode.' % object_name if object_name is not None else '') except ValueError: - raise exceptions.MalformedId( 'Wrong %s id specified, unable to decode.' % object_name if object_name is not None else '' ) + raise exceptions.MalformedId('Wrong %s id specified, unable to decode.' % object_name if object_name is not None else '') diff --git a/lib/galaxy/webapps/galaxy/api/forms.py b/lib/galaxy/webapps/galaxy/api/forms.py index 17295d2c8464..66ba15bcc8af 100644 --- a/lib/galaxy/webapps/galaxy/api/forms.py +++ b/lib/galaxy/webapps/galaxy/api/forms.py @@ -7,13 +7,13 @@ from galaxy.forms.forms import form_factory from xml.etree.ElementTree import XML -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class FormDefinitionAPIController( BaseAPIController ): +class FormDefinitionAPIController(BaseAPIController): @web.expose_api - def index( self, trans, **kwd ): + def index(self, trans, **kwd): """ GET /api/forms Displays a collection (list) of forms. @@ -21,39 +21,39 @@ def index( self, trans, **kwd ): if not trans.user_is_admin(): trans.response.status = 403 return "You are not authorized to view the list of forms." - query = trans.sa_session.query( trans.app.model.FormDefinition ) + query = trans.sa_session.query(trans.app.model.FormDefinition) rval = [] for form_definition in query: - item = form_definition.to_dict( value_mapper={ 'id': trans.security.encode_id, 'form_definition_current_id': trans.security.encode_id } ) - item['url'] = url_for( 'form', id=trans.security.encode_id( form_definition.id ) ) - rval.append( item ) + item = form_definition.to_dict(value_mapper={'id': trans.security.encode_id, 'form_definition_current_id': trans.security.encode_id}) + item['url'] = url_for('form', id=trans.security.encode_id(form_definition.id)) + rval.append(item) return rval @web.expose_api - def show( self, trans, id, **kwd ): + def show(self, trans, id, **kwd): """ GET /api/forms/{encoded_form_id} Displays information about a form. """ form_definition_id = id try: - decoded_form_definition_id = trans.security.decode_id( form_definition_id ) + decoded_form_definition_id = trans.security.decode_id(form_definition_id) except TypeError: trans.response.status = 400 - return "Malformed form definition id ( %s ) specified, unable to decode." % str( form_definition_id ) + return "Malformed form definition id ( %s ) specified, unable to decode." % str(form_definition_id) try: - form_definition = trans.sa_session.query( trans.app.model.FormDefinition ).get( decoded_form_definition_id ) + form_definition = trans.sa_session.query(trans.app.model.FormDefinition).get(decoded_form_definition_id) except: form_definition = None if not form_definition or not trans.user_is_admin(): trans.response.status = 400 - return "Invalid form definition id ( %s ) specified." % str( form_definition_id ) - item = form_definition.to_dict( view='element', value_mapper={ 'id': trans.security.encode_id, 'form_definition_current_id': trans.security.encode_id } ) - item['url'] = url_for( 'form', id=form_definition_id ) + return "Invalid form definition id ( %s ) specified." % str(form_definition_id) + item = form_definition.to_dict(view='element', value_mapper={'id': trans.security.encode_id, 'form_definition_current_id': trans.security.encode_id}) + item['url'] = url_for('form', id=form_definition_id) return item @web.expose_api - def create( self, trans, payload, **kwd ): + def create(self, trans, payload, **kwd): """ POST /api/forms Creates a new form. @@ -61,15 +61,15 @@ def create( self, trans, payload, **kwd ): if not trans.user_is_admin(): trans.response.status = 403 return "You are not authorized to create a new form." - xml_text = payload.get( 'xml_text', None ) + xml_text = payload.get('xml_text', None) if xml_text is None: trans.response.status = 400 return "Missing required parameter 'xml_text'." # enhance to allow creating from more than just xml - form_definition = form_factory.from_elem( XML( xml_text ) ) - trans.sa_session.add( form_definition ) + form_definition = form_factory.from_elem(XML(xml_text)) + trans.sa_session.add(form_definition) trans.sa_session.flush() - encoded_id = trans.security.encode_id( form_definition.id ) - item = form_definition.to_dict( view='element', value_mapper={ 'id': trans.security.encode_id, 'form_definition_current_id': trans.security.encode_id } ) - item['url'] = url_for( 'form', id=encoded_id ) - return [ item ] + encoded_id = trans.security.encode_id(form_definition.id) + item = form_definition.to_dict(view='element', value_mapper={'id': trans.security.encode_id, 'form_definition_current_id': trans.security.encode_id}) + item['url'] = url_for('form', id=encoded_id) + return [item] diff --git a/lib/galaxy/webapps/galaxy/api/genomes.py b/lib/galaxy/webapps/galaxy/api/genomes.py index ba525dbb49a0..ce8e928b9b9b 100644 --- a/lib/galaxy/webapps/galaxy/api/genomes.py +++ b/lib/galaxy/webapps/galaxy/api/genomes.py @@ -3,28 +3,28 @@ from galaxy.web.framework.helpers import is_true -def get_id( base, format ): +def get_id(base, format): if format: - return "%s.%s" % ( base, format ) + return "%s.%s" % (base, format) else: return base -class GenomesController( BaseAPIController ): +class GenomesController(BaseAPIController): """ RESTful controller for interactions with genome data. """ @web.expose_api_anonymous - def index( self, trans, **kwd ): + def index(self, trans, **kwd): """ GET /api/genomes: returns a list of installed genomes """ - return self.app.genomes.get_dbkeys( trans, **kwd ) + return self.app.genomes.get_dbkeys(trans, **kwd) @web.json - def show( self, trans, id, num=None, chrom=None, low=None, high=None, **kwd ): + def show(self, trans, id, num=None, chrom=None, low=None, high=None, **kwd): """ GET /api/genomes/{id} @@ -32,16 +32,16 @@ def show( self, trans, id, num=None, chrom=None, low=None, high=None, **kwd ): """ # Process kwds. - id = get_id( id, kwd.get( 'format', None ) ) - reference = is_true( kwd.get( 'reference', False ) ) + id = get_id(id, kwd.get('format', None)) + reference = is_true(kwd.get('reference', False)) # Return info. rval = None if reference: - region = self.app.genomes.reference( trans, dbkey=id, chrom=chrom, low=low, high=high ) - rval = { 'dataset_type': 'refseq', 'data': region.sequence } + region = self.app.genomes.reference(trans, dbkey=id, chrom=chrom, low=low, high=high) + rval = {'dataset_type': 'refseq', 'data': region.sequence} else: - rval = self.app.genomes.chroms( trans, dbkey=id, num=num, chrom=chrom, low=low ) + rval = self.app.genomes.chroms(trans, dbkey=id, num=num, chrom=chrom, low=low) return rval @web.expose_api_raw_anonymous @@ -53,7 +53,7 @@ def indexes(self, trans, id, **kwd): For instance, /api/genomes/hg19/indexes?type=fasta_indexes """ index_extensions = {'fasta_indexes': '.fai'} - id = get_id( id, kwd.get( 'format', None ) ) + id = get_id(id, kwd.get('format', None)) index_type = kwd.get('type', None) tbl_entries = self.app.tool_data_tables.data_tables[index_type].data @@ -63,15 +63,15 @@ def indexes(self, trans, id, **kwd): return if_open.read() @web.expose_api_raw_anonymous - def sequences(self, trans, id, num=None, chrom=None, low=None, high=None, **kwd ): + def sequences(self, trans, id, num=None, chrom=None, low=None, high=None, **kwd): """ GET /api/genomes/{id}/sequences This is a wrapper for accepting sequence requests that want a raw return, not json """ - id = get_id( id, kwd.get( 'format', None ) ) - reference = is_true( kwd.get( 'reference', False ) ) + id = get_id(id, kwd.get('format', None)) + reference = is_true(kwd.get('reference', False)) assert reference - region = self.app.genomes.reference( trans, dbkey=id, chrom=chrom, low=low, high=high ) + region = self.app.genomes.reference(trans, dbkey=id, chrom=chrom, low=low, high=high) return region.sequence diff --git a/lib/galaxy/webapps/galaxy/api/group_roles.py b/lib/galaxy/webapps/galaxy/api/group_roles.py index f9ac634a1be8..a4d42cbbaf56 100644 --- a/lib/galaxy/webapps/galaxy/api/group_roles.py +++ b/lib/galaxy/webapps/galaxy/api/group_roles.py @@ -5,59 +5,59 @@ from galaxy.web.base.controller import BaseAPIController, url_for from galaxy import web -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class GroupRolesAPIController( BaseAPIController ): +class GroupRolesAPIController(BaseAPIController): @web.expose_api @web.require_admin - def index( self, trans, group_id, **kwd ): + def index(self, trans, group_id, **kwd): """ GET /api/groups/{encoded_group_id}/roles Displays a collection (list) of groups. """ - decoded_group_id = trans.security.decode_id( group_id ) + decoded_group_id = trans.security.decode_id(group_id) try: - group = trans.sa_session.query( trans.app.model.Group ).get( decoded_group_id ) + group = trans.sa_session.query(trans.app.model.Group).get(decoded_group_id) except: group = None if not group: trans.response.status = 400 - return "Invalid group id ( %s ) specified." % str( group_id ) + return "Invalid group id ( %s ) specified." % str(group_id) rval = [] try: for gra in group.roles: role = gra.role - encoded_id = trans.security.encode_id( role.id ) - rval.append( dict( id=encoded_id, - name=role.name, - url=url_for( 'group_role', group_id=group_id, id=encoded_id, ) ) ) + encoded_id = trans.security.encode_id(role.id) + rval.append(dict(id=encoded_id, + name=role.name, + url=url_for('group_role', group_id=group_id, id=encoded_id, ))) except Exception as e: rval = "Error in group API at listing roles" - log.error( rval + ": %s" % str(e) ) + log.error(rval + ": %s" % str(e)) trans.response.status = 500 return rval @web.expose_api @web.require_admin - def show( self, trans, id, group_id, **kwd ): + def show(self, trans, id, group_id, **kwd): """ GET /api/groups/{encoded_group_id}/roles/{encoded_role_id} Displays information about a group role. """ role_id = id - decoded_group_id = trans.security.decode_id( group_id ) - decoded_role_id = trans.security.decode_id( role_id ) + decoded_group_id = trans.security.decode_id(group_id) + decoded_role_id = trans.security.decode_id(role_id) item = None try: - group = trans.sa_session.query( trans.app.model.Group ).get( decoded_group_id ) - role = trans.sa_session.query( trans.app.model.Role ).get( decoded_role_id ) + group = trans.sa_session.query(trans.app.model.Group).get(decoded_group_id) + role = trans.sa_session.query(trans.app.model.Role).get(decoded_role_id) for gra in group.roles: if gra.role == role: - item = dict( id=role_id, - name=role.name, - url=url_for( 'group_role', group_id=group_id, id=role_id) ) # TODO Fix This + item = dict(id=role_id, + name=role.name, + url=url_for('group_role', group_id=group_id, id=role_id)) # TODO Fix This if not item: item = "role %s not in group %s" % (role.name, group.name) except Exception as e: @@ -67,31 +67,31 @@ def show( self, trans, id, group_id, **kwd ): @web.expose_api @web.require_admin - def update( self, trans, id, group_id, **kwd ): + def update(self, trans, id, group_id, **kwd): """ PUT /api/groups/{encoded_group_id}/roles/{encoded_role_id} Adds a role to a group """ role_id = id - decoded_group_id = trans.security.decode_id( group_id ) - decoded_role_id = trans.security.decode_id( role_id ) + decoded_group_id = trans.security.decode_id(group_id) + decoded_role_id = trans.security.decode_id(role_id) item = None try: - group = trans.sa_session.query( trans.app.model.Group ).get( decoded_group_id ) - role = trans.sa_session.query( trans.app.model.Role ).get( decoded_role_id ) + group = trans.sa_session.query(trans.app.model.Group).get(decoded_group_id) + role = trans.sa_session.query(trans.app.model.Role).get(decoded_role_id) for gra in group.roles: if gra.role == role: - item = dict( id=role_id, - name=role.name, - url=url_for( 'group_role', group_id=group_id, id=role_id) ) + item = dict(id=role_id, + name=role.name, + url=url_for('group_role', group_id=group_id, id=role_id)) if not item: - gra = trans.app.model.GroupRoleAssociation( group, role ) + gra = trans.app.model.GroupRoleAssociation(group, role) # Add GroupRoleAssociation - trans.sa_session.add( gra ) + trans.sa_session.add(gra) trans.sa_session.flush() - item = dict( id=role_id, - name=role.name, - url=url_for( 'group_role', group_id=group_id, id=role_id) ) + item = dict(id=role_id, + name=role.name, + url=url_for('group_role', group_id=group_id, id=role_id)) except Exception as e: item = "Error in group_role API Adding role %s to group %s" % (role.name, group.name) log.error(item + ": %s" % str(e)) @@ -99,24 +99,24 @@ def update( self, trans, id, group_id, **kwd ): @web.expose_api @web.require_admin - def delete( self, trans, id, group_id, **kwd ): + def delete(self, trans, id, group_id, **kwd): """ DELETE /api/groups/{encoded_group_id}/roles/{encoded_role_id} Removes a role from a group """ role_id = id - decoded_group_id = trans.security.decode_id( group_id ) - decoded_role_id = trans.security.decode_id( role_id ) + decoded_group_id = trans.security.decode_id(group_id) + decoded_role_id = trans.security.decode_id(role_id) try: - group = trans.sa_session.query( trans.app.model.Group ).get( decoded_group_id ) - role = trans.sa_session.query( trans.app.model.Role ).get( decoded_role_id ) + group = trans.sa_session.query(trans.app.model.Group).get(decoded_group_id) + role = trans.sa_session.query(trans.app.model.Role).get(decoded_role_id) for gra in group.roles: if gra.role == role: - trans.sa_session.delete( gra ) + trans.sa_session.delete(gra) trans.sa_session.flush() - item = dict( id=role_id, - name=role.name, - url=url_for( 'group_role', group_id=group_id, id=role_id) ) + item = dict(id=role_id, + name=role.name, + url=url_for('group_role', group_id=group_id, id=role_id)) if not item: item = "role %s not in group %s" % (role.name, group.name) except Exception as e: diff --git a/lib/galaxy/webapps/galaxy/api/group_users.py b/lib/galaxy/webapps/galaxy/api/group_users.py index 64a107dba707..04d47f4c0b94 100644 --- a/lib/galaxy/webapps/galaxy/api/group_users.py +++ b/lib/galaxy/webapps/galaxy/api/group_users.py @@ -5,59 +5,59 @@ from galaxy.web.base.controller import BaseAPIController, url_for from galaxy import web -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class GroupUsersAPIController( BaseAPIController ): +class GroupUsersAPIController(BaseAPIController): @web.expose_api @web.require_admin - def index( self, trans, group_id, **kwd ): + def index(self, trans, group_id, **kwd): """ GET /api/groups/{encoded_group_id}/users Displays a collection (list) of groups. """ - decoded_group_id = trans.security.decode_id( group_id ) + decoded_group_id = trans.security.decode_id(group_id) try: - group = trans.sa_session.query( trans.app.model.Group ).get( decoded_group_id ) + group = trans.sa_session.query(trans.app.model.Group).get(decoded_group_id) except: group = None if not group: trans.response.status = 400 - return "Invalid group id ( %s ) specified." % str( group_id ) + return "Invalid group id ( %s ) specified." % str(group_id) rval = [] try: for uga in group.users: user = uga.user - encoded_id = trans.security.encode_id( user.id ) - rval.append( dict( id=encoded_id, - email=user.email, - url=url_for( 'group_user', group_id=group_id, id=encoded_id, ) ) ) + encoded_id = trans.security.encode_id(user.id) + rval.append(dict(id=encoded_id, + email=user.email, + url=url_for('group_user', group_id=group_id, id=encoded_id, ))) except Exception as e: rval = "Error in group API at listing users" - log.error( rval + ": %s" % str(e) ) + log.error(rval + ": %s" % str(e)) trans.response.status = 500 return rval @web.expose_api @web.require_admin - def show( self, trans, id, group_id, **kwd ): + def show(self, trans, id, group_id, **kwd): """ GET /api/groups/{encoded_group_id}/users/{encoded_user_id} Displays information about a group user. """ user_id = id - decoded_group_id = trans.security.decode_id( group_id ) - decoded_user_id = trans.security.decode_id( user_id ) + decoded_group_id = trans.security.decode_id(group_id) + decoded_user_id = trans.security.decode_id(user_id) item = None try: - group = trans.sa_session.query( trans.app.model.Group ).get( decoded_group_id ) - user = trans.sa_session.query( trans.app.model.User ).get( decoded_user_id ) + group = trans.sa_session.query(trans.app.model.Group).get(decoded_group_id) + user = trans.sa_session.query(trans.app.model.User).get(decoded_user_id) for uga in group.users: if uga.user == user: - item = dict( id=user_id, - email=user.email, - url=url_for( 'group_user', group_id=group_id, id=user_id) ) # TODO Fix This + item = dict(id=user_id, + email=user.email, + url=url_for('group_user', group_id=group_id, id=user_id)) # TODO Fix This if not item: item = "user %s not in group %s" % (user.email, group.name) except Exception as e: @@ -67,31 +67,31 @@ def show( self, trans, id, group_id, **kwd ): @web.expose_api @web.require_admin - def update( self, trans, id, group_id, **kwd ): + def update(self, trans, id, group_id, **kwd): """ PUT /api/groups/{encoded_group_id}/users/{encoded_user_id} Adds a user to a group """ user_id = id - decoded_group_id = trans.security.decode_id( group_id ) - decoded_user_id = trans.security.decode_id( user_id ) + decoded_group_id = trans.security.decode_id(group_id) + decoded_user_id = trans.security.decode_id(user_id) item = None try: - group = trans.sa_session.query( trans.app.model.Group ).get( decoded_group_id ) - user = trans.sa_session.query( trans.app.model.User ).get( decoded_user_id ) + group = trans.sa_session.query(trans.app.model.Group).get(decoded_group_id) + user = trans.sa_session.query(trans.app.model.User).get(decoded_user_id) for uga in group.users: if uga.user == user: - item = dict( id=user_id, - email=user.email, - url=url_for( 'group_user', group_id=group_id, id=user_id) ) + item = dict(id=user_id, + email=user.email, + url=url_for('group_user', group_id=group_id, id=user_id)) if not item: - uga = trans.app.model.UserGroupAssociation( user, group ) + uga = trans.app.model.UserGroupAssociation(user, group) # Add UserGroupAssociations - trans.sa_session.add( uga ) + trans.sa_session.add(uga) trans.sa_session.flush() - item = dict( id=user_id, - email=user.email, - url=url_for( 'group_user', group_id=group_id, id=user_id) ) + item = dict(id=user_id, + email=user.email, + url=url_for('group_user', group_id=group_id, id=user_id)) except Exception as e: item = "Error in group_user API Adding user %s to group %s" % (user.email, group.name) log.error(item + ": %s" % str(e)) @@ -99,24 +99,24 @@ def update( self, trans, id, group_id, **kwd ): @web.expose_api @web.require_admin - def delete( self, trans, id, group_id, **kwd ): + def delete(self, trans, id, group_id, **kwd): """ DELETE /api/groups/{encoded_group_id}/users/{encoded_user_id} Removes a user from a group """ user_id = id - decoded_group_id = trans.security.decode_id( group_id ) - decoded_user_id = trans.security.decode_id( user_id ) + decoded_group_id = trans.security.decode_id(group_id) + decoded_user_id = trans.security.decode_id(user_id) try: - group = trans.sa_session.query( trans.app.model.Group ).get( decoded_group_id ) - user = trans.sa_session.query( trans.app.model.User ).get( decoded_user_id ) + group = trans.sa_session.query(trans.app.model.Group).get(decoded_group_id) + user = trans.sa_session.query(trans.app.model.User).get(decoded_user_id) for uga in group.users: if uga.user == user: - trans.sa_session.delete( uga ) + trans.sa_session.delete(uga) trans.sa_session.flush() - item = dict( id=user_id, - email=user.email, - url=url_for( 'group_user', group_id=group_id, id=user_id) ) + item = dict(id=user_id, + email=user.email, + url=url_for('group_user', group_id=group_id, id=user_id)) if not item: item = "user %s not in group %s" % (user.email, group.name) except Exception as e: diff --git a/lib/galaxy/webapps/galaxy/api/groups.py b/lib/galaxy/webapps/galaxy/api/groups.py index 0b362cc9a1a9..f398f865e5cb 100644 --- a/lib/galaxy/webapps/galaxy/api/groups.py +++ b/lib/galaxy/webapps/galaxy/api/groups.py @@ -8,29 +8,29 @@ from galaxy import web -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class GroupAPIController( BaseAPIController ): +class GroupAPIController(BaseAPIController): @web.expose_api @web.require_admin - def index( self, trans, **kwd ): + def index(self, trans, **kwd): """ GET /api/groups Displays a collection (list) of groups. """ rval = [] - for group in trans.sa_session.query( trans.app.model.Group ).filter( trans.app.model.Group.table.c.deleted == false() ): + for group in trans.sa_session.query(trans.app.model.Group).filter(trans.app.model.Group.table.c.deleted == false()): if trans.user_is_admin(): - item = group.to_dict( value_mapper={ 'id': trans.security.encode_id } ) - encoded_id = trans.security.encode_id( group.id ) - item['url'] = url_for( 'group', id=encoded_id ) - rval.append( item ) + item = group.to_dict(value_mapper={'id': trans.security.encode_id}) + encoded_id = trans.security.encode_id(group.id) + item['url'] = url_for('group', id=encoded_id) + rval.append(item) return rval @web.expose_api - def create( self, trans, payload, **kwd ): + def create(self, trans, payload, **kwd): """ POST /api/groups Creates a new group. @@ -39,24 +39,24 @@ def create( self, trans, payload, **kwd ): if not trans.user_is_admin(): trans.response.status = 403 return "You are not authorized to create a new group." - name = payload.get( 'name', None ) + name = payload.get('name', None) if not name: trans.response.status = 400 return "Enter a valid name" - if trans.sa_session.query( trans.app.model.Group ).filter( trans.app.model.Group.table.c.name == name ).first(): + if trans.sa_session.query(trans.app.model.Group).filter(trans.app.model.Group.table.c.name == name).first(): trans.response.status = 400 return "A group with that name already exists" - group = trans.app.model.Group( name=name ) - trans.sa_session.add( group ) - user_ids = payload.get( 'user_ids', [] ) + group = trans.app.model.Group(name=name) + trans.sa_session.add(group) + user_ids = payload.get('user_ids', []) for i in user_ids: - log.info("user_id: %s\n" % (i )) - log.info("%s %s\n" % (i, trans.security.decode_id( i ) )) - users = [ trans.sa_session.query( trans.model.User ).get( trans.security.decode_id( i ) ) for i in user_ids ] - role_ids = payload.get( 'role_ids', [] ) - roles = [ trans.sa_session.query( trans.model.Role ).get( trans.security.decode_id( i ) ) for i in role_ids ] - trans.app.security_agent.set_entity_group_associations( groups=[ group ], roles=roles, users=users ) + log.info("user_id: %s\n" % (i)) + log.info("%s %s\n" % (i, trans.security.decode_id(i))) + users = [trans.sa_session.query(trans.model.User).get(trans.security.decode_id(i)) for i in user_ids] + role_ids = payload.get('role_ids', []) + roles = [trans.sa_session.query(trans.model.Role).get(trans.security.decode_id(i)) for i in role_ids] + trans.app.security_agent.set_entity_group_associations(groups=[group], roles=roles, users=users) """ # Create the UserGroupAssociations for user in users: @@ -66,64 +66,64 @@ def create( self, trans, payload, **kwd ): trans.app.security_agent.associate_group_role( group, role ) """ trans.sa_session.flush() - encoded_id = trans.security.encode_id( group.id ) - item = group.to_dict( view='element', value_mapper={ 'id': trans.security.encode_id } ) - item['url'] = url_for( 'group', id=encoded_id ) - return [ item ] + encoded_id = trans.security.encode_id(group.id) + item = group.to_dict(view='element', value_mapper={'id': trans.security.encode_id}) + item['url'] = url_for('group', id=encoded_id) + return [item] @web.expose_api @web.require_admin - def show( self, trans, id, **kwd ): + def show(self, trans, id, **kwd): """ GET /api/groups/{encoded_group_id} Displays information about a group. """ group_id = id try: - decoded_group_id = trans.security.decode_id( group_id ) + decoded_group_id = trans.security.decode_id(group_id) except TypeError: trans.response.status = 400 - return "Malformed group id ( %s ) specified, unable to decode." % str( group_id ) + return "Malformed group id ( %s ) specified, unable to decode." % str(group_id) try: - group = trans.sa_session.query( trans.app.model.Group ).get( decoded_group_id ) + group = trans.sa_session.query(trans.app.model.Group).get(decoded_group_id) except: group = None if not group: trans.response.status = 400 - return "Invalid group id ( %s ) specified." % str( group_id ) - item = group.to_dict( view='element', value_mapper={ 'id': trans.security.encode_id } ) - item['url'] = url_for( 'group', id=group_id ) - item['users_url'] = url_for( 'group_users', group_id=group_id ) - item['roles_url'] = url_for( 'group_roles', group_id=group_id ) + return "Invalid group id ( %s ) specified." % str(group_id) + item = group.to_dict(view='element', value_mapper={'id': trans.security.encode_id}) + item['url'] = url_for('group', id=group_id) + item['users_url'] = url_for('group_users', group_id=group_id) + item['roles_url'] = url_for('group_roles', group_id=group_id) return item @web.expose_api @web.require_admin - def update( self, trans, id, payload, **kwd ): + def update(self, trans, id, payload, **kwd): """ PUT /api/groups/{encoded_group_id} Modifies a group. """ group_id = id try: - decoded_group_id = trans.security.decode_id( group_id ) + decoded_group_id = trans.security.decode_id(group_id) except TypeError: trans.response.status = 400 - return "Malformed group id ( %s ) specified, unable to decode." % str( group_id ) + return "Malformed group id ( %s ) specified, unable to decode." % str(group_id) try: - group = trans.sa_session.query( trans.app.model.Group ).get( decoded_group_id ) + group = trans.sa_session.query(trans.app.model.Group).get(decoded_group_id) except: group = None if not group: trans.response.status = 400 - return "Invalid group id ( %s ) specified." % str( group_id ) - name = payload.get( 'name', None ) + return "Invalid group id ( %s ) specified." % str(group_id) + name = payload.get('name', None) if name: group.name = name trans.sa_session.add(group) - user_ids = payload.get( 'user_ids', [] ) - users = [ trans.sa_session.query( trans.model.User ).get( trans.security.decode_id( i ) ) for i in user_ids ] - role_ids = payload.get( 'role_ids', [] ) - roles = [ trans.sa_session.query( trans.model.Role ).get( trans.security.decode_id( i ) ) for i in role_ids ] - trans.app.security_agent.set_entity_group_associations( groups=[ group ], roles=roles, users=users, delete_existing_assocs=False ) + user_ids = payload.get('user_ids', []) + users = [trans.sa_session.query(trans.model.User).get(trans.security.decode_id(i)) for i in user_ids] + role_ids = payload.get('role_ids', []) + roles = [trans.sa_session.query(trans.model.Role).get(trans.security.decode_id(i)) for i in role_ids] + trans.app.security_agent.set_entity_group_associations(groups=[group], roles=roles, users=users, delete_existing_assocs=False) trans.sa_session.flush() diff --git a/lib/galaxy/webapps/galaxy/api/histories.py b/lib/galaxy/webapps/galaxy/api/histories.py index bc76c785b707..832fb97ea904 100644 --- a/lib/galaxy/webapps/galaxy/api/histories.py +++ b/lib/galaxy/webapps/galaxy/api/histories.py @@ -39,22 +39,22 @@ ImportsHistoryMixin ) -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class HistoriesController( BaseAPIController, ExportsHistoryMixin, ImportsHistoryMixin ): +class HistoriesController(BaseAPIController, ExportsHistoryMixin, ImportsHistoryMixin): - def __init__( self, app ): - super( HistoriesController, self ).__init__( app ) - self.citations_manager = citations.CitationsManager( app ) - self.user_manager = users.UserManager( app ) - self.history_manager = histories.HistoryManager( app ) - self.history_serializer = histories.HistorySerializer( app ) - self.history_deserializer = histories.HistoryDeserializer( app ) - self.history_filters = histories.HistoryFilters( app ) + def __init__(self, app): + super(HistoriesController, self).__init__(app) + self.citations_manager = citations.CitationsManager(app) + self.user_manager = users.UserManager(app) + self.history_manager = histories.HistoryManager(app) + self.history_serializer = histories.HistorySerializer(app) + self.history_deserializer = histories.HistoryDeserializer(app) + self.history_filters = histories.HistoryFilters(app) @expose_api_anonymous - def index( self, trans, deleted='False', **kwd ): + def index(self, trans, deleted='False', **kwd): """ index( trans, deleted='False' ) * GET /api/histories: @@ -124,70 +124,70 @@ def index( self, trans, deleted='False', **kwd ): 'order' defaults to 'create_time-dsc' """ - serialization_params = self._parse_serialization_params( kwd, 'summary' ) - limit, offset = self.parse_limit_offset( kwd ) - filter_params = self.parse_filter_params( kwd ) + serialization_params = self._parse_serialization_params(kwd, 'summary') + limit, offset = self.parse_limit_offset(kwd) + filter_params = self.parse_filter_params(kwd) # bail early with current history if user is anonymous - current_user = self.user_manager.current_user( trans ) - if self.user_manager.is_anonymous( current_user ): - current_history = self.history_manager.get_current( trans ) + current_user = self.user_manager.current_user(trans) + if self.user_manager.is_anonymous(current_user): + current_history = self.history_manager.get_current(trans) if not current_history: return [] # note: ignores filters, limit, offset - return [ self.history_serializer.serialize_to_view( current_history, - user=current_user, trans=trans, **serialization_params ) ] + return [self.history_serializer.serialize_to_view(current_history, + user=current_user, trans=trans, **serialization_params)] filters = [] # support the old default of not-returning/filtering-out deleted histories - filters += self._get_deleted_filter( deleted, filter_params ) + filters += self._get_deleted_filter(deleted, filter_params) # users are limited to requesting only their own histories (here) - filters += [ self.app.model.History.user == current_user ] + filters += [self.app.model.History.user == current_user] # and any sent in from the query string - filters += self.history_filters.parse_filters( filter_params ) + filters += self.history_filters.parse_filters(filter_params) - order_by = self._parse_order_by( kwd.get( 'order', 'create_time-dsc' ) ) - histories = self.history_manager.list( filters=filters, order_by=order_by, limit=limit, offset=offset ) + order_by = self._parse_order_by(kwd.get('order', 'create_time-dsc')) + histories = self.history_manager.list(filters=filters, order_by=order_by, limit=limit, offset=offset) rval = [] for history in histories: - history_dict = self.history_serializer.serialize_to_view( history, user=trans.user, trans=trans, **serialization_params ) - rval.append( history_dict ) + history_dict = self.history_serializer.serialize_to_view(history, user=trans.user, trans=trans, **serialization_params) + rval.append(history_dict) return rval - def _get_deleted_filter( self, deleted, filter_params ): + def _get_deleted_filter(self, deleted, filter_params): # TODO: this should all be removed (along with the default) in v2 # support the old default of not-returning/filtering-out deleted histories try: # the consumer must explicitly ask for both deleted and non-deleted # but pull it from the parsed params (as the filter system will error on None) - deleted_filter_index = filter_params.index( ( 'deleted', 'eq', 'None' ) ) - filter_params.pop( deleted_filter_index ) + deleted_filter_index = filter_params.index(('deleted', 'eq', 'None')) + filter_params.pop(deleted_filter_index) return [] except ValueError: pass # the deleted string bool was also used as an 'include deleted' flag - if deleted in ( 'True', 'true' ): - return [ self.app.model.History.deleted == true() ] + if deleted in ('True', 'true'): + return [self.app.model.History.deleted == true()] # the third option not handled here is 'return only deleted' # if this is passed in (in the form below), simply return and let the filter system handle it - if ( 'deleted', 'eq', 'True' ) in filter_params: + if ('deleted', 'eq', 'True') in filter_params: return [] # otherwise, do the default filter of removing the deleted histories - return [ self.app.model.History.deleted == false() ] + return [self.app.model.History.deleted == false()] - def _parse_order_by( self, order_by_string ): + def _parse_order_by(self, order_by_string): ORDER_BY_SEP_CHAR = ',' manager = self.history_manager if ORDER_BY_SEP_CHAR in order_by_string: - return [ manager.parse_order_by( o ) for o in order_by_string.split( ORDER_BY_SEP_CHAR ) ] - return manager.parse_order_by( order_by_string ) + return [manager.parse_order_by(o) for o in order_by_string.split(ORDER_BY_SEP_CHAR)] + return manager.parse_order_by(order_by_string) @expose_api_anonymous - def show( self, trans, id, deleted='False', **kwd ): + def show(self, trans, id, deleted='False', **kwd): """ show( trans, id, deleted='False' ) * GET /api/histories/{id}: @@ -209,22 +209,22 @@ def show( self, trans, id, deleted='False', **kwd ): :returns: detailed history information """ history_id = id - deleted = string_as_bool( deleted ) + deleted = string_as_bool(deleted) if history_id == "most_recently_used": - history = self.history_manager.most_recent( trans.user, - filters=( self.app.model.History.deleted == false() ), current_history=trans.history ) + history = self.history_manager.most_recent(trans.user, + filters=(self.app.model.History.deleted == false()), current_history=trans.history) else: - history = self.history_manager.get_accessible( self.decode_id( history_id ), trans.user, current_history=trans.history ) + history = self.history_manager.get_accessible(self.decode_id(history_id), trans.user, current_history=trans.history) - return self.history_serializer.serialize_to_view( history, - user=trans.user, trans=trans, **self._parse_serialization_params( kwd, 'detailed' ) ) + return self.history_serializer.serialize_to_view(history, + user=trans.user, trans=trans, **self._parse_serialization_params(kwd, 'detailed')) @expose_api_anonymous - def citations( self, trans, history_id, **kwd ): + def citations(self, trans, history_id, **kwd): """ """ - history = self.history_manager.get_accessible( self.decode_id( history_id ), trans.user, current_history=trans.history ) + history = self.history_manager.get_accessible(self.decode_id(history_id), trans.user, current_history=trans.history) tool_ids = set([]) for dataset in history.datasets: job = dataset.creating_job @@ -234,10 +234,10 @@ def citations( self, trans, history_id, **kwd ): if not tool_id: continue tool_ids.add(tool_id) - return [citation.to_dict( "bibtex" ) for citation in self.citations_manager.citations_for_tool_ids( tool_ids )] + return [citation.to_dict("bibtex") for citation in self.citations_manager.citations_for_tool_ids(tool_ids)] @expose_api_anonymous_and_sessionless - def published( self, trans, **kwd ): + def published(self, trans, **kwd): """ published( self, trans, **kwd ): * GET /api/histories/published: @@ -248,21 +248,21 @@ def published( self, trans, **kwd ): Follows the same filtering logic as the index() method above. """ - limit, offset = self.parse_limit_offset( kwd ) - filter_params = self.parse_filter_params( kwd ) - filters = self.history_filters.parse_filters( filter_params ) - order_by = self._parse_order_by( kwd.get( 'order', 'create_time-dsc' ) ) - histories = self.history_manager.list_published( filters=filters, order_by=order_by, limit=limit, offset=offset ) + limit, offset = self.parse_limit_offset(kwd) + filter_params = self.parse_filter_params(kwd) + filters = self.history_filters.parse_filters(filter_params) + order_by = self._parse_order_by(kwd.get('order', 'create_time-dsc')) + histories = self.history_manager.list_published(filters=filters, order_by=order_by, limit=limit, offset=offset) rval = [] for history in histories: - history_dict = self.history_serializer.serialize_to_view( history, user=trans.user, trans=trans, - **self._parse_serialization_params( kwd, 'summary' ) ) - rval.append( history_dict ) + history_dict = self.history_serializer.serialize_to_view(history, user=trans.user, trans=trans, + **self._parse_serialization_params(kwd, 'summary')) + rval.append(history_dict) return rval # TODO: does this need to be anonymous_and_sessionless? Not just expose_api? @expose_api_anonymous_and_sessionless - def shared_with_me( self, trans, **kwd ): + def shared_with_me(self, trans, **kwd): """ shared_with_me( self, trans, **kwd ) * GET /api/histories/shared_with_me: @@ -274,21 +274,21 @@ def shared_with_me( self, trans, **kwd ): Follows the same filtering logic as the index() method above. """ current_user = trans.user - limit, offset = self.parse_limit_offset( kwd ) - filter_params = self.parse_filter_params( kwd ) - filters = self.history_filters.parse_filters( filter_params ) - order_by = self._parse_order_by( kwd.get( 'order', 'create_time-dsc' ) ) - histories = self.history_manager.list_shared_with( current_user, - filters=filters, order_by=order_by, limit=limit, offset=offset ) + limit, offset = self.parse_limit_offset(kwd) + filter_params = self.parse_filter_params(kwd) + filters = self.history_filters.parse_filters(filter_params) + order_by = self._parse_order_by(kwd.get('order', 'create_time-dsc')) + histories = self.history_manager.list_shared_with(current_user, + filters=filters, order_by=order_by, limit=limit, offset=offset) rval = [] for history in histories: - history_dict = self.history_serializer.serialize_to_view( history, user=current_user, trans=trans, - **self._parse_serialization_params( kwd, 'summary' ) ) - rval.append( history_dict ) + history_dict = self.history_serializer.serialize_to_view(history, user=current_user, trans=trans, + **self._parse_serialization_params(kwd, 'summary')) + rval.append(history_dict) return rval @expose_api_anonymous - def create( self, trans, payload, **kwd ): + def create(self, trans, payload, **kwd): """ create( trans, payload ) * POST /api/histories: @@ -309,42 +309,42 @@ def create( self, trans, payload, **kwd ): :returns: element view of new history """ hist_name = None - if payload.get( 'name', None ): - hist_name = restore_text( payload['name'] ) - copy_this_history_id = payload.get( 'history_id', None ) + if payload.get('name', None): + hist_name = restore_text(payload['name']) + copy_this_history_id = payload.get('history_id', None) - all_datasets = util.string_as_bool( payload.get( 'all_datasets', True ) ) + all_datasets = util.string_as_bool(payload.get('all_datasets', True)) if "archive_source" in payload: - archive_source = payload[ "archive_source" ] - archive_type = payload.get( "archive_type", "url" ) - self.queue_history_import( trans, archive_type=archive_type, archive_source=archive_source ) + archive_source = payload["archive_source"] + archive_type = payload.get("archive_type", "url") + self.queue_history_import(trans, archive_type=archive_type, archive_source=archive_source) return {} new_history = None # if a history id was passed, copy that history if copy_this_history_id: - decoded_id = self.decode_id( copy_this_history_id ) - original_history = self.history_manager.get_accessible( decoded_id, trans.user, current_history=trans.history ) - hist_name = hist_name or ( "Copy of '%s'" % original_history.name ) - new_history = original_history.copy( name=hist_name, target_user=trans.user, all_datasets=all_datasets ) + decoded_id = self.decode_id(copy_this_history_id) + original_history = self.history_manager.get_accessible(decoded_id, trans.user, current_history=trans.history) + hist_name = hist_name or ("Copy of '%s'" % original_history.name) + new_history = original_history.copy(name=hist_name, target_user=trans.user, all_datasets=all_datasets) # otherwise, create a new empty history else: - new_history = self.history_manager.create( user=trans.user, name=hist_name ) + new_history = self.history_manager.create(user=trans.user, name=hist_name) - trans.sa_session.add( new_history ) + trans.sa_session.add(new_history) trans.sa_session.flush() # an anonymous user can only have one history - if self.user_manager.is_anonymous( trans.user ): - self.history_manager.set_current( trans, new_history ) + if self.user_manager.is_anonymous(trans.user): + self.history_manager.set_current(trans, new_history) - return self.history_serializer.serialize_to_view( new_history, - user=trans.user, trans=trans, **self._parse_serialization_params( kwd, 'detailed' ) ) + return self.history_serializer.serialize_to_view(new_history, + user=trans.user, trans=trans, **self._parse_serialization_params(kwd, 'detailed')) @expose_api - def delete( self, trans, id, **kwd ): + def delete(self, trans, id, **kwd): """ delete( self, trans, id, **kwd ) * DELETE /api/histories/{id} @@ -367,22 +367,22 @@ def delete( self, trans, id, **kwd ): """ history_id = id # a request body is optional here - purge = string_as_bool( kwd.get( 'purge', False ) ) + purge = string_as_bool(kwd.get('purge', False)) # for backwards compat, keep the payload sub-dictionary - if kwd.get( 'payload', None ): - purge = string_as_bool( kwd['payload'].get( 'purge', purge ) ) + if kwd.get('payload', None): + purge = string_as_bool(kwd['payload'].get('purge', purge)) - history = self.history_manager.get_owned( self.decode_id( history_id ), trans.user, current_history=trans.history ) + history = self.history_manager.get_owned(self.decode_id(history_id), trans.user, current_history=trans.history) if purge: - self.history_manager.purge( history ) + self.history_manager.purge(history) else: - self.history_manager.delete( history ) + self.history_manager.delete(history) - return self.history_serializer.serialize_to_view( history, - user=trans.user, trans=trans, **self._parse_serialization_params( kwd, 'detailed' ) ) + return self.history_serializer.serialize_to_view(history, + user=trans.user, trans=trans, **self._parse_serialization_params(kwd, 'detailed')) @expose_api - def undelete( self, trans, id, **kwd ): + def undelete(self, trans, id, **kwd): """ undelete( self, trans, id, **kwd ) * POST /api/histories/deleted/{id}/undelete: @@ -399,14 +399,14 @@ def undelete( self, trans, id, **kwd ): """ # TODO: remove at v2 history_id = id - history = self.history_manager.get_owned( self.decode_id( history_id ), trans.user, current_history=trans.history ) - self.history_manager.undelete( history ) + history = self.history_manager.get_owned(self.decode_id(history_id), trans.user, current_history=trans.history) + self.history_manager.undelete(history) - return self.history_serializer.serialize_to_view( history, - user=trans.user, trans=trans, **self._parse_serialization_params( kwd, 'detailed' ) ) + return self.history_serializer.serialize_to_view(history, + user=trans.user, trans=trans, **self._parse_serialization_params(kwd, 'detailed')) @expose_api - def update( self, trans, id, payload, **kwd ): + def update(self, trans, id, payload, **kwd): """ update( self, trans, id, payload, **kwd ) * PUT /api/histories/{id} @@ -428,14 +428,14 @@ def update( self, trans, id, payload, **kwd ): any values that were different from the original and, therefore, updated """ # TODO: PUT /api/histories/{encoded_history_id} payload = { rating: rating } (w/ no security checks) - history = self.history_manager.get_owned( self.decode_id( id ), trans.user, current_history=trans.history ) + history = self.history_manager.get_owned(self.decode_id(id), trans.user, current_history=trans.history) - self.history_deserializer.deserialize( history, payload, user=trans.user, trans=trans ) - return self.history_serializer.serialize_to_view( history, - user=trans.user, trans=trans, **self._parse_serialization_params( kwd, 'detailed' ) ) + self.history_deserializer.deserialize(history, payload, user=trans.user, trans=trans) + return self.history_serializer.serialize_to_view(history, + user=trans.user, trans=trans, **self._parse_serialization_params(kwd, 'detailed')) @expose_api - def archive_export( self, trans, id, **kwds ): + def archive_export(self, trans, id, **kwds): """ export_archive( self, trans, id, payload ) * PUT /api/histories/{id}/exports: @@ -450,28 +450,28 @@ def archive_export( self, trans, id, **kwds ): """ # PUT instead of POST because multiple requests should just result # in one object being created. - history = self.history_manager.get_accessible( self.decode_id( id ), trans.user, current_history=trans.history ) + history = self.history_manager.get_accessible(self.decode_id(id), trans.user, current_history=trans.history) jeha = history.latest_export up_to_date = jeha and jeha.up_to_date if 'force' in kwds: up_to_date = False # Temp hack to force rebuild everytime during dev if not up_to_date: # Need to create new JEHA + job. - gzip = kwds.get( "gzip", True ) - include_hidden = kwds.get( "include_hidden", False ) - include_deleted = kwds.get( "include_deleted", False ) - self.queue_history_export( trans, history, gzip=gzip, include_hidden=include_hidden, include_deleted=include_deleted ) + gzip = kwds.get("gzip", True) + include_hidden = kwds.get("include_hidden", False) + include_deleted = kwds.get("include_deleted", False) + self.queue_history_export(trans, history, gzip=gzip, include_hidden=include_hidden, include_deleted=include_deleted) if up_to_date and jeha.ready: - jeha_id = trans.security.encode_id( jeha.id ) - return dict( download_url=url_for( "history_archive_download", id=id, jeha_id=jeha_id ) ) + jeha_id = trans.security.encode_id(jeha.id) + return dict(download_url=url_for("history_archive_download", id=id, jeha_id=jeha_id)) else: # Valid request, just resource is not ready yet. trans.response.status = "202 Accepted" return '' @expose_api_raw - def archive_download( self, trans, id, jeha_id, **kwds ): + def archive_download(self, trans, id, jeha_id, **kwds): """ export_download( self, trans, id, jeha_id ) * GET /api/histories/{id}/exports/{jeha_id}: @@ -483,18 +483,18 @@ def archive_download( self, trans, id, jeha_id, **kwds ): """ # Seems silly to put jeha_id in here, but want GET to be immuatable? # and this is being accomplished this way. - history = self.history_manager.get_accessible( self.decode_id( id ), trans.user, current_history=trans.history ) - matching_exports = [e for e in history.exports if trans.security.encode_id( e.id ) == jeha_id] + history = self.history_manager.get_accessible(self.decode_id(id), trans.user, current_history=trans.history) + matching_exports = [e for e in history.exports if trans.security.encode_id(e.id) == jeha_id] if not matching_exports: raise exceptions.ObjectNotFound() - jeha = matching_exports[ 0 ] + jeha = matching_exports[0] if not jeha.ready: # User should not have been given this URL, PUT export should have # return a 202. - raise exceptions.MessageException( "Export not available or not yet ready." ) + raise exceptions.MessageException("Export not available or not yet ready.") - return self.serve_ready_history_export( trans, jeha ) + return self.serve_ready_history_export(trans, jeha) @expose_api def get_custom_builds_metadata(self, trans, id, payload={}, **kwd): @@ -505,14 +505,14 @@ def get_custom_builds_metadata(self, trans, id, payload={}, **kwd): :param id: the encoded history id :type id: str """ - history = self.history_manager.get_accessible( self.decode_id( id ), trans.user, current_history=trans.history ) + history = self.history_manager.get_accessible(self.decode_id(id), trans.user, current_history=trans.history) installed_builds = [] - for build in glob.glob( os.path.join(trans.app.config.len_file_path, "*.len") ): - installed_builds.append( os.path.basename(build).split(".len")[0] ) - fasta_hdas = trans.sa_session.query( model.HistoryDatasetAssociation ) \ - .filter_by( history=history, extension="fasta", deleted=False ) \ - .order_by( model.HistoryDatasetAssociation.hid.desc() ) + for build in glob.glob(os.path.join(trans.app.config.len_file_path, "*.len")): + installed_builds.append(os.path.basename(build).split(".len")[0]) + fasta_hdas = trans.sa_session.query(model.HistoryDatasetAssociation) \ + .filter_by(history=history, extension="fasta", deleted=False) \ + .order_by(model.HistoryDatasetAssociation.hid.desc()) return { - 'installed_builds' : [ { 'label' : ins, 'value' : ins } for ins in installed_builds ], - 'fasta_hdas' : [ { 'label' : '%s: %s' % ( hda.hid, hda.name ), 'value' : trans.security.encode_id( hda.id ) } for hda in fasta_hdas ], + 'installed_builds' : [{'label' : ins, 'value' : ins} for ins in installed_builds], + 'fasta_hdas' : [{'label' : '%s: %s' % (hda.hid, hda.name), 'value' : trans.security.encode_id(hda.id)} for hda in fasta_hdas], } diff --git a/lib/galaxy/webapps/galaxy/api/history_contents.py b/lib/galaxy/webapps/galaxy/api/history_contents.py index e7bb9489aa62..7f027c7af014 100644 --- a/lib/galaxy/webapps/galaxy/api/history_contents.py +++ b/lib/galaxy/webapps/galaxy/api/history_contents.py @@ -31,24 +31,24 @@ ) import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class HistoryContentsController( BaseAPIController, UsesLibraryMixin, UsesLibraryMixinItems, UsesTagsMixin ): +class HistoryContentsController(BaseAPIController, UsesLibraryMixin, UsesLibraryMixinItems, UsesTagsMixin): - def __init__( self, app ): - super( HistoryContentsController, self ).__init__( app ) - self.hda_manager = hdas.HDAManager( app ) - self.history_manager = histories.HistoryManager( app ) - self.history_contents_manager = history_contents.HistoryContentsManager( app ) + def __init__(self, app): + super(HistoryContentsController, self).__init__(app) + self.hda_manager = hdas.HDAManager(app) + self.history_manager = histories.HistoryManager(app) + self.history_contents_manager = history_contents.HistoryContentsManager(app) self.folder_manager = folders.FolderManager() - self.hda_serializer = hdas.HDASerializer( app ) - self.hda_deserializer = hdas.HDADeserializer( app ) - self.hdca_serializer = hdcas.HDCASerializer( app ) - self.history_contents_filters = history_contents.HistoryContentsFilters( app ) + self.hda_serializer = hdas.HDASerializer(app) + self.hda_deserializer = hdas.HDADeserializer(app) + self.hdca_serializer = hdcas.HDCASerializer(app) + self.history_contents_filters = history_contents.HistoryContentsFilters(app) @expose_api_anonymous - def index( self, trans, history_id, ids=None, v=None, **kwd ): + def index(self, trans, history_id, ids=None, v=None, **kwd): """ index( self, trans, history_id, ids=None, **kwd ) * GET /api/histories/{history_id}/contents @@ -73,60 +73,60 @@ def index( self, trans, history_id, ids=None, v=None, **kwd ): :returns: dictionaries containing summary or detailed HDA information """ if v == 'dev': - return self.__index_v2( trans, history_id, **kwd ) + return self.__index_v2(trans, history_id, **kwd) rval = [] - history = self.history_manager.get_accessible( self.decode_id( history_id ), trans.user, current_history=trans.history ) + history = self.history_manager.get_accessible(self.decode_id(history_id), trans.user, current_history=trans.history) # Allow passing in type or types - for continuity rest of methods # take in type - but this one can be passed multiple types and # type=dataset,dataset_collection is a bit silly. - types = kwd.get( 'type', kwd.get( 'types', None ) ) or [] + types = kwd.get('type', kwd.get('types', None)) or [] if types: types = util.listify(types) else: - types = [ 'dataset', "dataset_collection" ] + types = ['dataset', "dataset_collection"] - contents_kwds = { 'types': types } + contents_kwds = {'types': types} if ids: - ids = map( lambda id: self.decode_id( id ), ids.split( ',' ) ) - contents_kwds[ 'ids' ] = ids + ids = map(lambda id: self.decode_id(id), ids.split(',')) + contents_kwds['ids'] = ids # If explicit ids given, always used detailed result. details = 'all' else: - contents_kwds[ 'deleted' ] = kwd.get( 'deleted', None ) - contents_kwds[ 'visible' ] = kwd.get( 'visible', None ) + contents_kwds['deleted'] = kwd.get('deleted', None) + contents_kwds['visible'] = kwd.get('visible', None) # details param allows a mixed set of summary and detailed hdas # Ever more convoluted due to backwards compat..., details # should be considered deprecated in favor of more specific # dataset_details (and to be implemented dataset_collection_details). - details = kwd.get( 'details', None ) or kwd.get( 'dataset_details', None ) or [] + details = kwd.get('details', None) or kwd.get('dataset_details', None) or [] if details and details != 'all': - details = util.listify( details ) + details = util.listify(details) - for content in history.contents_iter( **contents_kwds ): - encoded_content_id = trans.security.encode_id( content.id ) - detailed = details == 'all' or ( encoded_content_id in details ) + for content in history.contents_iter(**contents_kwds): + encoded_content_id = trans.security.encode_id(content.id) + detailed = details == 'all' or (encoded_content_id in details) - if isinstance( content, trans.app.model.HistoryDatasetAssociation ): + if isinstance(content, trans.app.model.HistoryDatasetAssociation): view = 'detailed' if detailed else 'summary' - hda_dict = self.hda_serializer.serialize_to_view( content, view=view, user=trans.user, trans=trans ) - rval.append( hda_dict ) + hda_dict = self.hda_serializer.serialize_to_view(content, view=view, user=trans.user, trans=trans) + rval.append(hda_dict) - elif isinstance( content, trans.app.model.HistoryDatasetCollectionAssociation ): + elif isinstance(content, trans.app.model.HistoryDatasetCollectionAssociation): view = 'element' if detailed else 'collection' - collection_dict = self.__collection_dict( trans, content, view=view ) - rval.append( collection_dict ) + collection_dict = self.__collection_dict(trans, content, view=view) + rval.append(collection_dict) return rval - def __collection_dict( self, trans, dataset_collection_instance, view="collection" ): - return dictify_dataset_collection_instance( dataset_collection_instance, - security=trans.security, parent=dataset_collection_instance.history, view=view ) + def __collection_dict(self, trans, dataset_collection_instance, view="collection"): + return dictify_dataset_collection_instance(dataset_collection_instance, + security=trans.security, parent=dataset_collection_instance.history, view=view) @expose_api_anonymous - def show( self, trans, id, history_id, **kwd ): + def show(self, trans, id, history_id, **kwd): """ show( self, trans, id, history_id, **kwd ) * GET /api/histories/{history_id}/contents/{id} @@ -143,20 +143,20 @@ def show( self, trans, id, history_id, **kwd ): """ contents_type = kwd.get('type', 'dataset') if contents_type == 'dataset': - return self.__show_dataset( trans, id, **kwd ) + return self.__show_dataset(trans, id, **kwd) elif contents_type == 'dataset_collection': - return self.__show_dataset_collection( trans, id, history_id, **kwd ) + return self.__show_dataset_collection(trans, id, history_id, **kwd) else: - return self.__handle_unknown_contents_type( trans, contents_type ) + return self.__handle_unknown_contents_type(trans, contents_type) - def __show_dataset( self, trans, id, **kwd ): - hda = self.hda_manager.get_accessible( self.decode_id( id ), trans.user ) - return self.hda_serializer.serialize_to_view( hda, - user=trans.user, - trans=trans, - **self._parse_serialization_params( kwd, 'detailed' ) ) + def __show_dataset(self, trans, id, **kwd): + hda = self.hda_manager.get_accessible(self.decode_id(id), trans.user) + return self.hda_serializer.serialize_to_view(hda, + user=trans.user, + trans=trans, + **self._parse_serialization_params(kwd, 'detailed')) - def __show_dataset_collection( self, trans, id, history_id, **kwd ): + def __show_dataset_collection(self, trans, id, history_id, **kwd): try: service = trans.app.dataset_collections_service dataset_collection_instance = service.get_dataset_collection_instance( @@ -164,11 +164,11 @@ def __show_dataset_collection( self, trans, id, history_id, **kwd ): instance_type='history', id=id, ) - return self.__collection_dict( trans, dataset_collection_instance, view="element" ) + return self.__collection_dict(trans, dataset_collection_instance, view="element") except Exception as e: - log.exception( "Error in history API at listing dataset collection" ) + log.exception("Error in history API at listing dataset collection") trans.response.status = 500 - return { 'error': str( e ) } + return {'error': str(e)} @expose_api_raw_anonymous def download_dataset_collection(self, trans, id, history_id, **kwd): @@ -191,9 +191,9 @@ def download_dataset_collection(self, trans, id, history_id, **kwd): return self.__stream_dataset_collection(trans, dataset_collection_instance) except Exception as e: - log.exception( "Error in API while creating dataset collection archive" ) + log.exception("Error in API while creating dataset collection archive") trans.response.status = 500 - return { 'error': str( e ) } + return {'error': str(e)} def __stream_dataset_collection(self, trans, dataset_collection_instance): archive_type_string = 'w|gz' @@ -216,7 +216,7 @@ def __stream_dataset_collection(self, trans, dataset_collection_instance): return archive.stream @expose_api_anonymous - def create( self, trans, history_id, payload, **kwd ): + def create(self, trans, history_id, payload, **kwd): """ create( self, trans, history_id, payload, **kwd ) * POST /api/histories/{history_id}/contents/{type} @@ -272,77 +272,77 @@ def create( self, trans, history_id, payload, **kwd ): # TODO: Flush out create new collection documentation above, need some # examples. See also bioblend and API tests for specific examples. - history = self.history_manager.get_owned( self.decode_id( history_id ), trans.user, - current_history=trans.history ) + history = self.history_manager.get_owned(self.decode_id(history_id), trans.user, + current_history=trans.history) - type = payload.get( 'type', 'dataset' ) + type = payload.get('type', 'dataset') if type == 'dataset': - source = payload.get( 'source', None ) + source = payload.get('source', None) if source == 'library_folder': - return self.__create_datasets_from_library_folder( trans, history, payload, **kwd ) + return self.__create_datasets_from_library_folder(trans, history, payload, **kwd) else: - return self.__create_dataset( trans, history, payload, **kwd ) + return self.__create_dataset(trans, history, payload, **kwd) elif type == 'dataset_collection': - return self.__create_dataset_collection( trans, history, payload, **kwd ) + return self.__create_dataset_collection(trans, history, payload, **kwd) else: - return self.__handle_unknown_contents_type( trans, type ) + return self.__handle_unknown_contents_type(trans, type) - def __create_dataset( self, trans, history, payload, **kwd ): - source = payload.get( 'source', None ) - if source not in ( 'library', 'hda' ): + def __create_dataset(self, trans, history, payload, **kwd): + source = payload.get('source', None) + if source not in ('library', 'hda'): raise exceptions.RequestParameterInvalidException( - "'source' must be either 'library' or 'hda': %s" % ( source ) ) - content = payload.get( 'content', None ) + "'source' must be either 'library' or 'hda': %s" % (source)) + content = payload.get('content', None) if content is None: - raise exceptions.RequestParameterMissingException( "'content' id of lda or hda is missing" ) + raise exceptions.RequestParameterMissingException("'content' id of lda or hda is missing") # copy from library dataset hda = None if source == 'library': - ld = self.get_library_dataset( trans, content, check_ownership=False, check_accessible=False ) + ld = self.get_library_dataset(trans, content, check_ownership=False, check_accessible=False) # TODO: why would get_library_dataset NOT return a library dataset? - if type( ld ) is not trans.app.model.LibraryDataset: + if type(ld) is not trans.app.model.LibraryDataset: raise exceptions.RequestParameterInvalidException( - "Library content id ( %s ) is not a dataset" % content ) + "Library content id ( %s ) is not a dataset" % content) # insert into history - hda = ld.library_dataset_dataset_association.to_history_dataset_association( history, add_to_history=True ) + hda = ld.library_dataset_dataset_association.to_history_dataset_association(history, add_to_history=True) # copy an existing, accessible hda elif source == 'hda': - unencoded_hda_id = self.decode_id( content ) - original = self.hda_manager.get_accessible( unencoded_hda_id, trans.user ) + unencoded_hda_id = self.decode_id(content) + original = self.hda_manager.get_accessible(unencoded_hda_id, trans.user) # check for access on history that contains the original hda as well - self.history_manager.error_unless_accessible( original.history, trans.user, current_history=trans.history ) - hda = self.hda_manager.copy( original, history=history ) + self.history_manager.error_unless_accessible(original.history, trans.user, current_history=trans.history) + hda = self.hda_manager.copy(original, history=history) trans.sa_session.flush() if not hda: return None - return self.hda_serializer.serialize_to_view( hda, - user=trans.user, trans=trans, **self._parse_serialization_params( kwd, 'detailed' ) ) + return self.hda_serializer.serialize_to_view(hda, + user=trans.user, trans=trans, **self._parse_serialization_params(kwd, 'detailed')) - def __create_datasets_from_library_folder( self, trans, history, payload, **kwd ): + def __create_datasets_from_library_folder(self, trans, history, payload, **kwd): rval = [] - source = payload.get( 'source', None ) + source = payload.get('source', None) if source == 'library_folder': - content = payload.get( 'content', None ) + content = payload.get('content', None) if content is None: - raise exceptions.RequestParameterMissingException( "'content' id of lda or hda is missing" ) + raise exceptions.RequestParameterMissingException("'content' id of lda or hda is missing") - folder_id = self.folder_manager.cut_and_decode( trans, content ) - folder = self.folder_manager.get( trans, folder_id ) + folder_id = self.folder_manager.cut_and_decode(trans, content) + folder = self.folder_manager.get(trans, folder_id) current_user_roles = trans.get_current_user_roles() - def traverse( folder ): + def traverse(folder): admin = trans.user_is_admin() rval = [] for subfolder in folder.active_folders: if not admin: - can_access, folder_ids = trans.app.security_agent.check_folder_contents( trans.user, current_user_roles, subfolder ) + can_access, folder_ids = trans.app.security_agent.check_folder_contents(trans.user, current_user_roles, subfolder) if (admin or can_access) and not subfolder.deleted: - rval.extend( traverse( subfolder ) ) + rval.extend(traverse(subfolder)) for ld in folder.datasets: if not admin: can_access = trans.app.security_agent.can_access_dataset( @@ -350,14 +350,14 @@ def traverse( folder ): ld.library_dataset_dataset_association.dataset ) if (admin or can_access) and not ld.deleted: - rval.append( ld ) + rval.append(ld) return rval - for ld in traverse( folder ): - hda = ld.library_dataset_dataset_association.to_history_dataset_association( history, add_to_history=True ) - hda_dict = self.hda_serializer.serialize_to_view( hda, - user=trans.user, trans=trans, **self._parse_serialization_params( kwd, 'detailed' ) ) - rval.append( hda_dict ) + for ld in traverse(folder): + hda = ld.library_dataset_dataset_association.to_history_dataset_association(history, add_to_history=True) + hda_dict = self.hda_serializer.serialize_to_view(hda, + user=trans.user, trans=trans, **self._parse_serialization_params(kwd, 'detailed')) + rval.append(hda_dict) else: message = "Invalid 'source' parameter in request %s" % source raise exceptions.RequestParameterInvalidException(message) @@ -365,20 +365,20 @@ def traverse( folder ): trans.sa_session.flush() return rval - def __create_dataset_collection( self, trans, history, payload, **kwd ): - source = kwd.get( "source", payload.get( "source", "new_collection" ) ) + def __create_dataset_collection(self, trans, history, payload, **kwd): + source = kwd.get("source", payload.get("source", "new_collection")) service = trans.app.dataset_collections_service if source == "new_collection": - create_params = api_payload_to_create_params( payload ) + create_params = api_payload_to_create_params(payload) dataset_collection_instance = service.create( trans, parent=history, **create_params ) elif source == "hdca": - content = payload.get( 'content', None ) + content = payload.get('content', None) if content is None: - raise exceptions.RequestParameterMissingException( "'content' id of target to copy is missing" ) + raise exceptions.RequestParameterMissingException("'content' id of target to copy is missing") dataset_collection_instance = service.copy( trans=trans, parent=history, @@ -391,13 +391,13 @@ def __create_dataset_collection( self, trans, history, payload, **kwd ): # if the consumer specified keys or view, use the secondary serializer if 'view' in kwd or 'keys' in kwd: - return self.hdca_serializer.serialize_to_view( dataset_collection_instance, - user=trans.user, trans=trans, **self._parse_serialization_params( kwd, 'detailed' ) ) + return self.hdca_serializer.serialize_to_view(dataset_collection_instance, + user=trans.user, trans=trans, **self._parse_serialization_params(kwd, 'detailed')) - return self.__collection_dict( trans, dataset_collection_instance, view="element" ) + return self.__collection_dict(trans, dataset_collection_instance, view="element") @expose_api_anonymous - def update( self, trans, history_id, id, payload, **kwd ): + def update(self, trans, history_id, id, payload, **kwd): """ update( self, trans, history_id, id, payload, **kwd ) * PUT /api/histories/{history_id}/contents/{id} @@ -421,54 +421,54 @@ def update( self, trans, history_id, id, payload, **kwd ): # TODO: PUT /api/histories/{encoded_history_id} payload = { rating: rating } (w/ no security checks) contents_type = kwd.get('type', 'dataset') if contents_type == "dataset": - return self.__update_dataset( trans, history_id, id, payload, **kwd ) + return self.__update_dataset(trans, history_id, id, payload, **kwd) elif contents_type == "dataset_collection": - return self.__update_dataset_collection( trans, history_id, id, payload, **kwd ) + return self.__update_dataset_collection(trans, history_id, id, payload, **kwd) else: - return self.__handle_unknown_contents_type( trans, contents_type ) + return self.__handle_unknown_contents_type(trans, contents_type) - def __update_dataset( self, trans, history_id, id, payload, **kwd ): + def __update_dataset(self, trans, history_id, id, payload, **kwd): # anon user: ensure that history ids match up and the history is the current, # check for uploading, and use only the subset of attribute keys manipulatable by anon users if trans.user is None: - hda = self.hda_manager.by_id( self.decode_id( id ) ) + hda = self.hda_manager.by_id(self.decode_id(id)) if hda.history != trans.history: - raise exceptions.AuthenticationRequired( 'API authentication required for this request' ) - hda = self.hda_manager.error_if_uploading( hda ) + raise exceptions.AuthenticationRequired('API authentication required for this request') + hda = self.hda_manager.error_if_uploading(hda) anon_allowed_payload = {} if 'deleted' in payload: - anon_allowed_payload[ 'deleted' ] = payload[ 'deleted' ] + anon_allowed_payload['deleted'] = payload['deleted'] if 'visible' in payload: - anon_allowed_payload[ 'visible' ] = payload[ 'visible' ] + anon_allowed_payload['visible'] = payload['visible'] payload = anon_allowed_payload # logged in user: use full payload, check state if deleting, and make sure the history is theirs else: - hda = self.hda_manager.get_owned( self.decode_id( id ), trans.user, current_history=trans.history ) + hda = self.hda_manager.get_owned(self.decode_id(id), trans.user, current_history=trans.history) # only check_state if not deleting, otherwise cannot delete uploading files - check_state = not payload.get( 'deleted', False ) + check_state = not payload.get('deleted', False) if check_state: - hda = self.hda_manager.error_if_uploading( hda ) + hda = self.hda_manager.error_if_uploading(hda) # make the actual changes if hda: - self.hda_deserializer.deserialize( hda, payload, user=trans.user, trans=trans ) + self.hda_deserializer.deserialize(hda, payload, user=trans.user, trans=trans) # TODO: this should be an effect of deleting the hda - if payload.get( 'deleted', False ): - self.hda_manager.stop_creating_job( hda ) - return self.hda_serializer.serialize_to_view( hda, - user=trans.user, trans=trans, **self._parse_serialization_params( kwd, 'detailed' ) ) + if payload.get('deleted', False): + self.hda_manager.stop_creating_job(hda) + return self.hda_serializer.serialize_to_view(hda, + user=trans.user, trans=trans, **self._parse_serialization_params(kwd, 'detailed')) return {} - def __update_dataset_collection( self, trans, history_id, id, payload, **kwd ): - return trans.app.dataset_collections_service.update( trans, "history", id, payload ) + def __update_dataset_collection(self, trans, history_id, id, payload, **kwd): + return trans.app.dataset_collections_service.update(trans, "history", id, payload) # TODO: allow anonymous del/purge and test security on this @expose_api - def delete( self, trans, history_id, id, purge=False, **kwd ): + def delete(self, trans, history_id, id, purge=False, **kwd): """ delete( self, trans, history_id, id, **kwd ) * DELETE /api/histories/{history_id}/contents/{id} @@ -496,34 +496,34 @@ def delete( self, trans, history_id, id, purge=False, **kwd ): """ contents_type = kwd.get('type', 'dataset') if contents_type == "dataset": - return self.__delete_dataset( trans, history_id, id, purge=purge, **kwd ) + return self.__delete_dataset(trans, history_id, id, purge=purge, **kwd) elif contents_type == "dataset_collection": - trans.app.dataset_collections_service.delete( trans, "history", id ) - return { 'id' : id, "deleted": True } + trans.app.dataset_collections_service.delete(trans, "history", id) + return {'id' : id, "deleted": True} else: - return self.__handle_unknown_contents_type( trans, contents_type ) + return self.__handle_unknown_contents_type(trans, contents_type) - def __delete_dataset( self, trans, history_id, id, purge, **kwd ): + def __delete_dataset(self, trans, history_id, id, purge, **kwd): # get purge from the query or from the request body payload (a request body is optional here) - purge = util.string_as_bool( purge ) - if kwd.get( 'payload', None ): + purge = util.string_as_bool(purge) + if kwd.get('payload', None): # payload takes priority - purge = util.string_as_bool( kwd['payload'].get( 'purge', purge ) ) + purge = util.string_as_bool(kwd['payload'].get('purge', purge)) - hda = self.hda_manager.get_owned( self.decode_id( id ), trans.user, current_history=trans.history ) - self.hda_manager.error_if_uploading( hda ) + hda = self.hda_manager.get_owned(self.decode_id(id), trans.user, current_history=trans.history) + self.hda_manager.error_if_uploading(hda) if purge: - self.hda_manager.purge( hda ) + self.hda_manager.purge(hda) else: - self.hda_manager.delete( hda ) - return self.hda_serializer.serialize_to_view( hda, - user=trans.user, trans=trans, **self._parse_serialization_params( kwd, 'detailed' ) ) + self.hda_manager.delete(hda) + return self.hda_serializer.serialize_to_view(hda, + user=trans.user, trans=trans, **self._parse_serialization_params(kwd, 'detailed')) - def __handle_unknown_contents_type( self, trans, contents_type ): + def __handle_unknown_contents_type(self, trans, contents_type): raise exceptions.UnknownContentsType('Unknown contents type: %s' % type) - def __index_v2( self, trans, history_id, **kwd ): + def __index_v2(self, trans, history_id, **kwd): """ index( self, trans, history_id, **kwd ) * GET /api/histories/{history_id}/contents @@ -599,60 +599,60 @@ def __index_v2( self, trans, history_id, **kwd ): """ rval = [] - history = self.history_manager.get_accessible( self.decode_id( history_id ), trans.user, - current_history=trans.history ) + history = self.history_manager.get_accessible(self.decode_id(history_id), trans.user, + current_history=trans.history) - filter_params = self.parse_filter_params( kwd ) - filters = self.history_contents_filters.parse_filters( filter_params ) - limit, offset = self.parse_limit_offset( kwd ) - order_by = self._parse_order_by( kwd.get( 'order', 'hid-asc' ) ) - serialization_params = self._parse_serialization_params( kwd, 'summary' ) + filter_params = self.parse_filter_params(kwd) + filters = self.history_contents_filters.parse_filters(filter_params) + limit, offset = self.parse_limit_offset(kwd) + order_by = self._parse_order_by(kwd.get('order', 'hid-asc')) + serialization_params = self._parse_serialization_params(kwd, 'summary') # TODO: > 16.04: remove these # TODO: remove 'dataset_details' and the following section when the UI doesn't need it # details param allows a mixed set of summary and detailed hdas # Ever more convoluted due to backwards compat..., details # should be considered deprecated in favor of more specific # dataset_details (and to be implemented dataset_collection_details). - details = kwd.get( 'details', [] ) + details = kwd.get('details', []) if details and details != 'all': - details = util.listify( details ) - view = serialization_params.pop( 'view' ) + details = util.listify(details) + view = serialization_params.pop('view') - contents = self.history_contents_manager.contents( history, - filters=filters, limit=limit, offset=offset, order_by=order_by ) + contents = self.history_contents_manager.contents(history, + filters=filters, limit=limit, offset=offset, order_by=order_by) for content in contents: # TODO: remove split - if isinstance( content, trans.app.model.HistoryDatasetAssociation ): + if isinstance(content, trans.app.model.HistoryDatasetAssociation): # TODO: remove split - if details == 'all' or trans.security.encode_id( content.id ) in details: - rval.append( self.hda_serializer.serialize_to_view( content, - user=trans.user, trans=trans, view='detailed', **serialization_params ) ) + if details == 'all' or trans.security.encode_id(content.id) in details: + rval.append(self.hda_serializer.serialize_to_view(content, + user=trans.user, trans=trans, view='detailed', **serialization_params)) else: - rval.append( self.hda_serializer.serialize_to_view( content, - user=trans.user, trans=trans, view=view, **serialization_params ) ) + rval.append(self.hda_serializer.serialize_to_view(content, + user=trans.user, trans=trans, view=view, **serialization_params)) - elif isinstance( content, trans.app.model.HistoryDatasetCollectionAssociation ): - collection = self.hdca_serializer.serialize_to_view( content, - user=trans.user, trans=trans, view=view, **serialization_params ) - rval.append( collection ) + elif isinstance(content, trans.app.model.HistoryDatasetCollectionAssociation): + collection = self.hdca_serializer.serialize_to_view(content, + user=trans.user, trans=trans, view=view, **serialization_params) + rval.append(collection) return rval - def encode_type_id( self, type_id ): + def encode_type_id(self, type_id): TYPE_ID_SEP = '-' - split = type_id.split( TYPE_ID_SEP, 1 ) - return TYPE_ID_SEP.join([ split[0], self.app.security.encode_id( split[1] )]) + split = type_id.split(TYPE_ID_SEP, 1) + return TYPE_ID_SEP.join([split[0], self.app.security.encode_id(split[1])]) - def _parse_order_by( self, order_by_string ): + def _parse_order_by(self, order_by_string): ORDER_BY_SEP_CHAR = ',' manager = self.history_contents_manager if ORDER_BY_SEP_CHAR in order_by_string: - return [ manager.parse_order_by( o ) for o in order_by_string.split( ORDER_BY_SEP_CHAR ) ] - return manager.parse_order_by( order_by_string ) + return [manager.parse_order_by(o) for o in order_by_string.split(ORDER_BY_SEP_CHAR)] + return manager.parse_order_by(order_by_string) @expose_api_raw - def archive( self, trans, history_id, filename='', format='tgz', dry_run=True, **kwd ): + def archive(self, trans, history_id, filename='', format='tgz', dry_run=True, **kwd): """ archive( self, trans, history_id, filename='', format='tgz', dry_run=True, **kwd ) * GET /api/histories/{history_id}/contents/archive/{id} @@ -670,87 +670,87 @@ def archive( self, trans, history_id, filename='', format='tgz', dry_run=True, * .. note:: this is a volatile endpoint and settings and behavior may change. """ # roughly from: http://stackoverflow.com/a/31976060 (windows, linux) - invalid_filename_char_regex = re.compile( r'[:<>|\\\/\?\* "]' ) + invalid_filename_char_regex = re.compile(r'[:<>|\\\/\?\* "]') # path format string - dot separator between id and name id_name_format = u'{}.{}' - def name_to_filename( name, max_length=150, replace_with=u'_' ): + def name_to_filename(name, max_length=150, replace_with=u'_'): # TODO: seems like shortening unicode with [:] would cause unpredictable display strings - return invalid_filename_char_regex.sub( replace_with, name )[0:max_length] + return invalid_filename_char_regex.sub(replace_with, name)[0:max_length] # given a set of parents for a dataset (HDCAs, DC, DCEs, etc.) - build a directory structure that # (roughly) recreates the nesting in the contents using the parent names and ids - def build_path_from_parents( parents ): + def build_path_from_parents(parents): parent_names = [] for parent in parents: # an HDCA - if hasattr( parent, 'hid' ): - name = name_to_filename( parent.name ) - parent_names.append( id_name_format.format( parent.hid, name ) ) + if hasattr(parent, 'hid'): + name = name_to_filename(parent.name) + parent_names.append(id_name_format.format(parent.hid, name)) # a DCE - elif hasattr( parent, 'element_index' ): - name = name_to_filename( parent.element_identifier ) - parent_names.append( id_name_format.format( parent.element_index, name ) ) + elif hasattr(parent, 'element_index'): + name = name_to_filename(parent.element_identifier) + parent_names.append(id_name_format.format(parent.element_index, name)) # NOTE: DCs are skipped and use the wrapping DCE info instead return parent_names # get the history used for the contents query and check for accessibility - history = self.history_manager.get_accessible( trans.security.decode_id( history_id ), trans.user ) - archive_base_name = filename or name_to_filename( history.name ) + history = self.history_manager.get_accessible(trans.security.decode_id(history_id), trans.user) + archive_base_name = filename or name_to_filename(history.name) # this is the fn applied to each dataset contained in the query paths_and_files = [] - def build_archive_files_and_paths( content, *parents ): + def build_archive_files_and_paths(content, *parents): archive_path = archive_base_name - if not self.hda_manager.is_accessible( content, trans.user ): + if not self.hda_manager.is_accessible(content, trans.user): # if the underlying dataset is not accessible, skip it silently return content_container_id = content.hid - content_name = name_to_filename( content.name ) + content_name = name_to_filename(content.name) if parents: - if hasattr( parents[0], 'element_index' ): + if hasattr(parents[0], 'element_index'): # if content is directly wrapped in a DCE, strip it from parents (and the resulting path) # and instead replace the content id and name with the DCE index and identifier parent_dce, parents = parents[0], parents[1:] content_container_id = parent_dce.element_index - content_name = name_to_filename( parent_dce.element_identifier ) + content_name = name_to_filename(parent_dce.element_identifier) # reverse for path from parents: oldest parent first - archive_path = os.path.join( archive_path, *build_path_from_parents( parents )[::-1] ) + archive_path = os.path.join(archive_path, *build_path_from_parents(parents)[::-1]) # TODO: this is brute force - building the path each time instead of re-using it # possibly cache # add the name as the last element in the archive path - content_id_and_name = id_name_format.format( content_container_id, content_name ) - archive_path = os.path.join( archive_path, content_id_and_name ) + content_id_and_name = id_name_format.format(content_container_id, content_name) + archive_path = os.path.join(archive_path, content_id_and_name) # ---- for composite files, we use id and name for a directory and, inside that, ... - if self.hda_manager.is_composite( content ): + if self.hda_manager.is_composite(content): # ...save the 'main' composite file (gen. html) - paths_and_files.append( ( content.file_name, os.path.join( archive_path, content.name + '.html' ) ) ) - for extra_file in self.hda_manager.extra_files( content ): - extra_file_basename = os.path.basename( extra_file ) - archive_extra_file_path = os.path.join( archive_path, extra_file_basename ) + paths_and_files.append((content.file_name, os.path.join(archive_path, content.name + '.html'))) + for extra_file in self.hda_manager.extra_files(content): + extra_file_basename = os.path.basename(extra_file) + archive_extra_file_path = os.path.join(archive_path, extra_file_basename) # ...and one for each file in the composite - paths_and_files.append( ( extra_file, archive_extra_file_path ) ) + paths_and_files.append((extra_file, archive_extra_file_path)) # ---- for single files, we add the true extension to id and name and store that single filename else: # some dataset names can contain their original file extensions, don't repeat - if not archive_path.endswith( '.' + content.extension ): + if not archive_path.endswith('.' + content.extension): archive_path += '.' + content.extension - paths_and_files.append( ( content.file_name, archive_path ) ) + paths_and_files.append((content.file_name, archive_path)) # filter the contents that contain datasets using any filters possible from index above and map the datasets - filter_params = self.parse_filter_params( kwd ) - filters = self.history_contents_filters.parse_filters( filter_params ) - self.history_contents_manager.map_datasets( history, build_archive_files_and_paths, filters=filters ) + filter_params = self.parse_filter_params(kwd) + filters = self.history_contents_filters.parse_filters(filter_params) + self.history_contents_manager.map_datasets(history, build_archive_files_and_paths, filters=filters) # if dry_run, return the structure as json for debugging if dry_run == 'True': trans.response.headers['Content-Type'] = 'application/json' - return safe_dumps( paths_and_files ) + return safe_dumps(paths_and_files) # create the archive, add the dataset files, then stream the archive as a download archive_type_string = 'w|gz' @@ -758,14 +758,14 @@ def build_archive_files_and_paths( content, *parents ): if self.app.config.upstream_gzip: archive_type_string = 'w|' archive_ext = 'tar' - archive = StreamBall( archive_type_string ) + archive = StreamBall(archive_type_string) for file_path, archive_path in paths_and_files: - archive.add( file_path, archive_path ) + archive.add(file_path, archive_path) - archive_name = '.'.join([ archive_base_name, archive_ext ]) - trans.response.set_content_type( "application/x-tar" ) - trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="{}"'.format( archive_name ) + archive_name = '.'.join([archive_base_name, archive_ext]) + trans.response.set_content_type("application/x-tar") + trans.response.headers["Content-Disposition"] = 'attachment; filename="{}"'.format(archive_name) archive.wsgi_status = trans.response.wsgi_status() archive.wsgi_headeritems = trans.response.wsgi_headeritems() return archive.stream diff --git a/lib/galaxy/webapps/galaxy/api/item_tags.py b/lib/galaxy/webapps/galaxy/api/item_tags.py index 0e3cc1f76160..995f0d3823e6 100644 --- a/lib/galaxy/webapps/galaxy/api/item_tags.py +++ b/lib/galaxy/webapps/galaxy/api/item_tags.py @@ -8,67 +8,67 @@ from galaxy import exceptions import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class BaseItemTagsController( BaseAPIController, UsesTagsMixin ): +class BaseItemTagsController(BaseAPIController, UsesTagsMixin): """ """ @expose_api - def index( self, trans, **kwd ): + def index(self, trans, **kwd): """ """ - tags = self._get_user_tags(trans, self.tagged_item_class, kwd[ self.tagged_item_id ]) - return [ self._api_value( tag, trans, view='collection' ) for tag in tags ] + tags = self._get_user_tags(trans, self.tagged_item_class, kwd[self.tagged_item_id]) + return [self._api_value(tag, trans, view='collection') for tag in tags] @expose_api - def show( self, trans, tag_name, **kwd ): + def show(self, trans, tag_name, **kwd): """ """ - tag = self._get_item_tag_assoc( trans, self.tagged_item_class, kwd[ self.tagged_item_id ], tag_name ) + tag = self._get_item_tag_assoc(trans, self.tagged_item_class, kwd[self.tagged_item_id], tag_name) if not tag: - raise exceptions.ObjectNotFound( "Failed to retrieve specified tag." ) - return self._api_value( tag, trans ) + raise exceptions.ObjectNotFound("Failed to retrieve specified tag.") + return self._api_value(tag, trans) @expose_api - def create( self, trans, tag_name, payload=None, **kwd ): + def create(self, trans, tag_name, payload=None, **kwd): """ """ payload = payload or {} - value = payload.get( "value", None ) - tag = self._apply_item_tag( trans, self.tagged_item_class, kwd[ self.tagged_item_id ], tag_name, value ) - return self._api_value( tag, trans ) + value = payload.get("value", None) + tag = self._apply_item_tag(trans, self.tagged_item_class, kwd[self.tagged_item_id], tag_name, value) + return self._api_value(tag, trans) # Not handling these differently at this time update = create @expose_api - def delete( self, trans, tag_name, **kwd ): + def delete(self, trans, tag_name, **kwd): """ """ - deleted = self._remove_items_tag( trans, self.tagged_item_class, kwd[ self.tagged_item_id ], tag_name ) + deleted = self._remove_items_tag(trans, self.tagged_item_class, kwd[self.tagged_item_id], tag_name) if not deleted: - raise exceptions.RequestParameterInvalidException( "Failed to delete specified tag." ) + raise exceptions.RequestParameterInvalidException("Failed to delete specified tag.") # TODO: ugh - 204 would be better return 'OK' - def _api_value( self, tag, trans, view='element' ): - return tag.to_dict( view=view, value_mapper={ 'id': trans.security.encode_id } ) + def _api_value(self, tag, trans, view='element'): + return tag.to_dict(view=view, value_mapper={'id': trans.security.encode_id}) -class HistoryContentTagsController( BaseItemTagsController ): +class HistoryContentTagsController(BaseItemTagsController): controller_name = "history_content_tags" tagged_item_class = "HistoryDatasetAssociation" tagged_item_id = "history_content_id" -class HistoryTagsController( BaseItemTagsController ): +class HistoryTagsController(BaseItemTagsController): controller_name = "history_tags" tagged_item_class = "History" tagged_item_id = "history_id" -class WorkflowTagsController( BaseItemTagsController ): +class WorkflowTagsController(BaseItemTagsController): controller_name = "workflow_tags" tagged_item_class = "StoredWorkflow" tagged_item_id = "workflow_id" diff --git a/lib/galaxy/webapps/galaxy/api/job_files.py b/lib/galaxy/webapps/galaxy/api/job_files.py index 3abac0f3b39d..9a9137f4ed2f 100644 --- a/lib/galaxy/webapps/galaxy/api/job_files.py +++ b/lib/galaxy/webapps/galaxy/api/job_files.py @@ -13,10 +13,10 @@ import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class JobFilesAPIController( BaseAPIController ): +class JobFilesAPIController(BaseAPIController): """ This job files controller allows remote job running mechanisms to read and modify the current state of files for queued and running jobs. It is certainly not meant to represent part of Galaxy's stable, user @@ -29,7 +29,7 @@ class JobFilesAPIController( BaseAPIController ): """ @expose_api_raw_anonymous_and_sessionless - def index( self, trans, job_id, **kwargs ): + def index(self, trans, job_id, **kwargs): """ index( self, trans, job_id, **kwargs ) * GET /api/jobs/{job_id}/files @@ -50,12 +50,12 @@ def index( self, trans, job_id, **kwargs ): :rtype: binary :returns: contents of file """ - self.__authorize_job_access( trans, job_id, **kwargs ) + self.__authorize_job_access(trans, job_id, **kwargs) path = kwargs.get("path", None) return open(path, 'rb') @expose_api_anonymous_and_sessionless - def create( self, trans, job_id, payload, **kwargs ): + def create(self, trans, job_id, payload, **kwargs): """ create( self, trans, job_id, payload, **kwargs ) * POST /api/jobs/{job_id}/files @@ -78,26 +78,26 @@ def create( self, trans, job_id, payload, **kwargs ): :rtype: dict :returns: an okay message """ - job = self.__authorize_job_access( trans, job_id, **payload ) - path = payload.get( "path" ) - self.__check_job_can_write_to_path( trans, job, path ) + job = self.__authorize_job_access(trans, job_id, **payload) + path = payload.get("path") + self.__check_job_can_write_to_path(trans, job, path) # Is this writing an unneeded file? Should this just copy in Python? if '__file_path' in payload: - file_path = payload.get( '__file_path' ) + file_path = payload.get('__file_path') upload_store = trans.app.config.nginx_upload_job_files_store - assert upload_store, ( "Request appears to have been processed by" - " nginx_upload_module but Galaxy is not" - " configured to recognize it" ) - assert file_path.startswith( upload_store ), \ - ( "Filename provided by nginx (%s) is not in correct" - " directory (%s)" % ( file_path, upload_store ) ) - input_file = open( file_path ) + assert upload_store, ("Request appears to have been processed by" + " nginx_upload_module but Galaxy is not" + " configured to recognize it") + assert file_path.startswith(upload_store), \ + ("Filename provided by nginx (%s) is not in correct" + " directory (%s)" % (file_path, upload_store)) + input_file = open(file_path) else: - input_file = payload.get( "file", - payload.get( "__file", None ) ).file + input_file = payload.get("file", + payload.get("__file", None)).file try: - shutil.move( input_file.name, path ) + shutil.move(input_file.name, path) finally: try: input_file.close() @@ -108,24 +108,24 @@ def create( self, trans, job_id, payload, **kwargs ): return {"message": "ok"} def __authorize_job_access(self, trans, encoded_job_id, **kwargs): - for key in [ "path", "job_key" ]: + for key in ["path", "job_key"]: if key not in kwargs: error_message = "Job files action requires a valid '%s'." % key - raise exceptions.ObjectAttributeMissingException( error_message ) + raise exceptions.ObjectAttributeMissingException(error_message) - job_id = trans.security.decode_id( encoded_job_id ) - job_key = trans.security.encode_id( job_id, kind="jobs_files" ) - if not util.safe_str_cmp( kwargs[ "job_key" ], job_key ): + job_id = trans.security.decode_id(encoded_job_id) + job_key = trans.security.encode_id(job_id, kind="jobs_files") + if not util.safe_str_cmp(kwargs["job_key"], job_key): raise exceptions.ItemAccessibilityException("Invalid job_key supplied.") # Verify job is active. Don't update the contents of complete jobs. - job = trans.sa_session.query( model.Job ).get( job_id ) + job = trans.sa_session.query(model.Job).get(job_id) if job.finished: error_message = "Attempting to read or modify the files of a job that has already completed." - raise exceptions.ItemAccessibilityException( error_message ) + raise exceptions.ItemAccessibilityException(error_message) return job - def __check_job_can_write_to_path( self, trans, job, path ): + def __check_job_can_write_to_path(self, trans, job, path): """ Verify an idealized job runner should actually be able to write to the specified path - it must be a dataset output, a dataset "extra file", or a some place in the working directory of this job. @@ -134,33 +134,33 @@ def __check_job_can_write_to_path( self, trans, job, path ): files make this very difficult. (See abandoned work here https://gist.github.com/jmchilton/9103619.) """ - in_work_dir = self.__in_working_directory( job, path, trans.app ) - allow_temp_dir_file = self.__is_allowed_temp_dir_file( trans.app, job, path ) - if not in_work_dir and not allow_temp_dir_file and not self.__is_output_dataset_path( job, path ): + in_work_dir = self.__in_working_directory(job, path, trans.app) + allow_temp_dir_file = self.__is_allowed_temp_dir_file(trans.app, job, path) + if not in_work_dir and not allow_temp_dir_file and not self.__is_output_dataset_path(job, path): raise exceptions.ItemAccessibilityException("Job is not authorized to write to supplied path.") - def __is_allowed_temp_dir_file( self, app, job, path ): + def __is_allowed_temp_dir_file(self, app, job, path): # grrr.. need to get away from new_file_path - these should be written # to job working directory like metadata files. - in_temp_dir = util.in_directory( path, app.config.new_file_path ) - return in_temp_dir and os.path.split( path )[ -1 ].startswith( "GALAXY_VERSION_") + in_temp_dir = util.in_directory(path, app.config.new_file_path) + return in_temp_dir and os.path.split(path)[-1].startswith("GALAXY_VERSION_") - def __is_output_dataset_path( self, job, path ): + def __is_output_dataset_path(self, job, path): """ Check if is an output path for this job or a file in the an output's extra files path. """ - da_lists = [ job.output_datasets, job.output_library_datasets ] + da_lists = [job.output_datasets, job.output_library_datasets] for da_list in da_lists: for job_dataset_association in da_list: dataset = job_dataset_association.dataset if not dataset: continue - if os.path.abspath( dataset.file_name ) == os.path.abspath( path ): + if os.path.abspath(dataset.file_name) == os.path.abspath(path): return True - elif util.in_directory( path, dataset.extra_files_path ): + elif util.in_directory(path, dataset.extra_files_path): return True return False - def __in_working_directory( self, job, path, app ): + def __in_working_directory(self, job, path, app): working_directory = app.object_store.get_filename(job, base_dir='job_work', dir_only=True, extra_dir=str(job.id)) - return util.in_directory( path, working_directory ) + return util.in_directory(path, working_directory) diff --git a/lib/galaxy/webapps/galaxy/api/jobs.py b/lib/galaxy/webapps/galaxy/api/jobs.py index 42d4008f8553..63367938600f 100644 --- a/lib/galaxy/webapps/galaxy/api/jobs.py +++ b/lib/galaxy/webapps/galaxy/api/jobs.py @@ -20,18 +20,18 @@ from galaxy.web.base.controller import BaseAPIController from galaxy.web.base.controller import UsesLibraryMixinItems -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class JobController( BaseAPIController, UsesLibraryMixinItems ): +class JobController(BaseAPIController, UsesLibraryMixinItems): - def __init__( self, app ): - super( JobController, self ).__init__( app ) - self.hda_manager = managers.hdas.HDAManager( app ) - self.dataset_manager = managers.datasets.DatasetManager( app ) + def __init__(self, app): + super(JobController, self).__init__(app) + self.hda_manager = managers.hdas.HDAManager(app) + self.dataset_manager = managers.datasets.DatasetManager(app) @expose_api - def index( self, trans, **kwd ): + def index(self, trans, **kwd): """ index( trans, state=None, tool_id=None, history_id=None, date_range_min=None, date_range_max=None, user_details=False ) * GET /api/jobs: @@ -63,50 +63,50 @@ def index( self, trans, **kwd ): :rtype: list :returns: list of dictionaries containing summary job information """ - state = kwd.get( 'state', None ) + state = kwd.get('state', None) is_admin = trans.user_is_admin() user_details = kwd.get('user_details', False) if is_admin: - query = trans.sa_session.query( trans.app.model.Job ) + query = trans.sa_session.query(trans.app.model.Job) else: - query = trans.sa_session.query( trans.app.model.Job ).filter(trans.app.model.Job.user == trans.user) + query = trans.sa_session.query(trans.app.model.Job).filter(trans.app.model.Job.user == trans.user) - def build_and_apply_filters( query, objects, filter_func ): + def build_and_apply_filters(query, objects, filter_func): if objects is not None: - if isinstance( objects, string_types ): - query = query.filter( filter_func( objects ) ) - elif isinstance( objects, list ): + if isinstance(objects, string_types): + query = query.filter(filter_func(objects)) + elif isinstance(objects, list): t = [] for obj in objects: - t.append( filter_func( obj ) ) - query = query.filter( or_( *t ) ) + t.append(filter_func(obj)) + query = query.filter(or_(*t)) return query - query = build_and_apply_filters( query, state, lambda s: trans.app.model.Job.state == s ) + query = build_and_apply_filters(query, state, lambda s: trans.app.model.Job.state == s) - query = build_and_apply_filters( query, kwd.get( 'tool_id', None ), lambda t: trans.app.model.Job.tool_id == t ) - query = build_and_apply_filters( query, kwd.get( 'tool_id_like', None ), lambda t: trans.app.model.Job.tool_id.like(t) ) + query = build_and_apply_filters(query, kwd.get('tool_id', None), lambda t: trans.app.model.Job.tool_id == t) + query = build_and_apply_filters(query, kwd.get('tool_id_like', None), lambda t: trans.app.model.Job.tool_id.like(t)) - query = build_and_apply_filters( query, kwd.get( 'date_range_min', None ), lambda dmin: trans.app.model.Job.table.c.update_time >= dmin ) - query = build_and_apply_filters( query, kwd.get( 'date_range_max', None ), lambda dmax: trans.app.model.Job.table.c.update_time <= dmax ) + query = build_and_apply_filters(query, kwd.get('date_range_min', None), lambda dmin: trans.app.model.Job.table.c.update_time >= dmin) + query = build_and_apply_filters(query, kwd.get('date_range_max', None), lambda dmax: trans.app.model.Job.table.c.update_time <= dmax) - history_id = kwd.get( 'history_id', None ) + history_id = kwd.get('history_id', None) if history_id is not None: try: decoded_history_id = self.decode_id(history_id) - query = query.filter( trans.app.model.Job.history_id == decoded_history_id ) + query = query.filter(trans.app.model.Job.history_id == decoded_history_id) except: raise exceptions.ObjectAttributeInvalidException() out = [] - if kwd.get( 'order_by' ) == 'create_time': + if kwd.get('order_by') == 'create_time': order_by = trans.app.model.Job.create_time.desc() else: order_by = trans.app.model.Job.update_time.desc() - for job in query.order_by( order_by ).all(): - job_dict = job.to_dict( 'collection', system_details=is_admin ) - j = self.encode_all_ids( trans, job_dict, True ) + for job in query.order_by(order_by).all(): + job_dict = job.to_dict('collection', system_details=is_admin) + j = self.encode_all_ids(trans, job_dict, True) if user_details: j['user_email'] = job.user.email out.append(j) @@ -114,7 +114,7 @@ def build_and_apply_filters( query, objects, filter_func ): return out @expose_api - def show( self, trans, id, **kwd ): + def show(self, trans, id, **kwd): """ show( trans, id ) * GET /api/jobs/{id}: @@ -129,12 +129,12 @@ def show( self, trans, id, **kwd ): :rtype: dictionary :returns: dictionary containing full description of job data """ - job = self.__get_job( trans, id ) + job = self.__get_job(trans, id) is_admin = trans.user_is_admin() - job_dict = self.encode_all_ids( trans, job.to_dict( 'element', system_details=is_admin ), True ) - full_output = util.asbool( kwd.get( 'full', 'false' ) ) + job_dict = self.encode_all_ids(trans, job.to_dict('element', system_details=is_admin), True) + full_output = util.asbool(kwd.get('full', 'false')) if full_output: - job_dict.update( dict( stderr=job.stderr, stdout=job.stdout ) ) + job_dict.update(dict(stderr=job.stderr, stdout=job.stdout)) if is_admin: job_dict['user_email'] = job.user.email @@ -155,7 +155,7 @@ def metric_to_dict(metric): return job_dict @expose_api - def inputs( self, trans, id, **kwd ): + def inputs(self, trans, id, **kwd): """ show( trans, id ) * GET /api/jobs/{id}/inputs @@ -167,11 +167,11 @@ def inputs( self, trans, id, **kwd ): :rtype: dictionary :returns: dictionary containing input dataset associations """ - job = self.__get_job( trans, id ) - return self.__dictify_associations( trans, job.input_datasets, job.input_library_datasets ) + job = self.__get_job(trans, id) + return self.__dictify_associations(trans, job.input_datasets, job.input_library_datasets) @expose_api - def outputs( self, trans, id, **kwd ): + def outputs(self, trans, id, **kwd): """ show( trans, id ) * GET /api/jobs/{id}/outputs @@ -183,11 +183,11 @@ def outputs( self, trans, id, **kwd ): :rtype: dictionary :returns: dictionary containing output dataset associations """ - job = self.__get_job( trans, id ) - return self.__dictify_associations( trans, job.output_datasets, job.output_library_datasets ) + job = self.__get_job(trans, id) + return self.__dictify_associations(trans, job.output_datasets, job.output_library_datasets) @expose_api_anonymous - def build_for_rerun( self, trans, id, **kwd ): + def build_for_rerun(self, trans, id, **kwd): """ * GET /api/jobs/{id}/build_for_rerun returns a tool input/param template prepopulated with this job's @@ -204,52 +204,52 @@ def build_for_rerun( self, trans, id, **kwd ): job = self.__get_job(trans, id) if not job: raise exceptions.ObjectNotFound("Could not access job with id '%s'" % id) - tool = self.app.toolbox.get_tool( job.tool_id, kwd.get('tool_version') or job.tool_version ) + tool = self.app.toolbox.get_tool(job.tool_id, kwd.get('tool_version') or job.tool_version) if tool is None: - raise exceptions.ObjectNotFound( "Requested tool not found" ) + raise exceptions.ObjectNotFound("Requested tool not found") if not tool.is_workflow_compatible: - raise exceptions.ConfigDoesNotAllowException( "Tool '%s' cannot be rerun." % ( job.tool_id ) ) + raise exceptions.ConfigDoesNotAllowException("Tool '%s' cannot be rerun." % (job.tool_id)) return tool.to_json(trans, {}, job=job) - def __dictify_associations( self, trans, *association_lists ): + def __dictify_associations(self, trans, *association_lists): rval = [] for association_list in association_lists: - rval.extend( map( lambda a: self.__dictify_association( trans, a ), association_list ) ) + rval.extend(map(lambda a: self.__dictify_association(trans, a), association_list)) return rval - def __dictify_association( self, trans, job_dataset_association ): + def __dictify_association(self, trans, job_dataset_association): dataset_dict = None dataset = job_dataset_association.dataset if dataset: - if isinstance( dataset, model.HistoryDatasetAssociation ): - dataset_dict = dict( src="hda", id=trans.security.encode_id( dataset.id ) ) + if isinstance(dataset, model.HistoryDatasetAssociation): + dataset_dict = dict(src="hda", id=trans.security.encode_id(dataset.id)) else: - dataset_dict = dict( src="ldda", id=trans.security.encode_id( dataset.id ) ) - return dict( name=job_dataset_association.name, dataset=dataset_dict ) + dataset_dict = dict(src="ldda", id=trans.security.encode_id(dataset.id)) + return dict(name=job_dataset_association.name, dataset=dataset_dict) - def __get_job( self, trans, id ): + def __get_job(self, trans, id): try: - decoded_job_id = self.decode_id( id ) + decoded_job_id = self.decode_id(id) except Exception: raise exceptions.MalformedId() - job = trans.sa_session.query( trans.app.model.Job ).filter( trans.app.model.Job.id == decoded_job_id ).first() + 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: if not job.output_datasets: - raise exceptions.ItemAccessibilityException( "Job has no output datasets." ) + raise exceptions.ItemAccessibilityException("Job has no output datasets.") for data_assoc in job.output_datasets: - if not self.dataset_manager.is_accessible( data_assoc.dataset.dataset, trans.user ): - raise exceptions.ItemAccessibilityException( "You are not allowed to rerun this job." ) + if not self.dataset_manager.is_accessible(data_assoc.dataset.dataset, trans.user): + raise exceptions.ItemAccessibilityException("You are not allowed to rerun this job.") return job @expose_api - def create( self, trans, payload, **kwd ): + def create(self, trans, payload, **kwd): """ See the create method in tools.py in order to submit a job. """ - raise exceptions.NotImplemented( 'Please POST to /api/tools instead.' ) + raise exceptions.NotImplemented('Please POST to /api/tools instead.') @expose_api - def search( self, trans, payload, **kwd ): + def search(self, trans, payload, **kwd): """ search( trans, payload ) * POST /api/jobs/search: @@ -269,35 +269,35 @@ def search( self, trans, payload, **kwd ): tool_id = None if 'tool_id' in payload: - tool_id = payload.get( 'tool_id' ) + tool_id = payload.get('tool_id') if tool_id is None: - raise exceptions.ObjectAttributeMissingException( "No tool id" ) + raise exceptions.ObjectAttributeMissingException("No tool id") - tool = trans.app.toolbox.get_tool( tool_id ) + tool = trans.app.toolbox.get_tool(tool_id) if tool is None: - raise exceptions.ObjectNotFound( "Requested tool not found" ) + raise exceptions.ObjectNotFound("Requested tool not found") if 'inputs' not in payload: - raise exceptions.ObjectAttributeMissingException( "No inputs defined" ) + raise exceptions.ObjectAttributeMissingException("No inputs defined") - inputs = payload[ 'inputs' ] + inputs = payload['inputs'] input_data = {} input_param = {} for k, v in inputs.items(): - if isinstance( v, dict ): + if isinstance(v, dict): if 'id' in v: - if 'src' not in v or v[ 'src' ] == 'hda': - hda_id = self.decode_id( v['id'] ) - dataset = self.hda_manager.get_accessible( hda_id, trans.user ) + if 'src' not in v or v['src'] == 'hda': + hda_id = self.decode_id(v['id']) + dataset = self.hda_manager.get_accessible(hda_id, trans.user) else: - dataset = self.get_library_dataset_dataset_association( trans, v['id'] ) + dataset = self.get_library_dataset_dataset_association(trans, v['id']) if dataset is None: - raise exceptions.ObjectNotFound( "Dataset %s not found" % ( v[ 'id' ] ) ) + raise exceptions.ObjectNotFound("Dataset %s not found" % (v['id'])) input_data[k] = dataset.dataset_id else: - input_param[k] = json.dumps( str(v) ) + input_param[k] = json.dumps(str(v)) - query = trans.sa_session.query( trans.app.model.Job ).filter( + query = trans.sa_session.query(trans.app.model.Job).filter( trans.app.model.Job.tool_id == tool_id, trans.app.model.Job.user == trans.user ) @@ -313,23 +313,23 @@ def search( self, trans, payload, **kwd ): ) ) else: - if isinstance( payload[ 'state' ], string_types ): - query = query.filter( trans.app.model.Job.state == payload[ 'state' ] ) - elif isinstance( payload[ 'state' ], list ): + if isinstance(payload['state'], string_types): + query = query.filter(trans.app.model.Job.state == payload['state']) + elif isinstance(payload['state'], list): o = [] - for s in payload[ 'state' ]: - o.append( trans.app.model.Job.state == s ) + for s in payload['state']: + o.append(trans.app.model.Job.state == s) query = query.filter( - or_( *o ) + or_(*o) ) for k, v in input_param.items(): - a = aliased( trans.app.model.JobParameter ) - query = query.filter( and_( + a = aliased(trans.app.model.JobParameter) + query = query.filter(and_( trans.app.model.Job.id == a.job_id, a.name == k, a.value == v - ) ) + )) for k, v in input_data.items(): # Here we are attempting to link the inputs to the underlying @@ -337,18 +337,49 @@ def search( self, trans, payload, **kwd ): # This way, if the calculation was done using a copied HDA # (copied from the library or another history), the search will # still find the job - a = aliased( trans.app.model.JobToInputDatasetAssociation ) - b = aliased( trans.app.model.HistoryDatasetAssociation ) - query = query.filter( and_( + a = aliased(trans.app.model.JobToInputDatasetAssociation) + b = aliased(trans.app.model.HistoryDatasetAssociation) + query = query.filter(and_( trans.app.model.Job.id == a.job_id, a.dataset_id == b.id, b.deleted == false(), b.dataset_id == v - ) ) + )) out = [] for job in query.all(): # check to make sure none of the output files have been deleted - if all( list( a.dataset.deleted is False for a in job.output_datasets ) ): - out.append( self.encode_all_ids( trans, job.to_dict( 'element' ), True ) ) + if all(list(a.dataset.deleted is False for a in job.output_datasets)): + out.append(self.encode_all_ids(trans, job.to_dict('element'), True)) return out + + @expose_api + def error(self, trans, id, **kwd): + """ + error( trans, id ) + * POST /api/jobs/{id}/error + submits a bug report via the API. + + :type id: string + :param id: Encoded job id + + :rtype: dictionary + :returns: dictionary containing information regarding where the error report was sent. + """ + # Get dataset on which this error was triggered + try: + decoded_dataset_id = self.decode_id(kwd['dataset_id']) + except Exception: + raise exceptions.MalformedId() + dataset = trans.sa_session.query(trans.app.model.HistoryDatasetAssociation).get(decoded_dataset_id) + + # Get job + job = self.__get_job(trans, id) + tool = trans.app.toolbox.get_tool(job.tool_id, tool_version=job.tool_version) or None + messages = trans.app.error_reports.default_error_plugin.submit_report( + dataset, job, tool, user_submission=True, user=trans.user, + email=kwd.get('email', trans.user.email), + message=kwd.get('message', None) + ) + + return {'messages': messages} diff --git a/lib/galaxy/webapps/galaxy/api/lda_datasets.py b/lib/galaxy/webapps/galaxy/api/lda_datasets.py index c1d5a1581f69..6da88342cb0b 100644 --- a/lib/galaxy/webapps/galaxy/api/lda_datasets.py +++ b/lib/galaxy/webapps/galaxy/api/lda_datasets.py @@ -16,7 +16,7 @@ from galaxy import util from galaxy import web from galaxy.exceptions import ObjectNotFound -from galaxy.managers import folders, roles +from galaxy.managers import folders, roles, tags from galaxy.tools.actions import upload_common from galaxy.tools.parameters import populate_state from galaxy.util.streamball import StreamBall @@ -24,18 +24,18 @@ from galaxy.web import _future_expose_api_anonymous as expose_api_anonymous from galaxy.web.base.controller import BaseAPIController, UsesVisualizationMixin -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class LibraryDatasetsController( BaseAPIController, UsesVisualizationMixin ): +class LibraryDatasetsController(BaseAPIController, UsesVisualizationMixin): - def __init__( self, app ): - super( LibraryDatasetsController, self ).__init__( app ) + def __init__(self, app): + super(LibraryDatasetsController, self).__init__(app) self.folder_manager = folders.FolderManager() - self.role_manager = roles.RoleManager( app ) + self.role_manager = roles.RoleManager(app) @expose_api_anonymous - def show( self, trans, id, **kwd ): + def show(self, trans, id, **kwd): """ show( self, trans, id, **kwd ) * GET /api/libraries/datasets/{encoded_dataset_id} @@ -50,41 +50,44 @@ def show( self, trans, id, **kwd ): .. seealso:: :attr:`galaxy.web.base.controller.UsesLibraryMixinItems.get_library_dataset` """ try: - library_dataset = self.get_library_dataset( trans, id=id, check_ownership=False, check_accessible=True ) + library_dataset = self.get_library_dataset(trans, id=id, check_ownership=False, check_accessible=True) except Exception: - raise exceptions.ObjectNotFound( 'Requested library_dataset was not found.' ) + raise exceptions.ObjectNotFound('Requested library_dataset was not found.') current_user_roles = trans.get_current_user_roles() + tag_manager = tags.GalaxyTagManager(trans.sa_session) + # Build the full path for breadcrumb purposes. - full_path = self._build_path( trans, library_dataset.folder ) - dataset_item = ( trans.security.encode_id( library_dataset.id ), library_dataset.name ) + full_path = self._build_path(trans, library_dataset.folder) + dataset_item = (trans.security.encode_id(library_dataset.id), library_dataset.name) full_path.insert(0, dataset_item) - full_path = full_path[ ::-1 ] + full_path = full_path[::-1] # Find expired versions of the library dataset expired_ldda_versions = [] for expired_ldda in library_dataset.expired_datasets: - expired_ldda_versions.append( ( trans.security.encode_id( expired_ldda.id ), expired_ldda.name ) ) + expired_ldda_versions.append((trans.security.encode_id(expired_ldda.id), expired_ldda.name)) - rval = trans.security.encode_all_ids( library_dataset.to_dict() ) + rval = trans.security.encode_all_ids(library_dataset.to_dict()) if len(expired_ldda_versions) > 0: - rval[ 'has_versions' ] = True - rval[ 'expired_versions' ] = expired_ldda_versions - rval[ 'deleted' ] = library_dataset.deleted - rval[ 'folder_id' ] = 'F' + rval[ 'folder_id' ] - rval[ 'full_path' ] = full_path - rval[ 'file_size' ] = util.nice_size( int( library_dataset.library_dataset_dataset_association.get_size() ) ) - rval[ 'date_uploaded' ] = library_dataset.library_dataset_dataset_association.create_time.strftime( "%Y-%m-%d %I:%M %p" ) - rval[ 'can_user_modify' ] = trans.app.security_agent.can_modify_library_item( current_user_roles, library_dataset) or trans.user_is_admin() - rval[ 'is_unrestricted' ] = trans.app.security_agent.dataset_is_public( library_dataset.library_dataset_dataset_association.dataset ) + rval['has_versions'] = True + rval['expired_versions'] = expired_ldda_versions + rval['deleted'] = library_dataset.deleted + rval['folder_id'] = 'F' + rval['folder_id'] + rval['full_path'] = full_path + rval['file_size'] = util.nice_size(int(library_dataset.library_dataset_dataset_association.get_size())) + rval['date_uploaded'] = library_dataset.library_dataset_dataset_association.create_time.strftime("%Y-%m-%d %I:%M %p") + rval['can_user_modify'] = trans.app.security_agent.can_modify_library_item(current_user_roles, library_dataset) or trans.user_is_admin() + rval['is_unrestricted'] = trans.app.security_agent.dataset_is_public(library_dataset.library_dataset_dataset_association.dataset) + rval['tags'] = tag_manager.get_tags_str(library_dataset.library_dataset_dataset_association.tags) # Manage dataset permission is always attached to the dataset itself, not the the ld or ldda to maintain consistency - rval[ 'can_user_manage' ] = trans.app.security_agent.can_manage_dataset( current_user_roles, library_dataset.library_dataset_dataset_association.dataset) or trans.user_is_admin() + rval['can_user_manage'] = trans.app.security_agent.can_manage_dataset(current_user_roles, library_dataset.library_dataset_dataset_association.dataset) or trans.user_is_admin() return rval @expose_api_anonymous - def show_version( self, trans, encoded_dataset_id, encoded_ldda_id, **kwd ): + def show_version(self, trans, encoded_dataset_id, encoded_ldda_id, **kwd): """ show_version( self, trans, encoded_dataset_id, encoded_ldda_id, **kwd ): * GET /api/libraries/datasets/{encoded_dataset_id}/versions/{encoded_ldda_id} @@ -100,23 +103,23 @@ def show_version( self, trans, encoded_dataset_id, encoded_ldda_id, **kwd ): :returns: dict of ldda's details """ try: - library_dataset = self.get_library_dataset( trans, id=encoded_dataset_id, check_ownership=False, check_accessible=True ) + library_dataset = self.get_library_dataset(trans, id=encoded_dataset_id, check_ownership=False, check_accessible=True) except Exception: - raise exceptions.ObjectNotFound( 'Requested library_dataset was not found.' ) + raise exceptions.ObjectNotFound('Requested library_dataset was not found.') try: - ldda = self.get_library_dataset_dataset_association( trans, id=encoded_ldda_id, check_ownership=False, check_accessible=False ) + ldda = self.get_library_dataset_dataset_association(trans, id=encoded_ldda_id, check_ownership=False, check_accessible=False) except Exception as e: - raise exceptions.ObjectNotFound( 'Requested version of library dataset was not found.' + str(e) ) + raise exceptions.ObjectNotFound('Requested version of library dataset was not found.' + str(e)) if ldda not in library_dataset.expired_datasets: - raise exceptions.ObjectNotFound( 'Given library dataset does not have the requested version.' ) + raise exceptions.ObjectNotFound('Given library dataset does not have the requested version.') - rval = trans.security.encode_all_ids( ldda.to_dict() ) + rval = trans.security.encode_all_ids(ldda.to_dict()) return rval @expose_api - def show_roles( self, trans, encoded_dataset_id, **kwd ): + def show_roles(self, trans, encoded_dataset_id, **kwd): """ show_roles( self, trans, id, **kwd ): * GET /api/libraries/datasets/{encoded_dataset_id}/permissions @@ -136,47 +139,47 @@ def show_roles( self, trans, encoded_dataset_id, **kwd ): current_user_roles = trans.get_current_user_roles() try: - library_dataset = self.get_library_dataset( trans, id=encoded_dataset_id, check_ownership=False, check_accessible=False ) + library_dataset = self.get_library_dataset(trans, id=encoded_dataset_id, check_ownership=False, check_accessible=False) except Exception as e: - raise exceptions.ObjectNotFound( 'Requested dataset was not found.' + str(e) ) + raise exceptions.ObjectNotFound('Requested dataset was not found.' + str(e)) dataset = library_dataset.library_dataset_dataset_association.dataset # User has to have manage permissions permission in order to see the roles. - can_manage = trans.app.security_agent.can_manage_dataset( current_user_roles, dataset ) or trans.user_is_admin() + can_manage = trans.app.security_agent.can_manage_dataset(current_user_roles, dataset) or trans.user_is_admin() if not can_manage: - raise exceptions.InsufficientPermissionsException( 'You do not have proper permission to access permissions.' ) + raise exceptions.InsufficientPermissionsException('You do not have proper permission to access permissions.') - scope = kwd.get( 'scope', None ) + scope = kwd.get('scope', None) if scope == 'current' or scope is None: - return self._get_current_roles( trans, library_dataset ) + return self._get_current_roles(trans, library_dataset) # Return roles that are available to select. elif scope == 'available': - page = kwd.get( 'page', None ) + page = kwd.get('page', None) if page is not None: - page = int( page ) + page = int(page) else: page = 1 - page_limit = kwd.get( 'page_limit', None ) + page_limit = kwd.get('page_limit', None) if page_limit is not None: - page_limit = int( page_limit ) + page_limit = int(page_limit) else: page_limit = 10 - query = kwd.get( 'q', None ) + query = kwd.get('q', None) - roles, total_roles = trans.app.security_agent.get_valid_roles( trans, dataset, query, page, page_limit ) + roles, total_roles = trans.app.security_agent.get_valid_roles(trans, dataset, query, page, page_limit) return_roles = [] for role in roles: - role_id = trans.security.encode_id( role.id ) - return_roles.append( dict( id=role_id, name=role.name, type=role.type ) ) - return dict( roles=return_roles, page=page, page_limit=page_limit, total=total_roles ) + role_id = trans.security.encode_id(role.id) + return_roles.append(dict(id=role_id, name=role.name, type=role.type)) + return dict(roles=return_roles, page=page, page_limit=page_limit, total=total_roles) else: - raise exceptions.RequestParameterInvalidException( "The value of 'scope' parameter is invalid. Alllowed values: current, available" ) + raise exceptions.RequestParameterInvalidException("The value of 'scope' parameter is invalid. Alllowed values: current, available") - def _get_current_roles( self, trans, library_dataset): + def _get_current_roles(self, trans, library_dataset): """ Find all roles currently connected to relevant permissions on the library dataset and the underlying dataset. @@ -190,18 +193,18 @@ def _get_current_roles( self, trans, library_dataset): dataset = library_dataset.library_dataset_dataset_association.dataset # Omit duplicated roles by converting to set - access_roles = set( dataset.get_access_roles( trans ) ) - modify_roles = set( trans.app.security_agent.get_roles_for_action( library_dataset, trans.app.security_agent.permitted_actions.LIBRARY_MODIFY ) ) - manage_roles = set( dataset.get_manage_permissions_roles( trans ) ) + access_roles = set(dataset.get_access_roles(trans)) + modify_roles = set(trans.app.security_agent.get_roles_for_action(library_dataset, trans.app.security_agent.permitted_actions.LIBRARY_MODIFY)) + manage_roles = set(dataset.get_manage_permissions_roles(trans)) - access_dataset_role_list = [ ( access_role.name, trans.security.encode_id( access_role.id ) ) for access_role in access_roles ] - manage_dataset_role_list = [ ( manage_role.name, trans.security.encode_id( manage_role.id ) ) for manage_role in manage_roles ] - modify_item_role_list = [ ( modify_role.name, trans.security.encode_id( modify_role.id ) ) for modify_role in modify_roles ] + access_dataset_role_list = [(access_role.name, trans.security.encode_id(access_role.id)) for access_role in access_roles] + manage_dataset_role_list = [(manage_role.name, trans.security.encode_id(manage_role.id)) for manage_role in manage_roles] + modify_item_role_list = [(modify_role.name, trans.security.encode_id(modify_role.id)) for modify_role in modify_roles] - return dict( access_dataset_roles=access_dataset_role_list, modify_item_roles=modify_item_role_list, manage_dataset_roles=manage_dataset_role_list ) + return dict(access_dataset_roles=access_dataset_role_list, modify_item_roles=modify_item_role_list, manage_dataset_roles=manage_dataset_role_list) @expose_api - def update_permissions( self, trans, encoded_dataset_id, payload=None, **kwd ): + def update_permissions(self, trans, encoded_dataset_id, payload=None, **kwd): """ *POST /api/libraries/datasets/{encoded_dataset_id}/permissions Set permissions of the given dataset to the given role ids. @@ -228,106 +231,106 @@ def update_permissions( self, trans, encoded_dataset_id, payload=None, **kwd ): if payload: kwd.update(payload) try: - library_dataset = self.get_library_dataset( trans, id=encoded_dataset_id, check_ownership=False, check_accessible=False ) + library_dataset = self.get_library_dataset(trans, id=encoded_dataset_id, check_ownership=False, check_accessible=False) except Exception as e: - raise exceptions.ObjectNotFound( 'Requested dataset was not found.' + str(e) ) + raise exceptions.ObjectNotFound('Requested dataset was not found.' + str(e)) dataset = library_dataset.library_dataset_dataset_association.dataset current_user_roles = trans.get_current_user_roles() - can_manage = trans.app.security_agent.can_manage_dataset( current_user_roles, dataset ) or trans.user_is_admin() + can_manage = trans.app.security_agent.can_manage_dataset(current_user_roles, dataset) or trans.user_is_admin() if not can_manage: - raise exceptions.InsufficientPermissionsException( 'You do not have proper permissions to manage permissions on this dataset.' ) - new_access_roles_ids = util.listify( kwd.get( 'access_ids[]', None ) ) - new_manage_roles_ids = util.listify( kwd.get( 'manage_ids[]', None ) ) - new_modify_roles_ids = util.listify( kwd.get( 'modify_ids[]', None ) ) - action = kwd.get( 'action', None ) + raise exceptions.InsufficientPermissionsException('You do not have proper permissions to manage permissions on this dataset.') + new_access_roles_ids = util.listify(kwd.get('access_ids[]', None)) + new_manage_roles_ids = util.listify(kwd.get('manage_ids[]', None)) + new_modify_roles_ids = util.listify(kwd.get('modify_ids[]', None)) + action = kwd.get('action', None) if action is None: - raise exceptions.RequestParameterMissingException( 'The mandatory parameter "action" is missing.' ) + raise exceptions.RequestParameterMissingException('The mandatory parameter "action" is missing.') elif action == 'remove_restrictions': - trans.app.security_agent.make_dataset_public( dataset ) - if not trans.app.security_agent.dataset_is_public( dataset ): - raise exceptions.InternalServerError( 'An error occured while making dataset public.' ) + trans.app.security_agent.make_dataset_public(dataset) + if not trans.app.security_agent.dataset_is_public(dataset): + raise exceptions.InternalServerError('An error occured while making dataset public.') elif action == 'make_private': - if not trans.app.security_agent.dataset_is_private_to_user( trans, library_dataset ): - private_role = trans.app.security_agent.get_private_user_role( trans.user ) - dp = trans.app.model.DatasetPermissions( trans.app.security_agent.permitted_actions.DATASET_ACCESS.action, dataset, private_role ) - trans.sa_session.add( dp ) + if not trans.app.security_agent.dataset_is_private_to_user(trans, library_dataset): + private_role = trans.app.security_agent.get_private_user_role(trans.user) + dp = trans.app.model.DatasetPermissions(trans.app.security_agent.permitted_actions.DATASET_ACCESS.action, dataset, private_role) + trans.sa_session.add(dp) trans.sa_session.flush() - if not trans.app.security_agent.dataset_is_private_to_user( trans, library_dataset ): + if not trans.app.security_agent.dataset_is_private_to_user(trans, library_dataset): # Check again and inform the user if dataset is not private. - raise exceptions.InternalServerError( 'An error occured and the dataset is NOT private.' ) + raise exceptions.InternalServerError('An error occured and the dataset is NOT private.') elif action == 'set_permissions': # ACCESS DATASET ROLES valid_access_roles = [] invalid_access_roles_ids = [] if new_access_roles_ids is None: - trans.app.security_agent.make_dataset_public( dataset ) + trans.app.security_agent.make_dataset_public(dataset) else: for role_id in new_access_roles_ids: - role = self.role_manager.get( trans, self.__decode_id( trans, role_id, 'role' ) ) + role = self.role_manager.get(trans, self.__decode_id(trans, role_id, 'role')) # Check whether role is in the set of allowed roles - valid_roles, total_roles = trans.app.security_agent.get_valid_roles( trans, dataset ) + valid_roles, total_roles = trans.app.security_agent.get_valid_roles(trans, dataset) if role in valid_roles: - valid_access_roles.append( role ) + valid_access_roles.append(role) else: - invalid_access_roles_ids.append( role_id ) - if len( invalid_access_roles_ids ) > 0: - log.warning( "The following roles could not be added to the dataset access permission: " + str( invalid_access_roles_ids ) ) + invalid_access_roles_ids.append(role_id) + if len(invalid_access_roles_ids) > 0: + log.warning("The following roles could not be added to the dataset access permission: " + str(invalid_access_roles_ids)) - access_permission = dict( access=valid_access_roles ) - trans.app.security_agent.set_dataset_permission( dataset, access_permission ) + access_permission = dict(access=valid_access_roles) + trans.app.security_agent.set_dataset_permission(dataset, access_permission) # MANAGE DATASET ROLES valid_manage_roles = [] invalid_manage_roles_ids = [] - new_manage_roles_ids = util.listify( new_manage_roles_ids ) + new_manage_roles_ids = util.listify(new_manage_roles_ids) # Load all access roles to check - active_access_roles = dataset.get_access_roles( trans ) + active_access_roles = dataset.get_access_roles(trans) for role_id in new_manage_roles_ids: - role = self.role_manager.get( trans, self.__decode_id( trans, role_id, 'role' ) ) + role = self.role_manager.get(trans, self.__decode_id(trans, role_id, 'role')) # Check whether role is in the set of access roles if role in active_access_roles: - valid_manage_roles.append( role ) + valid_manage_roles.append(role) else: - invalid_manage_roles_ids.append( role_id ) + invalid_manage_roles_ids.append(role_id) - if len( invalid_manage_roles_ids ) > 0: - log.warning( "The following roles could not be added to the dataset manage permission: " + str( invalid_manage_roles_ids ) ) + if len(invalid_manage_roles_ids) > 0: + log.warning("The following roles could not be added to the dataset manage permission: " + str(invalid_manage_roles_ids)) - manage_permission = { trans.app.security_agent.permitted_actions.DATASET_MANAGE_PERMISSIONS: valid_manage_roles } - trans.app.security_agent.set_dataset_permission( dataset, manage_permission ) + manage_permission = {trans.app.security_agent.permitted_actions.DATASET_MANAGE_PERMISSIONS: valid_manage_roles} + trans.app.security_agent.set_dataset_permission(dataset, manage_permission) # MODIFY LIBRARY ITEM ROLES valid_modify_roles = [] invalid_modify_roles_ids = [] - new_modify_roles_ids = util.listify( new_modify_roles_ids ) + new_modify_roles_ids = util.listify(new_modify_roles_ids) # Load all access roles to check - active_access_roles = dataset.get_access_roles( trans ) + active_access_roles = dataset.get_access_roles(trans) for role_id in new_modify_roles_ids: - role = self.role_manager.get( trans, self.__decode_id( trans, role_id, 'role' ) ) + role = self.role_manager.get(trans, self.__decode_id(trans, role_id, 'role')) # Check whether role is in the set of access roles if role in active_access_roles: - valid_modify_roles.append( role ) + valid_modify_roles.append(role) else: - invalid_modify_roles_ids.append( role_id ) + invalid_modify_roles_ids.append(role_id) - if len( invalid_modify_roles_ids ) > 0: - log.warning( "The following roles could not be added to the dataset modify permission: " + str( invalid_modify_roles_ids ) ) + if len(invalid_modify_roles_ids) > 0: + log.warning("The following roles could not be added to the dataset modify permission: " + str(invalid_modify_roles_ids)) - modify_permission = { trans.app.security_agent.permitted_actions.LIBRARY_MODIFY: valid_modify_roles } - trans.app.security_agent.set_library_item_permission( library_dataset, modify_permission ) + modify_permission = {trans.app.security_agent.permitted_actions.LIBRARY_MODIFY: valid_modify_roles} + trans.app.security_agent.set_library_item_permission(library_dataset, modify_permission) else: - raise exceptions.RequestParameterInvalidException( 'The mandatory parameter "action" has an invalid value. ' - 'Allowed values are: "remove_restrictions", "make_private", "set_permissions"' ) + raise exceptions.RequestParameterInvalidException('The mandatory parameter "action" has an invalid value. ' + 'Allowed values are: "remove_restrictions", "make_private", "set_permissions"') - return self._get_current_roles( trans, library_dataset ) + return self._get_current_roles(trans, library_dataset) @expose_api - def delete( self, trans, encoded_dataset_id, **kwd ): + def delete(self, trans, encoded_dataset_id, **kwd): """ delete( self, trans, encoded_dataset_id, **kwd ): * DELETE /api/libraries/datasets/{encoded_dataset_id} @@ -342,34 +345,34 @@ def delete( self, trans, encoded_dataset_id, **kwd ): :returns: dict containing information about the dataset :rtype: dictionary """ - undelete = util.string_as_bool( kwd.get( 'undelete', False ) ) + undelete = util.string_as_bool(kwd.get('undelete', False)) try: - dataset = self.get_library_dataset( trans, id=encoded_dataset_id, check_ownership=False, check_accessible=False ) + dataset = self.get_library_dataset(trans, id=encoded_dataset_id, check_ownership=False, check_accessible=False) except Exception as e: - raise exceptions.ObjectNotFound( 'Requested dataset was not found.' + str(e) ) + raise exceptions.ObjectNotFound('Requested dataset was not found.' + str(e)) current_user_roles = trans.get_current_user_roles() - allowed = trans.app.security_agent.can_modify_library_item( current_user_roles, dataset ) - if ( not allowed ) and ( not trans.user_is_admin() ): - raise exceptions.InsufficientPermissionsException( 'You do not have proper permissions to delete this dataset.') + allowed = trans.app.security_agent.can_modify_library_item(current_user_roles, dataset) + if (not allowed) and (not trans.user_is_admin()): + raise exceptions.InsufficientPermissionsException('You do not have proper permissions to delete this dataset.') if undelete: dataset.deleted = False else: dataset.deleted = True - trans.sa_session.add( dataset ) + trans.sa_session.add(dataset) trans.sa_session.flush() - rval = trans.security.encode_all_ids( dataset.to_dict() ) - nice_size = util.nice_size( int( dataset.library_dataset_dataset_association.get_size() ) ) - rval[ 'file_size' ] = nice_size - rval[ 'update_time' ] = dataset.update_time.strftime( "%Y-%m-%d %I:%M %p" ) - rval[ 'deleted' ] = dataset.deleted - rval[ 'folder_id' ] = 'F' + rval[ 'folder_id' ] + rval = trans.security.encode_all_ids(dataset.to_dict()) + nice_size = util.nice_size(int(dataset.library_dataset_dataset_association.get_size())) + rval['file_size'] = nice_size + rval['update_time'] = dataset.update_time.strftime("%Y-%m-%d %I:%M %p") + rval['deleted'] = dataset.deleted + rval['folder_id'] = 'F' + rval['folder_id'] return rval @expose_api - def load( self, trans, payload=None, **kwd ): + def load(self, trans, payload=None, **kwd): """ * POST /api/libraries/datasets Load dataset from the given source into the library. @@ -396,6 +399,8 @@ def load( self, trans, payload=None, **kwd ): :type file_type: str :param dbkey: dbkey of the loaded genome, defaults to '?' (unknown) :type dbkey: str + :param tag_using_filenames: flag whether to generate dataset tags from filenames + :type tag_using_filenames: bool :type dictionary :returns: dict containing information about the created upload job :rtype: dictionary @@ -404,105 +409,106 @@ def load( self, trans, payload=None, **kwd ): """ if payload: kwd.update(payload) - kwd[ 'space_to_tab' ] = False - kwd[ 'to_posix_lines' ] = True - kwd[ 'dbkey' ] = kwd.get( 'dbkey', '?' ) - kwd[ 'file_type' ] = kwd.get( 'file_type', 'auto' ) - kwd[ 'link_data_only' ] = 'link_to_files' if util.asbool( kwd.get( 'link_data', False ) ) else 'copy_files' - encoded_folder_id = kwd.get( 'encoded_folder_id', None ) + kwd['space_to_tab'] = False + kwd['to_posix_lines'] = True + kwd['dbkey'] = kwd.get('dbkey', '?') + kwd['file_type'] = kwd.get('file_type', 'auto') + kwd['link_data_only'] = 'link_to_files' if util.string_as_bool(kwd.get('link_data', False)) else 'copy_files' + kwd['tag_using_filenames'] = util.string_as_bool(kwd.get('tag_using_filenames', None)) + encoded_folder_id = kwd.get('encoded_folder_id', None) if encoded_folder_id is not None: - folder_id = self.folder_manager.cut_and_decode( trans, encoded_folder_id ) + folder_id = self.folder_manager.cut_and_decode(trans, encoded_folder_id) else: - raise exceptions.RequestParameterMissingException( 'The required attribute encoded_folder_id is missing.' ) - path = kwd.get( 'path', None) + raise exceptions.RequestParameterMissingException('The required attribute encoded_folder_id is missing.') + path = kwd.get('path', None) if path is None: - raise exceptions.RequestParameterMissingException( 'The required attribute path is missing.' ) - folder = self.folder_manager.get( trans, folder_id ) + raise exceptions.RequestParameterMissingException('The required attribute path is missing.') + folder = self.folder_manager.get(trans, folder_id) - source = kwd.get( 'source', None ) - if source not in [ 'userdir_file', 'userdir_folder', 'importdir_file', 'importdir_folder', 'admin_path' ]: - raise exceptions.RequestParameterMissingException( 'You have to specify "source" parameter. Possible values are "userdir_file", "userdir_folder", "admin_path", "importdir_file" and "importdir_folder". ') - if source in [ 'importdir_file', 'importdir_folder' ]: + source = kwd.get('source', None) + if source not in ['userdir_file', 'userdir_folder', 'importdir_file', 'importdir_folder', 'admin_path']: + raise exceptions.RequestParameterMissingException('You have to specify "source" parameter. Possible values are "userdir_file", "userdir_folder", "admin_path", "importdir_file" and "importdir_folder". ') + if source in ['importdir_file', 'importdir_folder']: if not trans.user_is_admin: - raise exceptions.AdminRequiredException( 'Only admins can import from importdir.' ) + raise exceptions.AdminRequiredException('Only admins can import from importdir.') if not trans.app.config.library_import_dir: - raise exceptions.ConfigDoesNotAllowException( 'The configuration of this Galaxy instance does not allow admins to import into library from importdir.' ) + raise exceptions.ConfigDoesNotAllowException('The configuration of this Galaxy instance does not allow admins to import into library from importdir.') import_base_dir = trans.app.config.library_import_dir - path = os.path.join( import_base_dir, path ) - if source in [ 'userdir_file', 'userdir_folder' ]: + path = os.path.join(import_base_dir, path) + if source in ['userdir_file', 'userdir_folder']: user_login = trans.user.email user_base_dir = trans.app.config.user_library_import_dir if user_base_dir is None: - raise exceptions.ConfigDoesNotAllowException( 'The configuration of this Galaxy instance does not allow upload from user directories.' ) - full_dir = os.path.join( user_base_dir, user_login ) - if not path.lower().startswith( full_dir.lower() ): - path = os.path.join( full_dir, path ) - if not os.path.exists( path ): - raise exceptions.RequestParameterInvalidException( 'Given path does not exist on the host.' ) - if not self.folder_manager.can_add_item( trans, folder ): - raise exceptions.InsufficientPermissionsException( 'You do not have proper permission to add items to the given folder.' ) + raise exceptions.ConfigDoesNotAllowException('The configuration of this Galaxy instance does not allow upload from user directories.') + full_dir = os.path.join(user_base_dir, user_login) + if not path.lower().startswith(full_dir.lower()): + path = os.path.join(full_dir, path) + if not os.path.exists(path): + raise exceptions.RequestParameterInvalidException('Given path does not exist on the host.') + if not self.folder_manager.can_add_item(trans, folder): + raise exceptions.InsufficientPermissionsException('You do not have proper permission to add items to the given folder.') if source == 'admin_path': if not trans.app.config.allow_library_path_paste: - raise exceptions.ConfigDoesNotAllowException( 'The configuration of this Galaxy instance does not allow admins to import into library from path.' ) + raise exceptions.ConfigDoesNotAllowException('The configuration of this Galaxy instance does not allow admins to import into library from path.') if not trans.user_is_admin: - raise exceptions.AdminRequiredException( 'Only admins can import from path.' ) + raise exceptions.AdminRequiredException('Only admins can import from path.') # Set up the traditional tool state/params tool_id = 'upload1' - tool = trans.app.toolbox.get_tool( tool_id ) - state = tool.new_state( trans ) - populate_state( trans, tool.inputs, kwd, state.inputs ) + tool = trans.app.toolbox.get_tool(tool_id) + state = tool.new_state(trans) + populate_state(trans, tool.inputs, kwd, state.inputs) tool_params = state.inputs dataset_upload_inputs = [] for input in tool.inputs.itervalues(): if input.type == "upload_dataset": - dataset_upload_inputs.append( input ) - library_bunch = upload_common.handle_library_params( trans, {}, trans.security.encode_id( folder.id ) ) + dataset_upload_inputs.append(input) + library_bunch = upload_common.handle_library_params(trans, {}, trans.security.encode_id(folder.id)) abspath_datasets = [] - kwd[ 'filesystem_paths' ] = path - if source in [ 'importdir_folder' ]: - kwd[ 'filesystem_paths' ] = os.path.join( import_base_dir, path ) + kwd['filesystem_paths'] = path + if source in ['importdir_folder']: + kwd['filesystem_paths'] = os.path.join(import_base_dir, path) # user wants to import one file only - if source in [ "userdir_file", "importdir_file" ]: - file = os.path.abspath( path ) - abspath_datasets.append( trans.webapp.controllers[ 'library_common' ].make_library_uploaded_dataset( - trans, 'api', kwd, os.path.basename( file ), file, 'server_dir', library_bunch ) ) + if source in ["userdir_file", "importdir_file"]: + file = os.path.abspath(path) + abspath_datasets.append(trans.webapp.controllers['library_common'].make_library_uploaded_dataset( + trans, 'api', kwd, os.path.basename(file), file, 'server_dir', library_bunch)) # user wants to import whole folder if source == "userdir_folder": - uploaded_datasets_bunch = trans.webapp.controllers[ 'library_common' ].get_path_paste_uploaded_datasets( - trans, 'api', kwd, library_bunch, 200, '' ) - uploaded_datasets = uploaded_datasets_bunch[ 0 ] + uploaded_datasets_bunch = trans.webapp.controllers['library_common'].get_path_paste_uploaded_datasets( + trans, 'api', kwd, library_bunch, 200, '') + uploaded_datasets = uploaded_datasets_bunch[0] if uploaded_datasets is None: - raise exceptions.ObjectNotFound( 'Given folder does not contain any datasets.' ) + raise exceptions.ObjectNotFound('Given folder does not contain any datasets.') for ud in uploaded_datasets: - ud.path = os.path.abspath( ud.path ) - abspath_datasets.append( ud ) + ud.path = os.path.abspath(ud.path) + abspath_datasets.append(ud) # user wants to import from path - if source in [ "admin_path", "importdir_folder" ]: + if source in ["admin_path", "importdir_folder"]: # validate the path is within root - uploaded_datasets_bunch = trans.webapp.controllers[ 'library_common' ].get_path_paste_uploaded_datasets( - trans, 'api', kwd, library_bunch, 200, '' ) + uploaded_datasets_bunch = trans.webapp.controllers['library_common'].get_path_paste_uploaded_datasets( + trans, 'api', kwd, library_bunch, 200, '') uploaded_datasets = uploaded_datasets_bunch[0] if uploaded_datasets is None: - raise exceptions.ObjectNotFound( 'Given folder does not contain any datasets.' ) + raise exceptions.ObjectNotFound('Given folder does not contain any datasets.') for ud in uploaded_datasets: - ud.path = os.path.abspath( ud.path ) - abspath_datasets.append( ud ) - json_file_path = upload_common.create_paramfile( trans, abspath_datasets ) - data_list = [ ud.data for ud in abspath_datasets ] + ud.path = os.path.abspath(ud.path) + abspath_datasets.append(ud) + json_file_path = upload_common.create_paramfile(trans, abspath_datasets) + data_list = [ud.data for ud in abspath_datasets] job_params = {} - job_params['link_data_only'] = dumps( kwd.get( 'link_data_only', 'copy_files' ) ) - job_params['uuid'] = dumps( kwd.get( 'uuid', None ) ) - job, output = upload_common.create_job( trans, tool_params, tool, json_file_path, data_list, folder=folder, job_params=job_params ) - trans.sa_session.add( job ) + job_params['link_data_only'] = dumps(kwd.get('link_data_only', 'copy_files')) + job_params['uuid'] = dumps(kwd.get('uuid', None)) + job, output = upload_common.create_job(trans, tool_params, tool, json_file_path, data_list, folder=folder, job_params=job_params) + trans.sa_session.add(job) trans.sa_session.flush() job_dict = job.to_dict() - job_dict[ 'id' ] = trans.security.encode_id( job_dict[ 'id' ] ) + job_dict['id'] = trans.security.encode_id(job_dict['id']) return job_dict @web.expose # TODO convert to expose_api - def download( self, trans, format, **kwd ): + def download(self, trans, format, **kwd): """ download( self, trans, format, **kwd ) * GET /api/libraries/datasets/download/{format} @@ -526,38 +532,38 @@ def download( self, trans, format, **kwd ): :raises: MessageException, ItemDeletionException, ItemAccessibilityException, HTTPBadRequest, OSError, IOError, ObjectNotFound """ library_datasets = [] - datasets_to_download = kwd.get( 'ld_ids%5B%5D', None ) + datasets_to_download = kwd.get('ld_ids%5B%5D', None) if datasets_to_download is None: - datasets_to_download = kwd.get( 'ld_ids', None ) + datasets_to_download = kwd.get('ld_ids', None) if datasets_to_download is not None: - datasets_to_download = util.listify( datasets_to_download ) + datasets_to_download = util.listify(datasets_to_download) for dataset_id in datasets_to_download: try: - library_dataset = self.get_library_dataset( trans, id=dataset_id, check_ownership=False, check_accessible=True ) - library_datasets.append( library_dataset ) + library_dataset = self.get_library_dataset(trans, id=dataset_id, check_ownership=False, check_accessible=True) + library_datasets.append(library_dataset) except HTTPBadRequest: - raise exceptions.RequestParameterInvalidException( 'Bad Request.' ) + raise exceptions.RequestParameterInvalidException('Bad Request.') except HTTPInternalServerError: - raise exceptions.InternalServerError( 'Internal error.' ) + raise exceptions.InternalServerError('Internal error.') except Exception as e: - raise exceptions.InternalServerError( 'Unknown error.' + str(e) ) + raise exceptions.InternalServerError('Unknown error.' + str(e)) - folders_to_download = kwd.get( 'folder_ids%5B%5D', None ) + folders_to_download = kwd.get('folder_ids%5B%5D', None) if folders_to_download is None: - folders_to_download = kwd.get( 'folder_ids', None ) + folders_to_download = kwd.get('folder_ids', None) if folders_to_download is not None: - folders_to_download = util.listify( folders_to_download ) + folders_to_download = util.listify(folders_to_download) current_user_roles = trans.get_current_user_roles() - def traverse( folder ): + def traverse(folder): admin = trans.user_is_admin() rval = [] for subfolder in folder.active_folders: if not admin: - can_access, folder_ids = trans.app.security_agent.check_folder_contents( trans.user, current_user_roles, subfolder ) + can_access, folder_ids = trans.app.security_agent.check_folder_contents(trans.user, current_user_roles, subfolder) if (admin or can_access) and not subfolder.deleted: - rval.extend( traverse( subfolder ) ) + rval.extend(traverse(subfolder)) for ld in folder.datasets: if not admin: can_access = trans.app.security_agent.can_access_dataset( @@ -565,49 +571,49 @@ def traverse( folder ): ld.library_dataset_dataset_association.dataset ) if (admin or can_access) and not ld.deleted: - rval.append( ld ) + rval.append(ld) return rval for encoded_folder_id in folders_to_download: - folder_id = self.folder_manager.cut_and_decode( trans, encoded_folder_id ) - folder = self.folder_manager.get( trans, folder_id ) - library_datasets.extend( traverse( folder ) ) + folder_id = self.folder_manager.cut_and_decode(trans, encoded_folder_id) + folder = self.folder_manager.get(trans, folder_id) + library_datasets.extend(traverse(folder)) if not library_datasets: - raise exceptions.RequestParameterMissingException( 'Request has to contain a list of dataset ids or folder ids to download.' ) + raise exceptions.RequestParameterMissingException('Request has to contain a list of dataset ids or folder ids to download.') - if format in [ 'zip', 'tgz', 'tbz' ]: + if format in ['zip', 'tgz', 'tbz']: # error = False killme = string.punctuation + string.whitespace - trantab = string.maketrans( killme, '_' * len( killme ) ) + trantab = string.maketrans(killme, '_' * len(killme)) try: outext = 'zip' if format == 'zip': # Can't use mkstemp - the file must not exist first tmpd = tempfile.mkdtemp() - util.umask_fix_perms( tmpd, trans.app.config.umask, 0777, self.app.config.gid ) - tmpf = os.path.join( tmpd, 'library_download.' + format ) + util.umask_fix_perms(tmpd, trans.app.config.umask, 0777, self.app.config.gid) + tmpf = os.path.join(tmpd, 'library_download.' + format) if trans.app.config.upstream_gzip: - archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_STORED, True ) + archive = zipfile.ZipFile(tmpf, 'w', zipfile.ZIP_STORED, True) else: - archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED, True ) - archive.add = lambda x, y: archive.write( x, y.encode( 'CP437' ) ) + archive = zipfile.ZipFile(tmpf, 'w', zipfile.ZIP_DEFLATED, True) + archive.add = lambda x, y: archive.write(x, y.encode('CP437')) elif format == 'tgz': if trans.app.config.upstream_gzip: - archive = StreamBall( 'w|' ) + archive = StreamBall('w|') outext = 'tar' else: - archive = StreamBall( 'w|gz' ) + archive = StreamBall('w|gz') outext = 'tgz' elif format == 'tbz': - archive = StreamBall( 'w|bz2' ) + archive = StreamBall('w|bz2') outext = 'tbz2' - except ( OSError, zipfile.BadZipfile ): - log.exception( "Unable to create archive for download" ) - raise exceptions.InternalServerError( "Unable to create archive for download." ) + except (OSError, zipfile.BadZipfile): + log.exception("Unable to create archive for download") + raise exceptions.InternalServerError("Unable to create archive for download.") except Exception: - log.exception( "Unexpected error in create archive for download" ) - raise exceptions.InternalServerError( "Unable to create archive for download." ) + log.exception("Unexpected error in create archive for download") + raise exceptions.InternalServerError("Unable to create archive for download.") composite_extensions = trans.app.datatypes_registry.get_composite_extensions() seen = [] for ld in library_datasets: @@ -619,34 +625,34 @@ def traverse( folder ): while parent_folder is not None: # Exclude the now-hidden "root folder" if parent_folder.parent is None: - path = os.path.join( parent_folder.library_root[ 0 ].name, path ) + path = os.path.join(parent_folder.library_root[0].name, path) break - path = os.path.join( parent_folder.name, path ) + path = os.path.join(parent_folder.name, path) parent_folder = parent_folder.parent path += ldda.name while path in seen: path += '_' - seen.append( path ) - zpath = os.path.split(path)[ -1 ] # comes as base_name/fname - outfname, zpathext = os.path.splitext( zpath ) + seen.append(path) + zpath = os.path.split(path)[-1] # comes as base_name/fname + outfname, zpathext = os.path.splitext(zpath) if is_composite: # need to add all the components from the extra_files_path to the zip if zpathext == '': zpath = '%s.html' % zpath # fake the real nature of the html file try: if format == 'zip': - archive.add( ldda.dataset.file_name, zpath ) # add the primary of a composite set + archive.add(ldda.dataset.file_name, zpath) # add the primary of a composite set else: - archive.add( ldda.dataset.file_name, zpath, check_file=True ) # add the primary of a composite set + archive.add(ldda.dataset.file_name, zpath, check_file=True) # add the primary of a composite set except IOError: - log.exception( "Unable to add composite parent %s to temporary library download archive", ldda.dataset.file_name ) - raise exceptions.InternalServerError( "Unable to create archive for download." ) + log.exception("Unable to add composite parent %s to temporary library download archive", ldda.dataset.file_name) + raise exceptions.InternalServerError("Unable to create archive for download.") except ObjectNotFound: - log.exception( "Requested dataset %s does not exist on the host.", ldda.dataset.file_name ) - raise exceptions.ObjectNotFound( "Requested dataset not found. " ) + log.exception("Requested dataset %s does not exist on the host.", ldda.dataset.file_name) + raise exceptions.ObjectNotFound("Requested dataset not found. ") except Exception as e: - log.exception( "Unable to add composite parent %s to temporary library download archive", ldda.dataset.file_name ) - raise exceptions.InternalServerError( "Unable to add composite parent to temporary library download archive. " + str( e ) ) + log.exception("Unable to add composite parent %s to temporary library download archive", ldda.dataset.file_name) + raise exceptions.InternalServerError("Unable to add composite parent to temporary library download archive. " + str(e)) flist = glob.glob(os.path.join(ldda.dataset.extra_files_path, '*.*')) # glob returns full paths for fpath in flist: @@ -655,71 +661,71 @@ def traverse( folder ): fname = fname.translate(trantab) try: if format == 'zip': - archive.add( fpath, fname ) + archive.add(fpath, fname) else: - archive.add( fpath, fname, check_file=True ) + archive.add(fpath, fname, check_file=True) except IOError: - log.exception( "Unable to add %s to temporary library download archive %s", fname, outfname ) - raise exceptions.InternalServerError( "Unable to create archive for download." ) + log.exception("Unable to add %s to temporary library download archive %s", fname, outfname) + raise exceptions.InternalServerError("Unable to create archive for download.") except ObjectNotFound: - log.exception( "Requested dataset %s does not exist on the host.", fpath ) - raise exceptions.ObjectNotFound( "Requested dataset not found." ) + log.exception("Requested dataset %s does not exist on the host.", fpath) + raise exceptions.ObjectNotFound("Requested dataset not found.") except Exception as e: - log.exception( "Unable to add %s to temporary library download archive %s", fname, outfname ) - raise exceptions.InternalServerError( "Unable to add dataset to temporary library download archive . " + str( e ) ) + log.exception("Unable to add %s to temporary library download archive %s", fname, outfname) + raise exceptions.InternalServerError("Unable to add dataset to temporary library download archive . " + str(e)) else: # simple case try: if format == 'zip': - archive.add( ldda.dataset.file_name, path ) + archive.add(ldda.dataset.file_name, path) else: - archive.add( ldda.dataset.file_name, path, check_file=True ) + archive.add(ldda.dataset.file_name, path, check_file=True) except IOError: - log.exception( "Unable to write %s to temporary library download archive", ldda.dataset.file_name ) - raise exceptions.InternalServerError( "Unable to create archive for download" ) + log.exception("Unable to write %s to temporary library download archive", ldda.dataset.file_name) + raise exceptions.InternalServerError("Unable to create archive for download") except ObjectNotFound: - log.exception( "Requested dataset %s does not exist on the host.", ldda.dataset.file_name ) - raise exceptions.ObjectNotFound( "Requested dataset not found." ) + log.exception("Requested dataset %s does not exist on the host.", ldda.dataset.file_name) + raise exceptions.ObjectNotFound("Requested dataset not found.") except Exception as e: - log.exception( "Unable to add %s to temporary library download archive %s", fname, outfname ) - raise exceptions.InternalServerError( "Unknown error. " + str( e ) ) + log.exception("Unable to add %s to temporary library download archive %s", fname, outfname) + raise exceptions.InternalServerError("Unknown error. " + str(e)) lname = 'selected_dataset' - fname = lname.replace( ' ', '_' ) + '_files' + fname = lname.replace(' ', '_') + '_files' if format == 'zip': archive.close() - trans.response.set_content_type( "application/octet-stream" ) - trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s.%s"' % ( fname, outext ) - archive = util.streamball.ZipBall( tmpf, tmpd ) + trans.response.set_content_type("application/octet-stream") + trans.response.headers["Content-Disposition"] = 'attachment; filename="%s.%s"' % (fname, outext) + archive = util.streamball.ZipBall(tmpf, tmpd) archive.wsgi_status = trans.response.wsgi_status() archive.wsgi_headeritems = trans.response.wsgi_headeritems() return archive.stream else: - trans.response.set_content_type( "application/x-tar" ) - trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s.%s"' % ( fname, outext ) + trans.response.set_content_type("application/x-tar") + trans.response.headers["Content-Disposition"] = 'attachment; filename="%s.%s"' % (fname, outext) archive.wsgi_status = trans.response.wsgi_status() archive.wsgi_headeritems = trans.response.wsgi_headeritems() return archive.stream elif format == 'uncompressed': if len(library_datasets) != 1: - raise exceptions.RequestParameterInvalidException( "You can download only one uncompressed file at once." ) + raise exceptions.RequestParameterInvalidException("You can download only one uncompressed file at once.") else: - single_ld = library_datasets[ 0 ] + single_ld = library_datasets[0] ldda = single_ld.library_dataset_dataset_association dataset = ldda.dataset - fStat = os.stat( dataset.file_name ) - trans.response.set_content_type( ldda.get_mime() ) - trans.response.headers[ 'Content-Length' ] = int( fStat.st_size ) + fStat = os.stat(dataset.file_name) + trans.response.set_content_type(ldda.get_mime()) + trans.response.headers['Content-Length'] = int(fStat.st_size) fname = ldda.name - fname = ''.join( c in util.FILENAME_VALID_CHARS and c or '_' for c in fname )[ 0:150 ] - trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s"' % fname + fname = ''.join(c in util.FILENAME_VALID_CHARS and c or '_' for c in fname)[0:150] + trans.response.headers["Content-Disposition"] = 'attachment; filename="%s"' % fname try: - return open( dataset.file_name ) + return open(dataset.file_name) except: - raise exceptions.InternalServerError( "This dataset contains no content." ) + raise exceptions.InternalServerError("This dataset contains no content.") else: - raise exceptions.RequestParameterInvalidException( "Wrong format parameter specified" ) + raise exceptions.RequestParameterInvalidException("Wrong format parameter specified") - def _build_path( self, trans, folder ): + def _build_path(self, trans, folder): """ Search the path upwards recursively and load the whole route of names and ids for breadcrumb building purposes. @@ -733,15 +739,15 @@ def _build_path( self, trans, folder ): path_to_root = [] # We are almost in root if folder.parent_id is None: - path_to_root.append( ( 'F' + trans.security.encode_id( folder.id ), folder.name ) ) + path_to_root.append(('F' + trans.security.encode_id(folder.id), folder.name)) else: # We add the current folder and traverse up one folder. - path_to_root.append( ( 'F' + trans.security.encode_id( folder.id ), folder.name ) ) - upper_folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( folder.parent_id ) - path_to_root.extend( self._build_path( trans, upper_folder ) ) + path_to_root.append(('F' + trans.security.encode_id(folder.id), folder.name)) + upper_folder = trans.sa_session.query(trans.app.model.LibraryFolder).get(folder.parent_id) + path_to_root.extend(self._build_path(trans, upper_folder)) return path_to_root - def __decode_id( self, trans, encoded_id, object_name=None ): + def __decode_id(self, trans, encoded_id, object_name=None): """ Try to decode the id. @@ -749,8 +755,8 @@ def __decode_id( self, trans, encoded_id, object_name=None ): :type object_name: str """ try: - return trans.security.decode_id( encoded_id ) + return trans.security.decode_id(encoded_id) except TypeError: - raise exceptions.MalformedId( 'Malformed %s id specified, unable to decode.' % object_name if object_name is not None else '' ) + raise exceptions.MalformedId('Malformed %s id specified, unable to decode.' % object_name if object_name is not None else '') except ValueError: - raise exceptions.MalformedId( 'Wrong %s id specified, unable to decode.' % object_name if object_name is not None else '' ) + raise exceptions.MalformedId('Wrong %s id specified, unable to decode.' % object_name if object_name is not None else '') diff --git a/lib/galaxy/webapps/galaxy/api/libraries.py b/lib/galaxy/webapps/galaxy/api/libraries.py index 90316c066932..f3cdf44ca9ff 100644 --- a/lib/galaxy/webapps/galaxy/api/libraries.py +++ b/lib/galaxy/webapps/galaxy/api/libraries.py @@ -9,19 +9,19 @@ from galaxy.web.base.controller import BaseAPIController import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class LibrariesController( BaseAPIController ): +class LibrariesController(BaseAPIController): - def __init__( self, app ): - super( LibrariesController, self ).__init__( app ) + def __init__(self, app): + super(LibrariesController, self).__init__(app) self.folder_manager = folders.FolderManager() self.library_manager = libraries.LibraryManager() - self.role_manager = roles.RoleManager( app ) + self.role_manager = roles.RoleManager(app) @expose_api_anonymous - def index( self, trans, **kwd ): + def index(self, trans, **kwd): """ index( self, trans, **kwd ) * GET /api/libraries: @@ -36,14 +36,14 @@ def index( self, trans, **kwd ): .. seealso:: :attr:`galaxy.model.Library.dict_collection_visible_keys` """ - deleted = util.string_as_bool_or_none( kwd.get( 'deleted', None ) ) - query = self.library_manager.list( trans, deleted ) + deleted = util.string_as_bool_or_none(kwd.get('deleted', None)) + query = self.library_manager.list(trans, deleted) libraries = [] for library in query: - libraries.append( self.library_manager.get_library_dict( trans, library ) ) + libraries.append(self.library_manager.get_library_dict(trans, library)) return libraries - def __decode_id( self, trans, encoded_id, object_name=None ): + def __decode_id(self, trans, encoded_id, object_name=None): """ Try to decode the id. @@ -51,14 +51,14 @@ def __decode_id( self, trans, encoded_id, object_name=None ): :type object_name: str """ try: - return trans.security.decode_id( encoded_id ) + return trans.security.decode_id(encoded_id) except TypeError: - raise exceptions.MalformedId( 'Malformed %s id specified, unable to decode.' % object_name if object_name is not None else '' ) + raise exceptions.MalformedId('Malformed %s id specified, unable to decode.' % object_name if object_name is not None else '') except ValueError: - raise exceptions.MalformedId( 'Wrong %s id specified, unable to decode.' % object_name if object_name is not None else '' ) + raise exceptions.MalformedId('Wrong %s id specified, unable to decode.' % object_name if object_name is not None else '') @expose_api_anonymous - def show( self, trans, id, deleted='False', **kwd ): + def show(self, trans, id, deleted='False', **kwd): """ show( self, trans, id, deleted='False', **kwd ) * GET /api/libraries/{encoded_id}: @@ -78,12 +78,12 @@ def show( self, trans, id, deleted='False', **kwd ): :raises: MalformedId, ObjectNotFound """ - library = self.library_manager.get( trans, self.__decode_id( trans, id, 'library' ) ) - library_dict = self.library_manager.get_library_dict( trans, library ) + library = self.library_manager.get(trans, self.__decode_id(trans, id, 'library')) + library_dict = self.library_manager.get_library_dict(trans, library) return library_dict @expose_api - def create( self, trans, payload=None, **kwd ): + def create(self, trans, payload=None, **kwd): """ * POST /api/libraries: Creates a new library. @@ -106,17 +106,17 @@ def create( self, trans, payload=None, **kwd ): kwd.update(payload) name = kwd.get('name', None) if not name: - raise exceptions.RequestParameterMissingException( "Missing required parameter 'name'." ) - description = kwd.get( 'description', '' ) - synopsis = kwd.get( 'synopsis', '' ) - if synopsis in [ 'None', None ]: + raise exceptions.RequestParameterMissingException("Missing required parameter 'name'.") + description = kwd.get('description', '') + synopsis = kwd.get('synopsis', '') + if synopsis in ['None', None]: synopsis = '' - library = self.library_manager.create( trans, name, description, synopsis ) - library_dict = self.library_manager.get_library_dict( trans, library ) + library = self.library_manager.create(trans, name, description, synopsis) + library_dict = self.library_manager.get_library_dict(trans, library) return library_dict @expose_api - def update( self, trans, id, payload=None, **kwd ): + def update(self, trans, id, payload=None, **kwd): """ * PATCH /api/libraries/{encoded_id} Updates the library defined by an ``encoded_id`` with the data in the payload. @@ -137,20 +137,20 @@ def update( self, trans, id, payload=None, **kwd ): :rtype: dict :raises: RequestParameterMissingException """ - library = self.library_manager.get( trans, self.__decode_id( trans, id, 'library' ) ) + library = self.library_manager.get(trans, self.__decode_id(trans, id, 'library')) if payload: kwd.update(payload) - name = kwd.get( 'name', None ) + name = kwd.get('name', None) if name == '': - raise exceptions.RequestParameterMissingException( "Parameter 'name' of library is required. You cannot remove it." ) - description = kwd.get( 'description', None ) - synopsis = kwd.get( 'synopsis', None ) - updated_library = self.library_manager.update( trans, library, name, description, synopsis ) - library_dict = self.library_manager.get_library_dict( trans, updated_library ) + raise exceptions.RequestParameterMissingException("Parameter 'name' of library is required. You cannot remove it.") + description = kwd.get('description', None) + synopsis = kwd.get('synopsis', None) + updated_library = self.library_manager.update(trans, library, name, description, synopsis) + library_dict = self.library_manager.get_library_dict(trans, updated_library) return library_dict @expose_api - def delete( self, trans, id, payload=None, **kwd ): + def delete(self, trans, id, payload=None, **kwd): """ * DELETE /api/libraries/{id} marks the library with the given ``id`` as `deleted` (or removes the `deleted` mark if the `undelete` param is true) @@ -171,14 +171,14 @@ def delete( self, trans, id, payload=None, **kwd ): """ if payload: kwd.update(payload) - library = self.library_manager.get( trans, self.__decode_id( trans, id, 'library' )) - undelete = util.string_as_bool( kwd.get( 'undelete', False ) ) - library = self.library_manager.delete( trans, library, undelete ) - library_dict = self.library_manager.get_library_dict( trans, library ) + library = self.library_manager.get(trans, self.__decode_id(trans, id, 'library')) + undelete = util.string_as_bool(kwd.get('undelete', False)) + library = self.library_manager.delete(trans, library, undelete) + library_dict = self.library_manager.get_library_dict(trans, library) return library_dict @expose_api - def get_permissions( self, trans, encoded_library_id, **kwd ): + def get_permissions(self, trans, encoded_library_id, **kwd): """ * GET /api/libraries/{id}/permissions @@ -200,45 +200,45 @@ def get_permissions( self, trans, encoded_library_id, **kwd ): """ current_user_roles = trans.get_current_user_roles() is_admin = trans.user_is_admin() - library = self.library_manager.get( trans, self.__decode_id( trans, encoded_library_id, 'library' ) ) - if not ( is_admin or trans.app.security_agent.can_manage_library_item( current_user_roles, library ) ): - raise exceptions.InsufficientPermissionsException( 'You do not have proper permission to access permissions of this library.' ) + library = self.library_manager.get(trans, self.__decode_id(trans, encoded_library_id, 'library')) + if not (is_admin or trans.app.security_agent.can_manage_library_item(current_user_roles, library)): + raise exceptions.InsufficientPermissionsException('You do not have proper permission to access permissions of this library.') - scope = kwd.get( 'scope', None ) - is_library_access = util.string_as_bool( kwd.get( 'is_library_access', False ) ) + scope = kwd.get('scope', None) + is_library_access = util.string_as_bool(kwd.get('is_library_access', False)) if scope == 'current' or scope is None: - roles = self.library_manager.get_current_roles( trans, library ) + roles = self.library_manager.get_current_roles(trans, library) return roles # Return roles that are available to select. elif scope == 'available': - page = kwd.get( 'page', None ) + page = kwd.get('page', None) if page is not None: - page = int( page ) + page = int(page) else: page = 1 - page_limit = kwd.get( 'page_limit', None ) + page_limit = kwd.get('page_limit', None) if page_limit is not None: - page_limit = int( page_limit ) + page_limit = int(page_limit) else: page_limit = 10 - query = kwd.get( 'q', None ) + query = kwd.get('q', None) - roles, total_roles = trans.app.security_agent.get_valid_roles( trans, library, query, page, page_limit, is_library_access ) + roles, total_roles = trans.app.security_agent.get_valid_roles(trans, library, query, page, page_limit, is_library_access) return_roles = [] for role in roles: - role_id = trans.security.encode_id( role.id ) - return_roles.append( dict( id=role_id, name=role.name, type=role.type ) ) - return dict( roles=return_roles, page=page, page_limit=page_limit, total=total_roles ) + role_id = trans.security.encode_id(role.id) + return_roles.append(dict(id=role_id, name=role.name, type=role.type)) + return dict(roles=return_roles, page=page, page_limit=page_limit, total=total_roles) else: - raise exceptions.RequestParameterInvalidException( "The value of 'scope' parameter is invalid. Alllowed values: current, available" ) + raise exceptions.RequestParameterInvalidException("The value of 'scope' parameter is invalid. Alllowed values: current, available") @expose_api - def set_permissions( self, trans, encoded_library_id, payload=None, **kwd ): + def set_permissions(self, trans, encoded_library_id, payload=None, **kwd): """ *POST /api/libraries/{encoded_library_id}/permissions Set permissions of the given library to the given role ids. @@ -267,111 +267,111 @@ def set_permissions( self, trans, encoded_library_id, payload=None, **kwd ): kwd.update(payload) is_admin = trans.user_is_admin() current_user_roles = trans.get_current_user_roles() - library = self.library_manager.get( trans, self.__decode_id( trans, encoded_library_id, 'library' ) ) + library = self.library_manager.get(trans, self.__decode_id(trans, encoded_library_id, 'library')) - if not ( is_admin or trans.app.security_agent.can_manage_library_item( current_user_roles, library ) ): - raise exceptions.InsufficientPermissionsException( 'You do not have proper permission to modify permissions of this library.' ) + if not (is_admin or trans.app.security_agent.can_manage_library_item(current_user_roles, library)): + raise exceptions.InsufficientPermissionsException('You do not have proper permission to modify permissions of this library.') - new_access_roles_ids = util.listify( kwd.get( 'access_ids[]', None ) ) - new_add_roles_ids = util.listify( kwd.get( 'add_ids[]', None ) ) - new_manage_roles_ids = util.listify( kwd.get( 'manage_ids[]', None ) ) - new_modify_roles_ids = util.listify( kwd.get( 'modify_ids[]', None ) ) + new_access_roles_ids = util.listify(kwd.get('access_ids[]', None)) + new_add_roles_ids = util.listify(kwd.get('add_ids[]', None)) + new_manage_roles_ids = util.listify(kwd.get('manage_ids[]', None)) + new_modify_roles_ids = util.listify(kwd.get('modify_ids[]', None)) - action = kwd.get( 'action', None ) + action = kwd.get('action', None) if action is None: if payload is not None: - return self.set_permissions_old( trans, library, payload, **kwd ) + return self.set_permissions_old(trans, library, payload, **kwd) else: - raise exceptions.RequestParameterMissingException( 'The mandatory parameter "action" is missing.' ) + raise exceptions.RequestParameterMissingException('The mandatory parameter "action" is missing.') elif action == 'remove_restrictions': - is_public = self.library_manager.make_public( trans, library ) + is_public = self.library_manager.make_public(trans, library) if not is_public: - raise exceptions.InternalServerError( 'An error occured while making library public.' ) + raise exceptions.InternalServerError('An error occured while making library public.') elif action == 'set_permissions': # ACCESS LIBRARY ROLES valid_access_roles = [] invalid_access_roles_names = [] for role_id in new_access_roles_ids: - role = self.role_manager.get( trans, self.__decode_id( trans, role_id, 'role' ) ) - valid_roles, total_roles = trans.app.security_agent.get_valid_roles( trans, library, is_library_access=True ) + role = self.role_manager.get(trans, self.__decode_id(trans, role_id, 'role')) + valid_roles, total_roles = trans.app.security_agent.get_valid_roles(trans, library, is_library_access=True) if role in valid_roles: - valid_access_roles.append( role ) + valid_access_roles.append(role) else: - invalid_access_roles_names.append( role_id ) - if len( invalid_access_roles_names ) > 0: - log.warning( "The following roles could not be added to the library access permission: " + str( invalid_access_roles_names ) ) + invalid_access_roles_names.append(role_id) + if len(invalid_access_roles_names) > 0: + log.warning("The following roles could not be added to the library access permission: " + str(invalid_access_roles_names)) # ADD TO LIBRARY ROLES valid_add_roles = [] invalid_add_roles_names = [] for role_id in new_add_roles_ids: - role = self.role_manager.get( trans, self.__decode_id( trans, role_id, 'role' ) ) - valid_roles, total_roles = trans.app.security_agent.get_valid_roles( trans, library ) + role = self.role_manager.get(trans, self.__decode_id(trans, role_id, 'role')) + valid_roles, total_roles = trans.app.security_agent.get_valid_roles(trans, library) if role in valid_roles: - valid_add_roles.append( role ) + valid_add_roles.append(role) else: - invalid_add_roles_names.append( role_id ) - if len( invalid_add_roles_names ) > 0: - log.warning( "The following roles could not be added to the add library item permission: " + str( invalid_add_roles_names ) ) + invalid_add_roles_names.append(role_id) + if len(invalid_add_roles_names) > 0: + log.warning("The following roles could not be added to the add library item permission: " + str(invalid_add_roles_names)) # MANAGE LIBRARY ROLES valid_manage_roles = [] invalid_manage_roles_names = [] for role_id in new_manage_roles_ids: - role = self.role_manager.get( trans, self.__decode_id( trans, role_id, 'role' ) ) - valid_roles, total_roles = trans.app.security_agent.get_valid_roles( trans, library ) + role = self.role_manager.get(trans, self.__decode_id(trans, role_id, 'role')) + valid_roles, total_roles = trans.app.security_agent.get_valid_roles(trans, library) if role in valid_roles: - valid_manage_roles.append( role ) + valid_manage_roles.append(role) else: - invalid_manage_roles_names.append( role_id ) - if len( invalid_manage_roles_names ) > 0: - log.warning( "The following roles could not be added to the manage library permission: " + str( invalid_manage_roles_names ) ) + invalid_manage_roles_names.append(role_id) + if len(invalid_manage_roles_names) > 0: + log.warning("The following roles could not be added to the manage library permission: " + str(invalid_manage_roles_names)) # MODIFY LIBRARY ROLES valid_modify_roles = [] invalid_modify_roles_names = [] for role_id in new_modify_roles_ids: - role = self.role_manager.get( trans, self.__decode_id( trans, role_id, 'role' ) ) - valid_roles, total_roles = trans.app.security_agent.get_valid_roles( trans, library ) + role = self.role_manager.get(trans, self.__decode_id(trans, role_id, 'role')) + valid_roles, total_roles = trans.app.security_agent.get_valid_roles(trans, library) if role in valid_roles: - valid_modify_roles.append( role ) + valid_modify_roles.append(role) else: - invalid_modify_roles_names.append( role_id ) - if len( invalid_modify_roles_names ) > 0: - log.warning( "The following roles could not be added to the modify library permission: " + str( invalid_modify_roles_names ) ) + invalid_modify_roles_names.append(role_id) + if len(invalid_modify_roles_names) > 0: + log.warning("The following roles could not be added to the modify library permission: " + str(invalid_modify_roles_names)) - permissions = { trans.app.security_agent.permitted_actions.LIBRARY_ACCESS: valid_access_roles } - permissions.update( { trans.app.security_agent.permitted_actions.LIBRARY_ADD: valid_add_roles } ) - permissions.update( { trans.app.security_agent.permitted_actions.LIBRARY_MANAGE: valid_manage_roles } ) - permissions.update( { trans.app.security_agent.permitted_actions.LIBRARY_MODIFY: valid_modify_roles } ) + permissions = {trans.app.security_agent.permitted_actions.LIBRARY_ACCESS: valid_access_roles} + permissions.update({trans.app.security_agent.permitted_actions.LIBRARY_ADD: valid_add_roles}) + permissions.update({trans.app.security_agent.permitted_actions.LIBRARY_MANAGE: valid_manage_roles}) + permissions.update({trans.app.security_agent.permitted_actions.LIBRARY_MODIFY: valid_modify_roles}) - trans.app.security_agent.set_all_library_permissions( trans, library, permissions ) - trans.sa_session.refresh( library ) + trans.app.security_agent.set_all_library_permissions(trans, library, permissions) + trans.sa_session.refresh(library) # Copy the permissions to the root folder - trans.app.security_agent.copy_library_permissions( trans, library, library.root_folder ) + trans.app.security_agent.copy_library_permissions(trans, library, library.root_folder) else: - raise exceptions.RequestParameterInvalidException( 'The mandatory parameter "action" has an invalid value.' - 'Allowed values are: "remove_restrictions", set_permissions"' ) - roles = self.library_manager.get_current_roles( trans, library ) + raise exceptions.RequestParameterInvalidException('The mandatory parameter "action" has an invalid value.' + 'Allowed values are: "remove_restrictions", set_permissions"') + roles = self.library_manager.get_current_roles(trans, library) return roles - def set_permissions_old( self, trans, library, payload, **kwd ): + def set_permissions_old(self, trans, library, payload, **kwd): """ *** old implementation for backward compatibility *** POST /api/libraries/{encoded_library_id}/permissions Updates the library permissions. """ - params = util.Params( payload ) + params = util.Params(payload) permissions = {} for k, v in trans.app.model.Library.permitted_actions.items(): - role_params = params.get( k + '_in', [] ) - in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( trans.security.decode_id( x ) ) for x in util.listify( role_params ) ] - permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles - trans.app.security_agent.set_all_library_permissions( trans, library, permissions ) - trans.sa_session.refresh( library ) + role_params = params.get(k + '_in', []) + in_roles = [trans.sa_session.query(trans.app.model.Role).get(trans.security.decode_id(x)) for x in util.listify(role_params)] + permissions[trans.app.security_agent.get_action(v.action)] = in_roles + trans.app.security_agent.set_all_library_permissions(trans, library, permissions) + trans.sa_session.refresh(library) # Copy the permissions to the root folder - trans.app.security_agent.copy_library_permissions( trans, library, library.root_folder ) - item = library.to_dict( view='element', value_mapper={ 'id': trans.security.encode_id, 'root_folder_id': trans.security.encode_id } ) + trans.app.security_agent.copy_library_permissions(trans, library, library.root_folder) + item = library.to_dict(view='element', value_mapper={'id': trans.security.encode_id, 'root_folder_id': trans.security.encode_id}) return item diff --git a/lib/galaxy/webapps/galaxy/api/library_contents.py b/lib/galaxy/webapps/galaxy/api/library_contents.py index 7416406f062d..5992a8e84326 100644 --- a/lib/galaxy/webapps/galaxy/api/library_contents.py +++ b/lib/galaxy/webapps/galaxy/api/library_contents.py @@ -14,17 +14,17 @@ from sqlalchemy.orm.exc import NoResultFound import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class LibraryContentsController( BaseAPIController, UsesLibraryMixin, UsesLibraryMixinItems ): +class LibraryContentsController(BaseAPIController, UsesLibraryMixin, UsesLibraryMixinItems): - def __init__( self, app ): - super( LibraryContentsController, self ).__init__( app ) - self.hda_manager = managers.hdas.HDAManager( app ) + def __init__(self, app): + super(LibraryContentsController, self).__init__(app) + self.hda_manager = managers.hdas.HDAManager(app) @expose_api - def index( self, trans, library_id, **kwd ): + def index(self, trans, library_id, **kwd): """ index( self, trans, library_id, **kwd ) * GET /api/libraries/{library_id}/contents: @@ -49,17 +49,17 @@ def index( self, trans, library_id, **kwd ): rval = [] current_user_roles = trans.get_current_user_roles() - def traverse( folder ): + def traverse(folder): admin = trans.user_is_admin() rval = [] for subfolder in folder.active_folders: if not admin: - can_access, folder_ids = trans.app.security_agent.check_folder_contents( trans.user, current_user_roles, subfolder ) + can_access, folder_ids = trans.app.security_agent.check_folder_contents(trans.user, current_user_roles, subfolder) if (admin or can_access) and not subfolder.deleted: subfolder.api_path = folder.api_path + '/' + subfolder.name subfolder.api_type = 'folder' - rval.append( subfolder ) - rval.extend( traverse( subfolder ) ) + rval.append(subfolder) + rval.extend(traverse(subfolder)) for ld in folder.datasets: if not admin: can_access = trans.app.security_agent.can_access_dataset( @@ -69,42 +69,42 @@ def traverse( folder ): if (admin or can_access) and not ld.deleted: ld.api_path = folder.api_path + '/' + ld.name ld.api_type = 'file' - rval.append( ld ) + rval.append(ld) return rval try: - decoded_library_id = self.decode_id( library_id ) + decoded_library_id = self.decode_id(library_id) except Exception: - raise exceptions.MalformedId( 'Malformed library id ( %s ) specified, unable to decode.' % library_id ) + raise exceptions.MalformedId('Malformed library id ( %s ) specified, unable to decode.' % library_id) try: - library = trans.sa_session.query( trans.app.model.Library ).filter( trans.app.model.Library.table.c.id == decoded_library_id ).one() + library = trans.sa_session.query(trans.app.model.Library).filter(trans.app.model.Library.table.c.id == decoded_library_id).one() except MultipleResultsFound: - raise exceptions.InconsistentDatabase( 'Multiple libraries found with the same id.' ) + raise exceptions.InconsistentDatabase('Multiple libraries found with the same id.') except NoResultFound: - raise exceptions.RequestParameterInvalidException( 'No library found with the id provided.' ) + raise exceptions.RequestParameterInvalidException('No library found with the id provided.') except Exception as e: - raise exceptions.InternalServerError( 'Error loading from the database.' + str(e)) - if not ( trans.user_is_admin() or trans.app.security_agent.can_access_library( current_user_roles, library ) ): - raise exceptions.RequestParameterInvalidException( 'No library found with the id provided.' ) - encoded_id = 'F' + trans.security.encode_id( library.root_folder.id ) + raise exceptions.InternalServerError('Error loading from the database.' + str(e)) + if not (trans.user_is_admin() or trans.app.security_agent.can_access_library(current_user_roles, library)): + raise exceptions.RequestParameterInvalidException('No library found with the id provided.') + encoded_id = 'F' + trans.security.encode_id(library.root_folder.id) # appending root folder - rval.append( dict( id=encoded_id, - type='folder', - name='/', - url=url_for( 'library_content', library_id=library_id, id=encoded_id ) ) ) + rval.append(dict(id=encoded_id, + type='folder', + name='/', + url=url_for('library_content', library_id=library_id, id=encoded_id))) library.root_folder.api_path = '' # appending all other items in the library recursively - for content in traverse( library.root_folder ): - encoded_id = trans.security.encode_id( content.id ) + for content in traverse(library.root_folder): + encoded_id = trans.security.encode_id(content.id) if content.api_type == 'folder': encoded_id = 'F' + encoded_id - rval.append( dict( id=encoded_id, - type=content.api_type, - name=content.api_path, - url=url_for( 'library_content', library_id=library_id, id=encoded_id, ) ) ) + rval.append(dict(id=encoded_id, + type=content.api_type, + name=content.api_path, + url=url_for('library_content', library_id=library_id, id=encoded_id, ))) return rval @expose_api - def show( self, trans, id, library_id, **kwd ): + def show(self, trans, id, library_id, **kwd): """ show( self, trans, id, library_id, **kwd ) * GET /api/libraries/{library_id}/contents/{id} @@ -123,25 +123,25 @@ def show( self, trans, id, library_id, **kwd ): :func:`galaxy.model.LibraryDataset.to_dict` and :attr:`galaxy.model.LibraryFolder.dict_element_visible_keys` """ - class_name, content_id = self.__decode_library_content_id( id ) + class_name, content_id = self.__decode_library_content_id(id) if class_name == 'LibraryFolder': - content = self.get_library_folder( trans, content_id, check_ownership=False, check_accessible=True ) - rval = content.to_dict( view='element', value_mapper={ 'id': trans.security.encode_id } ) - rval[ 'id' ] = 'F' + str( rval[ 'id' ] ) - if rval[ 'parent_id' ] is not None: # This can happen for root folders. - rval[ 'parent_id' ] = 'F' + str( trans.security.encode_id( rval[ 'parent_id' ] ) ) - rval[ 'parent_library_id' ] = trans.security.encode_id( rval[ 'parent_library_id' ] ) + content = self.get_library_folder(trans, content_id, check_ownership=False, check_accessible=True) + rval = content.to_dict(view='element', value_mapper={'id': trans.security.encode_id}) + rval['id'] = 'F' + str(rval['id']) + if rval['parent_id'] is not None: # This can happen for root folders. + rval['parent_id'] = 'F' + str(trans.security.encode_id(rval['parent_id'])) + rval['parent_library_id'] = trans.security.encode_id(rval['parent_library_id']) else: - content = self.get_library_dataset( trans, content_id, check_ownership=False, check_accessible=True ) - rval = content.to_dict( view='element') - rval[ 'id' ] = trans.security.encode_id( rval[ 'id' ] ) - rval[ 'ldda_id' ] = trans.security.encode_id( rval[ 'ldda_id' ] ) - rval[ 'folder_id' ] = 'F' + str( trans.security.encode_id( rval[ 'folder_id' ] ) ) - rval[ 'parent_library_id' ] = trans.security.encode_id( rval[ 'parent_library_id' ] ) + content = self.get_library_dataset(trans, content_id, check_ownership=False, check_accessible=True) + rval = content.to_dict(view='element') + rval['id'] = trans.security.encode_id(rval['id']) + rval['ldda_id'] = trans.security.encode_id(rval['ldda_id']) + rval['folder_id'] = 'F' + str(trans.security.encode_id(rval['folder_id'])) + rval['parent_library_id'] = trans.security.encode_id(rval['parent_library_id']) return rval @web.expose_api - def create( self, trans, library_id, payload, **kwd ): + def create(self, trans, library_id, payload, **kwd): """ create( self, trans, library_id, payload, **kwd ) * POST /api/libraries/{library_id}/contents: @@ -182,6 +182,8 @@ def create( self, trans, library_id, payload, **kwd ): folder to create * description: (optional, only if create_type is 'folder') description of the folder to create + * tag_using_filename: (optional) + create tags on datasets using the file's original name :returns: a dictionary describing the new item unless ``from_hdca_id`` is supplied, in that case a list of such dictionaries is returned. @@ -191,8 +193,8 @@ def create( self, trans, library_id, payload, **kwd ): trans.response.status = 400 return "Missing required 'create_type' parameter." else: - create_type = payload.pop( 'create_type' ) - if create_type not in ( 'file', 'folder', 'collection' ): + create_type = payload.pop('create_type') + if create_type not in ('file', 'folder', 'collection'): trans.response.status = 400 return "Invalid value for 'create_type' parameter ( %s ) specified." % create_type @@ -200,22 +202,22 @@ def create( self, trans, library_id, payload, **kwd ): trans.response.status = 400 return "Missing required 'folder_id' parameter." else: - folder_id = payload.pop( 'folder_id' ) - class_name, folder_id = self.__decode_library_content_id( folder_id ) + folder_id = payload.pop('folder_id') + class_name, folder_id = self.__decode_library_content_id(folder_id) try: # security is checked in the downstream controller - parent = self.get_library_folder( trans, folder_id, check_ownership=False, check_accessible=False ) + parent = self.get_library_folder(trans, folder_id, check_ownership=False, check_accessible=False) except Exception as e: - return str( e ) + return str(e) # The rest of the security happens in the library_common controller. - real_folder_id = trans.security.encode_id( parent.id ) + real_folder_id = trans.security.encode_id(parent.id) # are we copying an HDA to the library folder? # we'll need the id and any message to attach, then branch to that private function - from_hda_id, from_hdca_id, ldda_message = ( payload.pop( 'from_hda_id', None ), payload.pop( 'from_hdca_id', None ), payload.pop( 'ldda_message', '' ) ) + from_hda_id, from_hdca_id, ldda_message = (payload.pop('from_hda_id', None), payload.pop('from_hdca_id', None), payload.pop('ldda_message', '')) if create_type == 'file': if from_hda_id: - return self._copy_hda_to_library_folder( trans, self.hda_manager, self.decode_id(from_hda_id), real_folder_id, ldda_message ) + return self._copy_hda_to_library_folder(trans, self.hda_manager, self.decode_id(from_hda_id), real_folder_id, ldda_message) if from_hdca_id: return self._copy_hdca_to_library_folder(trans, self.hda_manager, self.decode_id(from_hdca_id), real_folder_id, ldda_message) @@ -225,18 +227,18 @@ def create( self, trans, library_id, payload, **kwd ): # Now create the desired content object, either file or folder. if create_type == 'file': - status, output = trans.webapp.controllers['library_common'].upload_library_dataset( trans, 'api', library_id, real_folder_id, **payload ) + status, output = trans.webapp.controllers['library_common'].upload_library_dataset(trans, 'api', library_id, real_folder_id, **payload) elif create_type == 'folder': - status, output = trans.webapp.controllers['library_common'].create_folder( trans, 'api', real_folder_id, library_id, **payload ) + status, output = trans.webapp.controllers['library_common'].create_folder(trans, 'api', real_folder_id, library_id, **payload) elif create_type == 'collection': # Not delegating to library_common, so need to check access to parent # folder here. - self.check_user_can_add_to_library_item( trans, parent, check_accessible=True ) - create_params = api_payload_to_create_params( payload ) - create_params[ 'parent' ] = parent + self.check_user_can_add_to_library_item(trans, parent, check_accessible=True) + create_params = api_payload_to_create_params(payload) + create_params['parent'] = parent service = trans.app.dataset_collections_service - dataset_collection_instance = service.create( **create_params ) - return [ dictify_dataset_collection_instance( dataset_collection_instance, security=trans.security, parent=parent ) ] + dataset_collection_instance = service.create(**create_params) + return [dictify_dataset_collection_instance(dataset_collection_instance, security=trans.security, parent=parent)] if status != 200: trans.response.status = status return output @@ -246,7 +248,7 @@ def create( self, trans, library_id, payload, **kwd ): if ex_meta_payload is not None: # If there is extended metadata, store it, attach it to the dataset, and index it ex_meta = ExtendedMetadata(ex_meta_payload) - trans.sa_session.add( ex_meta ) + trans.sa_session.add(ex_meta) v.extended_metadata = ex_meta trans.sa_session.add(v) trans.sa_session.flush() @@ -254,14 +256,14 @@ def create( self, trans, library_id, payload, **kwd ): meta_i = ExtendedMetadataIndex(ex_meta, path, value) trans.sa_session.add(meta_i) trans.sa_session.flush() - if type( v ) == trans.app.model.LibraryDatasetDatasetAssociation: + if type(v) == trans.app.model.LibraryDatasetDatasetAssociation: v = v.library_dataset - encoded_id = trans.security.encode_id( v.id ) + encoded_id = trans.security.encode_id(v.id) if create_type == 'folder': encoded_id = 'F' + encoded_id - rval.append( dict( id=encoded_id, - name=v.name, - url=url_for( 'library_content', library_id=library_id, id=encoded_id ) ) ) + rval.append(dict(id=encoded_id, + name=v.name, + url=url_for('library_content', library_id=library_id, id=encoded_id))) return rval def _scan_json_block(self, meta, prefix=""): @@ -292,7 +294,7 @@ def _scan_json_block(self, meta, prefix=""): yield prefix, ("%s" % (meta)).encode("utf8", errors='replace') @web.expose_api - def update( self, trans, id, library_id, payload, **kwd ): + def update(self, trans, id, library_id, payload, **kwd): """ update( self, trans, id, library_id, payload, **kwd ) * PUT /api/libraries/{library_id}/contents/{id} @@ -311,26 +313,26 @@ def update( self, trans, id, library_id, payload, **kwd ): :returns: None """ if 'converted_dataset_id' in payload: - converted_id = payload.pop( 'converted_dataset_id' ) - content = self.get_library_dataset( trans, id, check_ownership=False, check_accessible=False ) - content_conv = self.get_library_dataset( trans, converted_id, check_ownership=False, check_accessible=False ) - assoc = trans.app.model.ImplicitlyConvertedDatasetAssociation( parent=content.library_dataset_dataset_association, - dataset=content_conv.library_dataset_dataset_association, - file_type=content_conv.library_dataset_dataset_association.extension, - metadata_safe=True ) - trans.sa_session.add( assoc ) + converted_id = payload.pop('converted_dataset_id') + content = self.get_library_dataset(trans, id, check_ownership=False, check_accessible=False) + content_conv = self.get_library_dataset(trans, converted_id, check_ownership=False, check_accessible=False) + assoc = trans.app.model.ImplicitlyConvertedDatasetAssociation(parent=content.library_dataset_dataset_association, + dataset=content_conv.library_dataset_dataset_association, + file_type=content_conv.library_dataset_dataset_association.extension, + metadata_safe=True) + trans.sa_session.add(assoc) trans.sa_session.flush() - def __decode_library_content_id( self, content_id ): - if len( content_id ) % 16 == 0: + def __decode_library_content_id(self, content_id): + if len(content_id) % 16 == 0: return 'LibraryDataset', content_id - elif content_id.startswith( 'F' ): - return 'LibraryFolder', content_id[ 1: ] + elif content_id.startswith('F'): + return 'LibraryFolder', content_id[1:] else: - raise HTTPBadRequest( 'Malformed library content id ( %s ) specified, unable to decode.' % str( content_id ) ) + raise HTTPBadRequest('Malformed library content id ( %s ) specified, unable to decode.' % str(content_id)) @web.expose_api - def delete( self, trans, library_id, id, **kwd ): + def delete(self, trans, library_id, id, **kwd): """ delete( self, trans, library_id, id, **kwd ) * DELETE /api/libraries/{library_id}/contents/{id} @@ -352,53 +354,53 @@ def delete( self, trans, library_id, id, **kwd ): """ # a request body is optional here purge = False - if kwd.get( 'payload', None ): - purge = util.string_as_bool( kwd['payload'].get( 'purge', False ) ) + if kwd.get('payload', None): + purge = util.string_as_bool(kwd['payload'].get('purge', False)) - rval = { 'id': id } + rval = {'id': id} try: - ld = self.get_library_dataset( trans, id, check_ownership=False, check_accessible=True ) + ld = self.get_library_dataset(trans, id, check_ownership=False, check_accessible=True) user_is_admin = trans.user_is_admin() - can_modify = trans.app.security_agent.can_modify_library_item( trans.user.all_roles(), ld ) - log.debug( 'is_admin: %s, can_modify: %s', user_is_admin, can_modify ) - if not ( user_is_admin or can_modify ): + can_modify = trans.app.security_agent.can_modify_library_item(trans.user.all_roles(), ld) + log.debug('is_admin: %s, can_modify: %s', user_is_admin, can_modify) + if not (user_is_admin or can_modify): trans.response.status = 403 - rval.update({ 'error': 'Unauthorized to delete or purge this library dataset' }) + rval.update({'error': 'Unauthorized to delete or purge this library dataset'}) return rval ld.deleted = True if purge: ld.purged = True - trans.sa_session.add( ld ) + trans.sa_session.add(ld) trans.sa_session.flush() # TODO: had to change this up a bit from Dataset.user_can_purge dataset = ld.library_dataset_dataset_association.dataset - no_history_assoc = len( dataset.history_associations ) == len( dataset.purged_history_associations ) - no_library_assoc = dataset.library_associations == [ ld.library_dataset_dataset_association ] + no_history_assoc = len(dataset.history_associations) == len(dataset.purged_history_associations) + no_library_assoc = dataset.library_associations == [ld.library_dataset_dataset_association] can_purge_dataset = not dataset.purged and no_history_assoc and no_library_assoc if can_purge_dataset: try: ld.library_dataset_dataset_association.dataset.full_delete() - trans.sa_session.add( ld.dataset ) + trans.sa_session.add(ld.dataset) except: pass # flush now to preserve deleted state in case of later interruption trans.sa_session.flush() - rval[ 'purged' ] = True + rval['purged'] = True trans.sa_session.flush() - rval[ 'deleted' ] = True + rval['deleted'] = True except exceptions.httpexceptions.HTTPInternalServerError: - log.exception( 'Library_contents API, delete: uncaught HTTPInternalServerError: %s, %s', - id, str( kwd ) ) + log.exception('Library_contents API, delete: uncaught HTTPInternalServerError: %s, %s', + id, str(kwd)) raise except exceptions.httpexceptions.HTTPException: raise except Exception as exc: - log.exception( 'library_contents API, delete: uncaught exception: %s, %s', - id, str( kwd ) ) + log.exception('library_contents API, delete: uncaught exception: %s, %s', + id, str(kwd)) trans.response.status = 500 - rval.update({ 'error': str( exc ) }) + rval.update({'error': str(exc)}) return rval diff --git a/lib/galaxy/webapps/galaxy/api/metrics.py b/lib/galaxy/webapps/galaxy/api/metrics.py index dd6e727339be..270b4b064cfd 100644 --- a/lib/galaxy/webapps/galaxy/api/metrics.py +++ b/lib/galaxy/webapps/galaxy/api/metrics.py @@ -11,24 +11,24 @@ from galaxy.web.base.controller import BaseAPIController import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class MetricsController( BaseAPIController ): +class MetricsController(BaseAPIController): - def __init__( self, app ): - super( MetricsController, self ).__init__( app ) + def __init__(self, app): + super(MetricsController, self).__init__(app) #: set to true to send additional debugging info to the log self.debugging = True - def _deserialize_isoformat_date( self, datestring ): + def _deserialize_isoformat_date(self, datestring): """ Convert ISO formatted date string into python datetime. """ - return datetime.datetime.strptime( datestring, "%Y-%m-%dT%H:%M:%S.%fZ" ) + return datetime.datetime.strptime(datestring, "%Y-%m-%dT%H:%M:%S.%fZ") @expose_api_anonymous - def create( self, trans, payload, **kwd ): + def create(self, trans, payload, **kwd): """ create( trans, payload ) * POST /api/metrics: @@ -49,13 +49,13 @@ def create( self, trans, payload, **kwd ): """ user_id = trans.user.id if trans.user else None session_id = trans.galaxy_session.id if trans.galaxy_session else None - parsed_gen = self._parse_metrics( payload.get( 'metrics', None ), user_id, session_id ) - self._send_metrics( trans, parsed_gen ) - response = self._get_server_pong( trans ) + parsed_gen = self._parse_metrics(payload.get('metrics', None), user_id, session_id) + self._send_metrics(trans, parsed_gen) + response = self._get_server_pong(trans) return response # TODO: move the following to DAO/Manager object - def _parse_metrics( self, metrics, user_id=None, session_id=None ): + def _parse_metrics(self, metrics, user_id=None, session_id=None): """ Return a generator yielding the each given metric as a tuple: * label: the namespace of the metric @@ -68,17 +68,17 @@ def _parse_metrics( self, metrics, user_id=None, session_id=None ): """ metrics = metrics or [] for metric in metrics: - label = metric[ 'namespace' ] - time = self._deserialize_isoformat_date( metric[ 'time' ] ) + label = metric['namespace'] + time = self._deserialize_isoformat_date(metric['time']) kwargs = { - 'level' : metric[ 'level' ], - 'args' : metric[ 'args' ], + 'level' : metric['level'], + 'args' : metric['args'], 'user' : user_id, 'session' : session_id } - yield ( label, time, kwargs ) + yield (label, time, kwargs) - def _send_metrics( self, trans, metrics ): + def _send_metrics(self, trans, metrics): """ Send metrics to the app's `trace_logger` if set and send to `log.debug` if this controller if `self.debugging`. @@ -87,12 +87,12 @@ def _send_metrics( self, trans, metrics ): """ if trans.app.trace_logger: for label, time, kwargs in metrics: - trans.app.trace_logger.log( label, event_time=int( time ), **kwargs ) + trans.app.trace_logger.log(label, event_time=int(time), **kwargs) elif self.debugging: for label, time, kwargs in metrics: - log.debug( '%s %s %s', label, time, kwargs ) + log.debug('%s %s %s', label, time, kwargs) - def _get_server_pong( self, trans ): + def _get_server_pong(self, trans): """ Return some status message or object. diff --git a/lib/galaxy/webapps/galaxy/api/page_revisions.py b/lib/galaxy/webapps/galaxy/api/page_revisions.py index 56e3bded8dd6..83fe08607fe2 100644 --- a/lib/galaxy/webapps/galaxy/api/page_revisions.py +++ b/lib/galaxy/webapps/galaxy/api/page_revisions.py @@ -8,13 +8,13 @@ from galaxy.model.item_attrs import UsesAnnotations from galaxy.util.sanitize_html import sanitize_html -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class PageRevisionsController( BaseAPIController, SharableItemSecurityMixin, UsesAnnotations, SharableMixin ): +class PageRevisionsController(BaseAPIController, SharableItemSecurityMixin, UsesAnnotations, SharableMixin): @expose_api - def index( self, trans, page_id, **kwd ): + def index(self, trans, page_id, **kwd): """ index( self, trans, page_id, **kwd ) * GET /api/pages/{page_id}/revisions @@ -25,17 +25,17 @@ def index( self, trans, page_id, **kwd ): :rtype: list :returns: dictionaries containing different revisions of the page """ - page = self._get_page( trans, page_id ) - self._verify_page_ownership( trans, page ) + page = self._get_page(trans, page_id) + self._verify_page_ownership(trans, page) - r = trans.sa_session.query( trans.app.model.PageRevision ).filter_by( page_id=trans.security.decode_id(page_id) ) + r = trans.sa_session.query(trans.app.model.PageRevision).filter_by(page_id=trans.security.decode_id(page_id)) out = [] for page in r: - out.append( self.encode_all_ids( trans, page.to_dict(), True) ) + out.append(self.encode_all_ids(trans, page.to_dict(), True)) return out @expose_api - def create( self, trans, page_id, payload, **kwd ): + def create(self, trans, page_id, payload, **kwd): """ create( self, trans, page_id, payload **kwd ) * POST /api/pages/{page_id}/revisions @@ -53,15 +53,15 @@ def create( self, trans, page_id, payload, **kwd ): if not content: raise exceptions.ObjectAttributeMissingException("content undefined or empty") - page = self._get_page( trans, page_id ) - self._verify_page_ownership( trans, page ) + page = self._get_page(trans, page_id) + self._verify_page_ownership(trans, page) if 'title' in payload: title = payload['title'] else: title = page.title - content = sanitize_html( content, 'utf-8', 'text/html' ) + content = sanitize_html(content, 'utf-8', 'text/html') page_revision = trans.app.model.PageRevision() page_revision.title = title @@ -73,18 +73,18 @@ def create( self, trans, page_id, payload, **kwd ): session = trans.sa_session session.flush() - return page_revision.to_dict( view="element" ) + return page_revision.to_dict(view="element") - def _get_page( self, trans, page_id ): + def _get_page(self, trans, page_id): page = None try: - page = trans.sa_session.query( trans.app.model.Page ).get( trans.security.decode_id(page_id) ) + page = trans.sa_session.query(trans.app.model.Page).get(trans.security.decode_id(page_id)) except Exception: pass if not page: raise exceptions.ObjectNotFound() return page - def _verify_page_ownership( self, trans, page ): - if not self.security_check( trans, page, True, True ): + def _verify_page_ownership(self, trans, page): + if not self.security_check(trans, page, True, True): raise exceptions.ItemOwnershipException() diff --git a/lib/galaxy/webapps/galaxy/api/pages.py b/lib/galaxy/webapps/galaxy/api/pages.py index b69ea56ea690..fc3b9befa1e1 100644 --- a/lib/galaxy/webapps/galaxy/api/pages.py +++ b/lib/galaxy/webapps/galaxy/api/pages.py @@ -8,13 +8,13 @@ from galaxy.model.item_attrs import UsesAnnotations from galaxy.util.sanitize_html import sanitize_html -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class PagesController( BaseAPIController, SharableItemSecurityMixin, UsesAnnotations, SharableMixin ): +class PagesController(BaseAPIController, SharableItemSecurityMixin, UsesAnnotations, SharableMixin): @expose_api - def index( self, trans, deleted=False, **kwd ): + def index(self, trans, deleted=False, **kwd): """ index( self, trans, deleted=False, **kwd ) * GET /api/pages @@ -28,28 +28,28 @@ def index( self, trans, deleted=False, **kwd ): out = [] if trans.user_is_admin(): - r = trans.sa_session.query( trans.app.model.Page ) + r = trans.sa_session.query(trans.app.model.Page) if not deleted: r = r.filter_by(deleted=False) for row in r: - out.append( self.encode_all_ids( trans, row.to_dict(), True) ) + out.append(self.encode_all_ids(trans, row.to_dict(), True)) else: user = trans.get_user() - r = trans.sa_session.query( trans.app.model.Page ).filter_by( user=user ) + r = trans.sa_session.query(trans.app.model.Page).filter_by(user=user) if not deleted: r = r.filter_by(deleted=False) for row in r: - out.append( self.encode_all_ids( trans, row.to_dict(), True) ) - r = trans.sa_session.query( trans.app.model.Page ).filter( trans.app.model.Page.user != user ).filter_by(published=True) + out.append(self.encode_all_ids(trans, row.to_dict(), True)) + r = trans.sa_session.query(trans.app.model.Page).filter(trans.app.model.Page.user != user).filter_by(published=True) if not deleted: r = r.filter_by(deleted=False) for row in r: - out.append( self.encode_all_ids( trans, row.to_dict(), True) ) + out.append(self.encode_all_ids(trans, row.to_dict(), True)) return out @expose_api - def create( self, trans, payload, **kwd ): + def create(self, trans, payload, **kwd): """ create( self, trans, payload, **kwd ) * POST /api/pages @@ -67,23 +67,23 @@ def create( self, trans, payload, **kwd ): user = trans.get_user() if not payload.get("title", None): - raise exceptions.ObjectAttributeMissingException( "Page name is required" ) + raise exceptions.ObjectAttributeMissingException("Page name is required") elif not payload.get("slug", None): - raise exceptions.ObjectAttributeMissingException( "Page id is required" ) - elif not self._is_valid_slug( payload["slug"] ): - raise exceptions.ObjectAttributeInvalidException( "Page identifier must consist of only lowercase letters, numbers, and the '-' character" ) - elif trans.sa_session.query( trans.app.model.Page ).filter_by( user=user, slug=payload["slug"], deleted=False ).first(): - raise exceptions.DuplicatedSlugException( "Page slug must be unique" ) + raise exceptions.ObjectAttributeMissingException("Page id is required") + elif not self._is_valid_slug(payload["slug"]): + raise exceptions.ObjectAttributeInvalidException("Page identifier must consist of only lowercase letters, numbers, and the '-' character") + elif trans.sa_session.query(trans.app.model.Page).filter_by(user=user, slug=payload["slug"], deleted=False).first(): + raise exceptions.DuplicatedSlugException("Page slug must be unique") content = payload.get("content", "") - content = sanitize_html( content, 'utf-8', 'text/html' ) + content = sanitize_html(content, 'utf-8', 'text/html') # Create the new stored page page = trans.app.model.Page() page.title = payload['title'] page.slug = payload['slug'] - page_annotation = sanitize_html( payload.get( "annotation", "" ), 'utf-8', 'text/html' ) - self.add_item_annotation( trans.sa_session, trans.get_user(), page, page_annotation ) + page_annotation = sanitize_html(payload.get("annotation", ""), 'utf-8', 'text/html') + self.add_item_annotation(trans.sa_session, trans.get_user(), page, page_annotation) page.user = user # And the first (empty) page revision page_revision = trans.app.model.PageRevision() @@ -93,14 +93,14 @@ def create( self, trans, payload, **kwd ): page_revision.content = content # Persist session = trans.sa_session - session.add( page ) + session.add(page) session.flush() - rval = self.encode_all_ids( trans, page.to_dict(), True ) + rval = self.encode_all_ids(trans, page.to_dict(), True) return rval @expose_api - def delete( self, trans, id, **kwd ): + def delete(self, trans, id, **kwd): """ delete( self, trans, id, **kwd ) * DELETE /api/pages/{id} @@ -111,7 +111,7 @@ def delete( self, trans, id, **kwd ): :rtype: dict :returns: Dictionary with 'success' or 'error' element to indicate the result of the request """ - page = self._get_page( trans, id ) + page = self._get_page(trans, id) # Mark a page as deleted page.deleted = True @@ -119,7 +119,7 @@ def delete( self, trans, id, **kwd ): return '' # TODO: Figure out what to return on DELETE, document in guidelines! @expose_api - def show( self, trans, id, **kwd ): + def show(self, trans, id, **kwd): """ show( self, trans, id, **kwd ) * GET /api/pages/{id} @@ -130,15 +130,15 @@ def show( self, trans, id, **kwd ): :rtype: dict :returns: Dictionary return of the Page.to_dict call with the 'content' field populated by the most recent revision """ - page = self._get_page( trans, id ) - self.security_check( trans, page, check_ownership=False, check_accessible=True) - rval = self.encode_all_ids( trans, page.to_dict(), True ) + page = self._get_page(trans, id) + self.security_check(trans, page, check_ownership=False, check_accessible=True) + rval = self.encode_all_ids(trans, page.to_dict(), True) rval['content'] = page.latest_revision.content return rval - def _get_page( self, trans, id ): # Fetches page object and verifies security. + def _get_page(self, trans, id): # Fetches page object and verifies security. try: - page = trans.sa_session.query( trans.app.model.Page ).get( trans.security.decode_id( id ) ) + page = trans.sa_session.query(trans.app.model.Page).get(trans.security.decode_id(id)) except Exception: page = None diff --git a/lib/galaxy/webapps/galaxy/api/provenance.py b/lib/galaxy/webapps/galaxy/api/provenance.py index 4b9500833d22..f3d0c5f15422 100644 --- a/lib/galaxy/webapps/galaxy/api/provenance.py +++ b/lib/galaxy/webapps/galaxy/api/provenance.py @@ -7,44 +7,45 @@ from paste.httpexceptions import HTTPNotImplemented, HTTPBadRequest from galaxy import managers -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class BaseProvenanceController( BaseAPIController ): +class BaseProvenanceController(BaseAPIController): """ """ - def __init__( self, app ): - super( BaseProvenanceController, self ).__init__( app ) - self.hda_manager = managers.hdas.HDAManager( app ) + + def __init__(self, app): + super(BaseProvenanceController, self).__init__(app) + self.hda_manager = managers.hdas.HDAManager(app) @web.expose_api - def index( self, trans, **kwd ): + def index(self, trans, **kwd): follow = kwd.get('follow', False) - value = self._get_provenance( trans, self.provenance_item_class, kwd[self.provenance_item_id], follow ) + value = self._get_provenance(trans, self.provenance_item_class, kwd[self.provenance_item_id], follow) return value @web.expose_api - def show( self, trans, elem_name, **kwd ): + def show(self, trans, elem_name, **kwd): follow = kwd.get('follow', False) - value = self._get_provenance( trans, self.provenance_item_class, kwd[self.provenance_item_id], follow ) + value = self._get_provenance(trans, self.provenance_item_class, kwd[self.provenance_item_id], follow) return value @web.expose_api - def create( self, trans, tag_name, payload=None, **kwd ): + def create(self, trans, tag_name, payload=None, **kwd): payload = payload or {} raise HTTPNotImplemented() @web.expose_api - def delete( self, trans, tag_name, **kwd ): + def delete(self, trans, tag_name, **kwd): raise HTTPBadRequest("Cannot Delete Provenance") - def _get_provenance( self, trans, item_class_name, item_id, follow=True ): - provenance_item = self.get_object( trans, item_id, item_class_name, check_ownership=False, check_accessible=False) + def _get_provenance(self, trans, item_class_name, item_id, follow=True): + provenance_item = self.get_object(trans, item_id, item_class_name, check_ownership=False, check_accessible=False) if item_class_name == "HistoryDatasetAssociation": - self.hda_manager.error_unless_accessible( provenance_item, trans.user ) + self.hda_manager.error_unless_accessible(provenance_item, trans.user) else: - self.security_check( trans, provenance_item, check_accessible=True ) - out = self._get_record( trans, provenance_item, follow ) + self.security_check(trans, provenance_item, check_accessible=True) + out = self._get_record(trans, provenance_item, follow) return out def _get_record(self, trans, item, follow): @@ -55,8 +56,8 @@ def _get_record(self, trans, item, follow): if job is not None: return { "id": trans.security.encode_id(item.id), - "uuid": ( lambda uuid: str( uuid ) if uuid else None )( item.dataset.uuid), - "job_id": trans.security.encode_id( job.id ), + "uuid": (lambda uuid: str(uuid) if uuid else None)(item.dataset.uuid), + "job_id": trans.security.encode_id(job.id), "tool_id": job.tool_id, "parameters": self._get_job_record(trans, job, follow), "stderr": job.stderr, @@ -65,7 +66,7 @@ def _get_record(self, trans, item, follow): else: return { "id": trans.security.encode_id(item.id), - "uuid": ( lambda uuid: str( uuid ) if uuid else None )( item.dataset.uuid) + "uuid": (lambda uuid: str(uuid) if uuid else None)(item.dataset.uuid) } return None @@ -81,18 +82,18 @@ def _get_job_record(self, trans, job, follow): else: out[in_d.name] = { "id": trans.security.encode_id(in_d.dataset.id), - "uuid": ( lambda uuid: str( uuid ) if uuid else None )( in_d.dataset.dataset.uuid ), + "uuid": (lambda uuid: str(uuid) if uuid else None)(in_d.dataset.dataset.uuid), } return out -class HDAProvenanceController( BaseProvenanceController ): +class HDAProvenanceController(BaseProvenanceController): controller_name = "history_content_provenance" provenance_item_class = "HistoryDatasetAssociation" provenance_item_id = "history_content_id" -class LDDAProvenanceController( BaseProvenanceController ): +class LDDAProvenanceController(BaseProvenanceController): controller_name = "ldda_provenance" provenance_item_class = "LibraryDatasetDatasetAssociation" provenance_item_id = "library_content_id" diff --git a/lib/galaxy/webapps/galaxy/api/quotas.py b/lib/galaxy/webapps/galaxy/api/quotas.py index 2cca27db9563..4ba16de61335 100644 --- a/lib/galaxy/webapps/galaxy/api/quotas.py +++ b/lib/galaxy/webapps/galaxy/api/quotas.py @@ -10,138 +10,137 @@ from galaxy.actions.admin import AdminActions from galaxy.exceptions import ActionInputError from galaxy.web.base.controller import BaseAPIController, UsesQuotaMixin, url_for -from galaxy.web.base.controllers.admin import Admin from galaxy.web.params import QuotaParamParser -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class QuotaAPIController( BaseAPIController, Admin, AdminActions, UsesQuotaMixin, QuotaParamParser ): +class QuotaAPIController(BaseAPIController, AdminActions, UsesQuotaMixin, QuotaParamParser): @web.expose_api @web.require_admin - def index( self, trans, deleted='False', **kwd ): + def index(self, trans, deleted='False', **kwd): """ GET /api/quotas GET /api/quotas/deleted Displays a collection (list) of quotas. """ rval = [] - deleted = util.string_as_bool( deleted ) - query = trans.sa_session.query( trans.app.model.Quota ) + deleted = util.string_as_bool(deleted) + query = trans.sa_session.query(trans.app.model.Quota) if deleted: route = 'deleted_quota' - query = query.filter( trans.app.model.Quota.table.c.deleted == true() ) + query = query.filter(trans.app.model.Quota.table.c.deleted == true()) else: route = 'quota' - query = query.filter( trans.app.model.Quota.table.c.deleted == false() ) + query = query.filter(trans.app.model.Quota.table.c.deleted == false()) for quota in query: - item = quota.to_dict( value_mapper={ 'id': trans.security.encode_id } ) - encoded_id = trans.security.encode_id( quota.id ) - item['url'] = url_for( route, id=encoded_id ) - rval.append( item ) + item = quota.to_dict(value_mapper={'id': trans.security.encode_id}) + encoded_id = trans.security.encode_id(quota.id) + item['url'] = url_for(route, id=encoded_id) + rval.append(item) return rval @web.expose_api @web.require_admin - def show( self, trans, id, deleted='False', **kwd ): + def show(self, trans, id, deleted='False', **kwd): """ GET /api/quotas/{encoded_quota_id} GET /api/quotas/deleted/{encoded_quota_id} Displays information about a quota. """ - quota = self.get_quota( trans, id, deleted=util.string_as_bool( deleted ) ) - return quota.to_dict( view='element', value_mapper={ 'id': trans.security.encode_id, 'total_disk_usage': float } ) + quota = self.get_quota(trans, id, deleted=util.string_as_bool(deleted)) + return quota.to_dict(view='element', value_mapper={'id': trans.security.encode_id, 'total_disk_usage': float}) @web.expose_api @web.require_admin - def create( self, trans, payload, **kwd ): + def create(self, trans, payload, **kwd): """ POST /api/quotas Creates a new quota. """ try: - self.validate_in_users_and_groups( trans, payload ) + self.validate_in_users_and_groups(trans, payload) except Exception as e: - raise HTTPBadRequest( detail=str( e ) ) - params = self.get_quota_params( payload ) + raise HTTPBadRequest(detail=str(e)) + params = self.get_quota_params(payload) try: - quota, message = self._create_quota( params ) + quota, message = self._create_quota(params) except ActionInputError as e: - raise HTTPBadRequest( detail=str( e ) ) - item = quota.to_dict( value_mapper={ 'id': trans.security.encode_id } ) - item['url'] = url_for( 'quota', id=trans.security.encode_id( quota.id ) ) + raise HTTPBadRequest(detail=str(e)) + item = quota.to_dict(value_mapper={'id': trans.security.encode_id}) + item['url'] = url_for('quota', id=trans.security.encode_id(quota.id)) item['message'] = message return item @web.expose_api @web.require_admin - def update( self, trans, id, payload, **kwd ): + def update(self, trans, id, payload, **kwd): """ PUT /api/quotas/{encoded_quota_id} Modifies a quota. """ try: - self.validate_in_users_and_groups( trans, payload ) + self.validate_in_users_and_groups(trans, payload) except Exception as e: - raise HTTPBadRequest( detail=str( e ) ) + raise HTTPBadRequest(detail=str(e)) - quota = self.get_quota( trans, id, deleted=False ) + quota = self.get_quota(trans, id, deleted=False) # FIXME: Doing it this way makes the update non-atomic if a method fails after an earlier one has succeeded. payload['id'] = id - params = self.get_quota_params( payload ) + params = self.get_quota_params(payload) methods = [] - if payload.get( 'name', None ) or payload.get( 'description', None ): - methods.append( self._rename_quota ) - if payload.get( 'amount', None ): - methods.append( self._edit_quota ) - if payload.get( 'default', None ) == 'no': - methods.append( self._unset_quota_default ) - elif payload.get( 'default', None ): - methods.append( self._set_quota_default ) - if payload.get( 'in_users', None ) or payload.get( 'in_groups', None ): - methods.append( self._manage_users_and_groups_for_quota ) + if payload.get('name', None) or payload.get('description', None): + methods.append(self._rename_quota) + if payload.get('amount', None): + methods.append(self._edit_quota) + if payload.get('default', None) == 'no': + methods.append(self._unset_quota_default) + elif payload.get('default', None): + methods.append(self._set_quota_default) + if payload.get('in_users', None) or payload.get('in_groups', None): + methods.append(self._manage_users_and_groups_for_quota) messages = [] for method in methods: try: - message = method( quota, params ) + message = method(quota, params) except ActionInputError as e: - raise HTTPBadRequest( detail=str( e ) ) - messages.append( message ) - return '; '.join( messages ) + raise HTTPBadRequest(detail=str(e)) + messages.append(message) + return '; '.join(messages) @web.expose_api @web.require_admin - def delete( self, trans, id, **kwd ): + def delete(self, trans, id, **kwd): """ DELETE /api/quotas/{encoded_quota_id} Deletes a quota """ - quota = self.get_quota( trans, id, deleted=False ) # deleted quotas are not technically members of this collection + quota = self.get_quota(trans, id, deleted=False) # deleted quotas are not technically members of this collection # a request body is optional here - payload = kwd.get( 'payload', {} ) + payload = kwd.get('payload', {}) payload['id'] = id - params = self.get_quota_params( payload ) + params = self.get_quota_params(payload) try: - message = self._mark_quota_deleted( quota, params ) - if util.string_as_bool( payload.get( 'purge', False ) ): - message += self._purge_quota( quota, params ) + message = self._delete_quota(quota, params) + if util.string_as_bool(payload.get('purge', False)): + message += self._purge_quota(quota, params) except ActionInputError as e: - raise HTTPBadRequest( detail=str( e ) ) + raise HTTPBadRequest(detail=str(e)) return message @web.expose_api @web.require_admin - def undelete( self, trans, id, **kwd ): + def undelete(self, trans, id, **kwd): """ POST /api/quotas/deleted/{encoded_quota_id}/undelete Undeletes a quota """ - quota = self.get_quota( trans, id, deleted=True ) + quota = self.get_quota(trans, id, deleted=True) try: - return self._undelete_quota( quota ) + return self._undelete_quota(quota) except ActionInputError as e: - raise HTTPBadRequest( detail=str( e ) ) + raise HTTPBadRequest(detail=str(e)) diff --git a/lib/galaxy/webapps/galaxy/api/remote_files.py b/lib/galaxy/webapps/galaxy/api/remote_files.py index 28d42cd45c02..9dc39fc8222f 100644 --- a/lib/galaxy/webapps/galaxy/api/remote_files.py +++ b/lib/galaxy/webapps/galaxy/api/remote_files.py @@ -11,13 +11,13 @@ from operator import itemgetter import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class RemoteFilesAPIController( BaseAPIController ): +class RemoteFilesAPIController(BaseAPIController): @expose_api - def index( self, trans, **kwd ): + def index(self, trans, **kwd): """ GET /api/remote_files/ @@ -33,96 +33,96 @@ def index( self, trans, **kwd ): :returns: list of available files :rtype: list """ - target = kwd.get( 'target', None ) - format = kwd.get( 'format', None ) + target = kwd.get('target', None) + format = kwd.get('format', None) if target == 'userdir': user_login = trans.user.email user_base_dir = trans.app.config.user_library_import_dir if user_base_dir is None: - raise exceptions.ConfigDoesNotAllowException( 'The configuration of this Galaxy instance does not allow upload from user directories.' ) - full_import_dir = os.path.join( user_base_dir, user_login ) + raise exceptions.ConfigDoesNotAllowException('The configuration of this Galaxy instance does not allow upload from user directories.') + full_import_dir = os.path.join(user_base_dir, user_login) if not os.path.exists(full_import_dir): raise exceptions.ObjectNotFound('You do not have any files in your user directory. Use FTP to upload there.') if full_import_dir is not None: if format == 'jstree': - disable = kwd.get( 'disable', 'folders') + disable = kwd.get('disable', 'folders') try: - userdir_jstree = self.__create_jstree( full_import_dir, disable ) + userdir_jstree = self.__create_jstree(full_import_dir, disable) response = userdir_jstree.jsonData() except Exception as exception: - log.debug( str( exception ) ) - raise exceptions.InternalServerError( 'Could not create tree representation of the given folder: ' + str( full_import_dir ) ) + log.debug(str(exception)) + raise exceptions.InternalServerError('Could not create tree representation of the given folder: ' + str(full_import_dir)) if not response: raise exceptions.ObjectNotFound('You do not have any files in your user directory. Use FTP to upload there.') elif format == 'ajax': - raise exceptions.NotImplemented( 'Not implemented yet. Sorry.' ) + raise exceptions.NotImplemented('Not implemented yet. Sorry.') else: try: - response = self.__load_all_filenames( full_import_dir ) + response = self.__load_all_filenames(full_import_dir) except Exception as exception: - log.error( 'Could not get user import files: %s', str( exception ), exc_info=True ) - raise exceptions.InternalServerError( 'Could not get the files from your user directory folder.' ) + log.error('Could not get user import files: %s', str(exception), exc_info=True) + raise exceptions.InternalServerError('Could not get the files from your user directory folder.') else: - raise exceptions.InternalServerError( 'Could not get the files from your user directory folder.' ) + raise exceptions.InternalServerError('Could not get the files from your user directory folder.') elif target == 'importdir': base_dir = trans.app.config.library_import_dir if base_dir is None: - raise exceptions.ConfigDoesNotAllowException( 'The configuration of this Galaxy instance does not allow usage of import directory.' ) + raise exceptions.ConfigDoesNotAllowException('The configuration of this Galaxy instance does not allow usage of import directory.') if format == 'jstree': - disable = kwd.get( 'disable', 'folders') - try: - importdir_jstree = self.__create_jstree( base_dir, disable ) - response = importdir_jstree.jsonData() - except Exception as exception: - log.debug( str( exception ) ) - raise exceptions.InternalServerError( 'Could not create tree representation of the given folder: ' + str( base_dir ) ) + disable = kwd.get('disable', 'folders') + try: + importdir_jstree = self.__create_jstree(base_dir, disable) + response = importdir_jstree.jsonData() + except Exception as exception: + log.debug(str(exception)) + raise exceptions.InternalServerError('Could not create tree representation of the given folder: ' + str(base_dir)) elif format == 'ajax': - raise exceptions.NotImplemented( 'Not implemented yet. Sorry.' ) + raise exceptions.NotImplemented('Not implemented yet. Sorry.') else: try: - response = self.__load_all_filenames( base_dir ) + response = self.__load_all_filenames(base_dir) except Exception as exception: - log.error( 'Could not get user import files: %s', str( exception ), exc_info=True ) - raise exceptions.InternalServerError( 'Could not get the files from your import directory folder.' ) + log.error('Could not get user import files: %s', str(exception), exc_info=True) + raise exceptions.InternalServerError('Could not get the files from your import directory folder.') else: user_ftp_base_dir = trans.app.config.ftp_upload_dir if user_ftp_base_dir is None: - raise exceptions.ConfigDoesNotAllowException( 'The configuration of this Galaxy instance does not allow upload from FTP directories.' ) + raise exceptions.ConfigDoesNotAllowException('The configuration of this Galaxy instance does not allow upload from FTP directories.') try: user_ftp_dir = trans.user_ftp_dir if user_ftp_dir is not None: - response = self.__load_all_filenames( user_ftp_dir ) + response = self.__load_all_filenames(user_ftp_dir) else: - log.warning( 'You do not have an FTP directory named as your login at this Galaxy instance.' ) + log.warning('You do not have an FTP directory named as your login at this Galaxy instance.') return None except Exception as exception: - log.warning( 'Could not get ftp files: %s', str( exception ), exc_info=True ) + log.warning('Could not get ftp files: %s', str(exception), exc_info=True) return None return response - def __load_all_filenames( self, directory ): + def __load_all_filenames(self, directory): """ Loads recursively all files within the given folder and its subfolders and returns a flat list. """ response = [] - if os.path.exists( directory ): - for ( dirpath, dirnames, filenames ) in os.walk( directory ): + if os.path.exists(directory): + for (dirpath, dirnames, filenames) in os.walk(directory): for filename in filenames: - path = os.path.relpath( os.path.join( dirpath, filename ), directory ) - statinfo = os.lstat( os.path.join( dirpath, filename ) ) - response.append( dict( path=path, - size=statinfo.st_size, - ctime=time.strftime( "%m/%d/%Y %I:%M:%S %p", time.localtime( statinfo.st_ctime ) ) ) ) + path = os.path.relpath(os.path.join(dirpath, filename), directory) + statinfo = os.lstat(os.path.join(dirpath, filename)) + response.append(dict(path=path, + size=statinfo.st_size, + ctime=time.strftime("%m/%d/%Y %I:%M:%S %p", time.localtime(statinfo.st_ctime)))) else: - log.warning( "The directory \"%s\" does not exist." % directory ) + log.warning("The directory \"%s\" does not exist." % directory) return response # sort by path response = sorted(response, key=itemgetter("path")) return response - def __create_jstree( self, directory, disable='folders' ): + def __create_jstree(self, directory, disable='folders'): """ Loads recursively all files and folders within the given folder and its subfolders and returns jstree representation @@ -130,20 +130,20 @@ def __create_jstree( self, directory, disable='folders' ): """ userdir_jstree = None jstree_paths = [] - if os.path.exists( directory ): - for ( dirpath, dirnames, filenames ) in os.walk( directory ): + if os.path.exists(directory): + for (dirpath, dirnames, filenames) in os.walk(directory): for dirname in dirnames: - dir_path = os.path.relpath( os.path.join( dirpath, dirname ), directory ) + dir_path = os.path.relpath(os.path.join(dirpath, dirname), directory) dir_path_hash = hashlib.sha1(unicodify(dir_path).encode('utf-8')).hexdigest() disabled = True if disable == 'folders' else False - jstree_paths.append( jstree.Path( dir_path, dir_path_hash, { 'type': 'folder', 'state': { 'disabled': disabled }, 'li_attr': { 'full_path': dir_path } } ) ) + jstree_paths.append(jstree.Path(dir_path, dir_path_hash, {'type': 'folder', 'state': {'disabled': disabled}, 'li_attr': {'full_path': dir_path}})) for filename in filenames: - file_path = os.path.relpath( os.path.join( dirpath, filename ), directory ) + file_path = os.path.relpath(os.path.join(dirpath, filename), directory) file_path_hash = hashlib.sha1(unicodify(file_path).encode('utf-8')).hexdigest() disabled = True if disable == 'files' else False - jstree_paths.append( jstree.Path( file_path, file_path_hash, { 'type': 'file', 'state': { 'disabled': disabled }, 'li_attr': { 'full_path': file_path } } ) ) + jstree_paths.append(jstree.Path(file_path, file_path_hash, {'type': 'file', 'state': {'disabled': disabled}, 'li_attr': {'full_path': file_path}})) else: - raise exceptions.ConfigDoesNotAllowException( 'The given directory does not exist.' ) - userdir_jstree = jstree.JSTree( jstree_paths ) + raise exceptions.ConfigDoesNotAllowException('The given directory does not exist.') + userdir_jstree = jstree.JSTree(jstree_paths) return userdir_jstree diff --git a/lib/galaxy/webapps/galaxy/api/request_types.py b/lib/galaxy/webapps/galaxy/api/request_types.py index 99cca3b26f23..d6dba58acaa8 100644 --- a/lib/galaxy/webapps/galaxy/api/request_types.py +++ b/lib/galaxy/webapps/galaxy/api/request_types.py @@ -7,52 +7,52 @@ from galaxy.sample_tracking.request_types import request_type_factory from xml.etree.ElementTree import XML -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class RequestTypeAPIController( BaseAPIController ): +class RequestTypeAPIController(BaseAPIController): @web.expose_api - def index( self, trans, **kwd ): + def index(self, trans, **kwd): """ GET /api/request_types Displays a collection (list) of request_types. """ rval = [] - for request_type in trans.app.security_agent.get_accessible_request_types( trans, trans.user ): - item = request_type.to_dict( value_mapper={ 'id': trans.security.encode_id, 'request_form_id': trans.security.encode_id, 'sample_form_id': trans.security.encode_id } ) - encoded_id = trans.security.encode_id( request_type.id ) - item['url'] = url_for( 'request_type', id=encoded_id ) - rval.append( item ) + for request_type in trans.app.security_agent.get_accessible_request_types(trans, trans.user): + item = request_type.to_dict(value_mapper={'id': trans.security.encode_id, 'request_form_id': trans.security.encode_id, 'sample_form_id': trans.security.encode_id}) + encoded_id = trans.security.encode_id(request_type.id) + item['url'] = url_for('request_type', id=encoded_id) + rval.append(item) return rval @web.expose_api - def show( self, trans, id, **kwd ): + def show(self, trans, id, **kwd): """ GET /api/request_types/{encoded_request_type_id} Displays information about a request_type. """ request_type_id = id try: - decoded_request_type_id = trans.security.decode_id( request_type_id ) + decoded_request_type_id = trans.security.decode_id(request_type_id) except TypeError: trans.response.status = 400 - return "Malformed request type id ( %s ) specified, unable to decode." % str( request_type_id ) + return "Malformed request type id ( %s ) specified, unable to decode." % str(request_type_id) try: - request_type = trans.sa_session.query( trans.app.model.RequestType ).get( decoded_request_type_id ) + request_type = trans.sa_session.query(trans.app.model.RequestType).get(decoded_request_type_id) except: request_type = None if not request_type: # or not trans.user_is_admin(): trans.response.status = 400 - return "Invalid request_type id ( %s ) specified." % str( request_type_id ) - if not trans.app.security_agent.can_access_request_type( trans.user.all_roles(), request_type ): + return "Invalid request_type id ( %s ) specified." % str(request_type_id) + if not trans.app.security_agent.can_access_request_type(trans.user.all_roles(), request_type): trans.response.status = 400 - return "No permission to access request_type ( %s )." % str( request_type_id ) - item = request_type.to_dict( view='element', value_mapper={ 'id': trans.security.encode_id, 'request_form_id': trans.security.encode_id, 'sample_form_id': trans.security.encode_id } ) - item['url'] = url_for( 'request_type', id=request_type_id ) + return "No permission to access request_type ( %s )." % str(request_type_id) + item = request_type.to_dict(view='element', value_mapper={'id': trans.security.encode_id, 'request_form_id': trans.security.encode_id, 'sample_form_id': trans.security.encode_id}) + item['url'] = url_for('request_type', id=request_type_id) return item @web.expose_api - def create( self, trans, payload, **kwd ): + def create(self, trans, payload, **kwd): """ POST /api/request_types Creates a new request type (external_service configuration). @@ -60,43 +60,43 @@ def create( self, trans, payload, **kwd ): if not trans.user_is_admin(): trans.response.status = 403 return "You are not authorized to create a new request type (external_service configuration)." - xml_text = payload.get( 'xml_text', None ) + xml_text = payload.get('xml_text', None) if xml_text is None: trans.response.status = 400 return "Missing required parameter 'xml_text'." - elem = XML( xml_text ) - request_form_id = payload.get( 'request_form_id', None ) + elem = XML(xml_text) + request_form_id = payload.get('request_form_id', None) if request_form_id is None: trans.response.status = 400 return "Missing required parameter 'request_form_id'." - request_form = trans.sa_session.query( trans.app.model.FormDefinition ).get( trans.security.decode_id( request_form_id ) ) - sample_form_id = payload.get( 'sample_form_id', None ) + request_form = trans.sa_session.query(trans.app.model.FormDefinition).get(trans.security.decode_id(request_form_id)) + sample_form_id = payload.get('sample_form_id', None) if sample_form_id is None: trans.response.status = 400 return "Missing required parameter 'sample_form_id'." - sample_form = trans.sa_session.query( trans.app.model.FormDefinition ).get( trans.security.decode_id( sample_form_id ) ) - external_service_id = payload.get( 'external_service_id', None ) + sample_form = trans.sa_session.query(trans.app.model.FormDefinition).get(trans.security.decode_id(sample_form_id)) + external_service_id = payload.get('external_service_id', None) if external_service_id is None: trans.response.status = 400 return "Missing required parameter 'external_service_id'." - external_service = trans.sa_session.query( trans.app.model.ExternalService ).get( trans.security.decode_id( external_service_id ) ) - request_type = request_type_factory.from_elem( elem, request_form, sample_form, external_service ) + external_service = trans.sa_session.query(trans.app.model.ExternalService).get(trans.security.decode_id(external_service_id)) + request_type = request_type_factory.from_elem(elem, request_form, sample_form, external_service) # FIXME: move permission building/setting to separate abstract method call and # allow setting individual permissions by role (currently only one action, so not strictly needed) - role_ids = payload.get( 'role_ids', [] ) - roles = [ trans.sa_session.query( trans.model.Role ).get( trans.security.decode_id( i ) ) for i in role_ids ] # if trans.app.security_agent.ok_to_display( trans.user, i ) ] + role_ids = payload.get('role_ids', []) + roles = [trans.sa_session.query(trans.model.Role).get(trans.security.decode_id(i)) for i in role_ids] # if trans.app.security_agent.ok_to_display( trans.user, i ) ] permissions = {} if roles: # yikes, there has to be a better way? for k, v in trans.model.RequestType.permitted_actions.items(): - permissions[ trans.app.security_agent.get_action( v.action ) ] = roles + permissions[trans.app.security_agent.get_action(v.action)] = roles if permissions: - trans.app.security_agent.set_request_type_permissions( request_type, permissions ) + trans.app.security_agent.set_request_type_permissions(request_type, permissions) # flush objects - trans.sa_session.add( request_type ) + trans.sa_session.add(request_type) trans.sa_session.flush() - encoded_id = trans.security.encode_id( request_type.id ) - item = request_type.to_dict( view='element', value_mapper={ 'id': trans.security.encode_id, 'request_form_id': trans.security.encode_id, 'sample_form_id': trans.security.encode_id } ) - item['url'] = url_for( 'request_type', id=encoded_id ) - return [ item ] + encoded_id = trans.security.encode_id(request_type.id) + item = request_type.to_dict(view='element', value_mapper={'id': trans.security.encode_id, 'request_form_id': trans.security.encode_id, 'sample_form_id': trans.security.encode_id}) + item['url'] = url_for('request_type', id=encoded_id) + return [item] diff --git a/lib/galaxy/webapps/galaxy/api/requests.py b/lib/galaxy/webapps/galaxy/api/requests.py index 10fa82d81771..7b35a54541df 100644 --- a/lib/galaxy/webapps/galaxy/api/requests.py +++ b/lib/galaxy/webapps/galaxy/api/requests.py @@ -10,66 +10,66 @@ from galaxy.web import url_for from galaxy.web.base.controller import BaseAPIController -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class RequestsAPIController( BaseAPIController ): - _update_types = Bunch( REQUEST='request_state' ) +class RequestsAPIController(BaseAPIController): + _update_types = Bunch(REQUEST='request_state') _update_type_values = [v[1] for v in _update_types.items()] @web.expose_api - def index( self, trans, **kwd ): + def index(self, trans, **kwd): """ GET /api/requests Displays a collection (list) of sequencing requests. """ # if admin user then return all requests if trans.user_is_admin(): - query = trans.sa_session.query( trans.app.model.Request ) \ - .filter( trans.app.model.Request.table.c.deleted == false() )\ + query = trans.sa_session.query(trans.app.model.Request) \ + .filter(trans.app.model.Request.table.c.deleted == false())\ .all() else: - query = trans.sa_session.query( trans.app.model.Request )\ - .filter( and_( trans.app.model.Request.table.c.user_id == trans.user.id and - trans.app.model.Request.table.c.deleted == false() ) ) \ + query = trans.sa_session.query(trans.app.model.Request)\ + .filter(and_(trans.app.model.Request.table.c.user_id == trans.user.id and + trans.app.model.Request.table.c.deleted == false())) \ .all() rval = [] for request in query: item = request.to_dict() - item['url'] = url_for( 'requests', id=trans.security.encode_id( request.id ) ) - item['id'] = trans.security.encode_id( item['id'] ) + item['url'] = url_for('requests', id=trans.security.encode_id(request.id)) + item['id'] = trans.security.encode_id(item['id']) if trans.user_is_admin(): item['user'] = request.user.email - rval.append( item ) + rval.append(item) return rval @web.expose_api - def show( self, trans, id, **kwd ): + def show(self, trans, id, **kwd): """ GET /api/requests/{encoded_request_id} Displays details of a sequencing request. """ try: - request_id = trans.security.decode_id( id ) + request_id = trans.security.decode_id(id) except TypeError: trans.response.status = 400 - return "Malformed id ( %s ) specified, unable to decode." % ( str( id ) ) + return "Malformed id ( %s ) specified, unable to decode." % (str(id)) try: - request = trans.sa_session.query( trans.app.model.Request ).get( request_id ) + request = trans.sa_session.query(trans.app.model.Request).get(request_id) except: request = None - if not request or not ( trans.user_is_admin() or request.user.id == trans.user.id ): + if not request or not (trans.user_is_admin() or request.user.id == trans.user.id): trans.response.status = 400 - return "Invalid request id ( %s ) specified." % str( request_id ) + return "Invalid request id ( %s ) specified." % str(request_id) item = request.to_dict() - item['url'] = url_for( 'requests', id=trans.security.encode_id( request.id ) ) - item['id'] = trans.security.encode_id( item['id'] ) + item['url'] = url_for('requests', id=trans.security.encode_id(request.id)) + item['id'] = trans.security.encode_id(item['id']) item['user'] = request.user.email item['num_of_samples'] = len(request.samples) return item @web.expose_api - def update( self, trans, id, key, payload, **kwd ): + def update(self, trans, id, key, payload, **kwd): """ PUT /api/requests/{encoded_request_id} Updates a request state, sample state or sample dataset transfer status @@ -80,29 +80,29 @@ def update( self, trans, id, key, payload, **kwd ): trans.response.status = 400 return "Missing required 'update_type' parameter. Please consult the API documentation for help." else: - update_type = payload.pop( 'update_type' ) + update_type = payload.pop('update_type') if update_type not in self._update_type_values: trans.response.status = 400 return "Invalid value for 'update_type' parameter ( %s ) specified. Please consult the API documentation for help." % update_type try: - request_id = trans.security.decode_id( id ) + request_id = trans.security.decode_id(id) except TypeError: trans.response.status = 400 - return "Malformed request id ( %s ) specified, unable to decode." % str( id ) + return "Malformed request id ( %s ) specified, unable to decode." % str(id) try: - request = trans.sa_session.query( trans.app.model.Request ).get( request_id ) + request = trans.sa_session.query(trans.app.model.Request).get(request_id) except: request = None - if not request or not ( trans.user_is_admin() or request.user.id == trans.user.id ): + if not request or not (trans.user_is_admin() or request.user.id == trans.user.id): trans.response.status = 400 - return "Invalid request id ( %s ) specified." % str( request_id ) + return "Invalid request id ( %s ) specified." % str(request_id) # check update type if update_type == 'request_state': - return self.__update_request_state( trans, encoded_request_id=id ) + return self.__update_request_state(trans, encoded_request_id=id) - def __update_request_state( self, trans, encoded_request_id ): + def __update_request_state(self, trans, encoded_request_id): requests_common_cntrller = trans.webapp.controllers['requests_common'] - status, output = requests_common_cntrller.update_request_state( trans, - cntrller='api', - request_id=encoded_request_id ) + status, output = requests_common_cntrller.update_request_state(trans, + cntrller='api', + request_id=encoded_request_id) return status, output diff --git a/lib/galaxy/webapps/galaxy/api/roles.py b/lib/galaxy/webapps/galaxy/api/roles.py index d61bf370917c..2cf06a265f3d 100644 --- a/lib/galaxy/webapps/galaxy/api/roles.py +++ b/lib/galaxy/webapps/galaxy/api/roles.py @@ -8,51 +8,51 @@ from galaxy import web from galaxy.web.base.controller import BaseAPIController, url_for -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class RoleAPIController( BaseAPIController ): +class RoleAPIController(BaseAPIController): @web.expose_api - def index( self, trans, **kwd ): + def index(self, trans, **kwd): """ GET /api/roles Displays a collection (list) of roles. """ rval = [] - for role in trans.sa_session.query( trans.app.model.Role ).filter( trans.app.model.Role.table.c.deleted == false() ): - if trans.user_is_admin() or trans.app.security_agent.ok_to_display( trans.user, role ): - item = role.to_dict( value_mapper={ 'id': trans.security.encode_id } ) - encoded_id = trans.security.encode_id( role.id ) - item['url'] = url_for( 'role', id=encoded_id ) - rval.append( item ) + for role in trans.sa_session.query(trans.app.model.Role).filter(trans.app.model.Role.table.c.deleted == false()): + if trans.user_is_admin() or trans.app.security_agent.ok_to_display(trans.user, role): + item = role.to_dict(value_mapper={'id': trans.security.encode_id}) + encoded_id = trans.security.encode_id(role.id) + item['url'] = url_for('role', id=encoded_id) + rval.append(item) return rval @web.expose_api - def show( self, trans, id, **kwd ): + def show(self, trans, id, **kwd): """ GET /api/roles/{encoded_role_id} Displays information about a role. """ role_id = id try: - decoded_role_id = trans.security.decode_id( role_id ) + decoded_role_id = trans.security.decode_id(role_id) except TypeError: trans.response.status = 400 - return "Malformed role id ( %s ) specified, unable to decode." % str( role_id ) + return "Malformed role id ( %s ) specified, unable to decode." % str(role_id) try: - role = trans.sa_session.query( trans.app.model.Role ).get( decoded_role_id ) + role = trans.sa_session.query(trans.app.model.Role).get(decoded_role_id) except: role = None - if not role or not (trans.user_is_admin() or trans.app.security_agent.ok_to_display( trans.user, role )): + if not role or not (trans.user_is_admin() or trans.app.security_agent.ok_to_display(trans.user, role)): trans.response.status = 400 - return "Invalid role id ( %s ) specified." % str( role_id ) - item = role.to_dict( view='element', value_mapper={ 'id': trans.security.encode_id } ) - item['url'] = url_for( 'role', id=role_id ) + return "Invalid role id ( %s ) specified." % str(role_id) + item = role.to_dict(view='element', value_mapper={'id': trans.security.encode_id}) + item['url'] = url_for('role', id=role_id) return item @web.expose_api - def create( self, trans, payload, **kwd ): + def create(self, trans, payload, **kwd): """ POST /api/roles Creates a new role. @@ -60,34 +60,34 @@ def create( self, trans, payload, **kwd ): if not trans.user_is_admin(): trans.response.status = 403 return "You are not authorized to create a new role." - name = payload.get( 'name', None ) - description = payload.get( 'description', None ) + name = payload.get('name', None) + description = payload.get('description', None) if not name or not description: trans.response.status = 400 return "Enter a valid name and a description" - if trans.sa_session.query( trans.app.model.Role ).filter( trans.app.model.Role.table.c.name == name ).first(): + if trans.sa_session.query(trans.app.model.Role).filter(trans.app.model.Role.table.c.name == name).first(): trans.response.status = 400 return "A role with that name already exists" role_type = trans.app.model.Role.types.ADMIN # TODO: allow non-admins to create roles - role = trans.app.model.Role( name=name, description=description, type=role_type ) - trans.sa_session.add( role ) - user_ids = payload.get( 'user_ids', [] ) - users = [ trans.sa_session.query( trans.model.User ).get( trans.security.decode_id( i ) ) for i in user_ids ] - group_ids = payload.get( 'group_ids', [] ) - groups = [ trans.sa_session.query( trans.model.Group ).get( trans.security.decode_id( i ) ) for i in group_ids ] + role = trans.app.model.Role(name=name, description=description, type=role_type) + trans.sa_session.add(role) + user_ids = payload.get('user_ids', []) + users = [trans.sa_session.query(trans.model.User).get(trans.security.decode_id(i)) for i in user_ids] + group_ids = payload.get('group_ids', []) + groups = [trans.sa_session.query(trans.model.Group).get(trans.security.decode_id(i)) for i in group_ids] # Create the UserRoleAssociations for user in users: - trans.app.security_agent.associate_user_role( user, role ) + trans.app.security_agent.associate_user_role(user, role) # Create the GroupRoleAssociations for group in groups: - trans.app.security_agent.associate_group_role( group, role ) + trans.app.security_agent.associate_group_role(group, role) trans.sa_session.flush() - encoded_id = trans.security.encode_id( role.id ) - item = role.to_dict( view='element', value_mapper={ 'id': trans.security.encode_id } ) - item['url'] = url_for( 'role', id=encoded_id ) - return [ item ] + encoded_id = trans.security.encode_id(role.id) + item = role.to_dict(view='element', value_mapper={'id': trans.security.encode_id}) + item['url'] = url_for('role', id=encoded_id) + return [item] diff --git a/lib/galaxy/webapps/galaxy/api/samples.py b/lib/galaxy/webapps/galaxy/api/samples.py index cba2145d7ccc..356b194b9e2b 100644 --- a/lib/galaxy/webapps/galaxy/api/samples.py +++ b/lib/galaxy/webapps/galaxy/api/samples.py @@ -8,46 +8,46 @@ from galaxy.web import url_for from galaxy.web.base.controller import BaseAPIController, web -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class SamplesAPIController( BaseAPIController ): - update_types = Bunch( SAMPLE=[ 'sample_state', 'run_details' ], - SAMPLE_DATASET=[ 'sample_dataset_transfer_status' ] ) +class SamplesAPIController(BaseAPIController): + update_types = Bunch(SAMPLE=['sample_state', 'run_details'], + SAMPLE_DATASET=['sample_dataset_transfer_status']) update_type_values = [] for k, v in update_types.items(): - update_type_values.extend( v ) + update_type_values.extend(v) @web.expose_api - def index( self, trans, **kwd ): + def index(self, trans, **kwd): """ GET /api/requests/{encoded_request_id}/samples Displays a collection (list) of sample of a sequencing request. """ try: - request_id = trans.security.decode_id( kwd[ 'request_id' ] ) + request_id = trans.security.decode_id(kwd['request_id']) except TypeError: trans.response.status = 400 - return "Malformed request id ( %s ) specified, unable to decode." % str( kwd[ 'request_id' ] ) + return "Malformed request id ( %s ) specified, unable to decode." % str(kwd['request_id']) try: - request = trans.sa_session.query( trans.app.model.Request ).get( request_id ) + request = trans.sa_session.query(trans.app.model.Request).get(request_id) except: request = None - if not request or not ( trans.user_is_admin() or request.user.id == trans.user.id ): + if not request or not (trans.user_is_admin() or request.user.id == trans.user.id): trans.response.status = 400 - return "Invalid request id ( %s ) specified." % str( request_id ) + return "Invalid request id ( %s ) specified." % str(request_id) rval = [] for sample in request.samples: item = sample.to_dict() - item['url'] = url_for( 'samples', - request_id=trans.security.encode_id( request_id ), - id=trans.security.encode_id( sample.id ) ) - item['id'] = trans.security.encode_id( item['id'] ) - rval.append( item ) + item['url'] = url_for('samples', + request_id=trans.security.encode_id(request_id), + id=trans.security.encode_id(sample.id)) + item['id'] = trans.security.encode_id(item['id']) + rval.append(item) return rval @web.expose_api - def update( self, trans, id, payload, **kwd ): + def update(self, trans, id, payload, **kwd): """ PUT /api/samples/{encoded_sample_id} Updates a sample or objects related ( mapped ) to a sample. @@ -57,48 +57,48 @@ def update( self, trans, id, payload, **kwd ): trans.response.status = 400 return "Missing required 'update_type' parameter, consult the API documentation for help." else: - update_type = payload.pop( 'update_type' ) + update_type = payload.pop('update_type') if update_type not in self.update_type_values: trans.response.status = 400 return "Invalid value for 'update_type' parameter (%s) specified, consult the API documentation for help." % update_type - sample_id = util.restore_text( id ) + sample_id = util.restore_text(id) try: - decoded_sample_id = trans.security.decode_id( sample_id ) + decoded_sample_id = trans.security.decode_id(sample_id) except TypeError: trans.response.status = 400 - return "Malformed sample_id (%s) specified, unable to decode." % str( sample_id ) + return "Malformed sample_id (%s) specified, unable to decode." % str(sample_id) try: - sample = trans.sa_session.query( trans.app.model.Sample ).get( decoded_sample_id ) + sample = trans.sa_session.query(trans.app.model.Sample).get(decoded_sample_id) except: sample = None if not sample: trans.response.status = 400 - return "Invalid sample id ( %s ) specified." % str( sample_id ) + return "Invalid sample id ( %s ) specified." % str(sample_id) if not trans.user_is_admin(): trans.response.status = 403 return "You are not authorized to update samples." - requests_admin_controller = trans.webapp.controllers[ 'requests_admin' ] + requests_admin_controller = trans.webapp.controllers['requests_admin'] if update_type == 'run_details': - deferred_plugin = payload.pop( 'deferred_plugin', None ) + deferred_plugin = payload.pop('deferred_plugin', None) if deferred_plugin: try: - trans.app.job_manager.deferred_job_queue.plugins[deferred_plugin].create_job( trans, sample=sample, **payload ) + trans.app.job_manager.deferred_job_queue.plugins[deferred_plugin].create_job(trans, sample=sample, **payload) except: - log.exception( 'update() called with a deferred job plugin (%s) but creating the deferred job failed:' % deferred_plugin ) - status, output = requests_admin_controller.edit_template_info( trans, - cntrller='api', - item_type='sample', - form_type=trans.model.FormDefinition.types.RUN_DETAILS_TEMPLATE, - sample_id=sample_id, - **payload ) + log.exception('update() called with a deferred job plugin (%s) but creating the deferred job failed:' % deferred_plugin) + status, output = requests_admin_controller.edit_template_info(trans, + cntrller='api', + item_type='sample', + form_type=trans.model.FormDefinition.types.RUN_DETAILS_TEMPLATE, + sample_id=sample_id, + **payload) return status, output elif update_type == 'sample_state': - return self.__update_sample_state( trans, sample, sample_id, **payload ) + return self.__update_sample_state(trans, sample, sample_id, **payload) elif update_type == 'sample_dataset_transfer_status': # update sample_dataset transfer status - return self.__update_sample_dataset_status( trans, **payload ) + return self.__update_sample_dataset_status(trans, **payload) - def __update_sample_state( self, trans, sample, encoded_sample_id, **payload ): + def __update_sample_state(self, trans, sample, encoded_sample_id, **payload): # only admin user may update sample state in Galaxy sample tracking if not trans.user_is_admin(): trans.response.status = 403 @@ -106,8 +106,8 @@ def __update_sample_state( self, trans, sample, encoded_sample_id, **payload ): if 'new_state' not in payload: trans.response.status = 400 return "Missing required parameter: 'new_state'." - new_state_name = payload.pop( 'new_state' ) - comment = payload.get( 'comment', '' ) + new_state_name = payload.pop('new_state') + comment = payload.get('comment', '') # check if the new state is a valid sample state possible_states = sample.request.type.states new_state = None @@ -117,15 +117,15 @@ def __update_sample_state( self, trans, sample, encoded_sample_id, **payload ): if not new_state: trans.response.status = 400 return "Invalid sample state requested ( %s )." % new_state_name - requests_common_cntrller = trans.webapp.controllers[ 'requests_common' ] - status, output = requests_common_cntrller.update_sample_state( trans=trans, - cntrller='api', - sample_ids=[ encoded_sample_id ], - new_state=new_state, - comment=comment ) + requests_common_cntrller = trans.webapp.controllers['requests_common'] + status, output = requests_common_cntrller.update_sample_state(trans=trans, + cntrller='api', + sample_ids=[encoded_sample_id], + new_state=new_state, + comment=comment) return status, output - def __update_sample_dataset_status( self, trans, **payload ): + def __update_sample_dataset_status(self, trans, **payload): # only admin user may transfer sample datasets in Galaxy sample tracking if not trans.user_is_admin(): trans.response.status = 403 @@ -133,13 +133,13 @@ def __update_sample_dataset_status( self, trans, **payload ): if 'sample_dataset_ids' not in payload or 'new_status' not in payload: trans.response.status = 400 return "Missing one or more required parameters: 'sample_dataset_ids' and 'new_status'." - sample_dataset_ids = payload.pop( 'sample_dataset_ids' ) - new_status = payload.pop( 'new_status' ) - error_msg = payload.get( 'error_msg', '' ) - requests_admin_cntrller = trans.webapp.controllers[ 'requests_admin' ] - status, output = requests_admin_cntrller.update_sample_dataset_status( trans=trans, - cntrller='api', - sample_dataset_ids=sample_dataset_ids, - new_status=new_status, - error_msg=error_msg ) + sample_dataset_ids = payload.pop('sample_dataset_ids') + new_status = payload.pop('new_status') + error_msg = payload.get('error_msg', '') + requests_admin_cntrller = trans.webapp.controllers['requests_admin'] + status, output = requests_admin_cntrller.update_sample_dataset_status(trans=trans, + cntrller='api', + sample_dataset_ids=sample_dataset_ids, + new_status=new_status, + error_msg=error_msg) return status, output diff --git a/lib/galaxy/webapps/galaxy/api/search.py b/lib/galaxy/webapps/galaxy/api/search.py index c037562451e4..e19501cc8240 100644 --- a/lib/galaxy/webapps/galaxy/api/search.py +++ b/lib/galaxy/webapps/galaxy/api/search.py @@ -7,13 +7,13 @@ from galaxy.model.search import GalaxySearchEngine from galaxy.exceptions import ItemAccessibilityException -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class SearchController( BaseAPIController, SharableItemSecurityMixin ): +class SearchController(BaseAPIController, SharableItemSecurityMixin): @web.expose_api - def create( self, trans, payload, **kwd ): + def create(self, trans, payload, **kwd): """ POST /api/search Do a search of the various elements of Galaxy. @@ -38,29 +38,29 @@ def create( self, trans, payload, **kwd ): if trans.user_is_admin(): append = True if not append: - if type( item ) in [ trans.app.model.LibraryFolder, trans.app.model.LibraryDatasetDatasetAssociation, trans.app.model.LibraryDataset ]: - if (trans.app.security_agent.can_access_library_item( trans.get_current_user_roles(), item, trans.user ) ): + if type(item) in [trans.app.model.LibraryFolder, trans.app.model.LibraryDatasetDatasetAssociation, trans.app.model.LibraryDataset]: + if (trans.app.security_agent.can_access_library_item(trans.get_current_user_roles(), item, trans.user)): append = True - elif type( item ) in [ trans.app.model.Job ]: + elif type(item) in [trans.app.model.Job]: if item.used_id == trans.user or trans.user_is_admin(): append = True - elif type( item ) in [ trans.app.model.Page, trans.app.model.StoredWorkflow ]: + elif type(item) in [trans.app.model.Page, trans.app.model.StoredWorkflow]: try: - if self.security_check( trans, item, False, True): + if self.security_check(trans, item, False, True): append = True except ItemAccessibilityException: append = False - elif type( item ) in [ trans.app.model.PageRevision ]: + elif type(item) in [trans.app.model.PageRevision]: try: - if self.security_check( trans, item.page, False, True): + if self.security_check(trans, item.page, False, True): append = True except ItemAccessibilityException: append = False elif hasattr(item, 'dataset'): - if trans.app.security_agent.can_access_dataset( current_user_roles, item.dataset ): + if trans.app.security_agent.can_access_dataset(current_user_roles, item.dataset): append = True if append: row = query.item_to_api_value(item) - out.append( self.encode_all_ids( trans, row, True) ) - return { 'results': out } + out.append(self.encode_all_ids(trans, row, True)) + return {'results': out} diff --git a/lib/galaxy/webapps/galaxy/api/tool_data.py b/lib/galaxy/webapps/galaxy/api/tool_data.py index 6e371e0cafb5..0fbf91c5f221 100644 --- a/lib/galaxy/webapps/galaxy/api/tool_data.py +++ b/lib/galaxy/webapps/galaxy/api/tool_data.py @@ -8,28 +8,28 @@ import galaxy.queue_worker -class ToolData( BaseAPIController ): +class ToolData(BaseAPIController): """ RESTful controller for interactions with tool data """ @web.require_admin @expose_api - def index( self, trans, **kwds ): + def index(self, trans, **kwds): """ GET /api/tool_data: returns a list tool_data tables:: """ - return list( a.to_dict() for a in self._data_tables.values() ) + return list(a.to_dict() for a in self._data_tables.values()) @web.require_admin @expose_api - def show( self, trans, id, **kwds ): + def show(self, trans, id, **kwds): return self._data_table(id).to_dict(view='element') @web.require_admin @expose_api - def reload( self, trans, id, **kwd ): + def reload(self, trans, id, **kwd): """ GET /api/tool_data/{id}/reload @@ -38,14 +38,14 @@ def reload( self, trans, id, **kwd ): decoded_tool_data_id = id data_table = trans.app.tool_data_tables.data_tables.get(decoded_tool_data_id) data_table.reload_from_files() - galaxy.queue_worker.send_control_task( trans.app, 'reload_tool_data_tables', - noop_self=True, - kwargs={'table_name': decoded_tool_data_id} ) - return self._data_table( decoded_tool_data_id ).to_dict( view='element' ) + galaxy.queue_worker.send_control_task(trans.app, 'reload_tool_data_tables', + noop_self=True, + kwargs={'table_name': decoded_tool_data_id}) + return self._data_table(decoded_tool_data_id).to_dict(view='element') @web.require_admin @expose_api - def delete( self, trans, id, **kwd ): + def delete(self, trans, id, **kwd): """ DELETE /api/tool_data/{id} Removes an item from a data table @@ -66,60 +66,60 @@ def delete( self, trans, id, **kwd ): data_table = None if not data_table: trans.response.status = 400 - return "Invalid data table id ( %s ) specified." % str( decoded_tool_data_id ) + return "Invalid data table id ( %s ) specified." % str(decoded_tool_data_id) values = None - if kwd.get( 'payload', None ): - values = kwd['payload'].get( 'values', '' ) + if kwd.get('payload', None): + values = kwd['payload'].get('values', '') if not values: trans.response.status = 400 - return "Invalid data table item ( %s ) specified." % str( values ) + return "Invalid data table item ( %s ) specified." % str(values) split_values = values.split("\t") if len(split_values) != len(data_table.get_column_name_list()): trans.response.status = 400 - return "Invalid data table item ( %s ) specified. Wrong number of columns (%s given, %s required)." % ( str( values ), str(len(split_values)), str(len(data_table.get_column_name_list()))) + return "Invalid data table item ( %s ) specified. Wrong number of columns (%s given, %s required)." % (str(values), str(len(split_values)), str(len(data_table.get_column_name_list()))) data_table.remove_entry(split_values) - galaxy.queue_worker.send_control_task( trans.app, 'reload_tool_data_tables', - noop_self=True, - kwargs={'table_name': decoded_tool_data_id} ) - return self._data_table( decoded_tool_data_id ).to_dict( view='element' ) + galaxy.queue_worker.send_control_task(trans.app, 'reload_tool_data_tables', + noop_self=True, + kwargs={'table_name': decoded_tool_data_id}) + return self._data_table(decoded_tool_data_id).to_dict(view='element') @web.require_admin @expose_api - def show_field( self, trans, id, value, **kwds ): + def show_field(self, trans, id, value, **kwds): """ GET /api/tool_data//fields/ Get information about a partiular field in a tool_data table """ - return self._data_table_field( id, value ).to_dict() + return self._data_table_field(id, value).to_dict() @web.require_admin @expose_api_raw - def download_field_file( self, trans, id, value, path, **kwds ): - field_value = self._data_table_field( id, value ) + def download_field_file(self, trans, id, value, path, **kwds): + field_value = self._data_table_field(id, value) base_dir = field_value.get_base_dir() - full_path = os.path.join( base_dir, path ) + full_path = os.path.join(base_dir, path) if full_path not in field_value.get_files(): raise exceptions.ObjectNotFound("No such path in data table field.") return open(full_path, "r") - def _data_table_field( self, id, value ): + def _data_table_field(self, id, value): out = self._data_table(id).get_field(value) if out is None: raise exceptions.ObjectNotFound("No such field %s in data table %s." % (value, id)) return out - def _data_table( self, id ): + def _data_table(self, id): try: return self._data_tables[id] except IndexError: raise exceptions.ObjectNotFound("No such data table %s" % id) @property - def _data_tables( self ): + def _data_tables(self): return self.app.tool_data_tables.data_tables diff --git a/lib/galaxy/webapps/galaxy/api/tool_dependencies.py b/lib/galaxy/webapps/galaxy/api/tool_dependencies.py index 8aace0fc90fb..17f52c880bd8 100644 --- a/lib/galaxy/webapps/galaxy/api/tool_dependencies.py +++ b/lib/galaxy/webapps/galaxy/api/tool_dependencies.py @@ -10,10 +10,10 @@ import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class ToolDependenciesAPIController( BaseAPIController ): +class ToolDependenciesAPIController(BaseAPIController): def __init__(self, app): super(ToolDependenciesAPIController, self).__init__(app) diff --git a/lib/galaxy/webapps/galaxy/api/tool_shed_repositories.py b/lib/galaxy/webapps/galaxy/api/tool_shed_repositories.py index 005961ba0b39..a9047c4fba67 100644 --- a/lib/galaxy/webapps/galaxy/api/tool_shed_repositories.py +++ b/lib/galaxy/webapps/galaxy/api/tool_shed_repositories.py @@ -26,7 +26,7 @@ from tool_shed.util import workflow_util -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) def get_message_for_no_shed_tool_config(): @@ -39,81 +39,81 @@ def get_message_for_no_shed_tool_config(): return message -class ToolShedRepositoriesController( BaseAPIController ): +class ToolShedRepositoriesController(BaseAPIController): """RESTful controller for interactions with tool shed repositories.""" - def __ensure_can_install_repos( self, trans ): + def __ensure_can_install_repos(self, trans): # Make sure this Galaxy instance is configured with a shed-related tool panel configuration file. - if not suc.have_shed_tool_conf_for_install( self.app ): + if not suc.have_shed_tool_conf_for_install(self.app): message = get_message_for_no_shed_tool_config() - log.debug( message ) - return dict( status='error', error=message ) + log.debug(message) + return dict(status='error', error=message) # Make sure the current user's API key proves he is an admin user in this Galaxy instance. if not trans.user_is_admin(): - raise exceptions.AdminRequiredException( 'You are not authorized to request the latest installable revision for a repository in this Galaxy instance.' ) + raise exceptions.AdminRequiredException('You are not authorized to request the latest installable revision for a repository in this Galaxy instance.') - def __flatten_repository_dependency_list( self, trans, tool_shed_repository ): + def __flatten_repository_dependency_list(self, trans, tool_shed_repository): ''' Return a recursive exclusive flattened list of all tool_shed_repository's dependencies. ''' dependencies = [] for dependency in tool_shed_repository.repository_dependencies: - if len( dependency.repository_dependencies ) > 0: - sub_dependencies = self.__flatten_repository_dependency_list( trans, dependency ) + if len(dependency.repository_dependencies) > 0: + sub_dependencies = self.__flatten_repository_dependency_list(trans, dependency) for sub_dependency in sub_dependencies: if sub_dependency not in dependencies: - dependencies.append( sub_dependency ) + dependencies.append(sub_dependency) if dependency not in dependencies: - dependencies.append( dependency.as_dict( value_mapper=self.__get_value_mapper( trans, tool_shed_repository ) ) ) + dependencies.append(dependency.as_dict(value_mapper=self.__get_value_mapper(trans, tool_shed_repository))) return dependencies - def __get_repo_info_dict( self, trans, repositories, tool_shed_url ): + def __get_repo_info_dict(self, trans, repositories, tool_shed_url): repo_ids = [] changesets = [] for repository_id, changeset in repositories: - repo_ids.append( repository_id ) - changesets.append( changeset ) - params = dict( repository_ids=str( ','.join( repo_ids ) ), changeset_revisions=str( ','.join( changesets ) ) ) - pathspec = [ 'repository', 'get_repository_information' ] - raw_text = util.url_get( tool_shed_url, password_mgr=self.app.tool_shed_registry.url_auth( tool_shed_url ), pathspec=pathspec, params=params ) - return json.loads( raw_text ) - - def __get_value_mapper( self, trans, tool_shed_repository ): - value_mapper = { 'id': trans.security.encode_id( tool_shed_repository.id ), - 'error_message': tool_shed_repository.error_message or '' } + repo_ids.append(repository_id) + changesets.append(changeset) + params = dict(repository_ids=str(','.join(repo_ids)), changeset_revisions=str(','.join(changesets))) + pathspec = ['repository', 'get_repository_information'] + raw_text = util.url_get(tool_shed_url, password_mgr=self.app.tool_shed_registry.url_auth(tool_shed_url), pathspec=pathspec, params=params) + return json.loads(raw_text) + + def __get_value_mapper(self, trans, tool_shed_repository): + value_mapper = {'id': trans.security.encode_id(tool_shed_repository.id), + 'error_message': tool_shed_repository.error_message or ''} return value_mapper - def __get_tool_dependencies( self, metadata, tool_dependencies=None ): + def __get_tool_dependencies(self, metadata, tool_dependencies=None): if tool_dependencies is None: tool_dependencies = [] - for key, dependency_dict in metadata[ 'tool_dependencies' ].items(): + for key, dependency_dict in metadata['tool_dependencies'].items(): if 'readme' in dependency_dict: - del( dependency_dict[ 'readme' ] ) + del(dependency_dict['readme']) if dependency_dict not in tool_dependencies: - tool_dependencies.append( dependency_dict ) - if metadata[ 'has_repository_dependencies' ]: - for dependency in metadata[ 'repository_dependencies' ]: - tool_dependencies = self.__get_tool_dependencies( dependency, tool_dependencies ) + tool_dependencies.append(dependency_dict) + if metadata['has_repository_dependencies']: + for dependency in metadata['repository_dependencies']: + tool_dependencies = self.__get_tool_dependencies(dependency, tool_dependencies) return tool_dependencies - def __get_tools( self, metadata, tools=None ): + def __get_tools(self, metadata, tools=None): if tools is None: tools = [] - if metadata[ 'includes_tools_for_display_in_tool_panel' ]: - for key, tool_dict in metadata[ 'tools' ]: - tool_info = dict( clean=re.sub( '[^a-zA-Z0-9]+', '_', tool_dict[ 'name' ] ).lower(), - name=tool_dict[ 'name' ], - version=tool_dict[ 'version' ], - description=tool_dict[ 'description' ] ) + if metadata['includes_tools_for_display_in_tool_panel']: + for key, tool_dict in metadata['tools']: + tool_info = dict(clean=re.sub('[^a-zA-Z0-9]+', '_', tool_dict['name']).lower(), + name=tool_dict['name'], + version=tool_dict['version'], + description=tool_dict['description']) if tool_info not in tools: - tools.append( tool_info ) - if metadata[ 'has_repository_dependencies' ]: - for dependency in metadata[ 'repository_dependencies' ]: - tools = self.__get_tools( dependency, tools ) + tools.append(tool_info) + if metadata['has_repository_dependencies']: + for dependency in metadata['repository_dependencies']: + tools = self.__get_tools(dependency, tools) return tools @expose_api - def check_for_updates( self, trans, **kwd ): + def check_for_updates(self, trans, **kwd): ''' GET /api/tool_shed_repositories/check_for_updates Check for updates to the specified repository, or all installed repositories. @@ -121,12 +121,12 @@ def check_for_updates( self, trans, **kwd ): :param key: the current Galaxy admin user's API key :param id: the galaxy-side encoded repository ID ''' - repository_id = kwd.get( 'id', None ) - message, status = repository_util.check_for_updates( self.app, trans.install_model, repository_id ) - return { 'status': status, 'message': message } + repository_id = kwd.get('id', None) + message, status = repository_util.check_for_updates(self.app, trans.install_model, repository_id) + return {'status': status, 'message': message} @expose_api - def exported_workflows( self, trans, id, **kwd ): + def exported_workflows(self, trans, id, **kwd): """ GET /api/tool_shed_repositories/{encoded_tool_shed_repository_id}/exported_workflows @@ -138,28 +138,28 @@ def exported_workflows( self, trans, id, **kwd ): # Since exported workflows are dictionaries with very few attributes that differentiate them from each # other, we'll build the list based on the following dictionary of those few attributes. exported_workflows = [] - repository = repository_util.get_tool_shed_repository_by_id( self.app, id ) + repository = repository_util.get_tool_shed_repository_by_id(self.app, id) metadata = repository.metadata if metadata: - exported_workflow_tups = metadata.get( 'workflows', [] ) + exported_workflow_tups = metadata.get('workflows', []) else: exported_workflow_tups = [] - for index, exported_workflow_tup in enumerate( exported_workflow_tups ): + for index, exported_workflow_tup in enumerate(exported_workflow_tups): # The exported_workflow_tup looks like ( relative_path, exported_workflow_dict ), where the value of # relative_path is the location on disk (relative to the root of the installed repository) where the # exported_workflow_dict file (.ga file) is located. - exported_workflow_dict = exported_workflow_tup[ 1 ] - annotation = exported_workflow_dict.get( 'annotation', '' ) - format_version = exported_workflow_dict.get( 'format-version', '' ) - workflow_name = exported_workflow_dict.get( 'name', '' ) + exported_workflow_dict = exported_workflow_tup[1] + annotation = exported_workflow_dict.get('annotation', '') + format_version = exported_workflow_dict.get('format-version', '') + workflow_name = exported_workflow_dict.get('name', '') # Since we don't have an in-memory object with an id, we'll identify the exported workflow via its # location (i.e., index) in the list. - display_dict = dict( index=index, annotation=annotation, format_version=format_version, workflow_name=workflow_name ) - exported_workflows.append( display_dict ) + display_dict = dict(index=index, annotation=annotation, format_version=format_version, workflow_name=workflow_name) + exported_workflows.append(display_dict) return exported_workflows @expose_api - def get_latest_installable_revision( self, trans, payload, **kwd ): + def get_latest_installable_revision(self, trans, payload, **kwd): """ POST /api/tool_shed_repositories/get_latest_installable_revision Get the latest installable revision of a specified repository from a specified Tool Shed. @@ -172,30 +172,30 @@ def get_latest_installable_revision( self, trans, payload, **kwd ): :param owner (required): the owner of the Repository """ # Get the information about the repository to be installed from the payload. - tool_shed_url, name, owner = self.__parse_repository_from_payload( payload ) + tool_shed_url, name, owner = self.__parse_repository_from_payload(payload) # Make sure the current user's API key proves he is an admin user in this Galaxy instance. if not trans.user_is_admin(): - raise exceptions.AdminRequiredException( 'You are not authorized to request the latest installable revision for a repository in this Galaxy instance.' ) - params = dict( name=name, owner=owner ) - pathspec = [ 'api', 'repositories', 'get_ordered_installable_revisions' ] + raise exceptions.AdminRequiredException('You are not authorized to request the latest installable revision for a repository in this Galaxy instance.') + params = dict(name=name, owner=owner) + pathspec = ['api', 'repositories', 'get_ordered_installable_revisions'] try: - raw_text = util.url_get( tool_shed_url, password_mgr=self.app.tool_shed_registry.url_auth( tool_shed_url ), pathspec=pathspec, params=params ) + raw_text = util.url_get(tool_shed_url, password_mgr=self.app.tool_shed_registry.url_auth(tool_shed_url), pathspec=pathspec, params=params) except Exception as e: message = "Error attempting to retrieve the latest installable revision from tool shed %s for repository %s owned by %s: %s" % \ - ( str( tool_shed_url ), str( name ), str( owner ), str( e ) ) - log.debug( message ) - return dict( status='error', error=message ) + (str(tool_shed_url), str(name), str(owner), str(e)) + log.debug(message) + return dict(status='error', error=message) if raw_text: # If successful, the response from get_ordered_installable_revisions will be a list of # changeset_revision hash strings. - changeset_revisions = json.loads( raw_text ) - if len( changeset_revisions ) >= 1: - return changeset_revisions[ -1 ] + changeset_revisions = json.loads(raw_text) + if len(changeset_revisions) >= 1: + return changeset_revisions[-1] return hg_util.INITIAL_CHANGELOG_HASH @expose_api @web.require_admin - def shed_category( self, trans, **kwd ): + def shed_category(self, trans, **kwd): """ GET /api/tool_shed_repositories/shed_category @@ -204,17 +204,17 @@ def shed_category( self, trans, **kwd ): :param tool_shed_url: the url of the toolshed to get repositories from :param category_id: the category to get repositories from """ - tool_shed_url = kwd.get( 'tool_shed_url', '' ) - category_id = kwd.get( 'category_id', '' ) - params = dict( installable=True ) - tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry( trans.app, tool_shed_url ) - url = util.build_url( tool_shed_url, pathspec=[ 'api', 'categories', category_id, 'repositories' ], params=params ) - category = json.loads( util.url_get( url ) ) + tool_shed_url = kwd.get('tool_shed_url', '') + category_id = kwd.get('category_id', '') + params = dict(installable=True) + tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry(trans.app, tool_shed_url) + url = util.build_url(tool_shed_url, pathspec=['api', 'categories', category_id, 'repositories'], params=params) + category = json.loads(util.url_get(url)) return category @expose_api @web.require_admin - def shed_repository( self, trans, **kwd ): + def shed_repository(self, trans, **kwd): """ GET /api/tool_shed_repositories/shed_repository @@ -231,69 +231,69 @@ def shed_repository( self, trans, **kwd ): """ tool_dependencies = dict() tools = dict() - tool_shed_url = kwd.get( 'tool_shed_url', '' ) - tsr_id = kwd.get( 'tsr_id', None ) - tool_ids = kwd.get( 'tool_ids', None ) + tool_shed_url = kwd.get('tool_shed_url', '') + tsr_id = kwd.get('tsr_id', None) + tool_ids = kwd.get('tool_ids', None) if tool_ids is not None: - tool_ids = util.listify( tool_ids ) - tool_panel_section_select_field = tool_util.build_tool_panel_section_select_field( trans.app ) - tool_panel_section_dict = { 'name': tool_panel_section_select_field.name, - 'id': tool_panel_section_select_field.field_id, - 'sections': [] } + tool_ids = util.listify(tool_ids) + tool_panel_section_select_field = tool_util.build_tool_panel_section_select_field(trans.app) + tool_panel_section_dict = {'name': tool_panel_section_select_field.name, + 'id': tool_panel_section_select_field.field_id, + 'sections': []} for name, id, _ in tool_panel_section_select_field.options: - tool_panel_section_dict['sections'].append( dict( id=id, name=name ) ) + tool_panel_section_dict['sections'].append(dict(id=id, name=name)) repository_data = dict() if tool_ids is not None: - if len( tool_shed_url ) == 0: + if len(tool_shed_url) == 0: # By design, this list should always be from the same toolshed. If # this is ever not the case, this code will need to be updated. - tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry( self.app, tool_ids[ 0 ].split( '/' )[ 0 ] ) - found_repository = json.loads( util.url_get( tool_shed_url, params=dict( tool_ids=','.join( tool_ids ) ), pathspec=[ 'api', 'repositories' ] ) ) + tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry(self.app, tool_ids[0].split('/')[0]) + found_repository = json.loads(util.url_get(tool_shed_url, params=dict(tool_ids=','.join(tool_ids)), pathspec=['api', 'repositories'])) fr_keys = found_repository.keys() - tsr_id = found_repository[ fr_keys[0] ][ 'repository_id' ] - repository_data[ 'current_changeset' ] = found_repository[ 'current_changeset' ] - repository_data[ 'repository' ] = json.loads( util.url_get( tool_shed_url, pathspec=[ 'api', 'repositories', tsr_id ] ) ) - del found_repository[ 'current_changeset' ] - repository_data[ 'repository' ][ 'metadata' ] = found_repository - repository_data[ 'tool_shed_url' ] = tool_shed_url + tsr_id = found_repository[fr_keys[0]]['repository_id'] + repository_data['current_changeset'] = found_repository['current_changeset'] + repository_data['repository'] = json.loads(util.url_get(tool_shed_url, pathspec=['api', 'repositories', tsr_id])) + del found_repository['current_changeset'] + repository_data['repository']['metadata'] = found_repository + repository_data['tool_shed_url'] = tool_shed_url else: - repository_data[ 'repository' ] = json.loads( util.url_get( tool_shed_url, pathspec=[ 'api', 'repositories', tsr_id ] ) ) - repository_data[ 'repository' ][ 'metadata' ] = json.loads( util.url_get( tool_shed_url, pathspec=[ 'api', 'repositories', tsr_id, 'metadata' ] ) ) - repository_data[ 'shed_conf' ] = tool_util.build_shed_tool_conf_select_field( trans.app ).get_html().replace('\n', '') - repository_data[ 'panel_section_html' ] = tool_panel_section_select_field.get_html( extra_attr={ 'style': 'width: 30em;' } ).replace( '\n', '' ) - repository_data[ 'panel_section_dict' ] = tool_panel_section_dict - for changeset, metadata in repository_data[ 'repository' ][ 'metadata' ].items(): + repository_data['repository'] = json.loads(util.url_get(tool_shed_url, pathspec=['api', 'repositories', tsr_id])) + repository_data['repository']['metadata'] = json.loads(util.url_get(tool_shed_url, pathspec=['api', 'repositories', tsr_id, 'metadata'])) + repository_data['shed_conf'] = tool_util.build_shed_tool_conf_select_field(trans.app).get_html().replace('\n', '') + repository_data['panel_section_html'] = tool_panel_section_select_field.get_html(extra_attr={'style': 'width: 30em;'}).replace('\n', '') + repository_data['panel_section_dict'] = tool_panel_section_dict + for changeset, metadata in repository_data['repository']['metadata'].items(): if changeset not in tool_dependencies: - tool_dependencies[ changeset ] = [] - if metadata[ 'includes_tools_for_display_in_tool_panel' ]: + tool_dependencies[changeset] = [] + if metadata['includes_tools_for_display_in_tool_panel']: if changeset not in tools: - tools[ changeset ] = [] - for tool_dict in metadata[ 'tools' ]: - tool_info = dict( clean=re.sub( '[^a-zA-Z0-9]+', '_', tool_dict[ 'name' ] ).lower(), - guid=tool_dict[ 'guid' ], - name=tool_dict[ 'name' ], - version=tool_dict[ 'version' ], - description=tool_dict[ 'description' ] ) - if tool_info not in tools[ changeset ]: - tools[ changeset ].append( tool_info ) - if metadata[ 'has_repository_dependencies' ]: - for repository_dependency in metadata[ 'repository_dependencies' ]: - tools[ changeset ] = self.__get_tools( repository_dependency, tools[ changeset ] ) - repository_data[ 'tools' ] = tools - for key, dependency_dict in metadata[ 'tool_dependencies' ].items(): + tools[changeset] = [] + for tool_dict in metadata['tools']: + tool_info = dict(clean=re.sub('[^a-zA-Z0-9]+', '_', tool_dict['name']).lower(), + guid=tool_dict['guid'], + name=tool_dict['name'], + version=tool_dict['version'], + description=tool_dict['description']) + if tool_info not in tools[changeset]: + tools[changeset].append(tool_info) + if metadata['has_repository_dependencies']: + for repository_dependency in metadata['repository_dependencies']: + tools[changeset] = self.__get_tools(repository_dependency, tools[changeset]) + repository_data['tools'] = tools + for key, dependency_dict in metadata['tool_dependencies'].items(): if 'readme' in dependency_dict: - del( dependency_dict[ 'readme' ] ) - if dependency_dict not in tool_dependencies[ changeset ]: - tool_dependencies[ changeset ].append( dependency_dict ) - if metadata[ 'has_repository_dependencies' ]: - for repository_dependency in metadata[ 'repository_dependencies' ]: - tool_dependencies[ changeset ] = self.__get_tool_dependencies( repository_dependency, tool_dependencies[ changeset ] ) - repository_data[ 'tool_dependencies' ] = tool_dependencies + del(dependency_dict['readme']) + if dependency_dict not in tool_dependencies[changeset]: + tool_dependencies[changeset].append(dependency_dict) + if metadata['has_repository_dependencies']: + for repository_dependency in metadata['repository_dependencies']: + tool_dependencies[changeset] = self.__get_tool_dependencies(repository_dependency, tool_dependencies[changeset]) + repository_data['tool_dependencies'] = tool_dependencies return repository_data @expose_api @web.require_admin - def shed_search( self, trans, **kwd ): + def shed_search(self, trans, **kwd): """ GET /api/tool_shed_repositories/shed_search @@ -305,15 +305,15 @@ def shed_search( self, trans, **kwd ): :param tool_shed_url: the URL of the toolshed to search :param tool_shed_url: str """ - tool_shed_url = kwd.get( 'tool_shed_url', None ) - q = kwd.get( 'term', None ) - if None in [ q, tool_shed_url ]: + tool_shed_url = kwd.get('tool_shed_url', None) + q = kwd.get('term', None) + if None in [q, tool_shed_url]: return {} - response = json.loads( util.url_get( tool_shed_url, params=dict( q=q ), pathspec=[ 'api', 'repositories' ] ) ) + response = json.loads(util.url_get(tool_shed_url, params=dict(q=q), pathspec=['api', 'repositories'])) return response @expose_api - def import_workflow( self, trans, payload, **kwd ): + def import_workflow(self, trans, payload, **kwd): """ POST /api/tool_shed_repositories/import_workflow @@ -325,28 +325,28 @@ def import_workflow( self, trans, payload, **kwd ): The following parameters are included in the payload. :param index: the index location of the workflow tuple in the list of exported workflows stored in the metadata for the specified repository """ - api_key = kwd.get( 'key', None ) + api_key = kwd.get('key', None) if api_key is None: - raise HTTPBadRequest( detail="Missing required parameter 'key' whose value is the API key for the Galaxy user importing the specified workflow." ) - tool_shed_repository_id = kwd.get( 'id', '' ) + raise HTTPBadRequest(detail="Missing required parameter 'key' whose value is the API key for the Galaxy user importing the specified workflow.") + tool_shed_repository_id = kwd.get('id', '') if not tool_shed_repository_id: - raise HTTPBadRequest( detail="Missing required parameter 'id'." ) - index = payload.get( 'index', None ) + raise HTTPBadRequest(detail="Missing required parameter 'id'.") + index = payload.get('index', None) if index is None: - raise HTTPBadRequest( detail="Missing required parameter 'index'." ) - repository = repository_util.get_tool_shed_repository_by_id( self.app, tool_shed_repository_id ) - exported_workflows = json.loads( self.exported_workflows( trans, tool_shed_repository_id ) ) + raise HTTPBadRequest(detail="Missing required parameter 'index'.") + repository = repository_util.get_tool_shed_repository_by_id(self.app, tool_shed_repository_id) + exported_workflows = json.loads(self.exported_workflows(trans, tool_shed_repository_id)) # Since we don't have an in-memory object with an id, we'll identify the exported workflow via its location (i.e., index) in the list. - exported_workflow = exported_workflows[ int( index ) ] - workflow_name = exported_workflow[ 'workflow_name' ] - workflow, status, error_message = workflow_util.import_workflow( trans, repository, workflow_name ) + exported_workflow = exported_workflows[int(index)] + workflow_name = exported_workflow['workflow_name'] + workflow, status, error_message = workflow_util.import_workflow(trans, repository, workflow_name) if status == 'error': - log.debug( error_message ) + log.debug(error_message) return {} - return workflow.to_dict( view='element' ) + return workflow.to_dict(view='element') @expose_api - def import_workflows( self, trans, **kwd ): + def import_workflows(self, trans, **kwd): """ POST /api/tool_shed_repositories/import_workflows @@ -355,26 +355,26 @@ def import_workflows( self, trans, **kwd ): :param key: the API key of the Galaxy user with which the imported workflows will be associated. :param id: the encoded id of the ToolShedRepository object """ - api_key = kwd.get( 'key', None ) + api_key = kwd.get('key', None) if api_key is None: - raise HTTPBadRequest( detail="Missing required parameter 'key' whose value is the API key for the Galaxy user importing the specified workflow." ) - tool_shed_repository_id = kwd.get( 'id', '' ) + raise HTTPBadRequest(detail="Missing required parameter 'key' whose value is the API key for the Galaxy user importing the specified workflow.") + tool_shed_repository_id = kwd.get('id', '') if not tool_shed_repository_id: - raise HTTPBadRequest( detail="Missing required parameter 'id'." ) - repository = repository_util.get_tool_shed_repository_by_id( self.app, tool_shed_repository_id ) - exported_workflows = json.loads( self.exported_workflows( trans, tool_shed_repository_id ) ) + raise HTTPBadRequest(detail="Missing required parameter 'id'.") + repository = repository_util.get_tool_shed_repository_by_id(self.app, tool_shed_repository_id) + exported_workflows = json.loads(self.exported_workflows(trans, tool_shed_repository_id)) imported_workflow_dicts = [] for exported_workflow_dict in exported_workflows: - workflow_name = exported_workflow_dict[ 'workflow_name' ] - workflow, status, error_message = workflow_util.import_workflow( trans, repository, workflow_name ) + workflow_name = exported_workflow_dict['workflow_name'] + workflow, status, error_message = workflow_util.import_workflow(trans, repository, workflow_name) if status == 'error': - log.debug( error_message ) + log.debug(error_message) else: - imported_workflow_dicts.append( workflow.to_dict( view='element' ) ) + imported_workflow_dicts.append(workflow.to_dict(view='element')) return imported_workflow_dicts @expose_api - def index( self, trans, **kwd ): + def index(self, trans, **kwd): """ GET /api/tool_shed_repositories Display a list of dictionaries containing information about installed tool shed repositories. @@ -382,28 +382,28 @@ def index( self, trans, **kwd ): # Example URL: http://localhost:8763/api/tool_shed_repositories clause_list = [] if 'name' in kwd: - clause_list.append( self.app.install_model.ToolShedRepository.table.c.name == kwd.get( 'name', None ) ) + clause_list.append(self.app.install_model.ToolShedRepository.table.c.name == kwd.get('name', None)) if 'owner' in kwd: - clause_list.append( self.app.install_model.ToolShedRepository.table.c.owner == kwd.get( 'owner', None ) ) + clause_list.append(self.app.install_model.ToolShedRepository.table.c.owner == kwd.get('owner', None)) if 'changeset' in kwd: - clause_list.append( self.app.install_model.ToolShedRepository.table.c.changeset_revision == kwd.get( 'changeset', None ) ) + clause_list.append(self.app.install_model.ToolShedRepository.table.c.changeset_revision == kwd.get('changeset', None)) tool_shed_repository_dicts = [] - query = trans.install_model.context.query( self.app.install_model.ToolShedRepository ) \ - .order_by( self.app.install_model.ToolShedRepository.table.c.name ) - if len( clause_list ) > 0: - query = query.filter( and_( *clause_list ) ) + query = trans.install_model.context.query(self.app.install_model.ToolShedRepository) \ + .order_by(self.app.install_model.ToolShedRepository.table.c.name) + if len(clause_list) > 0: + query = query.filter(and_(*clause_list)) for tool_shed_repository in query.all(): tool_shed_repository_dict = \ - tool_shed_repository.to_dict( value_mapper=self.__get_value_mapper( trans, tool_shed_repository ) ) - tool_shed_repository_dict[ 'url' ] = web.url_for( controller='tool_shed_repositories', - action='show', - id=trans.security.encode_id( tool_shed_repository.id ) ) - tool_shed_repository_dicts.append( tool_shed_repository_dict ) + tool_shed_repository.to_dict(value_mapper=self.__get_value_mapper(trans, tool_shed_repository)) + tool_shed_repository_dict['url'] = web.url_for(controller='tool_shed_repositories', + action='show', + id=trans.security.encode_id(tool_shed_repository.id)) + tool_shed_repository_dicts.append(tool_shed_repository_dict) return tool_shed_repository_dicts @expose_api @web.require_admin - def install( self, trans, **kwd ): + def install(self, trans, **kwd): """ POST /api/tool_shed_repositories/install Initiate the installation of a repository. @@ -417,75 +417,75 @@ def install( self, trans, **kwd ): :param tool_shed_url: The URL for the toolshed whence this repository is being installed :param changeset: The changeset to update to after cloning the repository """ - irm = InstallRepositoryManager( self.app ) - tool_shed_url = kwd.get( 'tool_shed_url', None ) - repositories = json.loads( kwd.get( 'repositories', '[]' ) ) - repo_info_dict = self.__get_repo_info_dict( trans, repositories, tool_shed_url ) + irm = InstallRepositoryManager(self.app) + tool_shed_url = kwd.get('tool_shed_url', None) + repositories = json.loads(kwd.get('repositories', '[]')) + repo_info_dict = self.__get_repo_info_dict(trans, repositories, tool_shed_url) includes_tools = False includes_tools_for_display_in_tool_panel = False has_repository_dependencies = False includes_tool_dependencies = False - install_resolver_dependencies = util.asbool( kwd.get( 'install_resolver_dependencies', False ) ) - for encoded_repo_info_dict in repo_info_dict.get( 'repo_info_dicts', [] ): - decoded_repo_info_dict = encoding_util.tool_shed_decode( encoded_repo_info_dict ) + install_resolver_dependencies = util.asbool(kwd.get('install_resolver_dependencies', False)) + for encoded_repo_info_dict in repo_info_dict.get('repo_info_dicts', []): + decoded_repo_info_dict = encoding_util.tool_shed_decode(encoded_repo_info_dict) if not includes_tools: - includes_tools = util.string_as_bool( decoded_repo_info_dict.get( 'includes_tools', False ) ) + includes_tools = util.string_as_bool(decoded_repo_info_dict.get('includes_tools', False)) if not includes_tools_for_display_in_tool_panel: includes_tools_for_display_in_tool_panel = \ - util.string_as_bool( decoded_repo_info_dict.get( 'includes_tools_for_display_in_tool_panel', False ) ) + util.string_as_bool(decoded_repo_info_dict.get('includes_tools_for_display_in_tool_panel', False)) if not has_repository_dependencies: - has_repository_dependencies = util.string_as_bool( repo_info_dict.get( 'has_repository_dependencies', False ) ) + has_repository_dependencies = util.string_as_bool(repo_info_dict.get('has_repository_dependencies', False)) if not includes_tool_dependencies: - includes_tool_dependencies = util.string_as_bool( repo_info_dict.get( 'includes_tool_dependencies', False ) ) - encoded_repo_info_dicts = util.listify( repo_info_dict.get( 'repo_info_dicts', [] ) ) - repo_info_dicts = [ encoding_util.tool_shed_decode( encoded_repo_info_dict ) for encoded_repo_info_dict in encoded_repo_info_dicts ] - tool_panel_section_id = kwd.get( 'tool_panel_section_id', None ) - new_tool_panel_section_label = kwd.get( 'new_tool_panel_section', None ) - tool_panel_section_mapping = json.loads( kwd.get( 'tool_panel_section', '{}' ) ) - install_tool_dependencies = util.asbool( kwd.get( 'install_tool_dependencies', False ) ) - install_repository_dependencies = util.asbool( kwd.get( 'install_repository_dependencies', False ) ) - shed_tool_conf = kwd.get( 'shed_tool_conf', None ) - tool_path = suc.get_tool_path_by_shed_tool_conf_filename( self.app, shed_tool_conf ) - installation_dict = dict( install_repository_dependencies=install_repository_dependencies, - new_tool_panel_section_label=new_tool_panel_section_label, - no_changes_checked=False, - repo_info_dicts=repo_info_dicts, - tool_panel_section_id=tool_panel_section_id, - tool_path=tool_path, - tool_shed_url=tool_shed_url ) - new_repositories, tool_panel_keys, repo_info_dicts, filtered_repos = irm.handle_tool_shed_repositories( installation_dict ) + includes_tool_dependencies = util.string_as_bool(repo_info_dict.get('includes_tool_dependencies', False)) + encoded_repo_info_dicts = util.listify(repo_info_dict.get('repo_info_dicts', [])) + repo_info_dicts = [encoding_util.tool_shed_decode(encoded_repo_info_dict) for encoded_repo_info_dict in encoded_repo_info_dicts] + tool_panel_section_id = kwd.get('tool_panel_section_id', None) + new_tool_panel_section_label = kwd.get('new_tool_panel_section', None) + tool_panel_section_mapping = json.loads(kwd.get('tool_panel_section', '{}')) + install_tool_dependencies = util.asbool(kwd.get('install_tool_dependencies', False)) + install_repository_dependencies = util.asbool(kwd.get('install_repository_dependencies', False)) + shed_tool_conf = kwd.get('shed_tool_conf', None) + tool_path = suc.get_tool_path_by_shed_tool_conf_filename(self.app, shed_tool_conf) + installation_dict = dict(install_repository_dependencies=install_repository_dependencies, + new_tool_panel_section_label=new_tool_panel_section_label, + no_changes_checked=False, + repo_info_dicts=repo_info_dicts, + tool_panel_section_id=tool_panel_section_id, + tool_path=tool_path, + tool_shed_url=tool_shed_url) + new_repositories, tool_panel_keys, repo_info_dicts, filtered_repos = irm.handle_tool_shed_repositories(installation_dict) if new_repositories: - installation_dict = dict( created_or_updated_tool_shed_repositories=new_repositories, - filtered_repo_info_dicts=filtered_repos, - has_repository_dependencies=has_repository_dependencies, - includes_tool_dependencies=includes_tool_dependencies, - includes_tools=includes_tools, - includes_tools_for_display_in_tool_panel=includes_tools_for_display_in_tool_panel, - install_repository_dependencies=install_repository_dependencies, - install_tool_dependencies=install_tool_dependencies, - message='', - new_tool_panel_section_label=new_tool_panel_section_label, - tool_panel_section_mapping=tool_panel_section_mapping, - install_resolver_dependencies=install_resolver_dependencies, - shed_tool_conf=shed_tool_conf, - status='ok', - tool_panel_section_id=tool_panel_section_id, - tool_panel_section_keys=tool_panel_keys, - tool_path=tool_path, - tool_shed_url=tool_shed_url ) - encoded_kwd, query, tool_shed_repositories, encoded_repository_ids = \ - irm.initiate_repository_installation( installation_dict ) - return json.dumps( dict( operation='install', - api=True, - install_resolver_dependencies=install_resolver_dependencies, + installation_dict = dict(created_or_updated_tool_shed_repositories=new_repositories, + filtered_repo_info_dicts=filtered_repos, + has_repository_dependencies=has_repository_dependencies, + includes_tool_dependencies=includes_tool_dependencies, + includes_tools=includes_tools, + includes_tools_for_display_in_tool_panel=includes_tools_for_display_in_tool_panel, + install_repository_dependencies=install_repository_dependencies, install_tool_dependencies=install_tool_dependencies, - encoded_kwd=encoded_kwd, - reinstalling=False, - tool_shed_repository_ids=json.dumps( [ repo[0] for repo in repositories ] ), - repositories=[ trans.security.encode_id( repo.id ) for repo in new_repositories ] ) ) + message='', + new_tool_panel_section_label=new_tool_panel_section_label, + tool_panel_section_mapping=tool_panel_section_mapping, + install_resolver_dependencies=install_resolver_dependencies, + shed_tool_conf=shed_tool_conf, + status='ok', + tool_panel_section_id=tool_panel_section_id, + tool_panel_section_keys=tool_panel_keys, + tool_path=tool_path, + tool_shed_url=tool_shed_url) + encoded_kwd, query, tool_shed_repositories, encoded_repository_ids = \ + irm.initiate_repository_installation(installation_dict) + return json.dumps(dict(operation='install', + api=True, + install_resolver_dependencies=install_resolver_dependencies, + install_tool_dependencies=install_tool_dependencies, + encoded_kwd=encoded_kwd, + reinstalling=False, + tool_shed_repository_ids=json.dumps([repo[0] for repo in repositories]), + repositories=[trans.security.encode_id(repo.id) for repo in new_repositories])) @expose_api - def install_repository_revision( self, trans, payload, **kwd ): + def install_repository_revision(self, trans, payload, **kwd): """ POST /api/tool_shed_repositories/install_repository_revision Install a specified repository revision from a specified tool shed into Galaxy. @@ -518,28 +518,28 @@ def install_repository_revision( self, trans, payload, **kwd ): file will be selected automatically. """ # Get the information about the repository to be installed from the payload. - tool_shed_url, name, owner, changeset_revision = self.__parse_repository_from_payload( payload, include_changeset=True ) - self.__ensure_can_install_repos( trans ) - irm = InstallRepositoryManager( self.app ) - installed_tool_shed_repositories = irm.install( tool_shed_url, - name, - owner, - changeset_revision, - payload ) - - def to_dict( tool_shed_repository ): - tool_shed_repository_dict = tool_shed_repository.as_dict( value_mapper=self.__get_value_mapper( trans, tool_shed_repository ) ) - tool_shed_repository_dict[ 'url' ] = web.url_for( controller='tool_shed_repositories', - action='show', - id=trans.security.encode_id( tool_shed_repository.id ) ) + tool_shed_url, name, owner, changeset_revision = self.__parse_repository_from_payload(payload, include_changeset=True) + self.__ensure_can_install_repos(trans) + irm = InstallRepositoryManager(self.app) + installed_tool_shed_repositories = irm.install(tool_shed_url, + name, + owner, + changeset_revision, + payload) + + def to_dict(tool_shed_repository): + tool_shed_repository_dict = tool_shed_repository.as_dict(value_mapper=self.__get_value_mapper(trans, tool_shed_repository)) + tool_shed_repository_dict['url'] = web.url_for(controller='tool_shed_repositories', + action='show', + id=trans.security.encode_id(tool_shed_repository.id)) return tool_shed_repository_dict if installed_tool_shed_repositories: - return map( to_dict, installed_tool_shed_repositories ) + return map(to_dict, installed_tool_shed_repositories) message = "No repositories were installed, possibly because the selected repository has already been installed." - return dict( status="ok", message=message ) + return dict(status="ok", message=message) @expose_api - def install_repository_revisions( self, trans, payload, **kwd ): + def install_repository_revisions(self, trans, payload, **kwd): """ POST /api/tool_shed_repositories/install_repository_revisions Install one or more specified repository revisions from one or more specified tool sheds into Galaxy. The received parameters @@ -573,48 +573,48 @@ def install_repository_revisions( self, trans, payload, **kwd ): (e.g., ). If this parameter is not set, a shed-related tool panel configuration file will be selected automatically. """ - self.__ensure_can_install_repos( trans ) + self.__ensure_can_install_repos(trans) # Get the information about all of the repositories to be installed. - tool_shed_urls = util.listify( payload.get( 'tool_shed_urls', '' ) ) - names = util.listify( payload.get( 'names', '' ) ) - owners = util.listify( payload.get( 'owners', '' ) ) - changeset_revisions = util.listify( payload.get( 'changeset_revisions', '' ) ) - num_specified_repositories = len( tool_shed_urls ) - if len( names ) != num_specified_repositories or \ - len( owners ) != num_specified_repositories or \ - len( changeset_revisions ) != num_specified_repositories: + tool_shed_urls = util.listify(payload.get('tool_shed_urls', '')) + names = util.listify(payload.get('names', '')) + owners = util.listify(payload.get('owners', '')) + changeset_revisions = util.listify(payload.get('changeset_revisions', '')) + num_specified_repositories = len(tool_shed_urls) + if len(names) != num_specified_repositories or \ + len(owners) != num_specified_repositories or \ + len(changeset_revisions) != num_specified_repositories: message = 'Error in tool_shed_repositories API in install_repository_revisions: the received parameters must be ordered ' message += 'lists so that positional values in tool_shed_urls, names, owners and changeset_revisions are associated.' - log.debug( message ) - return dict( status='error', error=message ) + log.debug(message) + return dict(status='error', error=message) # Get the information about the Galaxy components (e.g., tool pane section, tool config file, etc) that will contain information # about each of the repositories being installed. # TODO: we may want to enhance this method to allow for each of the following to be associated with each repository instead of # forcing all repositories to use the same settings. - install_repository_dependencies = payload.get( 'install_repository_dependencies', False ) - install_resolver_dependencies = payload.get( 'install_resolver_dependencies', False ) - install_tool_dependencies = payload.get( 'install_tool_dependencies', False ) - new_tool_panel_section_label = payload.get( 'new_tool_panel_section_label', '' ) - shed_tool_conf = payload.get( 'shed_tool_conf', None ) - tool_panel_section_id = payload.get( 'tool_panel_section_id', '' ) + install_repository_dependencies = payload.get('install_repository_dependencies', False) + install_resolver_dependencies = payload.get('install_resolver_dependencies', False) + install_tool_dependencies = payload.get('install_tool_dependencies', False) + new_tool_panel_section_label = payload.get('new_tool_panel_section_label', '') + shed_tool_conf = payload.get('shed_tool_conf', None) + tool_panel_section_id = payload.get('tool_panel_section_id', '') all_installed_tool_shed_repositories = [] - for tool_shed_url, name, owner, changeset_revision in zip( tool_shed_urls, names, owners, changeset_revisions ): - current_payload = dict( tool_shed_url=tool_shed_url, - name=name, - owner=owner, - changeset_revision=changeset_revision, - new_tool_panel_section_label=new_tool_panel_section_label, - tool_panel_section_id=tool_panel_section_id, - install_repository_dependencies=install_repository_dependencies, - install_resolver_dependencies=install_resolver_dependencies, - install_tool_dependencies=install_tool_dependencies, - shed_tool_conf=shed_tool_conf ) - installed_tool_shed_repositories = self.install_repository_revision( trans, **current_payload ) - if isinstance( installed_tool_shed_repositories, dict ): + for tool_shed_url, name, owner, changeset_revision in zip(tool_shed_urls, names, owners, changeset_revisions): + current_payload = dict(tool_shed_url=tool_shed_url, + name=name, + owner=owner, + changeset_revision=changeset_revision, + new_tool_panel_section_label=new_tool_panel_section_label, + tool_panel_section_id=tool_panel_section_id, + install_repository_dependencies=install_repository_dependencies, + install_resolver_dependencies=install_resolver_dependencies, + install_tool_dependencies=install_tool_dependencies, + shed_tool_conf=shed_tool_conf) + installed_tool_shed_repositories = self.install_repository_revision(trans, **current_payload) + if isinstance(installed_tool_shed_repositories, dict): # We encountered an error. return installed_tool_shed_repositories - elif isinstance( installed_tool_shed_repositories, list ): - all_installed_tool_shed_repositories.extend( installed_tool_shed_repositories ) + elif isinstance(installed_tool_shed_repositories, list): + all_installed_tool_shed_repositories.extend(installed_tool_shed_repositories) return all_installed_tool_shed_repositories @expose_api @@ -658,7 +658,7 @@ def uninstall_repository(self, trans, id=None, **kwd): raise Exception('Attempting to uninstall tool dependencies for repository named %s resulted in errors: %s' % (repository.name, errors)) @expose_api - def repair_repository_revision( self, trans, payload, **kwd ): + def repair_repository_revision(self, trans, payload, **kwd): """ POST /api/tool_shed_repositories/repair_repository_revision Repair a specified repository revision previously installed into Galaxy. @@ -672,57 +672,57 @@ def repair_repository_revision( self, trans, payload, **kwd ): :param changeset_revision (required): the changeset_revision of the RepositoryMetadata object associated with the Repository """ # Get the information about the repository to be installed from the payload. - tool_shed_url, name, owner, changeset_revision = self.__parse_repository_from_payload( payload, include_changeset=True ) + tool_shed_url, name, owner, changeset_revision = self.__parse_repository_from_payload(payload, include_changeset=True) tool_shed_repositories = [] - tool_shed_repository = repository_util.get_installed_repository( self.app, - tool_shed=tool_shed_url, - name=name, - owner=owner, - changeset_revision=changeset_revision ) - rrm = RepairRepositoryManager( self.app ) - repair_dict = rrm.get_repair_dict( tool_shed_repository ) - ordered_tsr_ids = repair_dict.get( 'ordered_tsr_ids', [] ) - ordered_repo_info_dicts = repair_dict.get( 'ordered_repo_info_dicts', [] ) + tool_shed_repository = repository_util.get_installed_repository(self.app, + tool_shed=tool_shed_url, + name=name, + owner=owner, + changeset_revision=changeset_revision) + rrm = RepairRepositoryManager(self.app) + repair_dict = rrm.get_repair_dict(tool_shed_repository) + ordered_tsr_ids = repair_dict.get('ordered_tsr_ids', []) + ordered_repo_info_dicts = repair_dict.get('ordered_repo_info_dicts', []) if ordered_tsr_ids and ordered_repo_info_dicts: - for index, tsr_id in enumerate( ordered_tsr_ids ): - repository = trans.install_model.context.query( trans.install_model.ToolShedRepository ).get( trans.security.decode_id( tsr_id ) ) - repo_info_dict = ordered_repo_info_dicts[ index ] + for index, tsr_id in enumerate(ordered_tsr_ids): + repository = trans.install_model.context.query(trans.install_model.ToolShedRepository).get(trans.security.decode_id(tsr_id)) + repo_info_dict = ordered_repo_info_dicts[index] # TODO: handle errors in repair_dict. - repair_dict = rrm.repair_tool_shed_repository( repository, - encoding_util.tool_shed_encode( repo_info_dict ) ) - repository_dict = repository.to_dict( value_mapper=self.__get_value_mapper( trans, repository ) ) - repository_dict[ 'url' ] = web.url_for( controller='tool_shed_repositories', - action='show', - id=trans.security.encode_id( repository.id ) ) + repair_dict = rrm.repair_tool_shed_repository(repository, + encoding_util.tool_shed_encode(repo_info_dict)) + repository_dict = repository.to_dict(value_mapper=self.__get_value_mapper(trans, repository)) + repository_dict['url'] = web.url_for(controller='tool_shed_repositories', + action='show', + id=trans.security.encode_id(repository.id)) if repair_dict: - errors = repair_dict.get( repository.name, [] ) - repository_dict[ 'errors_attempting_repair' ] = ' '.join( errors ) - tool_shed_repositories.append( repository_dict ) + errors = repair_dict.get(repository.name, []) + repository_dict['errors_attempting_repair'] = ' '.join(errors) + tool_shed_repositories.append(repository_dict) # Display the list of repaired repositories. return tool_shed_repositories - def __parse_repository_from_payload( self, payload, include_changeset=False ): + def __parse_repository_from_payload(self, payload, include_changeset=False): # Get the information about the repository to be installed from the payload. - tool_shed_url = payload.get( 'tool_shed_url', '' ) + tool_shed_url = payload.get('tool_shed_url', '') if not tool_shed_url: - raise exceptions.RequestParameterMissingException( "Missing required parameter 'tool_shed_url'." ) - name = payload.get( 'name', '' ) + raise exceptions.RequestParameterMissingException("Missing required parameter 'tool_shed_url'.") + name = payload.get('name', '') if not name: - raise exceptions.RequestParameterMissingException( "Missing required parameter 'name'." ) - owner = payload.get( 'owner', '' ) + raise exceptions.RequestParameterMissingException("Missing required parameter 'name'.") + owner = payload.get('owner', '') if not owner: - raise exceptions.RequestParameterMissingException( "Missing required parameter 'owner'." ) + raise exceptions.RequestParameterMissingException("Missing required parameter 'owner'.") if not include_changeset: return tool_shed_url, name, owner - changeset_revision = payload.get( 'changeset_revision', '' ) + changeset_revision = payload.get('changeset_revision', '') if not changeset_revision: - raise HTTPBadRequest( detail="Missing required parameter 'changeset_revision'." ) + raise HTTPBadRequest(detail="Missing required parameter 'changeset_revision'.") return tool_shed_url, name, owner, changeset_revision @expose_api - def reset_metadata_on_installed_repositories( self, trans, payload, **kwd ): + def reset_metadata_on_installed_repositories(self, trans, payload, **kwd): """ PUT /api/tool_shed_repositories/reset_metadata_on_installed_repositories @@ -730,44 +730,44 @@ def reset_metadata_on_installed_repositories( self, trans, payload, **kwd ): :param key: the API key of the Galaxy admin user. """ - start_time = strftime( "%Y-%m-%d %H:%M:%S" ) - results = dict( start_time=start_time, - successful_count=0, - unsuccessful_count=0, - repository_status=[] ) + start_time = strftime("%Y-%m-%d %H:%M:%S") + results = dict(start_time=start_time, + successful_count=0, + unsuccessful_count=0, + repository_status=[]) # Make sure the current user's API key proves he is an admin user in this Galaxy instance. if not trans.user_is_admin(): - raise HTTPForbidden( detail='You are not authorized to reset metadata on repositories installed into this Galaxy instance.' ) - irmm = InstalledRepositoryMetadataManager( self.app ) - query = irmm.get_query_for_setting_metadata_on_repositories( order=False ) + raise HTTPForbidden(detail='You are not authorized to reset metadata on repositories installed into this Galaxy instance.') + irmm = InstalledRepositoryMetadataManager(self.app) + query = irmm.get_query_for_setting_metadata_on_repositories(order=False) # Now reset metadata on all remaining repositories. for repository in query: try: - irmm.set_repository( repository ) + irmm.set_repository(repository) irmm.reset_all_metadata_on_installed_repository() irmm_invalid_file_tups = irmm.get_invalid_file_tups() if irmm_invalid_file_tups: - message = tool_util.generate_message_for_invalid_tools( self.app, - irmm_invalid_file_tups, - repository, - None, - as_html=False ) - results[ 'unsuccessful_count' ] += 1 + message = tool_util.generate_message_for_invalid_tools(self.app, + irmm_invalid_file_tups, + repository, + None, + as_html=False) + results['unsuccessful_count'] += 1 else: message = "Successfully reset metadata on repository %s owned by %s" % \ - ( str( repository.name ), str( repository.owner ) ) - results[ 'successful_count' ] += 1 + (str(repository.name), str(repository.owner)) + results['successful_count'] += 1 except Exception as e: message = "Error resetting metadata on repository %s owned by %s: %s" % \ - ( str( repository.name ), str( repository.owner ), str( e ) ) - results[ 'unsuccessful_count' ] += 1 - results[ 'repository_status' ].append( message ) - stop_time = strftime( "%Y-%m-%d %H:%M:%S" ) - results[ 'stop_time' ] = stop_time - return json.dumps( results, sort_keys=True, indent=4 ) + (str(repository.name), str(repository.owner), str(e)) + results['unsuccessful_count'] += 1 + results['repository_status'].append(message) + stop_time = strftime("%Y-%m-%d %H:%M:%S") + results['stop_time'] = stop_time + return json.dumps(results, sort_keys=True, indent=4) @expose_api - def show( self, trans, id, **kwd ): + def show(self, trans, id, **kwd): """ GET /api/tool_shed_repositories/{encoded_tool_shed_repsository_id} Display a dictionary containing information about a specified tool_shed_repository. @@ -775,19 +775,19 @@ def show( self, trans, id, **kwd ): :param id: the encoded id of the ToolShedRepository object """ # Example URL: http://localhost:8763/api/tool_shed_repositories/df7a1f0c02a5b08e - tool_shed_repository = repository_util.get_tool_shed_repository_by_id( self.app, id ) + tool_shed_repository = repository_util.get_tool_shed_repository_by_id(self.app, id) if tool_shed_repository is None: - log.debug( "Unable to locate tool_shed_repository record for id %s." % ( str( id ) ) ) + log.debug("Unable to locate tool_shed_repository record for id %s." % (str(id))) return {} - tool_shed_repository_dict = tool_shed_repository.as_dict( value_mapper=self.__get_value_mapper( trans, tool_shed_repository ) ) - tool_shed_repository_dict[ 'url' ] = web.url_for( controller='tool_shed_repositories', - action='show', - id=trans.security.encode_id( tool_shed_repository.id ) ) + tool_shed_repository_dict = tool_shed_repository.as_dict(value_mapper=self.__get_value_mapper(trans, tool_shed_repository)) + tool_shed_repository_dict['url'] = web.url_for(controller='tool_shed_repositories', + action='show', + id=trans.security.encode_id(tool_shed_repository.id)) return tool_shed_repository_dict @expose_api @web.require_admin - def status( self, trans, id, **kwd ): + def status(self, trans, id, **kwd): """ GET /api/tool_shed_repositories/{id}/status Display a dictionary containing information about a specified repository's installation @@ -795,13 +795,13 @@ def status( self, trans, id, **kwd ): :param id: the repository's encoded id """ - tool_shed_repository = repository_util.get_tool_shed_repository_by_id( self.app, id ) + tool_shed_repository = repository_util.get_tool_shed_repository_by_id(self.app, id) if tool_shed_repository is None: - log.debug( "Unable to locate tool_shed_repository record for id %s." % ( str( id ) ) ) + log.debug("Unable to locate tool_shed_repository record for id %s." % (str(id))) return {} - tool_shed_repository_dict = tool_shed_repository.as_dict( value_mapper=self.__get_value_mapper( trans, tool_shed_repository ) ) - tool_shed_repository_dict[ 'url' ] = web.url_for( controller='tool_shed_repositories', - action='show', - id=trans.security.encode_id( tool_shed_repository.id ) ) - tool_shed_repository_dict[ 'repository_dependencies' ] = self.__flatten_repository_dependency_list( trans, tool_shed_repository ) + tool_shed_repository_dict = tool_shed_repository.as_dict(value_mapper=self.__get_value_mapper(trans, tool_shed_repository)) + tool_shed_repository_dict['url'] = web.url_for(controller='tool_shed_repositories', + action='show', + id=trans.security.encode_id(tool_shed_repository.id)) + tool_shed_repository_dict['repository_dependencies'] = self.__flatten_repository_dependency_list(trans, tool_shed_repository) return tool_shed_repository_dict diff --git a/lib/galaxy/webapps/galaxy/api/tools.py b/lib/galaxy/webapps/galaxy/api/tools.py index 812f51d1c11b..691531d3c928 100644 --- a/lib/galaxy/webapps/galaxy/api/tools.py +++ b/lib/galaxy/webapps/galaxy/api/tools.py @@ -12,21 +12,21 @@ from galaxy.web.base.controller import BaseAPIController from galaxy.web.base.controller import UsesVisualizationMixin -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class ToolsController( BaseAPIController, UsesVisualizationMixin ): +class ToolsController(BaseAPIController, UsesVisualizationMixin): """ RESTful controller for interactions with tools. """ - def __init__( self, app ): - super( ToolsController, self ).__init__( app ) - self.history_manager = managers.histories.HistoryManager( app ) - self.hda_manager = managers.hdas.HDAManager( app ) + def __init__(self, app): + super(ToolsController, self).__init__(app) + self.history_manager = managers.histories.HistoryManager(app) + self.hda_manager = managers.hdas.HDAManager(app) @expose_api_anonymous_and_sessionless - def index( self, trans, **kwds ): + def index(self, trans, **kwds): """ GET /api/tools: returns a list of tools defined by parameters:: @@ -42,21 +42,21 @@ def index( self, trans, **kwds ): """ # Read params. - in_panel = util.string_as_bool( kwds.get( 'in_panel', 'True' ) ) - trackster = util.string_as_bool( kwds.get( 'trackster', 'False' ) ) - q = kwds.get( 'q', '' ) - tool_id = kwds.get( 'tool_id', '' ) + in_panel = util.string_as_bool(kwds.get('in_panel', 'True')) + trackster = util.string_as_bool(kwds.get('trackster', 'False')) + q = kwds.get('q', '') + tool_id = kwds.get('tool_id', '') # Find whether to search. if q: - hits = self._search( q ) + hits = self._search(q) results = [] if hits: for hit in hits: try: - tool = self._get_tool( hit, user=trans.user ) + tool = self._get_tool(hit, user=trans.user) if tool: - results.append( tool.id ) + results.append(tool.id) except exceptions.AuthenticationFailed: pass except exceptions.ObjectNotFound: @@ -65,50 +65,50 @@ def index( self, trans, **kwds ): # Find whether to detect. if tool_id: - detected_versions = self._detect( trans, tool_id ) + detected_versions = self._detect(trans, tool_id) return detected_versions # Return everything. try: - return self.app.toolbox.to_dict( trans, in_panel=in_panel, trackster=trackster) + return self.app.toolbox.to_dict(trans, in_panel=in_panel, trackster=trackster) except Exception: - raise exceptions.InternalServerError( "Error: Could not convert toolbox to dictionary" ) + raise exceptions.InternalServerError("Error: Could not convert toolbox to dictionary") @expose_api_anonymous_and_sessionless - def show( self, trans, id, **kwd ): + def show(self, trans, id, **kwd): """ GET /api/tools/{tool_id} Returns tool information, including parameters and inputs. """ - io_details = util.string_as_bool( kwd.get( 'io_details', False ) ) - link_details = util.string_as_bool( kwd.get( 'link_details', False ) ) - tool = self._get_tool( id, user=trans.user ) - return tool.to_dict( trans, io_details=io_details, link_details=link_details ) + io_details = util.string_as_bool(kwd.get('io_details', False)) + link_details = util.string_as_bool(kwd.get('link_details', False)) + tool = self._get_tool(id, user=trans.user) + return tool.to_dict(trans, io_details=io_details, link_details=link_details) @expose_api_anonymous - def build( self, trans, id, **kwd ): + def build(self, trans, id, **kwd): """ GET /api/tools/{tool_id}/build Returns a tool model including dynamic parameters and updated values, repeats block etc. """ if 'payload' in kwd: kwd = kwd.get('payload') - tool_version = kwd.get( 'tool_version', None ) - tool = self._get_tool( id, tool_version=tool_version, user=trans.user ) + tool_version = kwd.get('tool_version', None) + tool = self._get_tool(id, tool_version=tool_version, user=trans.user) return tool.to_json(trans, kwd.get('inputs', kwd)) @expose_api @web.require_admin - def reload( self, trans, id, **kwd ): + def reload(self, trans, id, **kwd): """ GET /api/tools/{tool_id}/reload Reload specified tool. """ - galaxy.queue_worker.send_control_task( trans.app, 'reload_tool', noop_self=True, kwargs={ 'tool_id': id } ) - message, status = trans.app.toolbox.reload_tool_by_id( id ) + galaxy.queue_worker.send_control_task(trans.app, 'reload_tool', noop_self=True, kwargs={'tool_id': id}) + message, status = trans.app.toolbox.reload_tool_by_id(id) if status == 'error': - raise exceptions.MessageException( message ) - return { 'message': message } + raise exceptions.MessageException(message) + return {'message': message} @expose_api @web.require_admin @@ -184,7 +184,7 @@ def build_dependency_cache(self, trans, id, **kwds): @expose_api @web.require_admin - def diagnostics( self, trans, id, **kwd ): + def diagnostics(self, trans, id, **kwd): """ GET /api/tools/{tool_id}/diagnostics Return diagnostic information to help debug panel @@ -194,8 +194,8 @@ def diagnostics( self, trans, id, **kwd ): def to_dict(x): return x.to_dict() - tool = self._get_tool( id, user=trans.user ) - if hasattr( tool, 'lineage' ): + tool = self._get_tool(id, user=trans.user) + if hasattr(tool, 'lineage'): lineage_dict = tool.lineage.to_dict() else: lineage_dict = None @@ -219,7 +219,7 @@ def to_dict(x): "guid": tool.guid, } - def _detect( self, trans, tool_id ): + def _detect(self, trans, tool_id): """ Detect whether the tool with the given id is installed. @@ -229,15 +229,15 @@ def _detect( self, trans, tool_id ): :return: list with available versions "return type: list """ - tools = self.app.toolbox.get_tool( tool_id, get_all_versions=True ) + tools = self.app.toolbox.get_tool(tool_id, get_all_versions=True) detected_versions = [] if tools: for tool in tools: - if tool and tool.allow_user_access( trans.user ): - detected_versions.append( tool.version ) + if tool and tool.allow_user_access(trans.user): + detected_versions.append(tool.version) return detected_versions - def _search( self, q ): + def _search(self, q): """ Perform the search on the given query. Boosts and numer of results are configurable in galaxy.ini file. @@ -248,83 +248,83 @@ def _search( self, q ): :return: Dictionary containing the tools' ids of the best hits. :return type: dict """ - tool_name_boost = self.app.config.get( 'tool_name_boost', 9 ) - tool_section_boost = self.app.config.get( 'tool_section_boost', 3 ) - tool_description_boost = self.app.config.get( 'tool_description_boost', 2 ) - tool_label_boost = self.app.config.get( 'tool_label_boost', 1 ) - tool_stub_boost = self.app.config.get( 'tool_stub_boost', 5 ) - tool_help_boost = self.app.config.get( 'tool_help_boost', 0.5 ) - tool_search_limit = self.app.config.get( 'tool_search_limit', 20 ) - tool_enable_ngram_search = self.app.config.get( 'tool_enable_ngram_search', False ) - tool_ngram_minsize = self.app.config.get( 'tool_ngram_minsize', 3 ) - tool_ngram_maxsize = self.app.config.get( 'tool_ngram_maxsize', 4 ) - - results = self.app.toolbox_search.search( q=q, - tool_name_boost=tool_name_boost, - tool_section_boost=tool_section_boost, - tool_description_boost=tool_description_boost, - tool_label_boost=tool_label_boost, - tool_stub_boost=tool_stub_boost, - tool_help_boost=tool_help_boost, - tool_search_limit=tool_search_limit, - tool_enable_ngram_search=tool_enable_ngram_search, - tool_ngram_minsize=tool_ngram_minsize, - tool_ngram_maxsize=tool_ngram_maxsize ) + tool_name_boost = self.app.config.get('tool_name_boost', 9) + tool_section_boost = self.app.config.get('tool_section_boost', 3) + tool_description_boost = self.app.config.get('tool_description_boost', 2) + tool_label_boost = self.app.config.get('tool_label_boost', 1) + tool_stub_boost = self.app.config.get('tool_stub_boost', 5) + tool_help_boost = self.app.config.get('tool_help_boost', 0.5) + tool_search_limit = self.app.config.get('tool_search_limit', 20) + tool_enable_ngram_search = self.app.config.get('tool_enable_ngram_search', False) + tool_ngram_minsize = self.app.config.get('tool_ngram_minsize', 3) + tool_ngram_maxsize = self.app.config.get('tool_ngram_maxsize', 4) + + results = self.app.toolbox_search.search(q=q, + tool_name_boost=tool_name_boost, + tool_section_boost=tool_section_boost, + tool_description_boost=tool_description_boost, + tool_label_boost=tool_label_boost, + tool_stub_boost=tool_stub_boost, + tool_help_boost=tool_help_boost, + tool_search_limit=tool_search_limit, + tool_enable_ngram_search=tool_enable_ngram_search, + tool_ngram_minsize=tool_ngram_minsize, + tool_ngram_maxsize=tool_ngram_maxsize) return results @expose_api_anonymous_and_sessionless - def citations( self, trans, id, **kwds ): - tool = self._get_tool( id, user=trans.user ) + def citations(self, trans, id, **kwds): + tool = self._get_tool(id, user=trans.user) rval = [] for citation in tool.citations: - rval.append( citation.to_dict( 'bibtex' ) ) + rval.append(citation.to_dict('bibtex')) return rval @web.expose_api_raw @web.require_admin - def download( self, trans, id, **kwds ): + def download(self, trans, id, **kwds): tool_tarball = trans.app.toolbox.package_tool(trans, id) trans.response.set_content_type('application/x-gzip') download_file = open(tool_tarball, "rb") - trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s.tgz"' % (id) + trans.response.headers["Content-Disposition"] = 'attachment; filename="%s.tgz"' % (id) return download_file @expose_api_anonymous - def create( self, trans, payload, **kwd ): + def create(self, trans, payload, **kwd): """ POST /api/tools Executes tool using specified inputs and returns tool's outputs. """ # HACK: for now, if action is rerun, rerun tool. - action = payload.get( 'action', None ) + action = payload.get('action', None) if action == 'rerun': - return self._rerun_tool( trans, payload, **kwd ) + return self._rerun_tool(trans, payload, **kwd) # -- Execute tool. -- # Get tool. - tool_version = payload.get( 'tool_version', None ) - tool = trans.app.toolbox.get_tool( payload[ 'tool_id' ], tool_version ) if 'tool_id' in payload else None - if not tool or not tool.allow_user_access( trans.user ): - raise exceptions.MessageException( 'Tool not found or not accessible.' ) + tool_version = payload.get('tool_version', None) + tool = trans.app.toolbox.get_tool(payload['tool_id'], tool_version) if 'tool_id' in payload else None + if not tool or not tool.allow_user_access(trans.user): + raise exceptions.MessageException('Tool not found or not accessible.') if trans.app.config.user_activation_on: if not trans.user: - log.warning( "Anonymous user attempts to execute tool, but account activation is turned on." ) + log.warning("Anonymous user attempts to execute tool, but account activation is turned on.") elif not trans.user.active: - log.warning( "User \"%s\" attempts to execute tool, but account activation is turned on and user account is not active." % trans.user.email ) + log.warning("User \"%s\" attempts to execute tool, but account activation is turned on and user account is not active." % trans.user.email) # Set running history from payload parameters. # History not set correctly as part of this API call for # dataset upload. history_id = payload.get('history_id', None) if history_id: - decoded_id = self.decode_id( history_id ) - target_history = self.history_manager.get_owned( decoded_id, trans.user, current_history=trans.history ) + decoded_id = self.decode_id(history_id) + target_history = self.history_manager.get_owned(decoded_id, trans.user, current_history=trans.history) else: target_history = None # Set up inputs. - inputs = payload.get( 'inputs', {} ) + inputs = payload.get('inputs', {}) # Find files coming in as multipart file data and add to inputs. for k, v in payload.iteritems(): if k.startswith('files_') or k.startswith('__files_'): @@ -334,8 +334,8 @@ def create( self, trans, payload, **kwd ): input_patch = {} for k, v in inputs.iteritems(): if isinstance(v, dict) and v.get('src', '') == 'ldda' and 'id' in v: - ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( self.decode_id(v['id']) ) - if trans.user_is_admin() or trans.app.security_agent.can_access_dataset( trans.get_current_user_roles(), ldda.dataset ): + ldda = trans.sa_session.query(trans.app.model.LibraryDatasetDatasetAssociation).get(self.decode_id(v['id'])) + if trans.user_is_admin() or trans.app.security_agent.can_access_dataset(trans.get_current_user_roles(), ldda.dataset): input_patch[k] = ldda.to_history_dataset_association(target_history, add_to_history=True) for k, v in input_patch.iteritems(): @@ -343,59 +343,59 @@ def create( self, trans, payload, **kwd ): # TODO: encode data ids and decode ids. # TODO: handle dbkeys - params = util.Params( inputs, sanitize=False ) + params = util.Params(inputs, sanitize=False) incoming = params.__dict__ - vars = tool.handle_input( trans, incoming, history=target_history ) + vars = tool.handle_input(trans, incoming, history=target_history) # TODO: check for errors and ensure that output dataset(s) are available. - output_datasets = vars.get( 'out_data', [] ) - rval = { 'outputs': [], 'output_collections': [], 'jobs': [], 'implicit_collections': [] } + output_datasets = vars.get('out_data', []) + rval = {'outputs': [], 'output_collections': [], 'jobs': [], 'implicit_collections': []} - job_errors = vars.get( 'job_errors', [] ) + job_errors = vars.get('job_errors', []) if job_errors: # If we are here - some jobs were successfully executed but some failed. - rval[ 'errors' ] = job_errors + rval['errors'] = job_errors - outputs = rval[ 'outputs' ] + outputs = rval['outputs'] # TODO:?? poss. only return ids? for output_name, output in output_datasets: output_dict = output.to_dict() # add the output name back into the output data structure # so it's possible to figure out which newly created elements # correspond with which tool file outputs - output_dict[ 'output_name' ] = output_name - outputs.append( trans.security.encode_dict_ids( output_dict, skip_startswith="metadata_" ) ) + output_dict['output_name'] = output_name + outputs.append(trans.security.encode_dict_ids(output_dict, skip_startswith="metadata_")) for job in vars.get('jobs', []): - rval[ 'jobs' ].append( self.encode_all_ids( trans, job.to_dict( view='collection' ), recursive=True ) ) + rval['jobs'].append(self.encode_all_ids(trans, job.to_dict(view='collection'), recursive=True)) for output_name, collection_instance in vars.get('output_collections', []): history = target_history or trans.history - output_dict = dictify_dataset_collection_instance( collection_instance, security=trans.security, parent=history ) - output_dict[ 'output_name' ] = output_name - rval[ 'output_collections' ].append( output_dict ) + output_dict = dictify_dataset_collection_instance(collection_instance, security=trans.security, parent=history) + output_dict['output_name'] = output_name + rval['output_collections'].append(output_dict) - for output_name, collection_instance in vars.get( 'implicit_collections', {} ).iteritems(): + for output_name, collection_instance in vars.get('implicit_collections', {}).iteritems(): history = target_history or trans.history - output_dict = dictify_dataset_collection_instance( collection_instance, security=trans.security, parent=history ) - output_dict[ 'output_name' ] = output_name - rval[ 'implicit_collections' ].append( output_dict ) + output_dict = dictify_dataset_collection_instance(collection_instance, security=trans.security, parent=history) + output_dict['output_name'] = output_name + rval['implicit_collections'].append(output_dict) return rval # # -- Helper methods -- # - def _get_tool( self, id, tool_version=None, user=None ): - id = urllib.unquote_plus( id ) - tool = self.app.toolbox.get_tool( id, tool_version ) + def _get_tool(self, id, tool_version=None, user=None): + id = urllib.unquote_plus(id) + tool = self.app.toolbox.get_tool(id, tool_version) if not tool: - raise exceptions.ObjectNotFound( "Could not find tool with id '%s'." % id ) - if not tool.allow_user_access( user ): - raise exceptions.AuthenticationFailed( "Access denied, please login for tool with id '%s'." % id ) + raise exceptions.ObjectNotFound("Could not find tool with id '%s'." % id) + if not tool.allow_user_access(user): + raise exceptions.AuthenticationFailed("Access denied, please login for tool with id '%s'." % id) return tool - def _rerun_tool( self, trans, payload, **kwargs ): + def _rerun_tool(self, trans, payload, **kwargs): """ Rerun a tool to produce a new output dataset that corresponds to a dataset that a user is currently viewing. @@ -407,16 +407,16 @@ def _rerun_tool( self, trans, payload, **kwargs ): # Run tool on region if region is specificied. run_on_regions = False - regions = payload.get( 'regions', None ) + regions = payload.get('regions', None) if regions: - if isinstance( regions, dict ): + if isinstance(regions, dict): # Regions is a single region. - regions = [ GenomeRegion.from_dict( regions ) ] - elif isinstance( regions, list ): + regions = [GenomeRegion.from_dict(regions)] + elif isinstance(regions, list): # There is a list of regions. - regions = [ GenomeRegion.from_dict( r ) for r in regions ] + regions = [GenomeRegion.from_dict(r) for r in regions] - if len( regions ) > 1: + if len(regions) > 1: # Sort by chrom name, start so that data is not fetched out of order. regions = sorted(regions, key=lambda r: (r.chrom.lower(), r.start)) @@ -429,26 +429,26 @@ def _rerun_tool( self, trans, payload, **kwargs ): if cur.chrom == prev.chrom and cur.start <= prev.end: # Found overlapping regions, so join them into prev. prev.end = cur.end - del regions[ index ] + del regions[index] else: # No overlap, move to next region. prev = cur index += 1 # Get next region or exit. - if index == len( regions ): + if index == len(regions): # Done. break else: - cur = regions[ index ] + cur = regions[index] run_on_regions = True # Dataset check. - decoded_dataset_id = self.decode_id( payload.get( 'target_dataset_id' ) ) - original_dataset = self.hda_manager.get_accessible( decoded_dataset_id, user=trans.user ) - original_dataset = self.hda_manager.error_if_uploading( original_dataset ) - msg = self.hda_manager.data_conversion_status( original_dataset ) + decoded_dataset_id = self.decode_id(payload.get('target_dataset_id')) + original_dataset = self.hda_manager.get_accessible(decoded_dataset_id, user=trans.user) + original_dataset = self.hda_manager.error_if_uploading(original_dataset) + msg = self.hda_manager.data_conversion_status(original_dataset) if msg: return msg @@ -456,18 +456,18 @@ def _rerun_tool( self, trans, payload, **kwargs ): # job's previous parameters and incoming parameters. Incoming parameters # have priority. # - original_job = self.hda_manager.creating_job( original_dataset ) - tool = trans.app.toolbox.get_tool( original_job.tool_id ) - if not tool or not tool.allow_user_access( trans.user ): + original_job = self.hda_manager.creating_job(original_dataset) + tool = trans.app.toolbox.get_tool(original_job.tool_id) + if not tool or not tool.allow_user_access(trans.user): return trans.app.model.Dataset.conversion_messages.NO_TOOL - tool_params = dict( [ ( p.name, p.value ) for p in original_job.parameters ] ) + tool_params = dict([(p.name, p.value) for p in original_job.parameters]) # TODO: rather than set new inputs using dict of json'ed value, unpack parameters and set using set_param_value below. # TODO: need to handle updates to conditional parameters; conditional # params are stored in dicts (and dicts within dicts). - new_inputs = payload[ 'inputs' ] - tool_params.update( dict( [ ( key, dumps( value ) ) for key, value in new_inputs.items() if key in tool.inputs and new_inputs[ key ] is not None ] ) ) - tool_params = tool.params_from_strings( tool_params, self.app ) + new_inputs = payload['inputs'] + tool_params.update(dict([(key, dumps(value)) for key, value in new_inputs.items() if key in tool.inputs and new_inputs[key] is not None])) + tool_params = tool.params_from_strings(tool_params, self.app) # # If running tool on region, convert input datasets (create indices) so @@ -478,17 +478,17 @@ def _rerun_tool( self, trans, payload, **kwargs ): if run_on_regions: for jida in original_job.input_datasets: input_dataset = jida.dataset - data_provider = data_provider_registry.get_data_provider( trans, original_dataset=input_dataset, source='data' ) - if data_provider and ( not data_provider.converted_dataset or - data_provider.converted_dataset.state != trans.app.model.Dataset.states.OK ): + data_provider = data_provider_registry.get_data_provider(trans, original_dataset=input_dataset, source='data') + if data_provider and (not data_provider.converted_dataset or + data_provider.converted_dataset.state != trans.app.model.Dataset.states.OK): # Can convert but no converted dataset yet, so return message about why. data_sources = input_dataset.datatype.data_sources - msg = input_dataset.convert_dataset( trans, data_sources[ 'data' ] ) + msg = input_dataset.convert_dataset(trans, data_sources['data']) if msg is not None: - messages_list.append( msg ) + messages_list.append(msg) # Return any messages generated during conversions. - return_message = self._get_highest_priority_msg( messages_list ) + return_message = self._get_highest_priority_msg(messages_list) if return_message: return return_message @@ -501,44 +501,44 @@ def _rerun_tool( self, trans, payload, **kwargs ): if original_dataset.history.user == trans.user: target_history = original_dataset.history else: - target_history = trans.get_history( create=True ) - hda_permissions = trans.app.security_agent.history_get_default_permissions( target_history ) + target_history = trans.get_history(create=True) + hda_permissions = trans.app.security_agent.history_get_default_permissions(target_history) - def set_param_value( param_dict, param_name, param_value ): + def set_param_value(param_dict, param_name, param_value): """ Set new parameter value in a tool's parameter dictionary. """ # Recursive function to set param value. - def set_value( param_dict, group_name, group_index, param_name, param_value ): + def set_value(param_dict, group_name, group_index, param_name, param_value): if group_name in param_dict: - param_dict[ group_name ][ group_index ][ param_name ] = param_value + param_dict[group_name][group_index][param_name] = param_value return True elif param_name in param_dict: - param_dict[ param_name ] = param_value + param_dict[param_name] = param_value return True else: # Recursive search. return_val = False for value in param_dict.values(): - if isinstance( value, dict ): - return_val = set_value( value, group_name, group_index, param_name, param_value) + if isinstance(value, dict): + return_val = set_value(value, group_name, group_index, param_name, param_value) if return_val: return return_val return False # Parse parameter name if necessary. - if param_name.find( "|" ) == -1: + if param_name.find("|") == -1: # Non-grouping parameter. group_name = group_index = None else: # Grouping parameter. - group, param_name = param_name.split( "|" ) - index = group.rfind( "_" ) - group_name = group[ :index ] - group_index = int( group[ index + 1: ] ) + group, param_name = param_name.split("|") + index = group.rfind("_") + group_name = group[:index] + group_index = int(group[index + 1:]) - return set_value( param_dict, group_name, group_index, param_name, param_value ) + return set_value(param_dict, group_name, group_index, param_name, param_value) # Set parameters based tool's trackster config. params_set = {} @@ -546,8 +546,8 @@ def set_value( param_dict, group_name, group_index, param_name, param_value ): success = False for joda in original_job.output_datasets: if joda.name == action.output_name: - set_param_value( tool_params, action.name, joda.dataset ) - params_set[ action.name ] = True + set_param_value(tool_params, action.name, joda.dataset) + params_set[action.name] = True success = True break @@ -559,7 +559,7 @@ def set_value( param_dict, group_name, group_index, param_name, param_value ): # when possible. # if run_on_regions: - regions_str = ",".join( [ str( r ) for r in regions ] ) + regions_str = ",".join([str(r) for r in regions]) for jida in original_job.input_datasets: # If param set previously by config actions, do nothing. if jida.name in params_set: @@ -567,73 +567,73 @@ def set_value( param_dict, group_name, group_index, param_name, param_value ): input_dataset = jida.dataset if input_dataset is None: # optional dataset and dataset wasn't selected - tool_params[ jida.name ] = None + tool_params[jida.name] = None elif run_on_regions and 'data' in input_dataset.datatype.data_sources: # Dataset is indexed and hence a subset can be extracted and used # as input. # Look for subset. - subset_dataset_association = trans.sa_session.query( trans.app.model.HistoryDatasetAssociationSubset ) \ - .filter_by( hda=input_dataset, location=regions_str ) \ + subset_dataset_association = trans.sa_session.query(trans.app.model.HistoryDatasetAssociationSubset) \ + .filter_by(hda=input_dataset, location=regions_str) \ .first() if subset_dataset_association: # Data subset exists. subset_dataset = subset_dataset_association.subset else: # Need to create subset. - data_source = input_dataset.datatype.data_sources[ 'data' ] - input_dataset.get_converted_dataset( trans, data_source ) - input_dataset.get_converted_dataset_deps( trans, data_source ) + data_source = input_dataset.datatype.data_sources['data'] + input_dataset.get_converted_dataset(trans, data_source) + input_dataset.get_converted_dataset_deps(trans, data_source) # Create new HDA for input dataset's subset. - new_dataset = trans.app.model.HistoryDatasetAssociation( extension=input_dataset.ext, - dbkey=input_dataset.dbkey, - create_dataset=True, - sa_session=trans.sa_session, - name="Subset [%s] of data %i" % - ( regions_str, input_dataset.hid ), - visible=False ) - target_history.add_dataset( new_dataset ) - trans.sa_session.add( new_dataset ) - trans.app.security_agent.set_all_dataset_permissions( new_dataset.dataset, hda_permissions ) + new_dataset = trans.app.model.HistoryDatasetAssociation(extension=input_dataset.ext, + dbkey=input_dataset.dbkey, + create_dataset=True, + sa_session=trans.sa_session, + name="Subset [%s] of data %i" % + (regions_str, input_dataset.hid), + visible=False) + target_history.add_dataset(new_dataset) + trans.sa_session.add(new_dataset) + trans.app.security_agent.set_all_dataset_permissions(new_dataset.dataset, hda_permissions) # Write subset of data to new dataset - data_provider = data_provider_registry.get_data_provider( trans, original_dataset=input_dataset, source='data' ) - trans.app.object_store.create( new_dataset.dataset ) - data_provider.write_data_to_file( regions, new_dataset.file_name ) + data_provider = data_provider_registry.get_data_provider(trans, original_dataset=input_dataset, source='data') + trans.app.object_store.create(new_dataset.dataset) + data_provider.write_data_to_file(regions, new_dataset.file_name) # TODO: (a) size not working; (b) need to set peek. new_dataset.set_size() new_dataset.info = "Data subset for trackster" - new_dataset.set_dataset_state( trans.app.model.Dataset.states.OK ) + new_dataset.set_dataset_state(trans.app.model.Dataset.states.OK) # Set metadata. # TODO: set meta internally if dataset is small enough? - trans.app.datatypes_registry.set_external_metadata_tool.tool_action.execute( trans.app.datatypes_registry.set_external_metadata_tool, - trans, incoming={ 'input1': new_dataset }, - overwrite=False, job_params={ "source": "trackster" } ) + trans.app.datatypes_registry.set_external_metadata_tool.tool_action.execute(trans.app.datatypes_registry.set_external_metadata_tool, + trans, incoming={'input1': new_dataset}, + overwrite=False, job_params={"source": "trackster"}) # Add HDA subset association. - subset_association = trans.app.model.HistoryDatasetAssociationSubset( hda=input_dataset, subset=new_dataset, location=regions_str ) - trans.sa_session.add( subset_association ) + subset_association = trans.app.model.HistoryDatasetAssociationSubset(hda=input_dataset, subset=new_dataset, location=regions_str) + trans.sa_session.add(subset_association) subset_dataset = new_dataset trans.sa_session.flush() # Add dataset to tool's parameters. - if not set_param_value( tool_params, jida.name, subset_dataset ): - return { "error": True, "message": "error setting parameter %s" % jida.name } + if not set_param_value(tool_params, jida.name, subset_dataset): + return {"error": True, "message": "error setting parameter %s" % jida.name} # # Execute tool and handle outputs. # try: - subset_job, subset_job_outputs = tool.execute( trans, incoming=tool_params, - history=target_history, - job_params={ "source": "trackster" } ) + subset_job, subset_job_outputs = tool.execute(trans, incoming=tool_params, + history=target_history, + job_params={"source": "trackster"}) except Exception as e: # Lots of things can go wrong when trying to execute tool. - return { "error": True, "message": e.__class__.__name__ + ": " + str(e) } + return {"error": True, "message": e.__class__.__name__ + ": " + str(e)} if run_on_regions: for output in subset_job_outputs.values(): output.visible = False @@ -652,6 +652,6 @@ def set_value( param_dict, group_name, group_index, param_name, param_value ): output_dataset = joda.dataset dataset_dict = output_dataset.to_dict() - dataset_dict[ 'id' ] = trans.security.encode_id( dataset_dict[ 'id' ] ) - dataset_dict[ 'track_config' ] = self.get_new_track_config( trans, output_dataset ) + dataset_dict['id'] = trans.security.encode_id(dataset_dict['id']) + dataset_dict['track_config'] = self.get_new_track_config(trans, output_dataset) return dataset_dict diff --git a/lib/galaxy/webapps/galaxy/api/toolshed.py b/lib/galaxy/webapps/galaxy/api/toolshed.py index f399de513f53..e0dbd459f97c 100644 --- a/lib/galaxy/webapps/galaxy/api/toolshed.py +++ b/lib/galaxy/webapps/galaxy/api/toolshed.py @@ -14,75 +14,75 @@ from tool_shed.util import repository_util from tool_shed.util import tool_util -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class ToolShedController( BaseAPIController ): +class ToolShedController(BaseAPIController): """RESTful controller for interactions with tool sheds.""" - def __get_repo_dict_by_id( self, id ): - tool_shed_repository = repository_util.get_tool_shed_repository_by_id( self.app, id ) + def __get_repo_dict_by_id(self, id): + tool_shed_repository = repository_util.get_tool_shed_repository_by_id(self.app, id) if tool_shed_repository is None: - log.debug( "Unable to locate tool_shed_repository record for id %s." % ( str( id ) ) ) + log.debug("Unable to locate tool_shed_repository record for id %s." % (str(id))) return {} - tool_shed_repository_dict = tool_shed_repository.as_dict( value_mapper=self.__get_value_mapper( tool_shed_repository ) ) - tool_shed_repository_dict[ 'url' ] = web.url_for( controller='tool_shed_repositories', - action='show', - id=self.app.security.encode_id( tool_shed_repository.id ) ) - tool_shed_repository_dict[ 'repository_dependencies' ] = self.__flatten_repository_dependency_list( tool_shed_repository ) + tool_shed_repository_dict = tool_shed_repository.as_dict(value_mapper=self.__get_value_mapper(tool_shed_repository)) + tool_shed_repository_dict['url'] = web.url_for(controller='tool_shed_repositories', + action='show', + id=self.app.security.encode_id(tool_shed_repository.id)) + tool_shed_repository_dict['repository_dependencies'] = self.__flatten_repository_dependency_list(tool_shed_repository) return tool_shed_repository_dict - def __get_tool_dependencies( self, metadata, tool_dependencies=None ): + def __get_tool_dependencies(self, metadata, tool_dependencies=None): if tool_dependencies is None: tool_dependencies = [] - for key, dependency_dict in metadata[ 'tool_dependencies' ].items(): + for key, dependency_dict in metadata['tool_dependencies'].items(): if 'readme' in dependency_dict: - del( dependency_dict[ 'readme' ] ) + del(dependency_dict['readme']) if dependency_dict not in tool_dependencies: - tool_dependencies.append( dependency_dict ) - if metadata[ 'has_repository_dependencies' ]: - for dependency in metadata[ 'repository_dependencies' ]: - tool_dependencies = self.__get_tool_dependencies( dependency, tool_dependencies ) + tool_dependencies.append(dependency_dict) + if metadata['has_repository_dependencies']: + for dependency in metadata['repository_dependencies']: + tool_dependencies = self.__get_tool_dependencies(dependency, tool_dependencies) return tool_dependencies - def __flatten_repository_dependency_list( self, tool_shed_repository ): + def __flatten_repository_dependency_list(self, tool_shed_repository): ''' Return a recursive exclusive flattened list of all tool_shed_repository's dependencies. ''' dependencies = [] for dependency in tool_shed_repository.repository_dependencies: - if len( dependency.repository_dependencies ) > 0: - sub_dependencies = self.__flatten_repository_dependency_list( dependency ) + if len(dependency.repository_dependencies) > 0: + sub_dependencies = self.__flatten_repository_dependency_list(dependency) for sub_dependency in sub_dependencies: if sub_dependency not in dependencies: - dependencies.append( sub_dependency ) + dependencies.append(sub_dependency) if dependency not in dependencies: - dependencies.append( dependency.as_dict( value_mapper=self.__get_value_mapper( tool_shed_repository ) ) ) + dependencies.append(dependency.as_dict(value_mapper=self.__get_value_mapper(tool_shed_repository))) return dependencies - def __get_value_mapper( self, tool_shed_repository ): - value_mapper = { 'id': self.app.security.encode_id( tool_shed_repository.id ), - 'error_message': tool_shed_repository.error_message or '' } + def __get_value_mapper(self, tool_shed_repository): + value_mapper = {'id': self.app.security.encode_id(tool_shed_repository.id), + 'error_message': tool_shed_repository.error_message or ''} return value_mapper - def __get_tools( self, metadata, tools=None ): + def __get_tools(self, metadata, tools=None): if tools is None: tools = [] - if metadata[ 'includes_tools_for_display_in_tool_panel' ]: - for key, tool_dict in metadata[ 'tools' ]: - tool_info = dict( clean=re.sub( '[^a-zA-Z0-9]+', '_', tool_dict[ 'name' ] ).lower(), - name=tool_dict[ 'name' ], - version=tool_dict[ 'version' ], - description=tool_dict[ 'description' ] ) + if metadata['includes_tools_for_display_in_tool_panel']: + for key, tool_dict in metadata['tools']: + tool_info = dict(clean=re.sub('[^a-zA-Z0-9]+', '_', tool_dict['name']).lower(), + name=tool_dict['name'], + version=tool_dict['version'], + description=tool_dict['description']) if tool_info not in tools: - tools.append( tool_info ) - if metadata[ 'has_repository_dependencies' ]: - for dependency in metadata[ 'repository_dependencies' ]: - tools = self.__get_tools( dependency, tools ) + tools.append(tool_info) + if metadata['has_repository_dependencies']: + for dependency in metadata['repository_dependencies']: + tools = self.__get_tools(dependency, tools) return tools @expose_api - def index( self, trans, **kwd ): + def index(self, trans, **kwd): """ GET /api/tool_shed Interact with this galaxy instance's toolshed registry. @@ -93,12 +93,12 @@ def index( self, trans, **kwd ): # action='contents', # tool_shed_url=urlquote( url, '' ), # qualified=True ) - sheds.append( dict( name=name, url=urlquote( url, '' ) ) ) + sheds.append(dict(name=name, url=urlquote(url, ''))) return sheds @expose_api @web.require_admin - def status( self, trans, **kwd ): + def status(self, trans, **kwd): """ GET /api/tool_shed_repositories/{id}/status Display a dictionary containing information about a specified repository's installation @@ -106,12 +106,12 @@ def status( self, trans, **kwd ): :param id: the repository's encoded id """ - repository_ids = kwd.get( 'repositories', None ) + repository_ids = kwd.get('repositories', None) repositories = [] if repository_ids is not None: - for repository_id in repository_ids.split( '|' ): - tool_shed_repository_dict = self.__get_repo_dict_by_id( repository_id ) - repositories.append( tool_shed_repository_dict ) + for repository_id in repository_ids.split('|'): + tool_shed_repository_dict = self.__get_repo_dict_by_id(repository_id) + repositories.append(tool_shed_repository_dict) return repositories else: return [] @@ -119,7 +119,7 @@ def status( self, trans, **kwd ): @expose_api @web.require_admin - def tool_json( self, trans, **kwd ): + def tool_json(self, trans, **kwd): """ GET /api/tool_shed_repositories/shed_tool_json @@ -137,19 +137,19 @@ def tool_json( self, trans, **kwd ): :param tool_shed_url: the URL of the toolshed to load from :param tool_shed_url: str """ - tool_shed_url = kwd.get( 'tool_shed_url', None ) - tsr_id = kwd.get( 'tsr_id', None ) - guid = kwd.get( 'guid', None ) - changeset = kwd.get( 'changeset', None ) - if None in [ tool_shed_url, tsr_id, guid, changeset ]: + tool_shed_url = kwd.get('tool_shed_url', None) + tsr_id = kwd.get('tsr_id', None) + guid = kwd.get('guid', None) + changeset = kwd.get('changeset', None) + if None in [tool_shed_url, tsr_id, guid, changeset]: message = 'Tool shed URL, changeset, repository ID, and tool GUID are all required parameters.' trans.response.status = 400 - return { 'status': 'error', 'message': message } - response = json.loads( util.url_get( tool_shed_url, params=dict( tsr_id=tsr_id, guid=guid, changeset=changeset.split( ':' )[ -1 ] ), pathspec=[ 'api', 'tools', 'json' ] ) ) + return {'status': 'error', 'message': message} + response = json.loads(util.url_get(tool_shed_url, params=dict(tsr_id=tsr_id, guid=guid, changeset=changeset.split(':')[-1]), pathspec=['api', 'tools', 'json'])) return response @expose_api - def show( self, trans, **kwd ): + def show(self, trans, **kwd): """ GET /api/tool_shed/contents @@ -157,23 +157,23 @@ def show( self, trans, **kwd ): :param tool_shed_url: the url of the toolshed to get categories from """ - tool_shed_url = urlunquote( kwd.get( 'tool_shed_url', '' ) ) - tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry( trans.app, tool_shed_url ) - url = util.build_url( tool_shed_url, pathspec=[ 'api', 'categories' ] ) + tool_shed_url = urlunquote(kwd.get('tool_shed_url', '')) + tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry(trans.app, tool_shed_url) + url = util.build_url(tool_shed_url, pathspec=['api', 'categories']) categories = [] - for category in json.loads( util.url_get( url ) ): - api_url = web.url_for( controller='api/tool_shed', - action='category', - tool_shed_url=urlquote( tool_shed_url ), - category_id=category[ 'id' ], - qualified=True ) - category[ 'url' ] = api_url - categories.append( category ) + for category in json.loads(util.url_get(url)): + api_url = web.url_for(controller='api/tool_shed', + action='category', + tool_shed_url=urlquote(tool_shed_url), + category_id=category['id'], + qualified=True) + category['url'] = api_url + categories.append(category) return categories @expose_api @web.require_admin - def category( self, trans, **kwd ): + def category(self, trans, **kwd): """ GET /api/tool_shed/category @@ -182,26 +182,26 @@ def category( self, trans, **kwd ): :param tool_shed_url: the url of the toolshed to get repositories from :param category_id: the category to get repositories from """ - tool_shed_url = urlunquote( kwd.get( 'tool_shed_url', '' ) ) - category_id = kwd.get( 'category_id', '' ) - params = dict( installable=True ) - tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry( trans.app, tool_shed_url ) - url = util.build_url( tool_shed_url, pathspec=[ 'api', 'categories', category_id, 'repositories' ], params=params ) + tool_shed_url = urlunquote(kwd.get('tool_shed_url', '')) + category_id = kwd.get('category_id', '') + params = dict(installable=True) + tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry(trans.app, tool_shed_url) + url = util.build_url(tool_shed_url, pathspec=['api', 'categories', category_id, 'repositories'], params=params) repositories = [] - return_json = json.loads( util.url_get( url ) ) - for repository in return_json[ 'repositories' ]: - api_url = web.url_for( controller='api/tool_shed', - action='repository', - tool_shed_url=urlquote( tool_shed_url ), - repository_id=repository[ 'id' ], - qualified=True ) - repository[ 'url' ] = api_url - repositories.append( repository ) - return_json[ 'repositories' ] = repositories + return_json = json.loads(util.url_get(url)) + for repository in return_json['repositories']: + api_url = web.url_for(controller='api/tool_shed', + action='repository', + tool_shed_url=urlquote(tool_shed_url), + repository_id=repository['id'], + qualified=True) + repository['url'] = api_url + repositories.append(repository) + return_json['repositories'] = repositories return return_json @expose_api - def repository( self, trans, **kwd ): + def repository(self, trans, **kwd): """ GET /api/tool_shed/repository @@ -218,69 +218,69 @@ def repository( self, trans, **kwd ): """ tool_dependencies = dict() tools = dict() - tool_shed_url = urlunquote( kwd.get( 'tool_shed_url', '' ) ) - log.debug( tool_shed_url ) - repository_id = kwd.get( 'repository_id', None ) - tool_ids = kwd.get( 'tool_ids', None ) + tool_shed_url = urlunquote(kwd.get('tool_shed_url', '')) + log.debug(tool_shed_url) + repository_id = kwd.get('repository_id', None) + tool_ids = kwd.get('tool_ids', None) if tool_ids is not None: - tool_ids = util.listify( tool_ids ) - tool_panel_section_select_field = tool_util.build_tool_panel_section_select_field( trans.app ) - tool_panel_section_dict = { 'name': tool_panel_section_select_field.name, - 'id': tool_panel_section_select_field.field_id, - 'sections': [] } + tool_ids = util.listify(tool_ids) + tool_panel_section_select_field = tool_util.build_tool_panel_section_select_field(trans.app) + tool_panel_section_dict = {'name': tool_panel_section_select_field.name, + 'id': tool_panel_section_select_field.field_id, + 'sections': []} for name, id, _ in tool_panel_section_select_field.options: - tool_panel_section_dict['sections'].append( dict( id=id, name=name ) ) + tool_panel_section_dict['sections'].append(dict(id=id, name=name)) repository_data = dict() if tool_ids is not None: - if len( tool_shed_url ) == 0: + if len(tool_shed_url) == 0: # By design, this list should always be from the same toolshed. If # this is ever not the case, this code will need to be updated. - tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry( self.app, tool_ids[ 0 ].split( '/' )[ 0 ] ) - found_repository = json.loads( util.url_get( tool_shed_url, params=dict( tool_ids=','.join( tool_ids ) ), pathspec=[ 'api', 'repositories' ] ) ) + tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry(self.app, tool_ids[0].split('/')[0]) + found_repository = json.loads(util.url_get(tool_shed_url, params=dict(tool_ids=','.join(tool_ids)), pathspec=['api', 'repositories'])) fr_keys = found_repository.keys() - repository_id = found_repository[ fr_keys[0] ][ 'repository_id' ] - repository_data[ 'current_changeset' ] = found_repository[ 'current_changeset' ] - repository_data[ 'repository' ] = json.loads( util.url_get( tool_shed_url, pathspec=[ 'api', 'repositories', repository_id ] ) ) - del found_repository[ 'current_changeset' ] - repository_data[ 'tool_shed_url' ] = tool_shed_url + repository_id = found_repository[fr_keys[0]]['repository_id'] + repository_data['current_changeset'] = found_repository['current_changeset'] + repository_data['repository'] = json.loads(util.url_get(tool_shed_url, pathspec=['api', 'repositories', repository_id])) + del found_repository['current_changeset'] + repository_data['tool_shed_url'] = tool_shed_url else: - repository_data[ 'repository' ] = json.loads( util.url_get( tool_shed_url, pathspec=[ 'api', 'repositories', repository_id ] ) ) - repository_data[ 'repository' ][ 'metadata' ] = json.loads( util.url_get( tool_shed_url, pathspec=[ 'api', 'repositories', repository_id, 'metadata' ] ) ) - repository_data[ 'shed_conf' ] = tool_util.build_shed_tool_conf_select_field( trans.app ).get_html().replace('\n', '') - repository_data[ 'panel_section_html' ] = tool_panel_section_select_field.get_html( extra_attr={ 'style': 'width: 30em;' } ).replace( '\n', '' ) - repository_data[ 'panel_section_dict' ] = tool_panel_section_dict - for changeset, metadata in repository_data[ 'repository' ][ 'metadata' ].items(): + repository_data['repository'] = json.loads(util.url_get(tool_shed_url, pathspec=['api', 'repositories', repository_id])) + repository_data['repository']['metadata'] = json.loads(util.url_get(tool_shed_url, pathspec=['api', 'repositories', repository_id, 'metadata'])) + repository_data['shed_conf'] = tool_util.build_shed_tool_conf_select_field(trans.app).get_html().replace('\n', '') + repository_data['panel_section_html'] = tool_panel_section_select_field.get_html(extra_attr={'style': 'width: 30em;'}).replace('\n', '') + repository_data['panel_section_dict'] = tool_panel_section_dict + for changeset, metadata in repository_data['repository']['metadata'].items(): if changeset not in tool_dependencies: - tool_dependencies[ changeset ] = [] - if metadata[ 'includes_tools_for_display_in_tool_panel' ]: + tool_dependencies[changeset] = [] + if metadata['includes_tools_for_display_in_tool_panel']: if changeset not in tools: - tools[ changeset ] = [] - for tool_dict in metadata[ 'tools' ]: - tool_info = dict( clean=re.sub( '[^a-zA-Z0-9]+', '_', tool_dict[ 'name' ] ).lower(), - guid=tool_dict[ 'guid' ], - name=tool_dict[ 'name' ], - version=tool_dict[ 'version' ], - description=tool_dict[ 'description' ] ) - if tool_info not in tools[ changeset ]: - tools[ changeset ].append( tool_info ) - if metadata[ 'has_repository_dependencies' ]: - for repository_dependency in metadata[ 'repository_dependencies' ]: - tools[ changeset ] = self.__get_tools( repository_dependency, tools[ changeset ] ) - repository_data[ 'tools' ] = tools - for key, dependency_dict in metadata[ 'tool_dependencies' ].items(): + tools[changeset] = [] + for tool_dict in metadata['tools']: + tool_info = dict(clean=re.sub('[^a-zA-Z0-9]+', '_', tool_dict['name']).lower(), + guid=tool_dict['guid'], + name=tool_dict['name'], + version=tool_dict['version'], + description=tool_dict['description']) + if tool_info not in tools[changeset]: + tools[changeset].append(tool_info) + if metadata['has_repository_dependencies']: + for repository_dependency in metadata['repository_dependencies']: + tools[changeset] = self.__get_tools(repository_dependency, tools[changeset]) + repository_data['tools'] = tools + for key, dependency_dict in metadata['tool_dependencies'].items(): if 'readme' in dependency_dict: - del( dependency_dict[ 'readme' ] ) - if dependency_dict not in tool_dependencies[ changeset ]: - tool_dependencies[ changeset ].append( dependency_dict ) - if metadata[ 'has_repository_dependencies' ]: - for repository_dependency in metadata[ 'repository_dependencies' ]: - tool_dependencies[ changeset ] = self.__get_tool_dependencies( repository_dependency, tool_dependencies[ changeset ] ) - repository_data[ 'tool_dependencies' ] = tool_dependencies + del(dependency_dict['readme']) + if dependency_dict not in tool_dependencies[changeset]: + tool_dependencies[changeset].append(dependency_dict) + if metadata['has_repository_dependencies']: + for repository_dependency in metadata['repository_dependencies']: + tool_dependencies[changeset] = self.__get_tool_dependencies(repository_dependency, tool_dependencies[changeset]) + repository_data['tool_dependencies'] = tool_dependencies return repository_data @expose_api @web.require_admin - def search( self, trans, **kwd ): + def search(self, trans, **kwd): """ GET /api/tool_shed/search Search for a specific repository in the toolshed. @@ -289,9 +289,9 @@ def search( self, trans, **kwd ): :param tool_shed_url: the URL of the toolshed to search :param tool_shed_url: str """ - tool_shed_url = kwd.get( 'tool_shed_url', None ) - q = kwd.get( 'term', None ) - if None in [ q, tool_shed_url ]: + tool_shed_url = kwd.get('tool_shed_url', None) + q = kwd.get('term', None) + if None in [q, tool_shed_url]: return {} - response = json.loads( util.url_get( tool_shed_url, params=dict( q=q ), pathspec=[ 'api', 'repositories' ] ) ) + response = json.loads(util.url_get(tool_shed_url, params=dict(q=q), pathspec=['api', 'repositories'])) return response diff --git a/lib/galaxy/webapps/galaxy/api/tours.py b/lib/galaxy/webapps/galaxy/api/tours.py index c72e6c800fc8..675c278d36a5 100644 --- a/lib/galaxy/webapps/galaxy/api/tours.py +++ b/lib/galaxy/webapps/galaxy/api/tours.py @@ -10,16 +10,16 @@ ) from galaxy.web.base.controller import BaseAPIController -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class ToursController( BaseAPIController ): +class ToursController(BaseAPIController): - def __init__( self, app ): - super( ToursController, self ).__init__( app ) + def __init__(self, app): + super(ToursController, self).__init__(app) @expose_api_anonymous_and_sessionless - def index( self, trans, **kwd ): + def index(self, trans, **kwd): """ *GET /api/tours/ Displays available tours @@ -27,7 +27,7 @@ def index( self, trans, **kwd ): return self.app.tour_registry.tours_by_id_with_description() @expose_api_anonymous_and_sessionless - def show( self, trans, tour_id, **kwd ): + def show(self, trans, tour_id, **kwd): """ load_config( self, trans, Tour_config_file, **kwd ) * GET /api/tours/{tour_id}: @@ -40,7 +40,7 @@ def show( self, trans, tour_id, **kwd ): @expose_api @require_admin - def update_tour( self, trans, tour_id, **kwd ): + def update_tour(self, trans, tour_id, **kwd): """ This simply reloads tours right now. It's a quick hack. diff --git a/lib/galaxy/webapps/galaxy/api/users.py b/lib/galaxy/webapps/galaxy/api/users.py index 261a50f934ae..67ec64351f37 100644 --- a/lib/galaxy/webapps/galaxy/api/users.py +++ b/lib/galaxy/webapps/galaxy/api/users.py @@ -1,50 +1,74 @@ """ API operations on User objects. """ -import logging import json +import logging import random import re import socket - from datetime import datetime -from markupsafe import escape -from sqlalchemy import false, true, and_, or_ -from galaxy import exceptions, util, web -from galaxy.exceptions import MessageException, ObjectInvalid +import six +import yaml +from markupsafe import escape +from sqlalchemy import ( + and_, + false, + or_, + true +) + +from galaxy import ( + exceptions, + util, + web +) +from galaxy.exceptions import ( + MessageException, + ObjectInvalid +) from galaxy.managers import users -from galaxy.security.validate_user_input import validate_email -from galaxy.security.validate_user_input import validate_password -from galaxy.security.validate_user_input import validate_publicname -from galaxy.web import url_for -from galaxy.web import _future_expose_api as expose_api -from galaxy.web import _future_expose_api_anonymous as expose_api_anonymous -from galaxy.web.base.controller import BaseAPIController -from galaxy.web.base.controller import CreatesApiKeysMixin -from galaxy.web.base.controller import CreatesUsersMixin -from galaxy.web.base.controller import UsesTagsMixin -from galaxy.web.base.controller import BaseUIController -from galaxy.web.base.controller import UsesFormDefinitionsMixin -from galaxy.web.form_builder import AddressField +from galaxy.security.validate_user_input import ( + validate_email, + validate_password, + validate_publicname +) from galaxy.tools.toolbox.filters import FilterFactory -from galaxy.util import docstring_trim, listify, hash_util +from galaxy.util import ( + docstring_trim, + hash_util, + listify +) from galaxy.util.odict import odict +from galaxy.web import ( + _future_expose_api as expose_api, + _future_expose_api_anonymous as expose_api_anonymous, + url_for +) +from galaxy.web.base.controller import ( + BaseAPIController, + BaseUIController, + CreatesApiKeysMixin, + CreatesUsersMixin, + UsesFormDefinitionsMixin, + UsesTagsMixin +) +from galaxy.web.form_builder import AddressField -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class UserAPIController( BaseAPIController, UsesTagsMixin, CreatesUsersMixin, CreatesApiKeysMixin, BaseUIController, UsesFormDefinitionsMixin ): +class UserAPIController(BaseAPIController, UsesTagsMixin, CreatesUsersMixin, CreatesApiKeysMixin, BaseUIController, UsesFormDefinitionsMixin): def __init__(self, app): super(UserAPIController, self).__init__(app) self.user_manager = users.UserManager(app) - self.user_serializer = users.UserSerializer( app ) - self.user_deserializer = users.UserDeserializer( app ) + self.user_serializer = users.UserSerializer(app) + self.user_deserializer = users.UserDeserializer(app) @expose_api - def index( self, trans, deleted='False', f_email=None, f_name=None, f_any=None, **kwd ): + def index(self, trans, deleted='False', f_email=None, f_name=None, f_any=None, **kwd): """ GET /api/users GET /api/users/deleted @@ -70,14 +94,14 @@ def index( self, trans, deleted='False', f_email=None, f_name=None, f_any=None, :type f_any: str """ rval = [] - query = trans.sa_session.query( trans.app.model.User ) - deleted = util.string_as_bool( deleted ) + query = trans.sa_session.query(trans.app.model.User) + deleted = util.string_as_bool(deleted) if f_email and (trans.user_is_admin() or trans.app.config.expose_user_email): - query = query.filter( trans.app.model.User.email.like("%%%s%%" % f_email) ) + query = query.filter(trans.app.model.User.email.like("%%%s%%" % f_email)) if f_name and (trans.user_is_admin() or trans.app.config.expose_user_name): - query = query.filter( trans.app.model.User.username.like("%%%s%%" % f_name) ) + query = query.filter(trans.app.model.User.username.like("%%%s%%" % f_name)) if f_any: if trans.user_is_admin(): @@ -92,25 +116,25 @@ def index( self, trans, deleted='False', f_email=None, f_name=None, f_any=None, trans.app.model.User.username.like("%%%s%%" % f_any) )) elif trans.app.config.expose_user_email: - query = query.filter( trans.app.model.User.email.like("%%%s%%" % f_any) ) + query = query.filter(trans.app.model.User.email.like("%%%s%%" % f_any)) elif trans.app.config.expose_user_name: - query = query.filter( trans.app.model.User.username.like("%%%s%%" % f_any) ) + query = query.filter(trans.app.model.User.username.like("%%%s%%" % f_any)) if deleted: - query = query.filter( trans.app.model.User.table.c.deleted == true() ) + query = query.filter(trans.app.model.User.table.c.deleted == true()) # only admins can see deleted users if not trans.user_is_admin(): return [] else: - query = query.filter( trans.app.model.User.table.c.deleted == false() ) + query = query.filter(trans.app.model.User.table.c.deleted == false()) # special case: user can see only their own user # special case2: if the galaxy admin has specified that other user email/names are # exposed, we don't want special case #1 if not trans.user_is_admin() and not trans.app.config.expose_user_name and not trans.app.config.expose_user_email: - item = trans.user.to_dict( value_mapper={ 'id': trans.security.encode_id } ) + item = trans.user.to_dict(value_mapper={'id': trans.security.encode_id}) return [item] for user in query: - item = user.to_dict( value_mapper={ 'id': trans.security.encode_id } ) + item = user.to_dict(value_mapper={'id': trans.security.encode_id}) # If NOT configured to expose_email, do not expose email UNLESS the user is self, or # the user is an admin if user is not trans.user and not trans.user_is_admin(): @@ -126,68 +150,68 @@ def index( self, trans, deleted='False', f_email=None, f_name=None, f_any=None, item = new_item # TODO: move into api_values - rval.append( item ) + rval.append(item) return rval @expose_api_anonymous - def show( self, trans, id, deleted='False', **kwd ): + def show(self, trans, id, deleted='False', **kwd): """ GET /api/users/{encoded_id} GET /api/users/deleted/{encoded_id} GET /api/users/current Displays information about a user. """ - deleted = util.string_as_bool( deleted ) + deleted = util.string_as_bool(deleted) try: # user is requesting data about themselves if id == "current": # ...and is anonymous - return usage and quota (if any) if not trans.user: - item = self.anon_user_api_value( trans ) + item = self.anon_user_api_value(trans) return item # ...and is logged in - return full else: user = trans.user else: - user = self.get_user( trans, id, deleted=deleted ) + user = self.get_user(trans, id, deleted=deleted) # check that the user is requesting themselves (and they aren't del'd) unless admin if not trans.user_is_admin(): assert trans.user == user assert not user.deleted except: - raise exceptions.RequestParameterInvalidException( 'Invalid user id specified', id=id ) + raise exceptions.RequestParameterInvalidException('Invalid user id specified', id=id) return self.user_serializer.serialize_to_view(user, view='detailed') @expose_api - def create( self, trans, payload, **kwd ): + def create(self, trans, payload, **kwd): """ POST /api/users Creates a new Galaxy user. """ if not trans.app.config.allow_user_creation and not trans.user_is_admin(): - raise exceptions.ConfigDoesNotAllowException( 'User creation is not allowed in this Galaxy instance' ) + raise exceptions.ConfigDoesNotAllowException('User creation is not allowed in this Galaxy instance') if trans.app.config.use_remote_user and trans.user_is_admin(): - user = trans.get_or_create_remote_user( remote_user_email=payload['remote_user_email'] ) + user = trans.get_or_create_remote_user(remote_user_email=payload['remote_user_email']) elif trans.user_is_admin(): - username = payload[ 'username' ] - email = payload[ 'email' ] - password = payload[ 'password' ] - message = "\n".join( [ validate_email( trans, email ), - validate_password( trans, password, password ), - validate_publicname( trans, username ) ] ).rstrip() + username = payload['username'] + email = payload['email'] + password = payload['password'] + message = "\n".join([validate_email(trans, email), + validate_password(trans, password, password), + validate_publicname(trans, username)]).rstrip() if message: - raise exceptions.RequestParameterInvalidException( message ) + raise exceptions.RequestParameterInvalidException(message) else: - user = self.create_user( trans=trans, email=email, username=username, password=password ) + user = self.create_user(trans=trans, email=email, username=username, password=password) else: raise exceptions.NotImplemented() - item = user.to_dict( view='element', value_mapper={ 'id': trans.security.encode_id, - 'total_disk_usage': float } ) + item = user.to_dict(view='element', value_mapper={'id': trans.security.encode_id, + 'total_disk_usage': float}) return item @expose_api - def update( self, trans, id, payload, **kwd ): + def update(self, trans, id, payload, **kwd): """ update( self, trans, id, payload, **kwd ) * PUT /api/users/{id} @@ -203,20 +227,20 @@ def update( self, trans, id, payload, **kwd ): the serialized item after any changes """ current_user = trans.user - user_to_update = self.user_manager.by_id( self.decode_id( id ) ) + user_to_update = self.user_manager.by_id(self.decode_id(id)) # only allow updating other users if they're admin editing_someone_else = current_user != user_to_update - is_admin = trans.api_inherit_admin or self.user_manager.is_admin( current_user ) + is_admin = trans.api_inherit_admin or self.user_manager.is_admin(current_user) if editing_someone_else and not is_admin: - raise exceptions.InsufficientPermissionsException( 'you are not allowed to update that user', id=id ) + raise exceptions.InsufficientPermissionsException('you are not allowed to update that user', id=id) - self.user_deserializer.deserialize( user_to_update, payload, user=current_user, trans=trans ) - return self.user_serializer.serialize_to_view( user_to_update, view='detailed' ) + self.user_deserializer.deserialize(user_to_update, payload, user=current_user, trans=trans) + return self.user_serializer.serialize_to_view(user_to_update, view='detailed') @expose_api @web.require_admin - def delete( self, trans, id, **kwd ): + def delete(self, trans, id, **kwd): """ DELETE /api/users/{id} delete the user with the given ``id`` @@ -228,7 +252,7 @@ def delete( self, trans, id, **kwd ): :type purge: bool """ if not trans.app.config.allow_user_deletion: - raise exceptions.ConfigDoesNotAllowException( 'The configuration of this Galaxy instance does not allow admins to delete users.' ) + raise exceptions.ConfigDoesNotAllowException('The configuration of this Galaxy instance does not allow admins to delete users.') purge = util.string_as_bool(kwd.get('purge', False)) if purge: raise exceptions.NotImplemented('Purge option has not been implemented yet') @@ -238,18 +262,58 @@ def delete( self, trans, id, **kwd ): @expose_api @web.require_admin - def undelete( self, trans, **kwd ): + def undelete(self, trans, **kwd): raise exceptions.NotImplemented() # TODO: move to more basal, common resource than this - def anon_user_api_value( self, trans ): + def anon_user_api_value(self, trans): """Return data for an anonymous user, truncated to only usage and quota_percent""" - usage = trans.app.quota_agent.get_usage( trans ) - percent = trans.app.quota_agent.get_percent( trans=trans, usage=usage ) - return {'total_disk_usage': int( usage ), - 'nice_total_disk_usage': util.nice_size( usage ), + usage = trans.app.quota_agent.get_usage(trans) + percent = trans.app.quota_agent.get_percent(trans=trans, usage=usage) + return {'total_disk_usage': int(usage), + 'nice_total_disk_usage': util.nice_size(usage), 'quota_percent': percent} + def _get_extra_user_preferences(self, trans): + """ + Reads the file user_preferences_extra_conf.yml to display + admin defined user informations + """ + path = trans.app.config.user_preferences_extra_config_file + try: + with open(path, 'r') as stream: + config = yaml.load(stream) + except: + log.warning('Config file (%s) could not be found or is malformed.' % path) + return {} + + return config['preferences'] if config else {} + + def _build_extra_user_pref_inputs(self, preferences, user): + """ + Build extra user preferences inputs list. + Add values to the fields if present + """ + if not preferences: + return [] + data = [] + # Get data if present + data_key = "extra_user_preferences" + if data_key in user.preferences: + data = json.loads(user.preferences[data_key]) + extra_pref_inputs = list() + # Build sections for different categories of inputs + for item, value in preferences.items(): + if value is not None: + for input in value["inputs"]: + input['help'] = 'Required' if input['required'] else '' + field = item + '|' + input['name'] + for data_item in data: + if field in data_item: + input['value'] = data[data_item] + extra_pref_inputs.append({'type': 'section', 'title': value['description'], 'name': item, 'expanded': True, 'inputs': value['inputs']}) + return extra_pref_inputs + @expose_api def get_information(self, trans, id, **kwd): """ @@ -302,6 +366,7 @@ def get_information(self, trans, id, **kwd): info_field['test_param']['data'].append({'label': info_form['name'], 'value': info_form['id']}) info_field['cases'].append({'value': info_form['id'], 'inputs': info_form['inputs']}) inputs.append(info_field) + address_inputs = [{'type': 'hidden', 'name': 'id', 'hidden': True}] for field in AddressField.fields(): address_inputs.append({'type': 'text', 'name': field[0], 'label': field[1], 'help': field[2]}) @@ -315,6 +380,11 @@ def get_information(self, trans, id, **kwd): address_cache.append(input_copy) address_repeat['cache'].append(address_cache) inputs.append(address_repeat) + + # Build input sections for extra user preferences + extra_user_pref = self._build_extra_user_pref_inputs(self._get_extra_user_preferences(trans), user) + for item in extra_user_pref: + inputs.append(item) else: if user.active_repositories: inputs.append(dict(id='name_input', name='username', label='Public name:', type='hidden', value=username, help='You cannot change your public name after you have created a repository in this tool shed.')) @@ -382,6 +452,26 @@ def set_information(self, trans, id, payload={}, **kwd): form_values = trans.model.FormValues(user_info_form, user_info_values) trans.sa_session.add(form_values) user.values = form_values + + # Update values for extra user preference items + extra_user_pref_data = dict() + get_extra_pref_keys = self._get_extra_user_preferences(trans) + if get_extra_pref_keys is not None: + for key in get_extra_pref_keys: + key_prefix = key + '|' + for item in payload: + if item.startswith(key_prefix): + # Show error message if the required field is empty + if payload[item] == "": + # Raise an exception when a required field is empty while saving the form + keys = item.split("|") + section = get_extra_pref_keys[keys[0]] + for input in section['inputs']: + if input['name'] == keys[1] and input['required']: + raise MessageException("Please fill the required field") + extra_user_pref_data[item] = payload[item] + user.preferences["extra_user_preferences"] = json.dumps(extra_user_pref_data) + # Update user addresses address_dicts = {} address_count = 0 @@ -417,16 +507,16 @@ def set_information(self, trans, id, payload={}, **kwd): trans.log_event('User information added') return {'message': 'User information has been saved.'} - def send_verification_email( self, trans, email, username ): + def send_verification_email(self, trans, email, username): """ Send the verification email containing the activation link to the user's email. """ if username is None: username = trans.user.username - activation_link = self.prepare_activation_link( trans, escape( email ) ) + activation_link = self.prepare_activation_link(trans, escape(email)) - host = trans.request.host.split( ':' )[ 0 ] - if host in [ 'localhost', '127.0.0.1', '0.0.0.0' ]: + host = trans.request.host.split(':')[0] + if host in ['localhost', '127.0.0.1', '0.0.0.0']: host = socket.getfqdn() body = ("Hello %s,\n\n" "In order to complete the activation process for %s begun on %s at %s, please click on the following link to verify your account:\n\n" @@ -434,8 +524,8 @@ def send_verification_email( self, trans, email, username ): "By clicking on the above link and opening a Galaxy account you are also confirming that you have read and agreed to Galaxy's Terms and Conditions for use of this service (%s). This includes a quota limit of one account per user. Attempts to subvert this limit by creating multiple accounts or through any other method may result in termination of all associated accounts and data.\n\n" "Please contact us if you need help with your account at: %s. You can also browse resources available at: %s. \n\n" "More about the Galaxy Project can be found at galaxyproject.org\n\n" - "Your Galaxy Team" % (escape( username ), escape( email ), - datetime.utcnow().strftime( "%D"), + "Your Galaxy Team" % (escape(username), escape(email), + datetime.utcnow().strftime("%D"), trans.request.host, activation_link, trans.app.config.terms_url, trans.app.config.error_email_to, @@ -444,36 +534,36 @@ def send_verification_email( self, trans, email, username ): frm = trans.app.config.email_from or 'galaxy-no-reply@' + host subject = 'Galaxy Account Activation' try: - util.send_mail( frm, to, subject, body, trans.app.config ) + util.send_mail(frm, to, subject, body, trans.app.config) return True except Exception: - log.exception( 'Unable to send the activation email.' ) + log.exception('Unable to send the activation email.') return False - def prepare_activation_link( self, trans, email ): + def prepare_activation_link(self, trans, email): """ Prepare the account activation link for the user. """ - activation_token = self.get_activation_token( trans, email ) - activation_link = url_for( controller='user', action='activate', activation_token=activation_token, email=email, qualified=True ) + activation_token = self.get_activation_token(trans, email) + activation_link = url_for(controller='user', action='activate', activation_token=activation_token, email=email, qualified=True) return activation_link - def get_activation_token( self, trans, email ): + def get_activation_token(self, trans, email): """ Check for the activation token. Create new activation token and store it in the database if no token found. """ - user = trans.sa_session.query( trans.app.model.User ).filter( trans.app.model.User.table.c.email == email ).first() + user = trans.sa_session.query(trans.app.model.User).filter(trans.app.model.User.table.c.email == email).first() activation_token = user.activation_token if activation_token is None: - activation_token = hash_util.new_secure_hash( str( random.getrandbits( 256 ) ) ) + activation_token = hash_util.new_secure_hash(str(random.getrandbits(256))) user.activation_token = activation_token - trans.sa_session.add( user ) + trans.sa_session.add(user) trans.sa_session.flush() return activation_token def _validate_email_publicname(self, email, username): ''' Validate email and username using regex ''' - if email == '' or not isinstance( email, basestring ): + if email == '' or not isinstance(email, six.string_types): return 'Please provide your email address.' if not re.match('^[a-z0-9\-]{3,255}$', username): return 'Public name must contain only lowercase letters, numbers and "-". It also has to be shorter than 255 characters but longer than 2.' @@ -487,11 +577,10 @@ def get_password(self, trans, id, payload={}, **kwd): """ Return available password inputs. """ - return {'message': 'Password unchanged.', - 'inputs': [ {'name': 'current', 'type': 'password', 'label': 'Current password'}, - {'name': 'password', 'type': 'password', 'label': 'New password'}, - {'name': 'confirm', 'type': 'password', 'label': 'Confirm password'}, - {'name': 'token', 'type': 'hidden', 'hidden': True, 'ignore': None} ]} + return {'inputs': [{'name': 'current', 'type': 'password', 'label': 'Current password'}, + {'name': 'password', 'type': 'password', 'label': 'New password'}, + {'name': 'confirm', 'type': 'password', 'label': 'Confirm password'}, + {'name': 'token', 'type': 'hidden', 'hidden': True, 'ignore': None}]} @expose_api def set_password(self, trans, id, payload={}, **kwd): @@ -548,18 +637,17 @@ def get_permissions(self, trans, id, payload={}, **kwd): """ user = self._get_user(trans, id) roles = user.all_roles() - permitted_actions = trans.app.model.Dataset.permitted_actions.items() inputs = [] - for index, action in permitted_actions: + for index, action in trans.app.model.Dataset.permitted_actions.items(): inputs.append({'type': 'select', 'multiple': True, 'optional': True, 'name': index, 'label': action.action, 'help': action.description, - 'options': [(r.name, r.id) for r in roles], + 'options': list(set((r.name, r.id) for r in roles)), 'value': [a.role.id for a in user.default_permissions if a.action == action.action]}) - return {'message': 'Permissions unchanged.', 'inputs': inputs} + return {'inputs': inputs} @expose_api def set_permissions(self, trans, id, payload={}, **kwd): @@ -567,9 +655,8 @@ def set_permissions(self, trans, id, payload={}, **kwd): Set the user's default permissions for the new histories """ user = self._get_user(trans, id) - permitted_actions = trans.app.model.Dataset.permitted_actions.items() permissions = {} - for index, action in permitted_actions: + for index, action in trans.app.model.Dataset.permitted_actions.items(): action_id = trans.app.security_agent.get_action(action.action).action permissions[action_id] = [trans.sa_session.query(trans.app.model.Role).get(x) for x in (payload.get(index) or [])] trans.app.security_agent.user_set_default_permissions(user, permissions) @@ -591,7 +678,7 @@ def get_toolbox_filters(self, trans, id, payload={}, **kwd): factory = FilterFactory(trans.app.toolbox) for filter_type in filter_types: self._add_filter_inputs(factory, filter_types, inputs, filter_type, saved_values) - return {'message': 'Toolbox filters unchanged.', 'inputs': inputs} + return {'inputs': inputs} @expose_api def set_toolbox_filters(self, trans, id, payload={}, **kwd): @@ -659,7 +746,7 @@ def set_api_key(self, trans, id, payload={}, **kwd): self.create_api_key(trans, user) return self._build_inputs_api_key(user, message='Generated a new web API key.') - def _build_inputs_api_key(self, user, message='' ): + def _build_inputs_api_key(self, user, message=''): """ Build API key inputs. """ @@ -677,8 +764,7 @@ def get_communication(self, trans, id, payload={}, **kwd): Build communication server inputs. """ user = self._get_user(trans, id) - return {'message': 'Communication server settings unchanged.', - 'inputs': [{'name': 'enable', + return {'inputs': [{'name': 'enable', 'type': 'boolean', 'label': 'Enable communication', 'value': user.preferences.get('communication_server', 'false')}]} @@ -712,12 +798,12 @@ def get_custom_builds(self, trans, id, payload={}, **kwd): dbkeys = json.loads(user.preferences['dbkeys']) if 'dbkeys' in user.preferences else {} update = False for key in dbkeys: - dbkey = dbkeys[ key ] + dbkey = dbkeys[key] if 'count' not in dbkey and 'linecount' in dbkey: - chrom_count_dataset = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( dbkey[ 'linecount' ] ) + chrom_count_dataset = trans.sa_session.query(trans.app.model.HistoryDatasetAssociation).get(dbkey['linecount']) if chrom_count_dataset.state == trans.app.model.Job.states.OK: - chrom_count = int( open( chrom_count_dataset.file_name ).readline() ) - dbkey[ 'count' ] = chrom_count + chrom_count = int(open(chrom_count_dataset.file_name).readline()) + dbkey['count'] = chrom_count update = True if update: user.preferences['dbkeys'] = json.dumps(dbkeys) @@ -747,7 +833,7 @@ def add_custom_builds(self, trans, id, key, payload={}, **kwd): name = payload.get('name') len_type = payload.get('len|type') len_value = payload.get('len|value') - if len_type not in [ 'file', 'fasta', 'text' ] or not len_value: + if len_type not in ['file', 'fasta', 'text'] or not len_value: raise MessageException('Please specify a valid data source type.') if not name or not key: raise MessageException('You must specify values for all the fields.') @@ -755,19 +841,19 @@ def add_custom_builds(self, trans, id, key, payload={}, **kwd): raise MessageException('There is already a custom build with that key. Delete it first if you want to replace it.') else: # Have everything needed; create new build. - build_dict = { 'name': name } - if len_type in [ 'text', 'file' ]: + build_dict = {'name': name} + if len_type in ['text', 'file']: # Create new len file - new_len = trans.app.model.HistoryDatasetAssociation( extension='len', create_dataset=True, sa_session=trans.sa_session ) - trans.sa_session.add( new_len ) + new_len = trans.app.model.HistoryDatasetAssociation(extension='len', create_dataset=True, sa_session=trans.sa_session) + trans.sa_session.add(new_len) new_len.name = name new_len.visible = False new_len.state = trans.app.model.Job.states.OK new_len.info = 'custom build .len file' try: - trans.app.object_store.create( new_len.dataset ) + trans.app.object_store.create(new_len.dataset) except ObjectInvalid: - raise MessageException( 'Unable to create output dataset: object store is full.' ) + raise MessageException('Unable to create output dataset: object store is full.') trans.sa_session.flush() counter = 0 lines_skipped = 0 @@ -787,24 +873,24 @@ def add_custom_builds(self, trans, id, key, payload={}, **kwd): lines_skipped += 1 continue if chrom != escape(chrom): - build_dict[ 'message' ] = 'Invalid chromosome(s) with HTML detected and skipped.' + build_dict['message'] = 'Invalid chromosome(s) with HTML detected and skipped.' lines_skipped += 1 continue counter += 1 - f.write( '%s\t%s\n' % (chrom, length) ) + f.write('%s\t%s\n' % (chrom, length)) f.close() - build_dict[ 'len' ] = new_len.id - build_dict[ 'count' ] = counter + build_dict['len'] = new_len.id + build_dict['count'] = counter else: - build_dict[ 'fasta' ] = trans.security.decode_id( len_value ) - dataset = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( build_dict[ 'fasta' ] ) + build_dict['fasta'] = trans.security.decode_id(len_value) + dataset = trans.sa_session.query(trans.app.model.HistoryDatasetAssociation).get(build_dict['fasta']) try: - new_len = dataset.get_converted_dataset( trans, 'len' ) - new_linecount = new_len.get_converted_dataset( trans, 'linecount' ) - build_dict[ 'len' ] = new_len.id - build_dict[ 'linecount' ] = new_linecount.id + new_len = dataset.get_converted_dataset(trans, 'len') + new_linecount = new_len.get_converted_dataset(trans, 'linecount') + build_dict['len'] = new_len.id + build_dict['linecount'] = new_linecount.id except: - raise MessageException( 'Failed to convert dataset.' ) + raise MessageException('Failed to convert dataset.') dbkeys[key] = build_dict user.preferences['dbkeys'] = json.dumps(dbkeys) trans.sa_session.flush() @@ -828,7 +914,7 @@ def delete_custom_builds(self, trans, id, key, payload={}, **kwd): del dbkeys[key] user.preferences['dbkeys'] = json.dumps(dbkeys) trans.sa_session.flush() - return { 'message': 'Deleted %s.' % key } + return {'message': 'Deleted %s.' % key} else: raise MessageException('Could not find and delete build (%s).' % key) diff --git a/lib/galaxy/webapps/galaxy/api/visualizations.py b/lib/galaxy/webapps/galaxy/api/visualizations.py index 90ee8244a4e0..d125c8021710 100644 --- a/lib/galaxy/webapps/galaxy/api/visualizations.py +++ b/lib/galaxy/webapps/galaxy/api/visualizations.py @@ -17,16 +17,16 @@ from galaxy import exceptions import json import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class VisualizationsController( BaseAPIController, UsesVisualizationMixin, SharableMixin, UsesAnnotations ): +class VisualizationsController(BaseAPIController, UsesVisualizationMixin, SharableMixin, UsesAnnotations): """ RESTful controller for interactions with visualizations. """ @expose_api - def index( self, trans, **kwargs ): + def index(self, trans, **kwargs): """ GET /api/visualizations: """ @@ -38,21 +38,21 @@ def index( self, trans, **kwargs ): # TODO: deleted # this is the default search - user's vis, vis shared with user, published vis - visualizations = self.get_visualizations_by_user( trans, user ) - visualizations += self.get_visualizations_shared_with_user( trans, user ) - visualizations += self.get_published_visualizations( trans, exclude_user=user ) + visualizations = self.get_visualizations_by_user(trans, user) + visualizations += self.get_visualizations_shared_with_user(trans, user) + visualizations += self.get_published_visualizations(trans, exclude_user=user) # TODO: the admin case - everything for visualization in visualizations: - item = self.get_visualization_summary_dict( visualization ) - item = trans.security.encode_dict_ids( item ) - item[ 'url' ] = web.url_for( 'visualization', id=item[ 'id' ] ) - rval.append( item ) + item = self.get_visualization_summary_dict(visualization) + item = trans.security.encode_dict_ids(item) + item['url'] = web.url_for('visualization', id=item['id']) + rval.append(item) return rval @expose_api - def show( self, trans, id, **kwargs ): + def show(self, trans, id, **kwargs): """ GET /api/visualizations/{viz_id} """ @@ -61,25 +61,25 @@ def show( self, trans, id, **kwargs ): rval = {} # TODO:?? /api/visualizations/registry -> json of registry.listings? - visualization = self.get_visualization( trans, id, check_ownership=False, check_accessible=True ) - dictionary = trans.security.encode_dict_ids( self.get_visualization_dict( visualization ) ) - dictionary[ 'url' ] = web.url_for( controller='visualization', - action="display_by_username_and_slug", username=visualization.user.username, slug=visualization.slug ) - dictionary[ 'annotation' ] = self.get_item_annotation_str( trans.sa_session, trans.user, visualization ) + visualization = self.get_visualization(trans, id, check_ownership=False, check_accessible=True) + dictionary = trans.security.encode_dict_ids(self.get_visualization_dict(visualization)) + dictionary['url'] = web.url_for(controller='visualization', + action="display_by_username_and_slug", username=visualization.user.username, slug=visualization.slug) + dictionary['annotation'] = self.get_item_annotation_str(trans.sa_session, trans.user, visualization) # need to encode ids in revisions as well encoded_revisions = [] - for revision in dictionary[ 'revisions' ]: + for revision in dictionary['revisions']: # NOTE: does not encode ids inside the configs - encoded_revisions.append( trans.security.encode_id( revision ) ) - dictionary[ 'revisions' ] = encoded_revisions - dictionary[ 'latest_revision' ] = trans.security.encode_dict_ids( dictionary[ 'latest_revision' ] ) + encoded_revisions.append(trans.security.encode_id(revision)) + dictionary['revisions'] = encoded_revisions + dictionary['latest_revision'] = trans.security.encode_dict_ids(dictionary['latest_revision']) rval = dictionary return rval @expose_api - def create( self, trans, payload, **kwargs ): + def create(self, trans, payload, **kwargs): """ POST /api/visualizations creates a new visualization using the given payload @@ -90,35 +90,35 @@ def create( self, trans, payload, **kwargs ): rval = None if 'import_id' in payload: - import_id = payload( 'import_id' ) - visualization = self.import_visualization( trans, import_id, user=trans.user ) + import_id = payload('import_id') + visualization = self.import_visualization(trans, import_id, user=trans.user) else: - payload = self._validate_and_parse_payload( payload ) + payload = self._validate_and_parse_payload(payload) # must have a type (I've taken this to be the visualization name) if 'type' not in payload: - raise exceptions.RequestParameterMissingException( "key/value 'type' is required" ) - vis_type = payload.pop( 'type', False ) + raise exceptions.RequestParameterMissingException("key/value 'type' is required") + vis_type = payload.pop('type', False) - payload[ 'save' ] = True + payload['save'] = True try: # generate defaults - this will err if given a weird key? - visualization = self.create_visualization( trans, vis_type, **payload ) + visualization = self.create_visualization(trans, vis_type, **payload) except ValueError as val_err: - raise exceptions.RequestParameterMissingException( str( val_err ) ) + raise exceptions.RequestParameterMissingException(str(val_err)) - rval = { 'id' : trans.security.encode_id( visualization.id ) } + rval = {'id' : trans.security.encode_id(visualization.id)} return rval @expose_api - def update( self, trans, id, payload, **kwargs ): + def update(self, trans, id, payload, **kwargs): """ PUT /api/visualizations/{encoded_visualization_id} """ rval = None - payload = self._validate_and_parse_payload( payload ) + payload = self._validate_and_parse_payload(payload) # there's a differentiation here between updating the visualiztion and creating a new revision # that needs to be handled clearly here @@ -128,17 +128,17 @@ def update( self, trans, id, payload, **kwargs ): # only create a new revsion on a different config # only update owned visualizations - visualization = self.get_visualization( trans, id, check_ownership=True ) - title = payload.get( 'title', visualization.latest_revision.title ) - dbkey = payload.get( 'dbkey', visualization.latest_revision.dbkey ) - config = payload.get( 'config', visualization.latest_revision.config ) + visualization = self.get_visualization(trans, id, check_ownership=True) + title = payload.get('title', visualization.latest_revision.title) + dbkey = payload.get('dbkey', visualization.latest_revision.dbkey) + config = payload.get('config', visualization.latest_revision.config) latest_config = visualization.latest_revision.config - if( ( title != visualization.latest_revision.title ) or - ( dbkey != visualization.latest_revision.dbkey ) or - ( json.dumps( config ) != json.dumps( latest_config ) ) ): - revision = self.add_visualization_revision( trans, visualization, config, title, dbkey ) - rval = { 'id' : id, 'revision' : revision.id } + if((title != visualization.latest_revision.title) or + (dbkey != visualization.latest_revision.dbkey) or + (json.dumps(config) != json.dumps(latest_config))): + revision = self.add_visualization_revision(trans, visualization, config, title, dbkey) + rval = {'id' : id, 'revision' : revision.id} # allow updating vis title visualization.title = title @@ -146,7 +146,7 @@ def update( self, trans, id, payload, **kwargs ): return rval - def _validate_and_parse_payload( self, payload ): + def _validate_and_parse_payload(self, payload): """ Validate and parse incomming data payload for a visualization. """ @@ -171,36 +171,36 @@ def _validate_and_parse_payload( self, payload ): for key, val in payload.items(): # TODO: validate types in VALID_TYPES/registry names at the mixin/model level? if key == 'type': - if not isinstance( val, string_types ): - raise ValidationError( '%s must be a string or unicode: %s' % ( key, str( type( val ) ) ) ) - val = util.sanitize_html.sanitize_html( val, 'utf-8' ) + if not isinstance(val, string_types): + raise ValidationError('%s must be a string or unicode: %s' % (key, str(type(val)))) + val = util.sanitize_html.sanitize_html(val, 'utf-8') elif key == 'config': - if not isinstance( val, dict ): - raise ValidationError( '%s must be a dictionary: %s' % ( key, str( type( val ) ) ) ) + if not isinstance(val, dict): + raise ValidationError('%s must be a dictionary: %s' % (key, str(type(val)))) elif key == 'annotation': - if not isinstance( val, string_types ): - raise ValidationError( '%s must be a string or unicode: %s' % ( key, str( type( val ) ) ) ) - val = util.sanitize_html.sanitize_html( val, 'utf-8' ) + if not isinstance(val, string_types): + raise ValidationError('%s must be a string or unicode: %s' % (key, str(type(val)))) + val = util.sanitize_html.sanitize_html(val, 'utf-8') # these are keys that actually only be *updated* at the revision level and not here # (they are still valid for create, tho) elif key == 'title': - if not isinstance( val, string_types ): - raise ValidationError( '%s must be a string or unicode: %s' % ( key, str( type( val ) ) ) ) - val = util.sanitize_html.sanitize_html( val, 'utf-8' ) + if not isinstance(val, string_types): + raise ValidationError('%s must be a string or unicode: %s' % (key, str(type(val)))) + val = util.sanitize_html.sanitize_html(val, 'utf-8') elif key == 'slug': - if not isinstance( val, string_types ): - raise ValidationError( '%s must be a string: %s' % ( key, str( type( val ) ) ) ) - val = util.sanitize_html.sanitize_html( val, 'utf-8' ) + if not isinstance(val, string_types): + raise ValidationError('%s must be a string: %s' % (key, str(type(val)))) + val = util.sanitize_html.sanitize_html(val, 'utf-8') elif key == 'dbkey': - if not isinstance( val, string_types ): - raise ValidationError( '%s must be a string or unicode: %s' % ( key, str( type( val ) ) ) ) - val = util.sanitize_html.sanitize_html( val, 'utf-8' ) + if not isinstance(val, string_types): + raise ValidationError('%s must be a string or unicode: %s' % (key, str(type(val)))) + val = util.sanitize_html.sanitize_html(val, 'utf-8') elif key not in valid_but_uneditable_keys: continue # raise AttributeError( 'unknown key: %s' %( str( key ) ) ) - validated_payload[ key ] = val + validated_payload[key] = val return validated_payload diff --git a/lib/galaxy/webapps/galaxy/api/workflows.py b/lib/galaxy/webapps/galaxy/api/workflows.py index 0d826ee9b226..650a2fc369d9 100644 --- a/lib/galaxy/webapps/galaxy/api/workflows.py +++ b/lib/galaxy/webapps/galaxy/api/workflows.py @@ -32,18 +32,20 @@ from galaxy.workflow.run import invoke, queue_invoke from galaxy.workflow.run_request import build_workflow_run_configs +from tool_shed.galaxy_install.install_manager import InstallRepositoryManager + log = logging.getLogger(__name__) class WorkflowsAPIController(BaseAPIController, UsesStoredWorkflowMixin, UsesAnnotations, SharableMixin): - def __init__( self, app ): - super( WorkflowsAPIController, self ).__init__( app ) - self.history_manager = histories.HistoryManager( app ) - self.workflow_manager = workflows.WorkflowsManager( app ) - self.workflow_contents_manager = workflows.WorkflowContentsManager( app ) + def __init__(self, app): + super(WorkflowsAPIController, self).__init__(app) + self.history_manager = histories.HistoryManager(app) + self.workflow_manager = workflows.WorkflowsManager(app) + self.workflow_contents_manager = workflows.WorkflowContentsManager(app) - def __get_full_shed_url( self, url ): + def __get_full_shed_url(self, url): for name, shed_url in self.app.tool_shed_registry.tool_sheds.items(): if url in shed_url: return shed_url @@ -54,57 +56,61 @@ def index(self, trans, **kwd): """ GET /api/workflows """ - return self.get_workflows_list( trans, False, kwd ) + return self.get_workflows_list(trans, kwd) @expose_api - def get_workflow_menu( self, trans, **kwd ): + def get_workflow_menu(self, trans, **kwd): """ Get workflows present in the tools panel GET /api/workflows/menu """ user = trans.get_user() - ids_in_menu = [ x.stored_workflow_id for x in user.stored_workflow_menu_entries ] + ids_in_menu = [x.stored_workflow_id for x in user.stored_workflow_menu_entries] return { 'ids_in_menu': ids_in_menu, - 'workflows': self.get_workflows_list( trans, True, kwd ) + 'workflows': self.get_workflows_list(trans, kwd) } @expose_api - def set_workflow_menu( self, trans, **kwd ): + def set_workflow_menu(self, trans, **kwd): """ Save workflow menu to be shown in the tool panel PUT /api/workflows/menu """ - payload = kwd.get( 'payload' ) + payload = kwd.get('payload') user = trans.get_user() - workflow_ids = payload.get( 'workflow_ids' ) + workflow_ids = payload.get('workflow_ids') if workflow_ids is None: workflow_ids = [] - elif type( workflow_ids ) != list: - workflow_ids = [ workflow_ids ] + elif type(workflow_ids) != list: + workflow_ids = [workflow_ids] + workflow_ids_decoded = [] + # Decode the encoded workflow ids + for ids in workflow_ids: + workflow_ids_decoded.append(trans.security.decode_id(ids)) sess = trans.sa_session # This explicit remove seems like a hack, need to figure out # how to make the association do it automatically. for m in user.stored_workflow_menu_entries: - sess.delete( m ) + sess.delete(m) user.stored_workflow_menu_entries = [] - q = sess.query( model.StoredWorkflow ) + q = sess.query(model.StoredWorkflow) # To ensure id list is unique seen_workflow_ids = set() - for id in workflow_ids: - if id in seen_workflow_ids: + for wf_id in workflow_ids_decoded: + if wf_id in seen_workflow_ids: continue else: - seen_workflow_ids.add( id ) + seen_workflow_ids.add(wf_id) m = model.StoredWorkflowMenuEntry() - m.stored_workflow = q.get( id ) - user.stored_workflow_menu_entries.append( m ) + m.stored_workflow = q.get(wf_id) + user.stored_workflow_menu_entries.append(m) sess.flush() message = "Menu updated." - trans.set_message( message ) - return { 'message': message, 'status': 'done' } + trans.set_message(message) + return {'message': message, 'status': 'done'} - def get_workflows_list( self, trans, for_menu, kwd ): + def get_workflows_list(self, trans, kwd): """ Displays a collection of workflows. @@ -113,37 +119,43 @@ def get_workflows_list( self, trans, for_menu, kwd ): :param missing_tools: if True, include a list of missing tools per workflow :type missing_tools: boolean """ - show_published = util.string_as_bool( kwd.get( 'show_published', 'False' ) ) - missing_tools = util.string_as_bool( kwd.get( 'missing_tools', 'False' ) ) + show_published = util.string_as_bool(kwd.get('show_published', 'False')) + missing_tools = util.string_as_bool(kwd.get('missing_tools', 'False')) rval = [] - filter1 = ( trans.app.model.StoredWorkflow.user == trans.user ) + filter1 = (trans.app.model.StoredWorkflow.user == trans.user) + user = trans.get_user() if show_published: - filter1 = or_( filter1, ( trans.app.model.StoredWorkflow.published == true() ) ) - for wf in trans.sa_session.query( trans.app.model.StoredWorkflow ).filter( - filter1, trans.app.model.StoredWorkflow.table.c.deleted == false() ).order_by( - desc( trans.app.model.StoredWorkflow.table.c.update_time ) ).all(): - if for_menu: - item = wf.to_dict() - else: - item = wf.to_dict( value_mapper={ 'id': trans.security.encode_id } ) - encoded_id = trans.security.encode_id(wf.id) - item['url'] = url_for('workflow', id=encoded_id) + filter1 = or_(filter1, (trans.app.model.StoredWorkflow.published == true())) + for wf in trans.sa_session.query(trans.app.model.StoredWorkflow).filter( + filter1, trans.app.model.StoredWorkflow.table.c.deleted == false()).order_by( + desc(trans.app.model.StoredWorkflow.table.c.update_time)).all(): + + item = wf.to_dict(value_mapper={'id': trans.security.encode_id}) + encoded_id = trans.security.encode_id(wf.id) + item['url'] = url_for('workflow', id=encoded_id) item['owner'] = wf.user.username - item['number_of_steps'] = len( wf.latest_workflow.steps ) + item['number_of_steps'] = len(wf.latest_workflow.steps) + item['show_in_tool_panel'] = False + for x in user.stored_workflow_menu_entries: + if x.stored_workflow_id == wf.id: + item['show_in_tool_panel'] = True + break rval.append(item) - for wf_sa in trans.sa_session.query( trans.app.model.StoredWorkflowUserShareAssociation ).filter_by( - user=trans.user ).join( 'stored_workflow' ).filter( - trans.app.model.StoredWorkflow.deleted == false() ).order_by( - desc( trans.app.model.StoredWorkflow.update_time ) ).all(): - if for_menu: - item = wf_sa.stored_workflow.to_dict() - else: - item = wf_sa.stored_workflow.to_dict( value_mapper={ 'id': trans.security.encode_id } ) - encoded_id = trans.security.encode_id(wf_sa.stored_workflow.id) - item['url'] = url_for( 'workflow', id=encoded_id ) - item['slug'] = wf_sa.stored_workflow.slug + for wf_sa in trans.sa_session.query(trans.app.model.StoredWorkflowUserShareAssociation).filter_by( + user=trans.user).join('stored_workflow').filter( + trans.app.model.StoredWorkflow.deleted == false()).order_by( + desc(trans.app.model.StoredWorkflow.update_time)).all(): + item = wf_sa.stored_workflow.to_dict(value_mapper={'id': trans.security.encode_id}) + encoded_id = trans.security.encode_id(wf_sa.stored_workflow.id) + item['url'] = url_for('workflow', id=encoded_id) + item['slug'] = wf_sa.stored_workflow.slug item['owner'] = wf_sa.stored_workflow.user.username - item['number_of_steps'] = len( wf_sa.stored_workflow.latest_workflow.steps ) + item['number_of_steps'] = len(wf_sa.stored_workflow.latest_workflow.steps) + item['show_in_tool_panel'] = False + for x in user.stored_workflow_menu_entries: + if x.stored_workflow_id == wf_sa.id: + item['show_in_tool_panel'] = True + break rval.append(item) if missing_tools: workflows_missing_tools = [] @@ -151,29 +163,29 @@ def get_workflows_list( self, trans, for_menu, kwd ): workflows_by_toolshed = dict() for key, value in enumerate(rval): tool_ids = [] - workflow_details = self.workflow_contents_manager.workflow_to_dict( trans, self.__get_stored_workflow( trans, value[ 'id' ] ), style='instance' ) + workflow_details = self.workflow_contents_manager.workflow_to_dict(trans, self.__get_stored_workflow(trans, value['id']), style='instance') if 'steps' in workflow_details: - for step in workflow_details[ 'steps' ]: - tool_id = workflow_details[ 'steps' ][ step ][ 'tool_id' ] - if tool_id not in tool_ids and self.app.toolbox.is_missing_shed_tool( tool_id ): - tool_ids.append( tool_id ) - if len( tool_ids ) > 0: - value[ 'missing_tools' ] = tool_ids - workflows_missing_tools.append( value ) + for step in workflow_details['steps']: + tool_id = workflow_details['steps'][step]['tool_id'] + if tool_id not in tool_ids and self.app.toolbox.is_missing_shed_tool(tool_id): + tool_ids.append(tool_id) + if len(tool_ids) > 0: + value['missing_tools'] = tool_ids + workflows_missing_tools.append(value) for workflow in workflows_missing_tools: - for tool_id in workflow[ 'missing_tools' ]: - toolshed, _, owner, name, tool, version = tool_id.split( '/' ) - shed_url = self.__get_full_shed_url( toolshed ) - repo_identifier = '/'.join( [ toolshed, owner, name ] ) + for tool_id in workflow['missing_tools']: + toolshed, _, owner, name, tool, version = tool_id.split('/') + shed_url = self.__get_full_shed_url(toolshed) + repo_identifier = '/'.join([toolshed, owner, name]) if repo_identifier not in workflows_by_toolshed: - workflows_by_toolshed[ repo_identifier ] = dict( shed=shed_url.rstrip('/'), repository=name, owner=owner, tools=[ tool_id ], workflows=[ workflow[ 'name' ] ] ) + workflows_by_toolshed[repo_identifier] = dict(shed=shed_url.rstrip('/'), repository=name, owner=owner, tools=[tool_id], workflows=[workflow['name']]) else: - if tool_id not in workflows_by_toolshed[ repo_identifier ][ 'tools' ]: - workflows_by_toolshed[ repo_identifier ][ 'tools' ].append( tool_id ) - if workflow[ 'name' ] not in workflows_by_toolshed[ repo_identifier ][ 'workflows' ]: - workflows_by_toolshed[ repo_identifier ][ 'workflows' ].append( workflow[ 'name' ] ) + if tool_id not in workflows_by_toolshed[repo_identifier]['tools']: + workflows_by_toolshed[repo_identifier]['tools'].append(tool_id) + if workflow['name'] not in workflows_by_toolshed[repo_identifier]['workflows']: + workflows_by_toolshed[repo_identifier]['workflows'].append(workflow['name']) for repo_tag in workflows_by_toolshed: - workflows.append( workflows_by_toolshed[ repo_tag ] ) + workflows.append(workflows_by_toolshed[repo_tag]) return workflows return rval @@ -184,16 +196,16 @@ def show(self, trans, id, **kwd): Displays information needed to run a workflow from the command line. """ - stored_workflow = self.__get_stored_workflow( trans, id ) + stored_workflow = self.__get_stored_workflow(trans, id) if stored_workflow.importable is False and stored_workflow.user != trans.user and not trans.user_is_admin(): if trans.sa_session.query(trans.app.model.StoredWorkflowUserShareAssociation).filter_by(user=trans.user, stored_workflow=stored_workflow).count() == 0: message = "Workflow is neither importable, nor owned by or shared with current user" - raise exceptions.ItemAccessibilityException( message ) + raise exceptions.ItemAccessibilityException(message) if kwd.get("legacy", False): style = "legacy" else: style = "instance" - return self.workflow_contents_manager.workflow_to_dict( trans, stored_workflow, style=style ) + return self.workflow_contents_manager.workflow_to_dict(trans, stored_workflow, style=style) @expose_api def create(self, trans, payload, **kwd): @@ -245,37 +257,37 @@ def create(self, trans, payload, **kwd): :param allow_tool_state_corrections: If set to True, any Tool parameter changes will not prevent running workflow, defaults to False :type allow_tool_state_corrections: bool """ - ways_to_create = set( [ + ways_to_create = set([ 'workflow_id', 'installed_repository_file', 'from_history_id', 'shared_workflow_id', 'workflow', - ] ) - if len( ways_to_create.intersection( payload ) ) == 0: - message = "One parameter among - %s - must be specified" % ", ".join( ways_to_create ) - raise exceptions.RequestParameterMissingException( message ) + ]) + if len(ways_to_create.intersection(payload)) == 0: + message = "One parameter among - %s - must be specified" % ", ".join(ways_to_create) + raise exceptions.RequestParameterMissingException(message) - if len( ways_to_create.intersection( payload ) ) > 1: - message = "Only one parameter among - %s - must be specified" % ", ".join( ways_to_create ) - raise exceptions.RequestParameterInvalidException( message ) + if len(ways_to_create.intersection(payload)) > 1: + message = "Only one parameter among - %s - must be specified" % ", ".join(ways_to_create) + raise exceptions.RequestParameterInvalidException(message) if 'installed_repository_file' in payload: - workflow_controller = trans.webapp.controllers[ 'workflow' ] - result = workflow_controller.import_workflow( trans=trans, - cntrller='api', - **payload) + workflow_controller = trans.webapp.controllers['workflow'] + result = workflow_controller.import_workflow(trans=trans, + cntrller='api', + **payload) return result if 'from_history_id' in payload: - from_history_id = payload.get( 'from_history_id' ) - from_history_id = self.decode_id( from_history_id ) - history = self.history_manager.get_accessible( from_history_id, trans.user, current_history=trans.history ) - - job_ids = [ self.decode_id(_) for _ in payload.get( 'job_ids', [] ) ] - dataset_ids = payload.get( 'dataset_ids', [] ) - dataset_collection_ids = payload.get( 'dataset_collection_ids', [] ) - workflow_name = payload[ 'workflow_name' ] + from_history_id = payload.get('from_history_id') + from_history_id = self.decode_id(from_history_id) + history = self.history_manager.get_accessible(from_history_id, trans.user, current_history=trans.history) + + job_ids = [self.decode_id(_) for _ in payload.get('job_ids', [])] + dataset_ids = payload.get('dataset_ids', []) + dataset_collection_ids = payload.get('dataset_collection_ids', []) + workflow_name = payload['workflow_name'] stored_workflow = extract_workflow( trans=trans, user=trans.get_user(), @@ -285,27 +297,27 @@ def create(self, trans, payload, **kwd): dataset_collection_ids=dataset_collection_ids, workflow_name=workflow_name, ) - item = stored_workflow.to_dict( value_mapper={ 'id': trans.security.encode_id } ) - item[ 'url' ] = url_for( 'workflow', id=item[ 'id' ] ) + item = stored_workflow.to_dict(value_mapper={'id': trans.security.encode_id}) + item['url'] = url_for('workflow', id=item['id']) return item if 'shared_workflow_id' in payload: - workflow_id = payload[ 'shared_workflow_id' ] - return self.__api_import_shared_workflow( trans, workflow_id, payload ) + workflow_id = payload['shared_workflow_id'] + return self.__api_import_shared_workflow(trans, workflow_id, payload) if 'workflow' in payload: - return self.__api_import_new_workflow( trans, payload, **kwd ) + return self.__api_import_new_workflow(trans, payload, **kwd) - workflow_id = payload.get( 'workflow_id', None ) + workflow_id = payload.get('workflow_id', None) if not workflow_id: message = "Invalid workflow_id specified." - raise exceptions.RequestParameterInvalidException( message ) + raise exceptions.RequestParameterInvalidException(message) # Get workflow + accessibility check. - stored_workflow = self.__get_stored_accessible_workflow( trans, workflow_id ) + stored_workflow = self.__get_stored_accessible_workflow(trans, workflow_id) workflow = stored_workflow.latest_workflow - run_configs = build_workflow_run_configs( trans, workflow, payload ) + run_configs = build_workflow_run_configs(trans, workflow, payload) assert len(run_configs) == 1 run_config = run_configs[0] history = run_config.target_history @@ -323,38 +335,38 @@ def create(self, trans, payload, **kwd): # Build legacy output - should probably include more information from # outputs. rval = {} - rval['history'] = trans.security.encode_id( history.id ) + rval['history'] = trans.security.encode_id(history.id) rval['outputs'] = [] for step in workflow.steps: if step.type == 'tool' or step.type is None: - for v in outputs[ step.id ].values(): - rval[ 'outputs' ].append( trans.security.encode_id( v.id ) ) + for v in outputs[step.id].values(): + rval['outputs'].append(trans.security.encode_id(v.id)) # Newer version of this API just returns the invocation as a dict, to # facilitate migration - produce the newer style response and blend in # the older information. - invocation_response = self.__encode_invocation( trans, invocation, step_details=kwd.get('step_details', False) ) - invocation_response.update( rval ) + invocation_response = self.__encode_invocation(trans, invocation, step_details=kwd.get('step_details', False)) + invocation_response.update(rval) return invocation_response @expose_api - def workflow_dict( self, trans, workflow_id, **kwd ): + def workflow_dict(self, trans, workflow_id, **kwd): """ GET /api/workflows/{encoded_workflow_id}/download Returns a selected workflow as a json dictionary. """ - stored_workflow = self.__get_stored_accessible_workflow( trans, workflow_id ) + stored_workflow = self.__get_stored_accessible_workflow(trans, workflow_id) style = kwd.get("style", "export") - ret_dict = self.workflow_contents_manager.workflow_to_dict( trans, stored_workflow, style=style ) + ret_dict = self.workflow_contents_manager.workflow_to_dict(trans, stored_workflow, style=style) if not ret_dict: # This workflow has a tool that's missing from the distribution message = "Workflow cannot be exported due to missing tools." - raise exceptions.MessageException( message ) + raise exceptions.MessageException(message) return ret_dict @expose_api - def delete( self, trans, id, **kwd ): + def delete(self, trans, id, **kwd): """ DELETE /api/workflows/{encoded_workflow_id} Deletes a specified workflow @@ -368,7 +380,7 @@ def delete( self, trans, id, **kwd ): stored_workflow = trans.sa_session.query(self.app.model.StoredWorkflow).get(self.decode_id(workflow_id)) except Exception as e: trans.response.status = 400 - return ("Workflow with ID='%s' can not be found\n Exception: %s") % (workflow_id, str( e )) + return ("Workflow with ID='%s' can not be found\n Exception: %s") % (workflow_id, str(e)) # check to see if user has permissions to selected workflow if stored_workflow.user != trans.user and not trans.user_is_admin(): @@ -380,7 +392,7 @@ def delete( self, trans, id, **kwd ): trans.sa_session.flush() # TODO: Unsure of response message to let api know that a workflow was successfully deleted - return ( "Workflow '%s' successfully deleted" % stored_workflow.name ) + return ("Workflow '%s' successfully deleted" % stored_workflow.name) @expose_api def import_new_workflow_deprecated(self, trans, payload, **kwd): @@ -394,10 +406,10 @@ def import_new_workflow_deprecated(self, trans, payload, **kwd): Deprecated in favor to POST /api/workflows with encoded 'workflow' in payload the same way. """ - return self.__api_import_new_workflow( trans, payload, **kwd ) + return self.__api_import_new_workflow(trans, payload, **kwd) @expose_api - def update( self, trans, id, payload, **kwds ): + def update(self, trans, id, payload, **kwds): """ * PUT /api/workflows/{id} updates the workflow stored with ``id`` @@ -423,7 +435,7 @@ def update( self, trans, id, payload, **kwds ): :rtype: dict :returns: serialized version of the workflow """ - stored_workflow = self.__get_stored_workflow( trans, id ) + stored_workflow = self.__get_stored_workflow(trans, id) if 'workflow' in payload: stored_workflow.name = sanitize_html(payload['name']) if ('name' in payload) else stored_workflow.name @@ -446,69 +458,73 @@ def update( self, trans, id, payload, **kwds ): workflow, errors = self.workflow_contents_manager.update_workflow_from_dict( trans, stored_workflow, - payload[ 'workflow' ], + payload['workflow'], ) except workflows.MissingToolsException: - raise exceptions.MessageException( "This workflow contains missing tools. It cannot be saved until they have been removed from the workflow or installed." ) + raise exceptions.MessageException("This workflow contains missing tools. It cannot be saved until they have been removed from the workflow or installed.") else: message = "Updating workflow requires dictionary containing 'workflow' attribute with new JSON description." - raise exceptions.RequestParameterInvalidException( message ) - return self.workflow_contents_manager.workflow_to_dict( trans, stored_workflow, style="instance" ) + raise exceptions.RequestParameterInvalidException(message) + return self.workflow_contents_manager.workflow_to_dict(trans, stored_workflow, style="instance") @expose_api - def build_module( self, trans, payload={} ): + def build_module(self, trans, payload={}): """ POST /api/workflows/build_module Builds module models for the workflow editor. """ - inputs = payload.get( 'inputs', {} ) - module = module_factory.from_dict( trans, payload ) + inputs = payload.get('inputs', {}) + module = module_factory.from_dict(trans, payload) module_state = {} - populate_state( trans, module.get_inputs(), inputs, module_state, check=False ) - module.recover_state( module_state ) + populate_state(trans, module.get_inputs(), inputs, module_state, check=False) + module.recover_state(module_state) return { - 'label' : inputs.get( '__label', '' ), - 'annotation' : inputs.get( '__annotation', '' ), + 'label' : inputs.get('__label', ''), + 'annotation' : inputs.get('__annotation', ''), 'name' : module.get_name(), 'tool_state' : module.get_state(), 'data_inputs' : module.get_data_inputs(), 'data_outputs' : module.get_data_outputs(), 'config_form' : module.get_config_form(), - 'post_job_actions' : module.get_post_job_actions( inputs ) + 'post_job_actions' : module.get_post_job_actions(inputs) } # # -- Helper methods -- # - def _get_tool( self, id, tool_version=None, user=None ): - id = unquote_plus( id ) - tool = self.app.toolbox.get_tool( id, tool_version ) - if not tool or not tool.allow_user_access( user ): + def _get_tool(self, id, tool_version=None, user=None): + id = unquote_plus(id) + tool = self.app.toolbox.get_tool(id, tool_version) + if not tool or not tool.allow_user_access(user): raise exceptions.ObjectNotFound("Could not find tool with id '%s'" % id) return tool - def __api_import_new_workflow( self, trans, payload, **kwd ): + def __api_import_new_workflow(self, trans, payload, **kwd): data = payload['workflow'] - publish = util.string_as_bool( payload.get( "publish", False ) ) + import_tools = util.string_as_bool(payload.get("import_tools", False)) + if import_tools and not trans.user_is_admin(): + raise exceptions.AdminRequiredException() + + publish = util.string_as_bool(payload.get("publish", False)) # If 'publish' set, default to importable. - importable = util.string_as_bool( payload.get( "importable", publish ) ) + importable = util.string_as_bool(payload.get("importable", publish)) # Galaxy will try to upgrade tool versions that don't match exactly during import, # this prevents that. - exact_tools = util.string_as_bool( payload.get( "exact_tools", False ) ) + exact_tools = util.string_as_bool(payload.get("exact_tools", False)) if publish and not importable: - raise exceptions.RequestParameterInvalidException( "Published workflow must be importable." ) + raise exceptions.RequestParameterInvalidException("Published workflow must be importable.") from_dict_kwds = dict( source="API", publish=publish, exact_tools=exact_tools, ) - workflow, missing_tool_tups = self._workflow_from_dict( trans, data, **from_dict_kwds ) + workflow, missing_tool_tups = self._workflow_from_dict(trans, data, **from_dict_kwds) if importable: - self._make_item_accessible( trans.sa_session, workflow ) + self._make_item_accessible(trans.sa_session, workflow) trans.sa_session.flush() # galaxy workflow newly created id @@ -524,6 +540,32 @@ def __api_import_new_workflow( self, trans, payload, **kwd ): rval.append(item) + # + if import_tools: + tools = {} + for key in data['steps']: + item = data['steps'][key] + if item is not None: + if 'tool_shed_repository' in item: + tool_shed_repository = item['tool_shed_repository'] + if 'owner' in tool_shed_repository and 'changeset_revision' in tool_shed_repository and 'name' in tool_shed_repository and 'tool_shed' in tool_shed_repository: + toolstr = tool_shed_repository['owner'] \ + + tool_shed_repository['changeset_revision'] \ + + tool_shed_repository['name'] \ + + tool_shed_repository['tool_shed'] + tools[toolstr] = tool_shed_repository + irm = InstallRepositoryManager(self.app) + for k in tools: + item = tools[k] + tool_shed_url = 'https://' + item['tool_shed'] + '/' + name = item['name'] + owner = item['owner'] + changeset_revision = item['changeset_revision'] + irm.install(tool_shed_url, + name, + owner, + changeset_revision, + payload) return item @expose_api @@ -540,26 +582,26 @@ def import_shared_workflow_deprecated(self, trans, payload, **kwd): # Pull parameters out of payload. workflow_id = payload.get('workflow_id', None) if workflow_id is None: - raise exceptions.ObjectAttributeMissingException( "Missing required parameter 'workflow_id'." ) - self.__api_import_shared_workflow( trans, workflow_id, payload ) + raise exceptions.ObjectAttributeMissingException("Missing required parameter 'workflow_id'.") + self.__api_import_shared_workflow(trans, workflow_id, payload) - def __api_import_shared_workflow( self, trans, workflow_id, payload, **kwd ): + def __api_import_shared_workflow(self, trans, workflow_id, payload, **kwd): try: - stored_workflow = self.get_stored_workflow( trans, workflow_id, check_ownership=False ) + stored_workflow = self.get_stored_workflow(trans, workflow_id, check_ownership=False) except: - raise exceptions.ObjectNotFound( "Malformed workflow id ( %s ) specified." % workflow_id ) + raise exceptions.ObjectNotFound("Malformed workflow id ( %s ) specified." % workflow_id) if stored_workflow.importable is False: - raise exceptions.ItemAccessibilityException( 'The owner of this workflow has disabled imports via this link.' ) + raise exceptions.ItemAccessibilityException('The owner of this workflow has disabled imports via this link.') elif stored_workflow.deleted: - raise exceptions.ItemDeletionException( "You can't import this workflow because it has been deleted." ) - imported_workflow = self._import_shared_workflow( trans, stored_workflow ) - item = imported_workflow.to_dict( value_mapper={ 'id': trans.security.encode_id } ) + raise exceptions.ItemDeletionException("You can't import this workflow because it has been deleted.") + imported_workflow = self._import_shared_workflow(trans, stored_workflow) + item = imported_workflow.to_dict(value_mapper={'id': trans.security.encode_id}) encoded_id = trans.security.encode_id(imported_workflow.id) item['url'] = url_for('workflow', id=encoded_id) return item @expose_api - def invoke( self, trans, workflow_id, payload, **kwd ): + def invoke(self, trans, workflow_id, payload, **kwd): """ POST /api/workflows/{encoded_workflow_id}/invocations @@ -606,11 +648,11 @@ def index_invocations(self, trans, workflow_id, **kwd): :raises: exceptions.MessageException, exceptions.ObjectNotFound """ - stored_workflow = self.__get_stored_workflow( trans, workflow_id ) - results = self.workflow_manager.build_invocations_query( trans, stored_workflow.id ) + stored_workflow = self.__get_stored_workflow(trans, workflow_id) + results = self.workflow_manager.build_invocations_query(trans, stored_workflow.id) out = [] for r in results: - out.append( self.__encode_invocation( trans, r, view="collection" ) ) + out.append(self.__encode_invocation(trans, r, view="collection")) return out @expose_api @@ -627,10 +669,10 @@ def show_invocation(self, trans, workflow_id, invocation_id, **kwd): :raises: exceptions.MessageException, exceptions.ObjectNotFound """ - decoded_workflow_invocation_id = self.decode_id( invocation_id ) - workflow_invocation = self.workflow_manager.get_invocation( trans, decoded_workflow_invocation_id ) + decoded_workflow_invocation_id = self.decode_id(invocation_id) + workflow_invocation = self.workflow_manager.get_invocation(trans, decoded_workflow_invocation_id) if workflow_invocation: - return self.__encode_invocation( trans, workflow_invocation, step_details=kwd.get('step_details', False) ) + return self.__encode_invocation(trans, workflow_invocation, step_details=kwd.get('step_details', False)) return None @expose_api @@ -647,9 +689,9 @@ def cancel_invocation(self, trans, workflow_id, invocation_id, **kwd): :raises: exceptions.MessageException, exceptions.ObjectNotFound """ - decoded_workflow_invocation_id = self.decode_id( invocation_id ) - workflow_invocation = self.workflow_manager.cancel_invocation( trans, decoded_workflow_invocation_id ) - return self.__encode_invocation( trans, workflow_invocation ) + decoded_workflow_invocation_id = self.decode_id(invocation_id) + workflow_invocation = self.workflow_manager.cancel_invocation(trans, decoded_workflow_invocation_id) + return self.__encode_invocation(trans, workflow_invocation) @expose_api def invocation_step(self, trans, workflow_id, invocation_id, step_id, **kwd): @@ -670,12 +712,12 @@ def invocation_step(self, trans, workflow_id, invocation_id, step_id, **kwd): :raises: exceptions.MessageException, exceptions.ObjectNotFound """ - decoded_invocation_step_id = self.decode_id( step_id ) + decoded_invocation_step_id = self.decode_id(step_id) invocation_step = self.workflow_manager.get_invocation_step( trans, decoded_invocation_step_id ) - return self.__encode_invocation_step( trans, invocation_step ) + return self.__encode_invocation_step(trans, invocation_step) @expose_api def update_invocation_step(self, trans, workflow_id, invocation_id, step_id, payload, **kwd): @@ -697,32 +739,32 @@ def update_invocation_step(self, trans, workflow_id, invocation_id, step_id, pay :raises: exceptions.MessageException, exceptions.ObjectNotFound """ - decoded_invocation_step_id = self.decode_id( step_id ) - action = payload.get( "action", None ) + decoded_invocation_step_id = self.decode_id(step_id) + action = payload.get("action", None) invocation_step = self.workflow_manager.update_invocation_step( trans, decoded_invocation_step_id, action=action, ) - return self.__encode_invocation_step( trans, invocation_step ) + return self.__encode_invocation_step(trans, invocation_step) - def __encode_invocation_step( self, trans, invocation_step ): + def __encode_invocation_step(self, trans, invocation_step): return self.encode_all_ids( trans, - invocation_step.to_dict( 'element' ), + invocation_step.to_dict('element'), True ) - def __get_stored_accessible_workflow( self, trans, workflow_id ): - return self.workflow_manager.get_stored_accessible_workflow( trans, workflow_id ) + def __get_stored_accessible_workflow(self, trans, workflow_id): + return self.workflow_manager.get_stored_accessible_workflow(trans, workflow_id) - def __get_stored_workflow( self, trans, workflow_id ): - return self.workflow_manager.get_stored_workflow( trans, workflow_id ) + def __get_stored_workflow(self, trans, workflow_id): + return self.workflow_manager.get_stored_workflow(trans, workflow_id) - def __encode_invocation( self, trans, invocation, view="element", step_details=False ): + def __encode_invocation(self, trans, invocation, view="element", step_details=False): return self.encode_all_ids( trans, - invocation.to_dict( view, step_details=step_details ), + invocation.to_dict(view, step_details=step_details), True ) diff --git a/lib/galaxy/webapps/galaxy/buildapp.py b/lib/galaxy/webapps/galaxy/buildapp.py index 4a6fd90bb9ad..d009bb6a9f9c 100644 --- a/lib/galaxy/webapps/galaxy/buildapp.py +++ b/lib/galaxy/webapps/galaxy/buildapp.py @@ -31,18 +31,18 @@ from paste import httpexceptions import logging -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class GalaxyWebApplication( galaxy.web.framework.webapp.WebApplication ): +class GalaxyWebApplication(galaxy.web.framework.webapp.WebApplication): pass -def app_factory( global_conf, **kwargs ): - return paste_app_factory( global_conf, **kwargs ) +def app_factory(global_conf, **kwargs): + return paste_app_factory(global_conf, **kwargs) -def paste_app_factory( global_conf, **kwargs ): +def paste_app_factory(global_conf, **kwargs): """ Return a wsgi application serving the root object """ @@ -51,85 +51,92 @@ def paste_app_factory( global_conf, **kwargs ): ) # Create the Galaxy application unless passed in if 'app' in kwargs: - app = kwargs.pop( 'app' ) + app = kwargs.pop('app') galaxy.app.app = app else: try: - app = galaxy.app.UniverseApplication( global_conf=global_conf, **kwargs ) + app = galaxy.app.UniverseApplication(global_conf=global_conf, **kwargs) galaxy.app.app = app except: import traceback traceback.print_exc() - sys.exit( 1 ) + sys.exit(1) # Call app's shutdown method when the interpeter exits, this cleanly stops # the various Galaxy application daemon threads - atexit.register( app.shutdown ) + atexit.register(app.shutdown) # Create the universe WSGI application - webapp = GalaxyWebApplication( app, session_cookie='galaxysession', name='galaxy' ) + webapp = GalaxyWebApplication(app, session_cookie='galaxysession', name='galaxy') # STANDARD CONTROLLER ROUTES - webapp.add_ui_controllers( 'galaxy.webapps.galaxy.controllers', app ) + webapp.add_ui_controllers('galaxy.webapps.galaxy.controllers', app) # Force /history to go to view of current - webapp.add_route( '/history', controller='history', action='view' ) - webapp.add_route( '/history/view/{id}', controller='history', action='view' ) + webapp.add_route('/history', controller='history', action='view') + webapp.add_route('/history/view/{id}', controller='history', action='view') # Force /activate to go to the controller - webapp.add_route( '/activate', controller='user', action='activate' ) + webapp.add_route('/activate', controller='user', action='activate') # These two routes handle our simple needs at the moment - webapp.add_route( '/async/{tool_id}/{data_id}/{data_secret}', controller='async', action='index', tool_id=None, data_id=None, data_secret=None ) - webapp.add_route( '/{controller}/{action}', action='index' ) - webapp.add_route( '/{action}', controller='root', action='index' ) + webapp.add_route('/async/{tool_id}/{data_id}/{data_secret}', controller='async', action='index', tool_id=None, data_id=None, data_secret=None) + webapp.add_route('/{controller}/{action}', action='index') + webapp.add_route('/{action}', controller='root', action='index') # allow for subdirectories in extra_files_path - webapp.add_route( '/datasets/{dataset_id}/display/{filename:.+?}', controller='dataset', action='display', dataset_id=None, filename=None) - webapp.add_route( '/datasets/{dataset_id}/{action}/{filename}', controller='dataset', action='index', dataset_id=None, filename=None) - webapp.add_route( '/display_application/{dataset_id}/{app_name}/{link_name}/{user_id}/{app_action}/{action_param}/{action_param_extra:.+?}', - controller='dataset', action='display_application', dataset_id=None, user_id=None, - app_name=None, link_name=None, app_action=None, action_param=None, action_param_extra=None ) - webapp.add_route( '/u/{username}/d/{slug}/{filename}', controller='dataset', action='display_by_username_and_slug', filename=None ) - webapp.add_route( '/u/{username}/p/{slug}', controller='page', action='display_by_username_and_slug' ) - webapp.add_route( '/u/{username}/h/{slug}', controller='history', action='display_by_username_and_slug' ) - webapp.add_route( '/u/{username}/w/{slug}', controller='workflow', action='display_by_username_and_slug' ) - webapp.add_route( '/u/{username}/w/{slug}/{format}', controller='workflow', action='display_by_username_and_slug' ) - webapp.add_route( '/u/{username}/v/{slug}', controller='visualization', action='display_by_username_and_slug' ) + webapp.add_route('/datasets/{dataset_id}/display/{filename:.+?}', controller='dataset', action='display', dataset_id=None, filename=None) + webapp.add_route('/datasets/{dataset_id}/{action}/{filename}', controller='dataset', action='index', dataset_id=None, filename=None) + webapp.add_route('/display_application/{dataset_id}/{app_name}/{link_name}/{user_id}/{app_action}/{action_param}/{action_param_extra:.+?}', + controller='dataset', action='display_application', dataset_id=None, user_id=None, + app_name=None, link_name=None, app_action=None, action_param=None, action_param_extra=None) + webapp.add_route('/u/{username}/d/{slug}/{filename}', controller='dataset', action='display_by_username_and_slug', filename=None) + webapp.add_route('/u/{username}/p/{slug}', controller='page', action='display_by_username_and_slug') + webapp.add_route('/u/{username}/h/{slug}', controller='history', action='display_by_username_and_slug') + webapp.add_route('/u/{username}/w/{slug}', controller='workflow', action='display_by_username_and_slug') + webapp.add_route('/u/{username}/w/{slug}/{format}', controller='workflow', action='display_by_username_and_slug') + webapp.add_route('/u/{username}/v/{slug}', controller='visualization', action='display_by_username_and_slug') # TODO: Refactor above routes into external method to allow testing in # isolation as well. - populate_api_routes( webapp, app ) + populate_api_routes(webapp, app) # CLIENTSIDE ROUTES # The following are routes that are handled completely on the clientside. # The following routes don't bootstrap any information, simply provide the # base analysis interface at which point the application takes over. - webapp.add_client_route( '/tours' ) - webapp.add_client_route( '/tours/{tour_id}' ) - webapp.add_client_route( '/user' ) - webapp.add_client_route( '/user/{form_id}' ) - webapp.add_client_route( '/workflow' ) - webapp.add_client_route( '/workflows/list_published' ) - webapp.add_client_route( '/visualizations/list_published' ) - webapp.add_client_route( '/visualizations/list' ) - webapp.add_client_route( '/pages/list' ) - webapp.add_client_route( '/pages/list_published' ) - webapp.add_client_route( '/histories/list' ) - webapp.add_client_route( '/histories/list_published' ) - webapp.add_client_route( '/histories/list_shared' ) - webapp.add_client_route( '/datasets/list' ) - webapp.add_client_route( '/workflow/run' ) - webapp.add_client_route( '/workflow/import_workflow' ) - webapp.add_client_route( '/workflow/configure_menu' ) - webapp.add_client_route( '/custom_builds' ) + webapp.add_client_route('/admin/users', 'admin') + webapp.add_client_route('/admin/roles', 'admin') + webapp.add_client_route('/admin/groups', 'admin') + webapp.add_client_route('/admin/tool_versions', 'admin') + webapp.add_client_route('/admin/quotas', 'admin') + webapp.add_client_route('/admin/forms/{form_id}', 'admin') + webapp.add_client_route('/tours') + webapp.add_client_route('/tours/{tour_id}') + webapp.add_client_route('/user') + webapp.add_client_route('/user/{form_id}') + webapp.add_client_route('/workflow') + webapp.add_client_route('/workflows/list_published') + webapp.add_client_route('/visualizations/list_published') + webapp.add_client_route('/visualizations/list') + webapp.add_client_route('/pages/list') + webapp.add_client_route('/pages/list_published') + webapp.add_client_route('/histories/list') + webapp.add_client_route('/histories/list_published') + webapp.add_client_route('/histories/list_shared') + webapp.add_client_route('/datasets/list') + webapp.add_client_route('/datasets/edit') + webapp.add_client_route('/datasets/error') + webapp.add_client_route('/workflow/run') + webapp.add_client_route('/workflow/import_workflow') + webapp.add_client_route('/custom_builds') # ==== Done # Indicate that all configuration settings have been provided webapp.finalize_config() # Wrap the webapp in some useful middleware - if kwargs.get( 'middleware', True ): + if kwargs.get('middleware', True): webapp = wrap_in_middleware(webapp, global_conf, app.application_stack, **kwargs) - if asbool( kwargs.get( 'static_enabled', True) ): + if asbool(kwargs.get('static_enabled', True)): webapp = wrap_if_allowed(webapp, app.application_stack, wrap_in_static, args=(global_conf,), kwargs=dict(plugin_frameworks=[app.visualizations_registry], **kwargs)) @@ -155,12 +162,14 @@ def paste_app_factory( global_conf, **kwargs ): def uwsgi_app_factory(): + # TODO: synchronize with galaxy.web.framework.webapp.build_native_uwsgi_app - should + # at least be using nice_config_parser for instance. import uwsgi root = os.path.abspath(uwsgi.opt.get('galaxy_root', os.getcwd())) config_file = uwsgi.opt.get('galaxy_config_file', os.path.join(root, 'config', 'galaxy.ini')) global_conf = { '__file__': config_file if os.path.exists(__file__) else None, - 'here': root } + 'here': root} parser = configparser.ConfigParser() parser.read(config_file) try: @@ -176,8 +185,8 @@ def postfork_setup(): app.control_worker.bind_and_start() -def populate_api_routes( webapp, app ): - webapp.add_api_controllers( 'galaxy.webapps.galaxy.api', app ) +def populate_api_routes(webapp, app): + webapp.add_api_controllers('galaxy.webapps.galaxy.api', app) valid_history_contents_types = [ 'dataset', @@ -185,127 +194,126 @@ def populate_api_routes( webapp, app ): ] # Accesss HDA details via histories/{history_id}/contents/datasets/{hda_id} - webapp.mapper.resource( "content_typed", - "{type:%s}s" % "|".join( valid_history_contents_types ), - name_prefix="history_", - controller='history_contents', - path_prefix='/api/histories/{history_id}/contents', - parent_resources=dict( member_name='history', collection_name='histories' ), - ) + webapp.mapper.resource("content_typed", + "{type:%s}s" % "|".join(valid_history_contents_types), + name_prefix="history_", + controller='history_contents', + path_prefix='/api/histories/{history_id}/contents', + parent_resources=dict(member_name='history', collection_name='histories')) # Legacy access to HDA details via histories/{history_id}/contents/{hda_id} - webapp.mapper.resource( 'content', - 'contents', - controller='history_contents', - name_prefix='history_', - path_prefix='/api/histories/{history_id}', - parent_resources=dict( member_name='history', collection_name='histories' ) ) - webapp.mapper.connect( "history_contents_display", - "/api/histories/{history_id}/contents/{history_content_id}/display", - controller="datasets", - action="display", - conditions=dict(method=["GET"])) - webapp.mapper.connect( "history_contents_metadata_file", - "/api/histories/{history_id}/contents/{history_content_id}/metadata_file", - controller="datasets", - action="get_metadata_file", - conditions=dict(method=["GET"])) - webapp.mapper.resource( 'user', - 'users', - controller='group_users', - name_prefix='group_', - path_prefix='/api/groups/{group_id}', - parent_resources=dict( member_name='group', collection_name='groups' ) ) - webapp.mapper.resource( 'role', - 'roles', - controller='group_roles', - name_prefix='group_', - path_prefix='/api/groups/{group_id}', - parent_resources=dict( member_name='group', collection_name='groups' ) ) - _add_item_tags_controller( webapp, - name_prefix="history_content_", - path_prefix='/api/histories/{history_id}/contents/{history_content_id}' ) - webapp.mapper.connect( '/api/histories/published', action='published', controller="histories", conditions=dict( method=[ "GET" ] ) ) - webapp.mapper.connect( '/api/histories/shared_with_me', action='shared_with_me', controller="histories" ) - _add_item_tags_controller( webapp, - name_prefix="history_", - path_prefix='/api/histories/{history_id}' ) - _add_item_tags_controller( webapp, - name_prefix="workflow_", - path_prefix='/api/workflows/{workflow_id}' ) - _add_item_annotation_controller( webapp, - name_prefix="history_content_", - path_prefix='/api/histories/{history_id}/contents/{history_content_id}' ) - _add_item_annotation_controller( webapp, - name_prefix="history_", - path_prefix='/api/histories/{history_id}' ) - _add_item_annotation_controller( webapp, - name_prefix="workflow_", - path_prefix='/api/workflows/{workflow_id}' ) - _add_item_provenance_controller( webapp, - name_prefix="history_content_", - path_prefix='/api/histories/{history_id}/contents/{history_content_id}' ) - - webapp.mapper.resource( 'dataset', 'datasets', path_prefix='/api' ) - webapp.mapper.resource( 'tool_data', 'tool_data', path_prefix='/api' ) - webapp.mapper.connect( '/api/tool_data/{id:.+?}/fields/{value:.+?}/files/{path:.+?}', action='download_field_file', controller="tool_data" ) - webapp.mapper.connect( '/api/tool_data/{id:.+?}/fields/{value:.+?}', action='show_field', controller="tool_data" ) - webapp.mapper.connect( '/api/tool_data/{id:.+?}/reload', action='reload', controller="tool_data" ) - webapp.mapper.resource( 'dataset_collection', 'dataset_collections', path_prefix='/api/') - webapp.mapper.resource( 'sample', 'samples', path_prefix='/api' ) - webapp.mapper.resource( 'request', 'requests', path_prefix='/api' ) - webapp.mapper.resource( 'form', 'forms', path_prefix='/api' ) - webapp.mapper.resource( 'request_type', 'request_types', path_prefix='/api' ) - webapp.mapper.resource( 'role', 'roles', path_prefix='/api' ) - webapp.mapper.connect( '/api/ftp_files', controller='remote_files' ) - webapp.mapper.resource( 'remote_file', 'remote_files', path_prefix='/api' ) - webapp.mapper.resource( 'group', 'groups', path_prefix='/api' ) - webapp.mapper.resource_with_deleted( 'quota', 'quotas', path_prefix='/api' ) - - webapp.mapper.connect( 'get_custom_builds_metadata', - '/api/histories/{id}/custom_builds_metadata', - controller='histories', - action='get_custom_builds_metadata', - conditions=dict( method=["GET"] ) ) + webapp.mapper.resource('content', + 'contents', + controller='history_contents', + name_prefix='history_', + path_prefix='/api/histories/{history_id}', + parent_resources=dict(member_name='history', collection_name='histories')) + webapp.mapper.connect("history_contents_display", + "/api/histories/{history_id}/contents/{history_content_id}/display", + controller="datasets", + action="display", + conditions=dict(method=["GET"])) + webapp.mapper.connect("history_contents_metadata_file", + "/api/histories/{history_id}/contents/{history_content_id}/metadata_file", + controller="datasets", + action="get_metadata_file", + conditions=dict(method=["GET"])) + webapp.mapper.resource('user', + 'users', + controller='group_users', + name_prefix='group_', + path_prefix='/api/groups/{group_id}', + parent_resources=dict(member_name='group', collection_name='groups')) + webapp.mapper.resource('role', + 'roles', + controller='group_roles', + name_prefix='group_', + path_prefix='/api/groups/{group_id}', + parent_resources=dict(member_name='group', collection_name='groups')) + _add_item_tags_controller(webapp, + name_prefix="history_content_", + path_prefix='/api/histories/{history_id}/contents/{history_content_id}') + webapp.mapper.connect('/api/histories/published', action='published', controller="histories", conditions=dict(method=["GET"])) + webapp.mapper.connect('/api/histories/shared_with_me', action='shared_with_me', controller="histories") + _add_item_tags_controller(webapp, + name_prefix="history_", + path_prefix='/api/histories/{history_id}') + _add_item_tags_controller(webapp, + name_prefix="workflow_", + path_prefix='/api/workflows/{workflow_id}') + _add_item_annotation_controller(webapp, + name_prefix="history_content_", + path_prefix='/api/histories/{history_id}/contents/{history_content_id}') + _add_item_annotation_controller(webapp, + name_prefix="history_", + path_prefix='/api/histories/{history_id}') + _add_item_annotation_controller(webapp, + name_prefix="workflow_", + path_prefix='/api/workflows/{workflow_id}') + _add_item_provenance_controller(webapp, + name_prefix="history_content_", + path_prefix='/api/histories/{history_id}/contents/{history_content_id}') + + webapp.mapper.resource('dataset', 'datasets', path_prefix='/api') + webapp.mapper.resource('tool_data', 'tool_data', path_prefix='/api') + webapp.mapper.connect('/api/tool_data/{id:.+?}/fields/{value:.+?}/files/{path:.+?}', action='download_field_file', controller="tool_data") + webapp.mapper.connect('/api/tool_data/{id:.+?}/fields/{value:.+?}', action='show_field', controller="tool_data") + webapp.mapper.connect('/api/tool_data/{id:.+?}/reload', action='reload', controller="tool_data") + webapp.mapper.resource('dataset_collection', 'dataset_collections', path_prefix='/api/') + webapp.mapper.resource('sample', 'samples', path_prefix='/api') + webapp.mapper.resource('request', 'requests', path_prefix='/api') + webapp.mapper.resource('form', 'forms', path_prefix='/api') + webapp.mapper.resource('request_type', 'request_types', path_prefix='/api') + webapp.mapper.resource('role', 'roles', path_prefix='/api') + webapp.mapper.connect('/api/ftp_files', controller='remote_files') + webapp.mapper.resource('remote_file', 'remote_files', path_prefix='/api') + webapp.mapper.resource('group', 'groups', path_prefix='/api') + webapp.mapper.resource_with_deleted('quota', 'quotas', path_prefix='/api') + + webapp.mapper.connect('get_custom_builds_metadata', + '/api/histories/{id}/custom_builds_metadata', + controller='histories', + action='get_custom_builds_metadata', + conditions=dict(method=["GET"])) # ======================= # ====== TOOLS API ====== # ======================= - webapp.mapper.connect( '/api/tools/all_requirements', action='all_requirements', controller="tools" ) - webapp.mapper.connect( '/api/tools/{id:.+?}/build', action='build', controller="tools" ) - webapp.mapper.connect( '/api/tools/{id:.+?}/reload', action='reload', controller="tools" ) - webapp.mapper.connect( '/api/tools/{id:.+?}/diagnostics', action='diagnostics', controller="tools" ) - webapp.mapper.connect( '/api/tools/{id:.+?}/citations', action='citations', controller="tools" ) - webapp.mapper.connect( '/api/tools/{id:.+?}/download', action='download', controller="tools" ) - webapp.mapper.connect( '/api/tools/{id:.+?}/requirements', action='requirements', controller="tools") - webapp.mapper.connect( '/api/tools/{id:.+?}/install_dependencies', action='install_dependencies', controller="tools", conditions=dict( method=[ "POST" ] )) - webapp.mapper.connect( '/api/tools/{id:.+?}/dependencies', action='install_dependencies', controller="tools", conditions=dict( method=[ "POST" ] )) - webapp.mapper.connect( '/api/tools/{id:.+?}/dependencies', action='uninstall_dependencies', controller="tools", conditions=dict( method=[ "DELETE" ] )) - webapp.mapper.connect( '/api/tools/{id:.+?}/build_dependency_cache', action='build_dependency_cache', controller="tools", conditions=dict( method=[ "POST" ] )) - webapp.mapper.connect( '/api/tools/{id:.+?}', action='show', controller="tools" ) - webapp.mapper.resource( 'tool', 'tools', path_prefix='/api' ) - - webapp.mapper.connect( '/api/dependency_resolvers/clean', action="clean", controller="tool_dependencies", conditions=dict( method=[ "POST" ]) ) - webapp.mapper.connect( '/api/dependency_resolvers/dependency', action="manager_dependency", controller="tool_dependencies", conditions=dict( method=[ "GET" ] ) ) - webapp.mapper.connect( '/api/dependency_resolvers/dependency', action="install_dependency", controller="tool_dependencies", conditions=dict( method=[ "POST" ] ) ) - webapp.mapper.connect( '/api/dependency_resolvers/requirements', action="manager_requirements", controller="tool_dependencies" ) - webapp.mapper.connect( '/api/dependency_resolvers/{id}/clean', action="clean", controller="tool_dependencies", conditions=dict( method=[ "POST" ]) ) - webapp.mapper.connect( '/api/dependency_resolvers/{id}/dependency', action="resolver_dependency", controller="tool_dependencies", conditions=dict( method=[ "GET" ] ) ) - webapp.mapper.connect( '/api/dependency_resolvers/{id}/dependency', action="install_dependency", controller="tool_dependencies", conditions=dict( method=[ "POST" ] ) ) - webapp.mapper.connect( '/api/dependency_resolvers/{id}/requirements', action="resolver_requirements", controller="tool_dependencies" ) - webapp.mapper.resource( 'dependency_resolver', 'dependency_resolvers', controller="tool_dependencies", path_prefix='api' ) - - webapp.mapper.resource_with_deleted( 'user', 'users', path_prefix='/api' ) - webapp.mapper.resource( 'genome', 'genomes', path_prefix='/api' ) - webapp.mapper.connect( '/api/genomes/{id}/indexes', controller='genomes', action='indexes' ) - webapp.mapper.connect( '/api/genomes/{id}/sequences', controller='genomes', action='sequences' ) - webapp.mapper.resource( 'visualization', 'visualizations', path_prefix='/api' ) - webapp.mapper.connect( '/api/workflows/build_module', action='build_module', controller="workflows" ) - webapp.mapper.connect( '/api/workflows/menu', action='get_workflow_menu', controller="workflows", conditions=dict( method=[ "GET" ] ) ) - webapp.mapper.connect( '/api/workflows/menu', action='set_workflow_menu', controller="workflows", conditions=dict( method=[ "PUT" ] ) ) - webapp.mapper.resource( 'workflow', 'workflows', path_prefix='/api' ) - webapp.mapper.resource_with_deleted( 'history', 'histories', path_prefix='/api' ) - webapp.mapper.connect( '/api/histories/{history_id}/citations', action='citations', controller="histories" ) + webapp.mapper.connect('/api/tools/all_requirements', action='all_requirements', controller="tools") + webapp.mapper.connect('/api/tools/{id:.+?}/build', action='build', controller="tools") + webapp.mapper.connect('/api/tools/{id:.+?}/reload', action='reload', controller="tools") + webapp.mapper.connect('/api/tools/{id:.+?}/diagnostics', action='diagnostics', controller="tools") + webapp.mapper.connect('/api/tools/{id:.+?}/citations', action='citations', controller="tools") + webapp.mapper.connect('/api/tools/{id:.+?}/download', action='download', controller="tools") + webapp.mapper.connect('/api/tools/{id:.+?}/requirements', action='requirements', controller="tools") + webapp.mapper.connect('/api/tools/{id:.+?}/install_dependencies', action='install_dependencies', controller="tools", conditions=dict(method=["POST"])) + webapp.mapper.connect('/api/tools/{id:.+?}/dependencies', action='install_dependencies', controller="tools", conditions=dict(method=["POST"])) + webapp.mapper.connect('/api/tools/{id:.+?}/dependencies', action='uninstall_dependencies', controller="tools", conditions=dict(method=["DELETE"])) + webapp.mapper.connect('/api/tools/{id:.+?}/build_dependency_cache', action='build_dependency_cache', controller="tools", conditions=dict(method=["POST"])) + webapp.mapper.connect('/api/tools/{id:.+?}', action='show', controller="tools") + webapp.mapper.resource('tool', 'tools', path_prefix='/api') + + webapp.mapper.connect('/api/dependency_resolvers/clean', action="clean", controller="tool_dependencies", conditions=dict(method=["POST"])) + webapp.mapper.connect('/api/dependency_resolvers/dependency', action="manager_dependency", controller="tool_dependencies", conditions=dict(method=["GET"])) + webapp.mapper.connect('/api/dependency_resolvers/dependency', action="install_dependency", controller="tool_dependencies", conditions=dict(method=["POST"])) + webapp.mapper.connect('/api/dependency_resolvers/requirements', action="manager_requirements", controller="tool_dependencies") + webapp.mapper.connect('/api/dependency_resolvers/{id}/clean', action="clean", controller="tool_dependencies", conditions=dict(method=["POST"])) + webapp.mapper.connect('/api/dependency_resolvers/{id}/dependency', action="resolver_dependency", controller="tool_dependencies", conditions=dict(method=["GET"])) + webapp.mapper.connect('/api/dependency_resolvers/{id}/dependency', action="install_dependency", controller="tool_dependencies", conditions=dict(method=["POST"])) + webapp.mapper.connect('/api/dependency_resolvers/{id}/requirements', action="resolver_requirements", controller="tool_dependencies") + webapp.mapper.resource('dependency_resolver', 'dependency_resolvers', controller="tool_dependencies", path_prefix='api') + + webapp.mapper.resource_with_deleted('user', 'users', path_prefix='/api') + webapp.mapper.resource('genome', 'genomes', path_prefix='/api') + webapp.mapper.connect('/api/genomes/{id}/indexes', controller='genomes', action='indexes') + webapp.mapper.connect('/api/genomes/{id}/sequences', controller='genomes', action='sequences') + webapp.mapper.resource('visualization', 'visualizations', path_prefix='/api') + webapp.mapper.connect('/api/workflows/build_module', action='build_module', controller="workflows") + webapp.mapper.connect('/api/workflows/menu', action='get_workflow_menu', controller="workflows", conditions=dict(method=["GET"])) + webapp.mapper.connect('/api/workflows/menu', action='set_workflow_menu', controller="workflows", conditions=dict(method=["PUT"])) + webapp.mapper.resource('workflow', 'workflows', path_prefix='/api') + webapp.mapper.resource_with_deleted('history', 'histories', path_prefix='/api') + webapp.mapper.connect('/api/histories/{history_id}/citations', action='citations', controller="histories") webapp.mapper.connect( 'dynamic_tool_confs', '/api/configuration/dynamic_tool_confs', @@ -322,39 +330,39 @@ def populate_api_routes( webapp, app ): '/api/configuration/toolbox', controller="configuration", action="reload_toolbox", - conditions=dict( method=["PUT"] ) + conditions=dict(method=["PUT"]) ) - webapp.mapper.resource( 'configuration', 'configuration', path_prefix='/api' ) - webapp.mapper.connect( "configuration_version", - "/api/version", controller="configuration", - action="version", conditions=dict( method=[ "GET" ] ) ) - webapp.mapper.connect( "api_whoami", - "/api/whoami", controller='configuration', - action='whoami', - conditions=dict( method=[ "GET" ] ) ) - webapp.mapper.resource( 'datatype', - 'datatypes', - path_prefix='/api', - collection={ 'sniffers': 'GET', 'mapping': 'GET', 'converters': 'GET', 'edam_data': 'GET', 'edam_formats': 'GET' }, - parent_resources=dict( member_name='datatype', collection_name='datatypes' ) ) - webapp.mapper.resource( 'search', 'search', path_prefix='/api' ) - webapp.mapper.resource( 'page', 'pages', path_prefix="/api") - webapp.mapper.resource( 'revision', 'revisions', - path_prefix='/api/pages/{page_id}', - controller='page_revisions', - parent_resources=dict( member_name='page', collection_name='pages' ) ) - - webapp.mapper.connect( "history_archive_export", - "/api/histories/{id}/exports", controller="histories", - action="archive_export", conditions=dict( method=[ "PUT" ] ) ) - webapp.mapper.connect( "history_archive_download", - "/api/histories/{id}/exports/{jeha_id}", controller="histories", - action="archive_download", conditions=dict( method=[ "GET" ] ) ) - - webapp.mapper.connect( '/api/histories/{history_id}/contents/archive', - controller='history_contents', action='archive') - webapp.mapper.connect( '/api/histories/{history_id}/contents/archive/{filename}{.format}', - controller='history_contents', action='archive') + webapp.mapper.resource('configuration', 'configuration', path_prefix='/api') + webapp.mapper.connect("configuration_version", + "/api/version", controller="configuration", + action="version", conditions=dict(method=["GET"])) + webapp.mapper.connect("api_whoami", + "/api/whoami", controller='configuration', + action='whoami', + conditions=dict(method=["GET"])) + webapp.mapper.resource('datatype', + 'datatypes', + path_prefix='/api', + collection={'sniffers': 'GET', 'mapping': 'GET', 'converters': 'GET', 'edam_data': 'GET', 'edam_formats': 'GET'}, + parent_resources=dict(member_name='datatype', collection_name='datatypes')) + webapp.mapper.resource('search', 'search', path_prefix='/api') + webapp.mapper.resource('page', 'pages', path_prefix="/api") + webapp.mapper.resource('revision', 'revisions', + path_prefix='/api/pages/{page_id}', + controller='page_revisions', + parent_resources=dict(member_name='page', collection_name='pages')) + + webapp.mapper.connect("history_archive_export", + "/api/histories/{id}/exports", controller="histories", + action="archive_export", conditions=dict(method=["PUT"])) + webapp.mapper.connect("history_archive_download", + "/api/histories/{id}/exports/{jeha_id}", controller="histories", + action="archive_download", conditions=dict(method=["GET"])) + + webapp.mapper.connect('/api/histories/{history_id}/contents/archive', + controller='history_contents', action='archive') + webapp.mapper.connect('/api/histories/{history_id}/contents/archive/{filename}{.format}', + controller='history_contents', action='archive') webapp.mapper.connect("/api/histories/{history_id}/contents/dataset_collections/{id}/download", controller='history_contents', action='download_dataset_collection', @@ -362,47 +370,47 @@ def populate_api_routes( webapp, app ): # ---- visualizations registry ---- generic template renderer # @deprecated: this route should be considered deprecated - webapp.add_route( '/visualization/show/{visualization_name}', controller='visualization', action='render', visualization_name=None ) + webapp.add_route('/visualization/show/{visualization_name}', controller='visualization', action='render', visualization_name=None) # provide an alternate route to visualization plugins that's closer to their static assets # (/plugins/visualizations/{visualization_name}/static) and allow them to use relative urls to those - webapp.mapper.connect( 'visualization_plugin', '/plugins/visualizations/{visualization_name}/show', - controller='visualization', action='render' ) - webapp.mapper.connect( 'saved_visualization', '/plugins/visualizations/{visualization_name}/saved', - controller='visualization', action='saved' ) + webapp.mapper.connect('visualization_plugin', '/plugins/visualizations/{visualization_name}/show', + controller='visualization', action='render') + webapp.mapper.connect('saved_visualization', '/plugins/visualizations/{visualization_name}/saved', + controller='visualization', action='saved') # same with IE's - webapp.mapper.connect( 'interactive_environment_plugin', '/plugins/interactive_environments/{visualization_name}/show', - controller='visualization', action='render' ) - webapp.mapper.connect( 'saved_interactive_environment', '/plugins/interactive_environments/{visualization_name}/saved', - controller='visualization', action='saved' ) + webapp.mapper.connect('interactive_environment_plugin', '/plugins/interactive_environments/{visualization_name}/show', + controller='visualization', action='render') + webapp.mapper.connect('saved_interactive_environment', '/plugins/interactive_environments/{visualization_name}/saved', + controller='visualization', action='saved') # Deprecated in favor of POST /api/workflows with 'workflow' in payload. - webapp.mapper.connect( 'import_workflow_deprecated', - '/api/workflows/upload', - controller='workflows', - action='import_new_workflow_deprecated', - conditions=dict( method=['POST'] ) ) - webapp.mapper.connect( 'workflow_dict', - '/api/workflows/{workflow_id}/download', - controller='workflows', - action='workflow_dict', - conditions=dict( method=['GET'] ) ) + webapp.mapper.connect('import_workflow_deprecated', + '/api/workflows/upload', + controller='workflows', + action='import_new_workflow_deprecated', + conditions=dict(method=['POST'])) + webapp.mapper.connect('workflow_dict', + '/api/workflows/{workflow_id}/download', + controller='workflows', + action='workflow_dict', + conditions=dict(method=['GET'])) # Preserve the following download route for now for dependent applications -- deprecate at some point - webapp.mapper.connect( 'workflow_dict', - '/api/workflows/download/{workflow_id}', - controller='workflows', - action='workflow_dict', - conditions=dict( method=['GET'] ) ) + webapp.mapper.connect('workflow_dict', + '/api/workflows/download/{workflow_id}', + controller='workflows', + action='workflow_dict', + conditions=dict(method=['GET'])) # Deprecated in favor of POST /api/workflows with shared_workflow_id in payload. - webapp.mapper.connect( 'import_shared_workflow_deprecated', - '/api/workflows/import', - controller='workflows', - action='import_shared_workflow_deprecated', - conditions=dict( method=['POST'] ) ) + webapp.mapper.connect('import_shared_workflow_deprecated', + '/api/workflows/import', + controller='workflows', + action='import_shared_workflow_deprecated', + conditions=dict(method=['POST'])) # route for creating/getting converted datasets - webapp.mapper.connect( '/api/datasets/{dataset_id}/converted', controller='datasets', action='converted', ext=None ) - webapp.mapper.connect( '/api/datasets/{dataset_id}/converted/{ext}', controller='datasets', action='converted' ) + webapp.mapper.connect('/api/datasets/{dataset_id}/converted', controller='datasets', action='converted', ext=None) + webapp.mapper.connect('/api/datasets/{dataset_id}/converted/{ext}', controller='datasets', action='converted') # API refers to usages and invocations - these mean the same thing but the # usage routes should be considered deprecated. @@ -457,416 +465,417 @@ def populate_api_routes( webapp, app ): '/api/workflows/{workflow_id}/%s' % noun, controller='workflows', action='invoke', - conditions=dict( method=['POST'] ) + conditions=dict(method=['POST']) ) # ============================ # ===== AUTHENTICATE API ===== # ============================ - webapp.mapper.connect( 'api_key_retrieval', - '/api/authenticate/baseauth/', - controller='authenticate', - action='get_api_key', - conditions=dict( method=[ "GET" ] ) ) + webapp.mapper.connect('api_key_retrieval', + '/api/authenticate/baseauth/', + controller='authenticate', + action='get_api_key', + conditions=dict(method=["GET"])) # ===================== # ===== TOURS API ===== # ===================== - webapp.mapper.connect( 'index', - '/api/tours', - controller='tours', - action='index', - conditions=dict( method=["GET"] ) ) + webapp.mapper.connect('index', + '/api/tours', + controller='tours', + action='index', + conditions=dict(method=["GET"])) - webapp.mapper.connect( 'show', - '/api/tours/{tour_id}', - controller='tours', - action='show', - conditions=dict( method=[ "GET" ] ) ) + webapp.mapper.connect('show', + '/api/tours/{tour_id}', + controller='tours', + action='show', + conditions=dict(method=["GET"])) - webapp.mapper.connect( 'update_tour', - '/api/tours/{tour_id}', - controller='tours', - action='update_tour', - conditions=dict( method=[ "POST" ] ) ) + webapp.mapper.connect('update_tour', + '/api/tours/{tour_id}', + controller='tours', + action='update_tour', + conditions=dict(method=["POST"])) # ================================ # ===== USERS API ===== # ================================ - webapp.mapper.connect( 'api_key', - '/api/users/{id}/api_key', - controller='users', - action='api_key', - conditions=dict( method=["POST"] ) ) - - webapp.mapper.connect( 'get_api_key', - '/api/users/{id}/api_key/inputs', - controller='users', - action='get_api_key', - conditions=dict( method=["GET"] ) ) - - webapp.mapper.connect( 'set_api_key', - '/api/users/{id}/api_key/inputs', - controller='users', - action='set_api_key', - conditions=dict( method=["PUT"] ) ) - - webapp.mapper.connect( 'get_information', - '/api/users/{id}/information/inputs', - controller='users', - action='get_information', - conditions=dict( method=["GET"] ) ) - - webapp.mapper.connect( 'set_information', - '/api/users/{id}/information/inputs', - controller='users', - action='set_information', - conditions=dict( method=["PUT"] ) ) - - webapp.mapper.connect( 'get_password', - '/api/users/{id}/password/inputs', - controller='users', - action='get_password', - conditions=dict( method=["GET"] ) ) - - webapp.mapper.connect( 'set_password', - '/api/users/{id}/password/inputs', - controller='users', - action='set_password', - conditions=dict( method=["PUT"] ) ) - - webapp.mapper.connect( 'get_permissions', - '/api/users/{id}/permissions/inputs', - controller='users', - action='get_permissions', - conditions=dict( method=["GET"] ) ) - - webapp.mapper.connect( 'set_permissions', - '/api/users/{id}/permissions/inputs', - controller='users', - action='set_permissions', - conditions=dict( method=["PUT"] ) ) - - webapp.mapper.connect( 'get_toolbox_filters', - '/api/users/{id}/toolbox_filters/inputs', - controller='users', - action='get_toolbox_filters', - conditions=dict( method=["GET"] ) ) - - webapp.mapper.connect( 'set_toolbox_filters', - '/api/users/{id}/toolbox_filters/inputs', - controller='users', - action='set_toolbox_filters', - conditions=dict( method=["PUT"] ) ) - - webapp.mapper.connect( 'get_communication', - '/api/users/{id}/communication/inputs', - controller='users', - action='get_communication', - conditions=dict( method=["GET"] ) ) - - webapp.mapper.connect( 'set_communication', - '/api/users/{id}/communication/inputs', - controller='users', - action='set_communication', - conditions=dict( method=["PUT"] ) ) - - webapp.mapper.connect( 'get_custom_builds', - '/api/users/{id}/custom_builds', - controller='users', - action='get_custom_builds', - conditions=dict( method=["GET"] ) ) - - webapp.mapper.connect( 'add_custom_builds', - '/api/users/{id}/custom_builds/{key}', - controller='users', - action='add_custom_builds', - conditions=dict( method=["PUT"] ) ) - - webapp.mapper.connect( 'delete_custom_builds', - '/api/users/{id}/custom_builds/{key}', - controller='users', - action='delete_custom_builds', - conditions=dict( method=["DELETE"] ) ) + webapp.mapper.connect('api_key', + '/api/users/{id}/api_key', + controller='users', + action='api_key', + conditions=dict(method=["POST"])) + + webapp.mapper.connect('get_api_key', + '/api/users/{id}/api_key/inputs', + controller='users', + action='get_api_key', + conditions=dict(method=["GET"])) + + webapp.mapper.connect('set_api_key', + '/api/users/{id}/api_key/inputs', + controller='users', + action='set_api_key', + conditions=dict(method=["PUT"])) + + webapp.mapper.connect('get_information', + '/api/users/{id}/information/inputs', + controller='users', + action='get_information', + conditions=dict(method=["GET"])) + + webapp.mapper.connect('set_information', + '/api/users/{id}/information/inputs', + controller='users', + action='set_information', + conditions=dict(method=["PUT"])) + + webapp.mapper.connect('get_password', + '/api/users/{id}/password/inputs', + controller='users', + action='get_password', + conditions=dict(method=["GET"])) + + webapp.mapper.connect('set_password', + '/api/users/{id}/password/inputs', + controller='users', + action='set_password', + conditions=dict(method=["PUT"])) + + webapp.mapper.connect('get_permissions', + '/api/users/{id}/permissions/inputs', + controller='users', + action='get_permissions', + conditions=dict(method=["GET"])) + + webapp.mapper.connect('set_permissions', + '/api/users/{id}/permissions/inputs', + controller='users', + action='set_permissions', + conditions=dict(method=["PUT"])) + + webapp.mapper.connect('get_toolbox_filters', + '/api/users/{id}/toolbox_filters/inputs', + controller='users', + action='get_toolbox_filters', + conditions=dict(method=["GET"])) + + webapp.mapper.connect('set_toolbox_filters', + '/api/users/{id}/toolbox_filters/inputs', + controller='users', + action='set_toolbox_filters', + conditions=dict(method=["PUT"])) + + webapp.mapper.connect('get_communication', + '/api/users/{id}/communication/inputs', + controller='users', + action='get_communication', + conditions=dict(method=["GET"])) + + webapp.mapper.connect('set_communication', + '/api/users/{id}/communication/inputs', + controller='users', + action='set_communication', + conditions=dict(method=["PUT"])) + + webapp.mapper.connect('get_custom_builds', + '/api/users/{id}/custom_builds', + controller='users', + action='get_custom_builds', + conditions=dict(method=["GET"])) + + webapp.mapper.connect('add_custom_builds', + '/api/users/{id}/custom_builds/{key}', + controller='users', + action='add_custom_builds', + conditions=dict(method=["PUT"])) + + webapp.mapper.connect('delete_custom_builds', + '/api/users/{id}/custom_builds/{key}', + controller='users', + action='delete_custom_builds', + conditions=dict(method=["DELETE"])) # ======================== # ===== WEBHOOKS API ===== # ======================== - webapp.mapper.connect( 'get_all', - '/api/webhooks', - controller='webhooks', - action='get_all', - conditions=dict( method=[ "GET" ] ) ) - - webapp.mapper.connect( 'get_random', - '/api/webhooks/{webhook_type}', - controller='webhooks', - action='get_random', - conditions=dict( method=[ "GET" ] ) ) - - webapp.mapper.connect( 'get_all_by_type', - '/api/webhooks/{webhook_type}/all', - controller='webhooks', - action='get_all_by_type', - conditions=dict( method=[ "GET" ] ) ) - - webapp.mapper.connect( 'get_data', - '/api/webhooks/{webhook_name}/get_data', - controller='webhooks', - action='get_data', - conditions=dict( method=[ "GET" ] ) ) + webapp.mapper.connect('get_all', + '/api/webhooks', + controller='webhooks', + action='get_all', + conditions=dict(method=["GET"])) + + webapp.mapper.connect('get_random', + '/api/webhooks/{webhook_type}', + controller='webhooks', + action='get_random', + conditions=dict(method=["GET"])) + + webapp.mapper.connect('get_all_by_type', + '/api/webhooks/{webhook_type}/all', + controller='webhooks', + action='get_all_by_type', + conditions=dict(method=["GET"])) + + webapp.mapper.connect('get_data', + '/api/webhooks/{webhook_name}/get_data', + controller='webhooks', + action='get_data', + conditions=dict(method=["GET"])) # ======================= # ===== LIBRARY API ===== # ======================= - webapp.mapper.connect( 'update_library', - '/api/libraries/{id}', - controller='libraries', - action='update', - conditions=dict( method=[ "PATCH", "PUT" ] ) ) - - webapp.mapper.connect( 'show_library_permissions', - '/api/libraries/{encoded_library_id}/permissions', - controller='libraries', - action='get_permissions', - conditions=dict( method=[ "GET" ] ) ) - - webapp.mapper.connect( 'set_library_permissions', - '/api/libraries/{encoded_library_id}/permissions', - controller='libraries', - action='set_permissions', - conditions=dict( method=[ "POST" ] ) ) - - webapp.mapper.connect( 'show_ld_item', - '/api/libraries/datasets/{id}', - controller='lda_datasets', - action='show', - conditions=dict( method=[ "GET" ] ) ) - - webapp.mapper.connect( 'load_ld', - '/api/libraries/datasets/', - controller='lda_datasets', - action='load', - conditions=dict( method=[ "POST" ] ) ) - - webapp.mapper.connect( 'show_version_of_ld_item', - '/api/libraries/datasets/{encoded_dataset_id}/versions/{encoded_ldda_id}', - controller='lda_datasets', - action='show_version', - conditions=dict( method=[ "GET" ] ) ) - - webapp.mapper.connect( 'show_legitimate_lda_roles', - '/api/libraries/datasets/{encoded_dataset_id}/permissions', - controller='lda_datasets', - action='show_roles', - conditions=dict( method=[ "GET" ] ) ) - - webapp.mapper.connect( 'update_lda_permissions', - '/api/libraries/datasets/{encoded_dataset_id}/permissions', - controller='lda_datasets', - action='update_permissions', - conditions=dict( method=[ "POST" ] ) ) - - webapp.mapper.connect( 'delete_lda_item', - '/api/libraries/datasets/{encoded_dataset_id}', - controller='lda_datasets', - action='delete', - conditions=dict( method=[ "DELETE" ] ) ) - - webapp.mapper.connect( 'download_lda_items', - '/api/libraries/datasets/download/{format}', - controller='lda_datasets', - action='download', - conditions=dict( method=[ "POST", "GET" ] ) ) - - webapp.mapper.resource_with_deleted( 'library', - 'libraries', - path_prefix='/api' ) - - webapp.mapper.resource( 'content', - 'contents', - controller='library_contents', - name_prefix='library_', - path_prefix='/api/libraries/{library_id}', - parent_resources=dict( member_name='library', collection_name='libraries' ) ) - - _add_item_extended_metadata_controller( webapp, - name_prefix="library_dataset_", - path_prefix='/api/libraries/{library_id}/contents/{library_content_id}' ) + webapp.mapper.connect('update_library', + '/api/libraries/{id}', + controller='libraries', + action='update', + conditions=dict(method=["PATCH", "PUT"])) + + webapp.mapper.connect('show_library_permissions', + '/api/libraries/{encoded_library_id}/permissions', + controller='libraries', + action='get_permissions', + conditions=dict(method=["GET"])) + + webapp.mapper.connect('set_library_permissions', + '/api/libraries/{encoded_library_id}/permissions', + controller='libraries', + action='set_permissions', + conditions=dict(method=["POST"])) + + webapp.mapper.connect('show_ld_item', + '/api/libraries/datasets/{id}', + controller='lda_datasets', + action='show', + conditions=dict(method=["GET"])) + + webapp.mapper.connect('load_ld', + '/api/libraries/datasets/', + controller='lda_datasets', + action='load', + conditions=dict(method=["POST"])) + + webapp.mapper.connect('show_version_of_ld_item', + '/api/libraries/datasets/{encoded_dataset_id}/versions/{encoded_ldda_id}', + controller='lda_datasets', + action='show_version', + conditions=dict(method=["GET"])) + + webapp.mapper.connect('show_legitimate_lda_roles', + '/api/libraries/datasets/{encoded_dataset_id}/permissions', + controller='lda_datasets', + action='show_roles', + conditions=dict(method=["GET"])) + + webapp.mapper.connect('update_lda_permissions', + '/api/libraries/datasets/{encoded_dataset_id}/permissions', + controller='lda_datasets', + action='update_permissions', + conditions=dict(method=["POST"])) + + webapp.mapper.connect('delete_lda_item', + '/api/libraries/datasets/{encoded_dataset_id}', + controller='lda_datasets', + action='delete', + conditions=dict(method=["DELETE"])) + + webapp.mapper.connect('download_lda_items', + '/api/libraries/datasets/download/{format}', + controller='lda_datasets', + action='download', + conditions=dict(method=["POST", "GET"])) + + webapp.mapper.resource_with_deleted('library', + 'libraries', + path_prefix='/api') + + webapp.mapper.resource('content', + 'contents', + controller='library_contents', + name_prefix='library_', + path_prefix='/api/libraries/{library_id}', + parent_resources=dict(member_name='library', collection_name='libraries')) + + _add_item_extended_metadata_controller(webapp, + name_prefix="library_dataset_", + path_prefix='/api/libraries/{library_id}/contents/{library_content_id}') # ======================= # ===== FOLDERS API ===== # ======================= - webapp.mapper.connect( 'add_history_datasets_to_library', - '/api/folders/{encoded_folder_id}/contents', + webapp.mapper.connect('add_history_datasets_to_library', + '/api/folders/{encoded_folder_id}/contents', + controller='folder_contents', + action='create', + conditions=dict(method=["POST"])) + + webapp.mapper.connect('create_folder', + '/api/folders/{encoded_parent_folder_id}', + controller='folders', + action='create', + conditions=dict(method=["POST"])) + + webapp.mapper.connect('delete_folder', + '/api/folders/{encoded_folder_id}', + controller='folders', + action='delete', + conditions=dict(method=["DELETE"])) + + webapp.mapper.connect('update_folder', + '/api/folders/{encoded_folder_id}', + controller='folders', + action='update', + conditions=dict(method=["PATCH", "PUT"])) + + webapp.mapper.resource('folder', + 'folders', + path_prefix='/api') + + webapp.mapper.connect('show_folder_permissions', + '/api/folders/{encoded_folder_id}/permissions', + controller='folders', + action='get_permissions', + conditions=dict(method=["GET"])) + + webapp.mapper.connect('set_folder_permissions', + '/api/folders/{encoded_folder_id}/permissions', + controller='folders', + action='set_permissions', + conditions=dict(method=["POST"])) + + webapp.mapper.resource('content', + 'contents', controller='folder_contents', - action='create', - conditions=dict( method=[ "POST" ] ) ) - - webapp.mapper.connect( 'create_folder', - '/api/folders/{encoded_parent_folder_id}', - controller='folders', - action='create', - conditions=dict( method=[ "POST" ] ) ) - - webapp.mapper.connect( 'delete_folder', - '/api/folders/{encoded_folder_id}', - controller='folders', - action='delete', - conditions=dict( method=[ "DELETE" ] ) ) - - webapp.mapper.connect( 'update_folder', - '/api/folders/{encoded_folder_id}', - controller='folders', - action='update', - conditions=dict( method=[ "PATCH", "PUT" ] ) ) - - webapp.mapper.resource( 'folder', - 'folders', - path_prefix='/api' ) - - webapp.mapper.connect( 'show_folder_permissions', - '/api/folders/{encoded_folder_id}/permissions', - controller='folders', - action='get_permissions', - conditions=dict( method=[ "GET" ] ) ) - - webapp.mapper.connect( 'set_folder_permissions', - '/api/folders/{encoded_folder_id}/permissions', - controller='folders', - action='set_permissions', - conditions=dict( method=[ "POST" ] ) ) - - webapp.mapper.resource( 'content', - 'contents', - controller='folder_contents', - name_prefix='folder_', - path_prefix='/api/folders/{folder_id}', - parent_resources=dict( member_name='folder', collection_name='folders' ), - conditions=dict( method=[ "GET" ] ) ) - - webapp.mapper.resource( 'job', - 'jobs', - path_prefix='/api' ) - webapp.mapper.connect( 'job_search', '/api/jobs/search', controller='jobs', action='search', conditions=dict( method=['POST'] ) ) - webapp.mapper.connect( 'job_inputs', '/api/jobs/{id}/inputs', controller='jobs', action='inputs', conditions=dict( method=['GET'] ) ) - webapp.mapper.connect( 'job_outputs', '/api/jobs/{id}/outputs', controller='jobs', action='outputs', conditions=dict( method=['GET'] ) ) - webapp.mapper.connect( 'build_for_rerun', '/api/jobs/{id}/build_for_rerun', controller='jobs', action='build_for_rerun', conditions=dict( method=['GET'] ) ) + name_prefix='folder_', + path_prefix='/api/folders/{folder_id}', + parent_resources=dict(member_name='folder', collection_name='folders'), + conditions=dict(method=["GET"])) + + webapp.mapper.resource('job', + 'jobs', + path_prefix='/api') + webapp.mapper.connect('job_search', '/api/jobs/search', controller='jobs', action='search', conditions=dict(method=['POST'])) + webapp.mapper.connect('job_inputs', '/api/jobs/{id}/inputs', controller='jobs', action='inputs', conditions=dict(method=['GET'])) + webapp.mapper.connect('job_outputs', '/api/jobs/{id}/outputs', controller='jobs', action='outputs', conditions=dict(method=['GET'])) + webapp.mapper.connect('build_for_rerun', '/api/jobs/{id}/build_for_rerun', controller='jobs', action='build_for_rerun', conditions=dict(method=['GET'])) + webapp.mapper.connect('job_error', '/api/jobs/{id}/error', controller='jobs', action='error', conditions=dict(method=['POST'])) # Job files controllers. Only for consumption by remote job runners. - webapp.mapper.resource( 'file', - 'files', - controller="job_files", - name_prefix="job_", - path_prefix='/api/jobs/{job_id}', - parent_resources=dict( member_name="job", collection_name="jobs" ) ) + webapp.mapper.resource('file', + 'files', + controller="job_files", + name_prefix="job_", + path_prefix='/api/jobs/{job_id}', + parent_resources=dict(member_name="job", collection_name="jobs")) - _add_item_extended_metadata_controller( webapp, - name_prefix="history_dataset_", - path_prefix='/api/histories/{history_id}/contents/{history_content_id}' ) + _add_item_extended_metadata_controller(webapp, + name_prefix="history_dataset_", + path_prefix='/api/histories/{history_id}/contents/{history_content_id}') # ==================== # ===== TOOLSHED ===== # ==================== # Handle displaying tool help images and README file images contained in repositories installed from the tool shed. - webapp.add_route( '/admin_toolshed/static/images/{repository_id}/{image_file:.+?}', - controller='admin_toolshed', - action='display_image_in_repository', - repository_id=None, - image_file=None ) - - webapp.mapper.connect( 'tool_shed_contents', - '/api/tool_shed/contents', - controller='toolshed', - action='show', - conditions=dict( method=[ "GET" ] ) ) - - webapp.mapper.connect( 'tool_shed_category_contents', - '/api/tool_shed/category', - controller='toolshed', - action='category', - conditions=dict( method=[ "GET" ] ) ) - - webapp.mapper.connect( 'tool_shed_repository_details', - '/api/tool_shed/repository', - controller='toolshed', - action='repository', - conditions=dict( method=[ "GET" ] ) ) - - webapp.mapper.connect( 'tool_sheds', - '/api/tool_shed', - controller='toolshed', - action='index', - conditions=dict( method=[ "GET" ] ) ) - - webapp.mapper.connect( 'tool_shed_search', - '/api/tool_shed/search', - controller='toolshed', - action='search', - conditions=dict( method=[ "GET", "POST" ] ) ) - - webapp.mapper.connect( 'tool_shed_status', - '/api/tool_shed/status', - controller='toolshed', - action='status', - conditions=dict( method=[ "GET", "POST" ] ) ) - - webapp.mapper.connect( 'shed_tool_json', - '/api/tool_shed/tool_json', - controller='toolshed', - action='tool_json', - conditions=dict( method=[ "GET" ] ) ) - - webapp.mapper.connect( 'tool_shed_repository', - '/api/tool_shed_repositories/:id/status', - controller='tool_shed_repositories', - action='status', - conditions=dict( method=[ "GET" ] ) ) + webapp.add_route('/admin_toolshed/static/images/{repository_id}/{image_file:.+?}', + controller='admin_toolshed', + action='display_image_in_repository', + repository_id=None, + image_file=None) + + webapp.mapper.connect('tool_shed_contents', + '/api/tool_shed/contents', + controller='toolshed', + action='show', + conditions=dict(method=["GET"])) - webapp.mapper.connect( 'install_repository', - '/api/tool_shed_repositories', - controller='tool_shed_repositories', - action='install_repository_revision', - conditions=dict( method=[ 'POST' ] ) ) + webapp.mapper.connect('tool_shed_category_contents', + '/api/tool_shed/category', + controller='toolshed', + action='category', + conditions=dict(method=["GET"])) - webapp.mapper.connect( 'install_repository', - '/api/tool_shed_repositories/install', - controller='tool_shed_repositories', - action='install', - conditions=dict( method=[ 'POST' ] ) ) + webapp.mapper.connect('tool_shed_repository_details', + '/api/tool_shed/repository', + controller='toolshed', + action='repository', + conditions=dict(method=["GET"])) - webapp.mapper.connect( 'tool_shed_repository', - '/api/tool_shed_repositories', - controller='tool_shed_repositories', - action='uninstall_repository', - conditions=dict( method=[ "DELETE" ])) + webapp.mapper.connect('tool_sheds', + '/api/tool_shed', + controller='toolshed', + action='index', + conditions=dict(method=["GET"])) - webapp.mapper.connect( 'tool_shed_repository', - '/api/tool_shed_repositories/{id}', - controller='tool_shed_repositories', - action='uninstall_repository', - conditions=dict( method=[ "DELETE" ])) + webapp.mapper.connect('tool_shed_search', + '/api/tool_shed/search', + controller='toolshed', + action='search', + conditions=dict(method=["GET", "POST"])) + + webapp.mapper.connect('tool_shed_status', + '/api/tool_shed/status', + controller='toolshed', + action='status', + conditions=dict(method=["GET", "POST"])) + + webapp.mapper.connect('shed_tool_json', + '/api/tool_shed/tool_json', + controller='toolshed', + action='tool_json', + conditions=dict(method=["GET"])) + + webapp.mapper.connect('tool_shed_repository', + '/api/tool_shed_repositories/:id/status', + controller='tool_shed_repositories', + action='status', + conditions=dict(method=["GET"])) + + webapp.mapper.connect('install_repository', + '/api/tool_shed_repositories', + controller='tool_shed_repositories', + action='install_repository_revision', + conditions=dict(method=['POST'])) + + webapp.mapper.connect('install_repository', + '/api/tool_shed_repositories/install', + controller='tool_shed_repositories', + action='install', + conditions=dict(method=['POST'])) + + webapp.mapper.connect('tool_shed_repository', + '/api/tool_shed_repositories', + controller='tool_shed_repositories', + action='uninstall_repository', + conditions=dict(method=["DELETE"])) + + webapp.mapper.connect('tool_shed_repository', + '/api/tool_shed_repositories/{id}', + controller='tool_shed_repositories', + action='uninstall_repository', + conditions=dict(method=["DELETE"])) # Galaxy API for tool shed features. - webapp.mapper.resource( 'tool_shed_repository', - 'tool_shed_repositories', - member={ 'repair_repository_revision': 'POST', - 'exported_workflows': 'GET', - 'import_workflow': 'POST', - 'import_workflows': 'POST' }, - collection={ 'get_latest_installable_revision': 'POST', - 'reset_metadata_on_installed_repositories': 'POST' }, - controller='tool_shed_repositories', - name_prefix='tool_shed_repository_', - path_prefix='/api', - new={ 'install_repository_revision': 'POST' }, - parent_resources=dict( member_name='tool_shed_repository', collection_name='tool_shed_repositories' ) ) + webapp.mapper.resource('tool_shed_repository', + 'tool_shed_repositories', + member={'repair_repository_revision': 'POST', + 'exported_workflows': 'GET', + 'import_workflow': 'POST', + 'import_workflows': 'POST'}, + collection={'get_latest_installable_revision': 'POST', + 'reset_metadata_on_installed_repositories': 'POST'}, + controller='tool_shed_repositories', + name_prefix='tool_shed_repository_', + path_prefix='/api', + new={'install_repository_revision': 'POST'}, + parent_resources=dict(member_name='tool_shed_repository', collection_name='tool_shed_repositories')) # ==== Trace/Metrics Logger # Connect logger from app @@ -878,11 +887,11 @@ def populate_api_routes( webapp, app ): # controller="metrics", action="index", conditions=dict( method=["GET"] ) ) # webapp.mapper.connect( "show", "/api/metrics/{id}", # controller="metrics", action="show", conditions=dict( method=["GET"] ) ) - webapp.mapper.connect( "create", "/api/metrics", controller="metrics", - action="create", conditions=dict( method=["POST"] ) ) + webapp.mapper.connect("create", "/api/metrics", controller="metrics", + action="create", conditions=dict(method=["POST"])) -def _add_item_tags_controller( webapp, name_prefix, path_prefix, **kwd ): +def _add_item_tags_controller(webapp, name_prefix, path_prefix, **kwd): # Not just using map.resources because actions should be based on name not id controller = "%stags" % name_prefix name = "%stag" % name_prefix @@ -910,25 +919,25 @@ def _add_item_tags_controller( webapp, name_prefix, path_prefix, **kwd ): conditions=dict(method=["GET"])) -def _add_item_extended_metadata_controller( webapp, name_prefix, path_prefix, **kwd ): +def _add_item_extended_metadata_controller(webapp, name_prefix, path_prefix, **kwd): controller = "%sextended_metadata" % name_prefix name = "%sextended_metadata" % name_prefix webapp.mapper.resource(name, "extended_metadata", path_prefix=path_prefix, controller=controller) -def _add_item_annotation_controller( webapp, name_prefix, path_prefix, **kwd ): +def _add_item_annotation_controller(webapp, name_prefix, path_prefix, **kwd): controller = "%sannotations" % name_prefix name = "%sannotation" % name_prefix webapp.mapper.resource(name, "annotation", path_prefix=path_prefix, controller=controller) -def _add_item_provenance_controller( webapp, name_prefix, path_prefix, **kwd ): +def _add_item_provenance_controller(webapp, name_prefix, path_prefix, **kwd): controller = "%sprovenance" % name_prefix name = "%sprovenance" % name_prefix webapp.mapper.resource(name, "provenance", path_prefix=path_prefix, controller=controller) -def wrap_in_middleware( app, global_conf, application_stack, **local_conf ): +def wrap_in_middleware(app, global_conf, application_stack, **local_conf): """ Based on the configuration wrap `app` in a set of common and useful middleware. @@ -939,104 +948,104 @@ def wrap_in_middleware( app, global_conf, application_stack, **local_conf ): # Merge the global and local configurations conf = global_conf.copy() conf.update(local_conf) - debug = asbool( conf.get( 'debug', False ) ) + debug = asbool(conf.get('debug', False)) # First put into place httpexceptions, which must be most closely # wrapped around the application (it can interact poorly with # other middleware): - app = wrap_if_allowed( app, stack, httpexceptions.make_middleware, name='paste.httpexceptions', args=(conf,) ) + app = wrap_if_allowed(app, stack, httpexceptions.make_middleware, name='paste.httpexceptions', args=(conf,)) # Statsd request timing and profiling statsd_host = conf.get('statsd_host', None) if statsd_host: from galaxy.web.framework.middleware.statsd import StatsdMiddleware - app = wrap_if_allowed( app, stack, StatsdMiddleware, - args=( statsd_host, - conf.get('statsd_port', 8125), - conf.get('statsd_prefix', 'galaxy') ) ) - log.debug( "Enabling 'statsd' middleware" ) + app = wrap_if_allowed(app, stack, StatsdMiddleware, + args=(statsd_host, + conf.get('statsd_port', 8125), + conf.get('statsd_prefix', 'galaxy'))) + log.debug("Enabling 'statsd' middleware") # graphite request timing and profiling graphite_host = conf.get('graphite_host', None) if graphite_host: from galaxy.web.framework.middleware.graphite import GraphiteMiddleware - app = wrap_if_allowed( app, stack, GraphiteMiddleware, - args=( graphite_host, - conf.get('graphite_port', 2003), - conf.get('graphite_prefix', 'galaxy') ) ) - log.debug( "Enabling 'graphite' middleware" ) + app = wrap_if_allowed(app, stack, GraphiteMiddleware, + args=(graphite_host, + conf.get('graphite_port', 2003), + conf.get('graphite_prefix', 'galaxy'))) + log.debug("Enabling 'graphite' middleware") # If we're using remote_user authentication, add middleware that # protects Galaxy from improperly configured authentication in the # upstream server - single_user = conf.get( 'single_user', None ) - use_remote_user = asbool(conf.get( 'use_remote_user', False )) or single_user + single_user = conf.get('single_user', None) + use_remote_user = asbool(conf.get('use_remote_user', False)) or single_user if use_remote_user: from galaxy.web.framework.middleware.remoteuser import RemoteUser - app = wrap_if_allowed( app, stack, RemoteUser, - kwargs=dict( - maildomain=conf.get('remote_user_maildomain', None), - display_servers=util.listify( conf.get('display_servers', '')), - single_user=single_user, - admin_users=conf.get('admin_users', '').split(','), - remote_user_header=conf.get('remote_user_header', 'HTTP_REMOTE_USER'), - remote_user_secret_header=conf.get('remote_user_secret', None), - normalize_remote_user_email=conf.get('normalize_remote_user_email', False)) ) + app = wrap_if_allowed(app, stack, RemoteUser, + kwargs=dict( + maildomain=conf.get('remote_user_maildomain', None), + display_servers=util.listify(conf.get('display_servers', '')), + single_user=single_user, + admin_users=conf.get('admin_users', '').split(','), + remote_user_header=conf.get('remote_user_header', 'HTTP_REMOTE_USER'), + remote_user_secret_header=conf.get('remote_user_secret', None), + normalize_remote_user_email=conf.get('normalize_remote_user_email', False))) # The recursive middleware allows for including requests in other # requests or forwarding of requests, all on the server side. if asbool(conf.get('use_recursive', True)): from paste import recursive - app = wrap_if_allowed( app, stack, recursive.RecursiveMiddleware, args=(conf,) ) + app = wrap_if_allowed(app, stack, recursive.RecursiveMiddleware, args=(conf,)) # If sentry logging is enabled, log here before propogating up to # the error middleware - sentry_dsn = conf.get( 'sentry_dsn', None ) + sentry_dsn = conf.get('sentry_dsn', None) if sentry_dsn: from galaxy.web.framework.middleware.sentry import Sentry - app = wrap_if_allowed( app, stack, Sentry, args=(sentry_dsn,) ) + app = wrap_if_allowed(app, stack, Sentry, args=(sentry_dsn,)) # Various debug middleware that can only be turned on if the debug # flag is set, either because they are insecure or greatly hurt # performance if debug: # Middleware to check for WSGI compliance - if asbool( conf.get( 'use_lint', False ) ): + if asbool(conf.get('use_lint', False)): from paste import lint - app = wrap_if_allowed( app, stack, lint.make_middleware, name='paste.lint', args=(conf,) ) + app = wrap_if_allowed(app, stack, lint.make_middleware, name='paste.lint', args=(conf,)) # Middleware to run the python profiler on each request - if asbool( conf.get( 'use_profile', False ) ): + if asbool(conf.get('use_profile', False)): from paste.debug import profile - app = wrap_if_allowed( app, stack, profile.ProfileMiddleware, args=(conf,) ) - if debug and asbool( conf.get( 'use_interactive', False ) ): + app = wrap_if_allowed(app, stack, profile.ProfileMiddleware, args=(conf,)) + if debug and asbool(conf.get('use_interactive', False)): # Interactive exception debugging, scary dangerous if publicly # accessible, if not enabled we'll use the regular error printing # middleware. try: from weberror import evalexception - app = wrap_if_allowed_or_fail( app, stack, evalexception.EvalException, - args=(conf,), - kwargs=dict(templating_formatters=build_template_error_formatters()) ) + app = wrap_if_allowed_or_fail(app, stack, evalexception.EvalException, + args=(conf,), + kwargs=dict(templating_formatters=build_template_error_formatters())) except MiddlewareWrapUnsupported as exc: log.warning(str(exc)) import galaxy.web.framework.middleware.error - app = wrap_if_allowed( app, stack, galaxy.web.framework.middleware.error.ErrorMiddleware, args=(conf,) ) + app = wrap_if_allowed(app, stack, galaxy.web.framework.middleware.error.ErrorMiddleware, args=(conf,)) else: # Not in interactive debug mode, just use the regular error middleware import galaxy.web.framework.middleware.error - app = wrap_if_allowed( app, stack, galaxy.web.framework.middleware.error.ErrorMiddleware, args=(conf,) ) + app = wrap_if_allowed(app, stack, galaxy.web.framework.middleware.error.ErrorMiddleware, args=(conf,)) # Transaction logging (apache access.log style) - if asbool( conf.get( 'use_translogger', True ) ): + if asbool(conf.get('use_translogger', True)): from galaxy.web.framework.middleware.translogger import TransLogger - app = wrap_if_allowed( app, stack, TransLogger ) + app = wrap_if_allowed(app, stack, TransLogger) # X-Forwarded-Host handling from galaxy.web.framework.middleware.xforwardedhost import XForwardedHostMiddleware - app = wrap_if_allowed( app, stack, XForwardedHostMiddleware ) + app = wrap_if_allowed(app, stack, XForwardedHostMiddleware) # Request ID middleware from galaxy.web.framework.middleware.request_id import RequestIDMiddleware - app = wrap_if_allowed( app, stack, RequestIDMiddleware ) + app = wrap_if_allowed(app, stack, RequestIDMiddleware) # api batch call processing middleware from galaxy.web.framework.middleware.batch import BatchMiddleware - app = wrap_if_allowed( app, stack, BatchMiddleware, args=(webapp, {}) ) + app = wrap_if_allowed(app, stack, BatchMiddleware, args=(webapp, {})) return app -def wrap_in_static( app, global_conf, plugin_frameworks=None, **local_conf ): +def wrap_in_static(app, global_conf, plugin_frameworks=None, **local_conf): from galaxy.web.framework.middleware.static import CacheableStaticURLParser as Static - urlmap, cache_time = galaxy.web.framework.webapp.build_url_map( app, global_conf, local_conf ) + urlmap, cache_time = galaxy.web.framework.webapp.build_url_map(app, global_conf, local_conf) # wrap any static dirs for plugins plugin_frameworks = plugin_frameworks or [] for framework in plugin_frameworks: @@ -1044,8 +1053,8 @@ def wrap_in_static( app, global_conf, plugin_frameworks=None, **local_conf ): # invert control to each plugin for finding their own static dirs for plugin_url, plugin_static_path in framework.get_static_urls_and_paths(): plugin_url = '/plugins/' + plugin_url - urlmap[( plugin_url )] = Static( plugin_static_path, cache_time ) - log.debug( 'added url, path to static middleware: %s, %s', plugin_url, plugin_static_path ) + urlmap[(plugin_url)] = Static(plugin_static_path, cache_time) + log.debug('added url, path to static middleware: %s, %s', plugin_url, plugin_static_path) # URL mapper becomes the root webapp return urlmap diff --git a/lib/galaxy/webapps/galaxy/config_watchers.py b/lib/galaxy/webapps/galaxy/config_watchers.py index 8311e2659090..74b6f4bd17bc 100644 --- a/lib/galaxy/webapps/galaxy/config_watchers.py +++ b/lib/galaxy/webapps/galaxy/config_watchers.py @@ -24,7 +24,7 @@ def __init__(self, app): self.tool_config_watcher = get_tool_conf_watcher(reload_callback=lambda: reload_toolbox(self.app), tool_cache=self.app.tool_cache) self.data_manager_config_watcher = get_tool_conf_watcher(reload_callback=lambda: reload_data_managers(self.app)) self.tool_data_watcher = get_tool_data_dir_watcher(self.app.tool_data_tables, config=self.app.config) - self.tool_watcher = get_tool_watcher( self, app.config ) + self.tool_watcher = get_tool_watcher(self, app.config) self.start() def start(self): diff --git a/lib/galaxy/webapps/galaxy/controllers/admin.py b/lib/galaxy/webapps/galaxy/controllers/admin.py index 228b4d88b770..4c82a359d28a 100644 --- a/lib/galaxy/webapps/galaxy/controllers/admin.py +++ b/lib/galaxy/webapps/galaxy/controllers/admin.py @@ -1,21 +1,23 @@ import imp import logging import os -from sqlalchemy.sql import expression +from datetime import datetime, timedelta +import six +from string import punctuation as PUNCTUATION +from sqlalchemy import and_, false, func, or_ import galaxy.queue_worker -import galaxy.util +from galaxy import util from galaxy import model from galaxy import web from galaxy.actions.admin import AdminActions -from galaxy.exceptions import MessageException +from galaxy.exceptions import ActionInputError, MessageException from galaxy.model import tool_shed_install as install_model from galaxy.util import nice_size, sanitize_text, url_get from galaxy.util.odict import odict from galaxy.web import url_for from galaxy.web.base import controller from galaxy.web.base.controller import UsesQuotaMixin -from galaxy.web.base.controllers.admin import Admin from galaxy.web.framework.helpers import grids, time_ago from galaxy.web.params import QuotaParamParser from galaxy.tools import global_tool_errors @@ -24,59 +26,60 @@ from tool_shed.util import repository_util from tool_shed.util.web_util import escape -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class UserListGrid( grids.Grid ): - class EmailColumn( grids.TextColumn ): - def get_value( self, trans, grid, user ): +class UserListGrid(grids.Grid): + + class EmailColumn(grids.TextColumn): + def get_value(self, trans, grid, user): return escape(user.email) - class UserNameColumn( grids.TextColumn ): - def get_value( self, trans, grid, user ): + class UserNameColumn(grids.TextColumn): + def get_value(self, trans, grid, user): if user.username: return escape(user.username) return 'not set' - class StatusColumn( grids.GridColumn ): - def get_value( self, trans, grid, user ): + class StatusColumn(grids.GridColumn): + def get_value(self, trans, grid, user): if user.purged: return "purged" elif user.deleted: return "deleted" return "" - class GroupsColumn( grids.GridColumn ): - def get_value( self, trans, grid, user ): + class GroupsColumn(grids.GridColumn): + def get_value(self, trans, grid, user): if user.groups: - return len( user.groups ) + return len(user.groups) return 0 - class RolesColumn( grids.GridColumn ): - def get_value( self, trans, grid, user ): + class RolesColumn(grids.GridColumn): + def get_value(self, trans, grid, user): if user.roles: - return len( user.roles ) + return len(user.roles) return 0 - class ExternalColumn( grids.GridColumn ): - def get_value( self, trans, grid, user ): + class ExternalColumn(grids.GridColumn): + def get_value(self, trans, grid, user): if user.external: return 'yes' return 'no' - class LastLoginColumn( grids.GridColumn ): - def get_value( self, trans, grid, user ): + class LastLoginColumn(grids.GridColumn): + def get_value(self, trans, grid, user): if user.galaxy_sessions: - return self.format( user.galaxy_sessions[ 0 ].update_time ) + return self.format(user.galaxy_sessions[0].update_time) return 'never' - class TimeCreatedColumn( grids.GridColumn ): - def get_value( self, trans, grid, user ): + class TimeCreatedColumn(grids.GridColumn): + def get_value(self, trans, grid, user): return user.create_time.strftime('%x') - class ActivatedColumn( grids.GridColumn ): - def get_value( self, trans, grid, user ): + class ActivatedColumn(grids.GridColumn): + def get_value(self, trans, grid, user): if user.active: return 'Y' else: @@ -85,383 +88,368 @@ def get_value( self, trans, grid, user ): # Grid definition title = "Users" model_class = model.User - template = '/admin/user/grid.mako' default_sort_key = "email" columns = [ - EmailColumn( "Email", - key="email", - model_class=model.User, - link=( lambda item: dict( operation="information", id=item.id, webapp="galaxy" ) ), - attach_popup=True, - filterable="advanced", - target="top" ), - UserNameColumn( "User Name", - key="username", - model_class=model.User, - attach_popup=False, - filterable="advanced" ), - GroupsColumn( "Groups", attach_popup=False ), - RolesColumn( "Roles", attach_popup=False ), - ExternalColumn( "External", attach_popup=False ), - LastLoginColumn( "Last Login", format=time_ago ), - StatusColumn( "Status", attach_popup=False ), - TimeCreatedColumn( "Created", attach_popup=False ), - ActivatedColumn( "Activated", attach_popup=False ), + EmailColumn("Email", + key="email", + model_class=model.User, + link=(lambda item: dict(controller="user", action="information", id=item.id, webapp="galaxy")), + attach_popup=True, + filterable="advanced", + target="top"), + UserNameColumn("User Name", + key="username", + model_class=model.User, + attach_popup=False, + filterable="advanced"), + GroupsColumn("Groups", attach_popup=False), + RolesColumn("Roles", attach_popup=False), + ExternalColumn("External", attach_popup=False), + LastLoginColumn("Last Login", format=time_ago), + StatusColumn("Status", attach_popup=False), + TimeCreatedColumn("Created", attach_popup=False), + ActivatedColumn("Activated", attach_popup=False), # Columns that are valid for filtering but are not visible. - grids.DeletedColumn( "Deleted", key="deleted", visible=False, filterable="advanced" ) + grids.DeletedColumn("Deleted", key="deleted", visible=False, filterable="advanced") ] - columns.append( grids.MulticolFilterColumn( "Search", - cols_to_filter=[ columns[0], columns[1] ], - key="free-text-search", - visible=False, - filterable="standard" ) ) + columns.append(grids.MulticolFilterColumn("Search", + cols_to_filter=[columns[0], columns[1]], + key="free-text-search", + visible=False, + filterable="standard")) global_actions = [ - grids.GridAction( "Create new user", dict( controller='admin', action='users', operation='create', webapp="galaxy" ) ) + grids.GridAction("Create new user", url_args=dict(webapp="galaxy", action="create_new_user")) ] operations = [ - grids.GridOperation( "Manage Roles and Groups", - condition=( lambda item: not item.deleted ), - allow_multiple=False, - url_args=dict( webapp="galaxy", action="manage_roles_and_groups_for_user" ) ), - grids.GridOperation( "Reset Password", - condition=( lambda item: not item.deleted ), - allow_multiple=True, - allow_popup=False, - url_args=dict( webapp="galaxy", action="reset_user_password" ) ), - grids.GridOperation( "Recalculate Disk Usage", - condition=( lambda item: not item.deleted ), - allow_multiple=False, - url_args=dict( webapp="galaxy", action="recalculate_user_disk_usage" ) ) + grids.GridOperation("Manage Roles and Groups", + condition=(lambda item: not item.deleted), + allow_multiple=False, + url_args=dict(action="forms/manage_roles_and_groups_for_user")), + grids.GridOperation("Reset Password", + condition=(lambda item: not item.deleted), + allow_multiple=True, + url_args=dict(action="forms/reset_user_password"), + target="top"), + grids.GridOperation("Recalculate Disk Usage", + condition=(lambda item: not item.deleted), + allow_multiple=False) ] standard_filters = [ - grids.GridColumnFilter( "Active", args=dict( deleted=False ) ), - grids.GridColumnFilter( "Deleted", args=dict( deleted=True, purged=False ) ), - grids.GridColumnFilter( "Purged", args=dict( purged=True ) ), - grids.GridColumnFilter( "All", args=dict( deleted='All' ) ) + grids.GridColumnFilter("Active", args=dict(deleted=False)), + grids.GridColumnFilter("Deleted", args=dict(deleted=True, purged=False)), + grids.GridColumnFilter("Purged", args=dict(purged=True)), + grids.GridColumnFilter("All", args=dict(deleted='All')) ] num_rows_per_page = 50 preserve_state = False use_paging = True - def get_current_item( self, trans, **kwargs ): + def get_current_item(self, trans, **kwargs): return trans.user -class RoleListGrid( grids.Grid ): +class RoleListGrid(grids.Grid): - class NameColumn( grids.TextColumn ): - def get_value( self, trans, grid, role ): + class NameColumn(grids.TextColumn): + def get_value(self, trans, grid, role): return escape(role.name) - class DescriptionColumn( grids.TextColumn ): - def get_value( self, trans, grid, role ): + class DescriptionColumn(grids.TextColumn): + def get_value(self, trans, grid, role): if role.description: return escape(role.description) return '' - class TypeColumn( grids.TextColumn ): - def get_value( self, trans, grid, role ): + class TypeColumn(grids.TextColumn): + def get_value(self, trans, grid, role): return role.type - class StatusColumn( grids.GridColumn ): - def get_value( self, trans, grid, role ): + class StatusColumn(grids.GridColumn): + def get_value(self, trans, grid, role): if role.deleted: return "deleted" return "" - class GroupsColumn( grids.GridColumn ): - def get_value( self, trans, grid, role ): + class GroupsColumn(grids.GridColumn): + def get_value(self, trans, grid, role): if role.groups: - return len( role.groups ) + return len(role.groups) return 0 - class UsersColumn( grids.GridColumn ): - def get_value( self, trans, grid, role ): + class UsersColumn(grids.GridColumn): + def get_value(self, trans, grid, role): if role.users: - return len( role.users ) + return len(role.users) return 0 # Grid definition title = "Roles" model_class = model.Role - template = '/admin/dataset_security/role/grid.mako' default_sort_key = "name" columns = [ - NameColumn( "Name", - key="name", - link=( lambda item: dict( operation="Manage users and groups", id=item.id, webapp="galaxy" ) ), - model_class=model.Role, - attach_popup=True, - filterable="advanced" ), - DescriptionColumn( "Description", - key='description', - model_class=model.Role, - attach_popup=False, - filterable="advanced" ), - TypeColumn( "Type", - key='type', - model_class=model.Role, - attach_popup=False, - filterable="advanced" ), - GroupsColumn( "Groups", attach_popup=False ), - UsersColumn( "Users", attach_popup=False ), - StatusColumn( "Status", attach_popup=False ), + NameColumn("Name", + key="name", + link=(lambda item: dict(action="forms/manage_users_and_groups_for_role", id=item.id, webapp="galaxy")), + model_class=model.Role, + attach_popup=True, + filterable="advanced", + target="top"), + DescriptionColumn("Description", + key='description', + model_class=model.Role, + attach_popup=False, + filterable="advanced"), + TypeColumn("Type", + key='type', + model_class=model.Role, + attach_popup=False, + filterable="advanced"), + GroupsColumn("Groups", attach_popup=False), + UsersColumn("Users", attach_popup=False), + StatusColumn("Status", attach_popup=False), # Columns that are valid for filtering but are not visible. - grids.DeletedColumn( "Deleted", key="deleted", visible=False, filterable="advanced" ) + grids.DeletedColumn("Deleted", key="deleted", visible=False, filterable="advanced") ] - columns.append( grids.MulticolFilterColumn( "Search", - cols_to_filter=[ columns[0], columns[1], columns[2] ], - key="free-text-search", - visible=False, - filterable="standard" ) ) + columns.append(grids.MulticolFilterColumn("Search", + cols_to_filter=[columns[0], columns[1], columns[2]], + key="free-text-search", + visible=False, + filterable="standard")) global_actions = [ - grids.GridAction( "Add new role", dict( controller='admin', action='roles', operation='create' ) ) + grids.GridAction("Add new role", url_args=dict(action="forms/create_role")) ] - operations = [ grids.GridOperation( "Edit", - condition=( lambda item: not item.deleted ), - allow_multiple=False, - url_args=dict( webapp="galaxy", action="rename_role" ) ), - grids.GridOperation( "Delete", - condition=( lambda item: not item.deleted ), - allow_multiple=True, - url_args=dict( webapp="galaxy", action="mark_role_deleted" ) ), - grids.GridOperation( "Undelete", - condition=( lambda item: item.deleted ), - allow_multiple=True, - url_args=dict( webapp="galaxy", action="undelete_role" ) ), - grids.GridOperation( "Purge", - condition=( lambda item: item.deleted ), - allow_multiple=True, - url_args=dict( webapp="galaxy", action="purge_role" ) ) ] + operations = [grids.GridOperation("Edit", + condition=(lambda item: not item.deleted), + allow_multiple=False, + url_args=dict(action="forms/rename_role")), + grids.GridOperation("Delete", + condition=(lambda item: not item.deleted), + allow_multiple=True), + grids.GridOperation("Undelete", + condition=(lambda item: item.deleted), + allow_multiple=True), + grids.GridOperation("Purge", + condition=(lambda item: item.deleted), + allow_multiple=True)] standard_filters = [ - grids.GridColumnFilter( "Active", args=dict( deleted=False ) ), - grids.GridColumnFilter( "Deleted", args=dict( deleted=True ) ), - grids.GridColumnFilter( "All", args=dict( deleted='All' ) ) + grids.GridColumnFilter("Active", args=dict(deleted=False)), + grids.GridColumnFilter("Deleted", args=dict(deleted=True)), + grids.GridColumnFilter("All", args=dict(deleted='All')) ] num_rows_per_page = 50 preserve_state = False use_paging = True - def apply_query_filter( self, trans, query, **kwargs ): - return query.filter( model.Role.type != model.Role.types.PRIVATE ) + def apply_query_filter(self, trans, query, **kwargs): + return query.filter(model.Role.type != model.Role.types.PRIVATE) -class GroupListGrid( grids.Grid ): +class GroupListGrid(grids.Grid): - class NameColumn( grids.TextColumn ): - def get_value( self, trans, grid, group ): + class NameColumn(grids.TextColumn): + def get_value(self, trans, grid, group): return escape(group.name) - class StatusColumn( grids.GridColumn ): - def get_value( self, trans, grid, group ): + class StatusColumn(grids.GridColumn): + def get_value(self, trans, grid, group): if group.deleted: return "deleted" return "" - class RolesColumn( grids.GridColumn ): - def get_value( self, trans, grid, group ): + class RolesColumn(grids.GridColumn): + def get_value(self, trans, grid, group): if group.roles: - return len( group.roles ) + return len(group.roles) return 0 - class UsersColumn( grids.GridColumn ): - def get_value( self, trans, grid, group ): + class UsersColumn(grids.GridColumn): + def get_value(self, trans, grid, group): if group.members: - return len( group.members ) + return len(group.members) return 0 # Grid definition title = "Groups" model_class = model.Group - template = '/admin/dataset_security/group/grid.mako' default_sort_key = "name" columns = [ - NameColumn( "Name", - key="name", - link=( lambda item: dict( operation="Manage users and roles", id=item.id, webapp="galaxy" ) ), - model_class=model.Group, - attach_popup=True, - filterable="advanced" ), - UsersColumn( "Users", attach_popup=False ), - RolesColumn( "Roles", attach_popup=False ), - StatusColumn( "Status", attach_popup=False ), + NameColumn("Name", + key="name", + link=(lambda item: dict(action="forms/manage_users_and_roles_for_group", id=item.id, webapp="galaxy")), + model_class=model.Group, + attach_popup=True, + filterable="advanced"), + UsersColumn("Users", attach_popup=False), + RolesColumn("Roles", attach_popup=False), + StatusColumn("Status", attach_popup=False), # Columns that are valid for filtering but are not visible. - grids.DeletedColumn( "Deleted", key="deleted", visible=False, filterable="advanced" ) + grids.DeletedColumn("Deleted", key="deleted", visible=False, filterable="advanced") ] - columns.append( grids.MulticolFilterColumn( "Search", - cols_to_filter=[ columns[0], columns[1], columns[2] ], - key="free-text-search", - visible=False, - filterable="standard" ) ) + columns.append(grids.MulticolFilterColumn("Search", + cols_to_filter=[columns[0]], + key="free-text-search", + visible=False, + filterable="standard")) global_actions = [ - grids.GridAction( "Add new group", dict( controller='admin', action='groups', operation='create', webapp="galaxy" ) ) + grids.GridAction("Add new group", url_args=dict(action="forms/create_group")) ] - operations = [ grids.GridOperation( "Rename", - condition=( lambda item: not item.deleted ), - allow_multiple=False, - url_args=dict( webapp="galaxy", action="rename_group" ) ), - grids.GridOperation( "Delete", - condition=( lambda item: not item.deleted ), - allow_multiple=True, - url_args=dict( webapp="galaxy", action="mark_group_deleted" ) ), - grids.GridOperation( "Undelete", - condition=( lambda item: item.deleted ), - allow_multiple=True, - url_args=dict( webapp="galaxy", action="undelete_group" ) ), - grids.GridOperation( "Purge", - condition=( lambda item: item.deleted ), - allow_multiple=True, - url_args=dict( webapp="galaxy", action="purge_group" ) ) ] + operations = [grids.GridOperation("Rename", + condition=(lambda item: not item.deleted), + allow_multiple=False, + url_args=dict(action="forms/rename_group")), + grids.GridOperation("Delete", + condition=(lambda item: not item.deleted), + allow_multiple=True), + grids.GridOperation("Undelete", + condition=(lambda item: item.deleted), + allow_multiple=True), + grids.GridOperation("Purge", + condition=(lambda item: item.deleted), + allow_multiple=True)] standard_filters = [ - grids.GridColumnFilter( "Active", args=dict( deleted=False ) ), - grids.GridColumnFilter( "Deleted", args=dict( deleted=True ) ), - grids.GridColumnFilter( "All", args=dict( deleted='All' ) ) + grids.GridColumnFilter("Active", args=dict(deleted=False)), + grids.GridColumnFilter("Deleted", args=dict(deleted=True)), + grids.GridColumnFilter("All", args=dict(deleted='All')) ] num_rows_per_page = 50 preserve_state = False use_paging = True -class QuotaListGrid( grids.Grid ): +class QuotaListGrid(grids.Grid): - class NameColumn( grids.TextColumn ): - def get_value( self, trans, grid, quota ): + class NameColumn(grids.TextColumn): + def get_value(self, trans, grid, quota): return escape(quota.name) - class DescriptionColumn( grids.TextColumn ): - def get_value( self, trans, grid, quota ): + class DescriptionColumn(grids.TextColumn): + def get_value(self, trans, grid, quota): if quota.description: return escape(quota.description) return '' - class AmountColumn( grids.TextColumn ): - def get_value( self, trans, grid, quota ): + class AmountColumn(grids.TextColumn): + def get_value(self, trans, grid, quota): return quota.operation + quota.display_amount - class StatusColumn( grids.GridColumn ): - def get_value( self, trans, grid, quota ): + class StatusColumn(grids.GridColumn): + def get_value(self, trans, grid, quota): if quota.deleted: return "deleted" elif quota.default: return "default for %s users" % quota.default[0].type return "" - class UsersColumn( grids.GridColumn ): - def get_value( self, trans, grid, quota ): + class UsersColumn(grids.GridColumn): + def get_value(self, trans, grid, quota): if quota.users: - return len( quota.users ) + return len(quota.users) return 0 - class GroupsColumn( grids.GridColumn ): - def get_value( self, trans, grid, quota ): + class GroupsColumn(grids.GridColumn): + def get_value(self, trans, grid, quota): if quota.groups: - return len( quota.groups ) + return len(quota.groups) return 0 # Grid definition title = "Quotas" model_class = model.Quota - template = '/admin/quota/grid.mako' default_sort_key = "name" columns = [ - NameColumn( "Name", - key="name", - link=( lambda item: dict( operation="Change amount", id=item.id, webapp="galaxy" ) ), - model_class=model.Quota, - attach_popup=True, - filterable="advanced" ), - DescriptionColumn( "Description", - key='description', - model_class=model.Quota, - attach_popup=False, - filterable="advanced" ), - AmountColumn( "Amount", - key='amount', - model_class=model.Quota, - attach_popup=False, - filterable="advanced" ), - UsersColumn( "Users", attach_popup=False ), - GroupsColumn( "Groups", attach_popup=False ), - StatusColumn( "Status", attach_popup=False ), + NameColumn("Name", + key="name", + link=(lambda item: dict(action="forms/edit_quota", id=item.id)), + model_class=model.Quota, + attach_popup=True, + filterable="advanced"), + DescriptionColumn("Description", + key='description', + model_class=model.Quota, + attach_popup=False, + filterable="advanced"), + AmountColumn("Amount", + key='amount', + model_class=model.Quota, + attach_popup=False), + UsersColumn("Users", attach_popup=False), + GroupsColumn("Groups", attach_popup=False), + StatusColumn("Status", attach_popup=False), # Columns that are valid for filtering but are not visible. - grids.DeletedColumn( "Deleted", key="deleted", visible=False, filterable="advanced" ) + grids.DeletedColumn("Deleted", key="deleted", visible=False, filterable="advanced") ] - columns.append( grids.MulticolFilterColumn( "Search", - cols_to_filter=[ columns[0], columns[1], columns[2] ], - key="free-text-search", - visible=False, - filterable="standard" ) ) + columns.append(grids.MulticolFilterColumn("Search", + cols_to_filter=[columns[0], columns[1]], + key="free-text-search", + visible=False, + filterable="standard")) global_actions = [ - grids.GridAction( "Add new quota", dict( controller='admin', action='quotas', operation='create' ) ) + grids.GridAction("Add new quota", dict(action='forms/create_quota')) ] - operations = [ grids.GridOperation( "Rename", - condition=( lambda item: not item.deleted ), - allow_multiple=False, - url_args=dict( webapp="galaxy", action="rename_quota" ) ), - grids.GridOperation( "Change amount", - condition=( lambda item: not item.deleted ), - allow_multiple=False, - url_args=dict( webapp="galaxy", action="edit_quota" ) ), - grids.GridOperation( "Manage users and groups", - condition=( lambda item: not item.default and not item.deleted ), - allow_multiple=False, - url_args=dict( webapp="galaxy", action="manage_users_and_groups_for_quota" ) ), - grids.GridOperation( "Set as different type of default", - condition=( lambda item: item.default ), - allow_multiple=False, - url_args=dict( webapp="galaxy", action="set_quota_default" ) ), - grids.GridOperation( "Set as default", - condition=( lambda item: not item.default and not item.deleted ), - allow_multiple=False, - url_args=dict( webapp="galaxy", action="set_quota_default" ) ), - grids.GridOperation( "Unset as default", - condition=( lambda item: item.default and not item.deleted ), - allow_multiple=False, - url_args=dict( webapp="galaxy", action="unset_quota_default" ) ), - grids.GridOperation( "Delete", - condition=( lambda item: not item.deleted and not item.default ), - allow_multiple=True, - url_args=dict( webapp="galaxy", action="mark_quota_deleted" ) ), - grids.GridOperation( "Undelete", - condition=( lambda item: item.deleted ), - allow_multiple=True, - url_args=dict( webapp="galaxy", action="undelete_quota" ) ), - grids.GridOperation( "Purge", - condition=( lambda item: item.deleted ), - allow_multiple=True, - url_args=dict( webapp="galaxy", action="purge_quota" ) ) ] + operations = [grids.GridOperation("Rename", + condition=(lambda item: not item.deleted), + allow_multiple=False, + url_args=dict(action="forms/rename_quota")), + grids.GridOperation("Change amount", + condition=(lambda item: not item.deleted), + allow_multiple=False, + url_args=dict(action="forms/edit_quota")), + grids.GridOperation("Manage users and groups", + condition=(lambda item: not item.default and not item.deleted), + allow_multiple=False, + url_args=dict(action="forms/manage_users_and_groups_for_quota")), + grids.GridOperation("Set as different type of default", + condition=(lambda item: item.default), + allow_multiple=False, + url_args=dict(action="forms/set_quota_default")), + grids.GridOperation("Set as default", + condition=(lambda item: not item.default and not item.deleted), + allow_multiple=False, + url_args=dict(action="forms/set_quota_default")), + grids.GridOperation("Unset as default", + condition=(lambda item: item.default and not item.deleted), + allow_multiple=False), + grids.GridOperation("Delete", + condition=(lambda item: not item.deleted and not item.default), + allow_multiple=True), + grids.GridOperation("Undelete", + condition=(lambda item: item.deleted), + allow_multiple=True), + grids.GridOperation("Purge", + condition=(lambda item: item.deleted), + allow_multiple=True)] standard_filters = [ - grids.GridColumnFilter( "Active", args=dict( deleted=False ) ), - grids.GridColumnFilter( "Deleted", args=dict( deleted=True ) ), - grids.GridColumnFilter( "All", args=dict( deleted='All' ) ) + grids.GridColumnFilter("Active", args=dict(deleted=False)), + grids.GridColumnFilter("Deleted", args=dict(deleted=True)), + grids.GridColumnFilter("All", args=dict(deleted='All')) ] num_rows_per_page = 50 preserve_state = False use_paging = True -class ToolVersionListGrid( grids.Grid ): +class ToolVersionListGrid(grids.Grid): - class ToolIdColumn( grids.TextColumn ): - def get_value( self, trans, grid, tool_version ): + class ToolIdColumn(grids.TextColumn): + def get_value(self, trans, grid, tool_version): toolbox = trans.app.toolbox - if toolbox.has_tool( tool_version.tool_id, exact=True ): - link = url_for( controller='tool_runner', tool_id=tool_version.tool_id ) + if toolbox.has_tool(tool_version.tool_id, exact=True): + link = url_for(controller='tool_runner', tool_id=tool_version.tool_id) link_str = '' % link - return '

    ' % ( link_str, tool_version.tool_id ) + return '
    %s%s
    ' % (link_str, tool_version.tool_id) return tool_version.tool_id - class ToolVersionsColumn( grids.TextColumn ): - def get_value( self, trans, grid, tool_version ): + class ToolVersionsColumn(grids.TextColumn): + def get_value(self, trans, grid, tool_version): tool_ids_str = '' toolbox = trans.app.toolbox tool = toolbox._tools_by_id.get(tool_version.tool_id) if tool: for tool_id in tool.lineage.tool_ids: - if toolbox.has_tool( tool_id, exact=True ): - link = url_for( controller='tool_runner', tool_id=tool_id ) + if toolbox.has_tool(tool_id, exact=True): + link = url_for(controller='tool_runner', tool_id=tool_id) link_str = '' % link - tool_ids_str += '
    ' % ( link_str, tool_id ) + tool_ids_str += '
    %s%s

    ' % (link_str, tool_id) else: tool_ids_str += '%s
    ' % tool_version.tool_id else: @@ -471,19 +459,18 @@ def get_value( self, trans, grid, tool_version ): # Grid definition title = "Tool versions" model_class = install_model.ToolVersion - template = '/admin/tool_version/grid.mako' default_sort_key = "tool_id" columns = [ - ToolIdColumn( "Tool id", - key='tool_id', - attach_popup=False ), - ToolVersionsColumn( "Version lineage by tool id (parent/child ordered)" ) + ToolIdColumn("Tool id", + key='tool_id', + attach_popup=False), + ToolVersionsColumn("Version lineage by tool id (parent/child ordered)") ] - columns.append( grids.MulticolFilterColumn( "Search tool id", - cols_to_filter=[ columns[0] ], - key="free-text-search", - visible=False, - filterable="standard" ) ) + columns.append(grids.MulticolFilterColumn("Search tool id", + cols_to_filter=[columns[0]], + key="free-text-search", + visible=False, + filterable="standard")) global_actions = [] operations = [] standard_filters = [] @@ -492,414 +479,1279 @@ def get_value( self, trans, grid, tool_version ): preserve_state = False use_paging = True - def build_initial_query( self, trans, **kwd ): - return trans.install_model.context.query( self.model_class ) + def build_initial_query(self, trans, **kwd): + return trans.install_model.context.query(self.model_class) -class AdminGalaxy( controller.JSAppLauncher, Admin, AdminActions, UsesQuotaMixin, QuotaParamParser ): +class AdminGalaxy(controller.JSAppLauncher, AdminActions, UsesQuotaMixin, QuotaParamParser): user_list_grid = UserListGrid() role_list_grid = RoleListGrid() group_list_grid = GroupListGrid() quota_list_grid = QuotaListGrid() tool_version_list_grid = ToolVersionListGrid() - delete_operation = grids.GridOperation( "Delete", condition=( lambda item: not item.deleted ), allow_multiple=True ) - undelete_operation = grids.GridOperation( "Undelete", condition=( lambda item: item.deleted and not item.purged ), allow_multiple=True ) - purge_operation = grids.GridOperation( "Purge", condition=( lambda item: item.deleted and not item.purged ), allow_multiple=True ) + delete_operation = grids.GridOperation("Delete", condition=(lambda item: not item.deleted), allow_multiple=True) + undelete_operation = grids.GridOperation("Undelete", condition=(lambda item: item.deleted and not item.purged), allow_multiple=True) + purge_operation = grids.GridOperation("Purge", condition=(lambda item: item.deleted and not item.purged), allow_multiple=True) @web.expose @web.require_admin - def index( self, trans, **kwd ): - message = escape( kwd.get( 'message', '' ) ) - status = kwd.get( 'status', 'done' ) + def index(self, trans, **kwd): + message = escape(kwd.get('message', '')) + status = kwd.get('status', 'done') settings = { - 'is_repo_installed' : trans.install_model.context.query( trans.install_model.ToolShedRepository ).first() is not None, - 'installing_repository_ids' : repository_util.get_ids_of_tool_shed_repositories_being_installed( trans.app, as_string=True ), - 'is_tool_shed_installed' : bool( trans.app.tool_shed_registry and trans.app.tool_shed_registry.tool_sheds ) + 'is_repo_installed' : trans.install_model.context.query(trans.install_model.ToolShedRepository).first() is not None, + 'installing_repository_ids' : repository_util.get_ids_of_tool_shed_repositories_being_installed(trans.app, as_string=True), + 'is_tool_shed_installed' : bool(trans.app.tool_shed_registry and trans.app.tool_shed_registry.tool_sheds) } - return self.template( trans, 'admin', settings=settings, message=message, status=status ) + return self.template(trans, 'admin', settings=settings, message=message, status=status) @web.expose + @web.json + @web.require_admin + def users_list(self, trans, **kwd): + message = kwd.get('message', '') + status = kwd.get('status', '') + if 'operation' in kwd: + id = kwd.get('id') + if not id: + message, status = ('Invalid user id (%s) received.' % str(id), 'error') + ids = util.listify(id) + operation = kwd['operation'].lower() + if operation == 'delete': + message, status = self._delete_user(trans, ids) + elif operation == 'undelete': + message, status = self._undelete_user(trans, ids) + elif operation == 'purge': + message, status = self._purge_user(trans, ids) + elif operation == 'recalculate disk usage': + message, status = self._recalculate_user(trans, id) + if trans.app.config.allow_user_deletion: + if self.delete_operation not in self.user_list_grid.operations: + self.user_list_grid.operations.append(self.delete_operation) + if self.undelete_operation not in self.user_list_grid.operations: + self.user_list_grid.operations.append(self.undelete_operation) + if self.purge_operation not in self.user_list_grid.operations: + self.user_list_grid.operations.append(self.purge_operation) + if message and status: + kwd['message'] = util.sanitize_text(message) + kwd['status'] = status + kwd['dict_format'] = True + return self.user_list_grid(trans, **kwd) + + @web.expose_api @web.require_admin - def quotas( self, trans, **kwargs ): + def quotas_list(self, trans, payload=None, **kwargs): + message = kwargs.get('message', '') + status = kwargs.get('status', '') if 'operation' in kwargs: + id = kwargs.get('id') + if not id: + return message_exception(trans, 'Invalid quota id (%s) received.' % str(id)) + quotas = [] + for quota_id in util.listify(id): + try: + quotas.append(get_quota(trans, quota_id)) + except MessageException as e: + return message_exception(trans, str(e)) operation = kwargs.pop('operation').lower() - if operation == "quotas": - return self.quota( trans, **kwargs ) - if operation == "create": - return self.create_quota( trans, **kwargs ) - if operation == "delete": - return self.mark_quota_deleted( trans, **kwargs ) - if operation == "undelete": - return self.undelete_quota( trans, **kwargs ) - if operation == "purge": - return self.purge_quota( trans, **kwargs ) - if operation == "change amount": - return self.edit_quota( trans, **kwargs ) - if operation == "manage users and groups": - return self.manage_users_and_groups_for_quota( trans, **kwargs ) - if operation == "rename": - return self.rename_quota( trans, **kwargs ) - if operation == "edit": - return self.edit_quota( trans, **kwargs ) - # Render the list view - return self.quota_list_grid( trans, **kwargs ) - - @web.expose - @web.require_admin - def create_quota( self, trans, **kwd ): - params = self.get_quota_params( kwd ) - if params.get( 'create_quota_button', False ): try: - quota, message = self._create_quota( params ) - return trans.response.send_redirect( web.url_for( controller='admin', - action='quotas', - webapp=params.webapp, - message=sanitize_text( message ), - status='done' ) ) - except MessageException as e: - params.message = str( e ) - params.status = 'error' - in_users = map( int, params.in_users ) - in_groups = map( int, params.in_groups ) - new_in_users = [] - new_in_groups = [] - for user in trans.sa_session.query( trans.app.model.User ) \ - .filter( trans.app.model.User.table.c.deleted == expression.false() ) \ - .order_by( trans.app.model.User.table.c.email ): - if user.id in in_users: - new_in_users.append( ( user.id, user.email ) ) - else: - params.out_users.append( ( user.id, user.email ) ) - for group in trans.sa_session.query( trans.app.model.Group ) \ - .filter( trans.app.model.Group.table.c.deleted == expression.false() ) \ - .order_by( trans.app.model.Group.table.c.name ): - if group.id in in_groups: - new_in_groups.append( ( group.id, group.name ) ) - else: - params.out_groups.append( ( group.id, group.name ) ) - return trans.fill_template( '/admin/quota/quota_create.mako', - webapp=params.webapp, - name=params.name, - description=params.description, - amount=params.amount, - operation=params.operation, - default=params.default, - in_users=new_in_users, - out_users=params.out_users, - in_groups=new_in_groups, - out_groups=params.out_groups, - message=params.message, - status=params.status ) - - @web.expose - @web.require_admin - def rename_quota( self, trans, **kwd ): - quota, params = self._quota_op( trans, 'rename_quota_button', self._rename_quota, kwd ) - if not quota: - return - return trans.fill_template( '/admin/quota/quota_rename.mako', - id=params.id, - name=params.name or quota.name, - description=params.description or quota.description, - webapp=params.webapp, - message=params.message, - status=params.status ) - - @web.expose - @web.require_admin - def manage_users_and_groups_for_quota( self, trans, **kwd ): - quota, params = self._quota_op( trans, 'quota_members_edit_button', self._manage_users_and_groups_for_quota, kwd ) - if not quota: - return - in_users = [] - out_users = [] - in_groups = [] - out_groups = [] - for user in trans.sa_session.query( trans.app.model.User ) \ - .filter( trans.app.model.User.table.c.deleted == expression.false() ) \ - .order_by( trans.app.model.User.table.c.email ): - if user in [ x.user for x in quota.users ]: - in_users.append( ( user.id, user.email ) ) - else: - out_users.append( ( user.id, user.email ) ) - for group in trans.sa_session.query( trans.app.model.Group ) \ - .filter( trans.app.model.Group.table.c.deleted == expression.false()) \ - .order_by( trans.app.model.Group.table.c.name ): - if group in [ x.group for x in quota.groups ]: - in_groups.append( ( group.id, group.name ) ) - else: - out_groups.append( ( group.id, group.name ) ) - return trans.fill_template( '/admin/quota/quota.mako', - id=params.id, - name=quota.name, - in_users=in_users, - out_users=out_users, - in_groups=in_groups, - out_groups=out_groups, - webapp=params.webapp, - message=params.message, - status=params.status ) - - @web.expose - @web.require_admin - def edit_quota( self, trans, **kwd ): - quota, params = self._quota_op( trans, 'edit_quota_button', self._edit_quota, kwd ) - if not quota: - return - return trans.fill_template( '/admin/quota/quota_edit.mako', - id=params.id, - operation=params.operation or quota.operation, - display_amount=params.amount or quota.display_amount, - webapp=params.webapp, - message=params.message, - status=params.status ) - - @web.expose + if operation == 'delete': + message = self._delete_quota(quotas) + elif operation == 'undelete': + message = self._undelete_quota(quotas) + elif operation == 'purge': + message = self._purge_quota(quotas) + elif operation == 'unset as default': + message = self._unset_quota_default(quotas[0]) + except ActionInputError as e: + message, status = (e.err_msg, 'error') + if message: + kwargs['message'] = util.sanitize_text(message) + kwargs['status'] = status or 'done' + kwargs['dict_format'] = True + return self.quota_list_grid(trans, **kwargs) + + @web.expose_api @web.require_admin - def set_quota_default( self, trans, **kwd ): - quota, params = self._quota_op( trans, 'set_default_quota_button', self._set_quota_default, kwd ) - if not quota: - return - if params.default: - default = params.default - elif quota.default: - default = quota.default[0].type + def create_quota(self, trans, payload=None, **kwd): + if trans.request.method == 'GET': + all_users = [] + all_groups = [] + for user in trans.sa_session.query(trans.app.model.User) \ + .filter(trans.app.model.User.table.c.deleted == false()) \ + .order_by(trans.app.model.User.table.c.email): + all_users.append((user.email, trans.security.encode_id(user.id))) + for group in trans.sa_session.query(trans.app.model.Group) \ + .filter(trans.app.model.Group.table.c.deleted == false()) \ + .order_by(trans.app.model.Group.table.c.name): + all_groups.append((group.name, trans.security.encode_id(group.id))) + default_options = [('No', 'no')] + for typ in trans.app.model.DefaultQuotaAssociation.types.__dict__.values(): + default_options.append(('Yes, ' + typ, typ)) + return {'title' : 'Create Quota', + 'inputs' : [ + { + 'name' : 'name', + 'label' : 'Name' + }, { + 'name' : 'description', + 'label' : 'Description' + }, { + 'name' : 'amount', + 'label' : 'Amount', + 'help' : 'Examples: "10000MB", "99 gb", "0.2T", "unlimited"' + }, { + 'name' : 'operation', + 'label' : 'Assign, increase by amount, or decrease by amount?', + 'options' : [('=', '='), ('+', '+'), ('-', '-')] + }, { + 'name' : 'default', + 'label' : 'Is this quota a default for a class of users (if yes, what type)?', + 'options' : default_options, + 'help' : 'Warning: Any users or groups associated with this quota will be disassociated.' + }, + build_select_input('in_groups', 'Groups', all_groups, []), + build_select_input('in_users', 'Users', all_users, [])]} else: - default = "no" - return trans.fill_template( '/admin/quota/quota_set_default.mako', - id=params.id, - default=default, - webapp=params.webapp, - message=params.message, - status=params.status ) + try: + quota, message = self._create_quota(util.Params(payload), decode_id=trans.security.decode_id) + return {'message': message} + except ActionInputError as e: + return message_exception(trans, e.err_msg) - @web.expose + @web.expose_api @web.require_admin - def unset_quota_default( self, trans, **kwd ): - quota, params = self._quota_op( trans, True, self._unset_quota_default, kwd ) - if not quota: - return - return trans.response.send_redirect( web.url_for( controller='admin', - action='quotas', - webapp=params.webapp, - message=sanitize_text( params.message ), - status='error' ) ) + def rename_quota(self, trans, payload=None, **kwd): + id = kwd.get('id') + if not id: + return message_exception(trans, 'No quota id received for renaming.') + quota = get_quota(trans, id) + if trans.request.method == 'GET': + return { + 'title' : 'Change quota name and description for \'%s\'' % util.sanitize_text(quota.name), + 'inputs' : [{ + 'name' : 'name', + 'label' : 'Name', + 'value' : quota.name + }, { + 'name' : 'description', + 'label' : 'Description', + 'value' : quota.description + }] + } + else: + try: + return {'message': self._rename_quota(quota, util.Params(payload))} + except ActionInputError as e: + return message_exception(trans, e.err_msg) - @web.expose + @web.expose_api @web.require_admin - def mark_quota_deleted( self, trans, **kwd ): - quota, params = self._quota_op( trans, True, self._mark_quota_deleted, kwd, listify=True ) - if not quota: - return - return trans.response.send_redirect( web.url_for( controller='admin', - action='quotas', - webapp=params.webapp, - message=sanitize_text( params.message ), - status='error' ) ) + def manage_users_and_groups_for_quota(self, trans, payload=None, **kwd): + quota_id = kwd.get('id') + if not quota_id: + return message_exception(trans, 'Invalid quota id (%s) received' % str(quota_id)) + quota = get_quota(trans, quota_id) + if trans.request.method == 'GET': + in_users = [] + all_users = [] + in_groups = [] + all_groups = [] + for user in trans.sa_session.query(trans.app.model.User) \ + .filter(trans.app.model.User.table.c.deleted == false()) \ + .order_by(trans.app.model.User.table.c.email): + if user in [x.user for x in quota.users]: + in_users.append(trans.security.encode_id(user.id)) + all_users.append((user.email, trans.security.encode_id(user.id))) + for group in trans.sa_session.query(trans.app.model.Group) \ + .filter(trans.app.model.Group.table.c.deleted == false()) \ + .order_by(trans.app.model.Group.table.c.name): + if group in [x.group for x in quota.groups]: + in_groups.append(trans.security.encode_id(group.id)) + all_groups.append((group.name, trans.security.encode_id(group.id))) + return {'title' : 'Quota \'%s\'' % quota.name, + 'message': 'Quota \'%s\' is currently associated with %d user(s) and %d group(s).' % + (quota.name, len(in_users), len(in_groups)), + 'status' : 'info', + 'inputs' : [build_select_input('in_groups', 'Groups', all_groups, in_groups), + build_select_input('in_users', 'Users', all_users, in_users)]} + else: + try: + return {'message': self._manage_users_and_groups_for_quota(quota, util.Params(payload), decode_id=trans.security.decode_id)} + except ActionInputError as e: + return message_exception(trans, e.err_msg) - @web.expose + @web.expose_api @web.require_admin - def undelete_quota( self, trans, **kwd ): - quota, params = self._quota_op( trans, True, self._undelete_quota, kwd, listify=True ) - if not quota: - return - return trans.response.send_redirect( web.url_for( controller='admin', - action='quotas', - webapp=params.webapp, - message=sanitize_text( params.message ), - status='error' ) ) + def edit_quota(self, trans, payload=None, **kwd): + id = kwd.get('id') + if not id: + return message_exception(trans, 'No quota id received for renaming.') + quota = get_quota(trans, id) + if trans.request.method == 'GET': + return { + 'title' : 'Edit quota size for \'%s\'' % util.sanitize_text(quota.name), + 'inputs' : [{ + 'name' : 'amount', + 'label' : 'Amount', + 'value' : quota.display_amount, + 'help' : 'Examples: "10000MB", "99 gb", "0.2T", "unlimited"' + }, { + 'name' : 'operation', + 'label' : 'Assign, increase by amount, or decrease by amount?', + 'options' : [('=', '='), ('+', '+'), ('-', '-')], + 'value' : quota.operation + }] + } + else: + try: + return {'message': self._edit_quota(quota, util.Params(payload))} + except ActionInputError as e: + return message_exception(trans, e.err_msg) - @web.expose + @web.expose_api @web.require_admin - def purge_quota( self, trans, **kwd ): - quota, params = self._quota_op( trans, True, self._purge_quota, kwd, listify=True ) - if not quota: - return - return trans.response.send_redirect( web.url_for( controller='admin', - action='quotas', - webapp=params.webapp, - message=sanitize_text( params.message ), - status='error' ) ) - - def _quota_op( self, trans, do_op, op_method, kwd, listify=False ): - params = self.get_quota_params( kwd ) - if listify: - quota = [] - messages = [] - for id in galaxy.util.listify( params.id ): - try: - quota.append( self.get_quota( trans, id ) ) - except MessageException as e: - messages.append( str( e ) ) - if messages: - return None, trans.response.send_redirect( web.url_for( controller='admin', - action='quotas', - webapp=params.webapp, - message=sanitize_text( ', '.join( messages ) ), - status='error' ) ) + def set_quota_default(self, trans, payload=None, **kwd): + id = kwd.get('id') + if not id: + return message_exception(trans, 'No quota id received for renaming.') + quota = get_quota(trans, id) + if trans.request.method == 'GET': + default_value = quota.default[0].type if quota.default else 'no' + default_options = [('No', 'no')] + for typ in trans.app.model.DefaultQuotaAssociation.types.__dict__.values(): + default_options.append(('Yes, ' + typ, typ)) + return { + 'title' : 'Set quota default for \'%s\'' % util.sanitize_text(quota.name), + 'inputs' : [{ + 'name' : 'default', + 'label' : 'Assign, increase by amount, or decrease by amount?', + 'options' : default_options, + 'value' : default_value, + 'help' : 'Warning: Any users or groups associated with this quota will be disassociated.' + }] + } else: try: - quota = self.get_quota( trans, params.id, deleted=False ) - except MessageException as e: - return None, trans.response.send_redirect( web.url_for( controller='admin', - action='quotas', - webapp=params.webapp, - message=sanitize_text( str( e ) ), - status='error' ) ) - if do_op is True or ( do_op is not False and params.get( do_op, False ) ): - try: - message = op_method( quota, params ) - return None, trans.response.send_redirect( web.url_for( controller='admin', - action='quotas', - webapp=params.webapp, - message=sanitize_text( message ), - status='done' ) ) - except MessageException as e: - params.message = e.err_msg - params.status = e.type - return quota, params + return {'message': self._set_quota_default(quota, util.Params(payload))} + except ActionInputError as e: + return message_exception(trans, e.err_msg) @web.expose @web.require_admin - def impersonate( self, trans, email=None, **kwd ): + def impersonate(self, trans, email=None, **kwd): if not trans.app.config.allow_user_impersonation: - return trans.show_error_message( "User impersonation is not enabled in this instance of Galaxy." ) + return trans.show_error_message("User impersonation is not enabled in this instance of Galaxy.") message = '' status = 'done' emails = None if email is not None: - user = trans.sa_session.query( trans.app.model.User ).filter_by( email=email ).first() + user = trans.sa_session.query(trans.app.model.User).filter_by(email=email).first() if user: trans.handle_user_logout() trans.handle_user_login(user) - message = 'You are now logged in as %s, return to the home page' % ( email, url_for( controller='root' ) ) + message = 'You are now logged in as %s, return to the home page' % (email, url_for(controller='root')) emails = [] else: message = 'Invalid user selected' status = 'error' if emails is None: - emails = [ u.email for u in trans.sa_session.query( trans.app.model.User ).enable_eagerloads( False ).all() ] - return trans.fill_template( 'admin/impersonate.mako', emails=emails, message=message, status=status ) + emails = [u.email for u in trans.sa_session.query(trans.app.model.User).enable_eagerloads(False).all()] + return trans.fill_template('admin/impersonate.mako', emails=emails, message=message, status=status) - def check_for_tool_dependencies( self, trans, migration_stage ): + def check_for_tool_dependencies(self, trans, migration_stage): # Get the 000x_tools.xml file associated with migration_stage. - tools_xml_file_path = os.path.abspath( os.path.join( trans.app.config.root, 'scripts', 'migrate_tools', '%04d_tools.xml' % migration_stage ) ) - tree = galaxy.util.parse_xml( tools_xml_file_path ) + tools_xml_file_path = os.path.abspath(os.path.join(trans.app.config.root, 'scripts', 'migrate_tools', '%04d_tools.xml' % migration_stage)) + tree = util.parse_xml(tools_xml_file_path) root = tree.getroot() - tool_shed = root.get( 'name' ) - shed_url = common_util.get_tool_shed_url_from_tool_shed_registry( trans.app, tool_shed ) + tool_shed = root.get('name') + shed_url = common_util.get_tool_shed_url_from_tool_shed_registry(trans.app, tool_shed) repo_name_dependency_tups = [] if shed_url: for elem in root: if elem.tag == 'repository': tool_dependencies = [] tool_dependencies_dict = {} - repository_name = elem.get( 'name' ) - changeset_revision = elem.get( 'changeset_revision' ) - params = dict( name=repository_name, owner='devteam', changeset_revision=changeset_revision ) - pathspec = [ 'repository', 'get_tool_dependencies' ] - text = url_get( shed_url, password_mgr=self.app.tool_shed_registry.url_auth( shed_url ), pathspec=pathspec, params=params ) + repository_name = elem.get('name') + changeset_revision = elem.get('changeset_revision') + params = dict(name=repository_name, owner='devteam', changeset_revision=changeset_revision) + pathspec = ['repository', 'get_tool_dependencies'] + text = url_get(shed_url, password_mgr=self.app.tool_shed_registry.url_auth(shed_url), pathspec=pathspec, params=params) if text: - tool_dependencies_dict = encoding_util.tool_shed_decode( text ) + tool_dependencies_dict = encoding_util.tool_shed_decode(text) for dependency_key, requirements_dict in tool_dependencies_dict.items(): - tool_dependency_name = requirements_dict[ 'name' ] - tool_dependency_version = requirements_dict[ 'version' ] - tool_dependency_type = requirements_dict[ 'type' ] - tool_dependency_readme = requirements_dict.get( 'readme', '' ) - tool_dependencies.append( ( tool_dependency_name, tool_dependency_version, tool_dependency_type, tool_dependency_readme ) ) - repo_name_dependency_tups.append( ( repository_name, tool_dependencies ) ) + tool_dependency_name = requirements_dict['name'] + tool_dependency_version = requirements_dict['version'] + tool_dependency_type = requirements_dict['type'] + tool_dependency_readme = requirements_dict.get('readme', '') + tool_dependencies.append((tool_dependency_name, tool_dependency_version, tool_dependency_type, tool_dependency_readme)) + repo_name_dependency_tups.append((repository_name, tool_dependencies)) return repo_name_dependency_tups @web.expose @web.require_admin - def review_tool_migration_stages( self, trans, **kwd ): - message = escape( galaxy.util.restore_text( kwd.get( 'message', '' ) ) ) - status = galaxy.util.restore_text( kwd.get( 'status', 'done' ) ) + def review_tool_migration_stages(self, trans, **kwd): + message = escape(util.restore_text(kwd.get('message', ''))) + status = util.restore_text(kwd.get('status', 'done')) migration_stages_dict = odict() migration_modules = [] - migration_scripts_dir = os.path.abspath( os.path.join( trans.app.config.root, 'lib', 'tool_shed', 'galaxy_install', 'migrate', 'versions' ) ) - migration_scripts_dir_contents = os.listdir( migration_scripts_dir ) + migration_scripts_dir = os.path.abspath(os.path.join(trans.app.config.root, 'lib', 'tool_shed', 'galaxy_install', 'migrate', 'versions')) + migration_scripts_dir_contents = os.listdir(migration_scripts_dir) for item in migration_scripts_dir_contents: - if os.path.isfile( os.path.join( migration_scripts_dir, item ) ) and item.endswith( '.py' ): - module = item.replace( '.py', '' ) - migration_modules.append( module ) + if os.path.isfile(os.path.join(migration_scripts_dir, item)) and item.endswith('.py'): + module = item.replace('.py', '') + migration_modules.append(module) if migration_modules: migration_modules.sort() # Remove the 0001_tools.py script since it is the seed. - migration_modules = migration_modules[ 1: ] + migration_modules = migration_modules[1:] # Reverse the list so viewing will be newest to oldest. migration_modules.reverse() for migration_module in migration_modules: - migration_stage = int( migration_module.replace( '_tools', '' ) ) - repo_name_dependency_tups = self.check_for_tool_dependencies( trans, migration_stage ) - open_file_obj, file_name, description = imp.find_module( migration_module, [ migration_scripts_dir ] ) - imported_module = imp.load_module( 'upgrade', open_file_obj, file_name, description ) + migration_stage = int(migration_module.replace('_tools', '')) + repo_name_dependency_tups = self.check_for_tool_dependencies(trans, migration_stage) + open_file_obj, file_name, description = imp.find_module(migration_module, [migration_scripts_dir]) + imported_module = imp.load_module('upgrade', open_file_obj, file_name, description) migration_info = imported_module.__doc__ open_file_obj.close() - migration_stages_dict[ migration_stage ] = ( migration_info, repo_name_dependency_tups ) - return trans.fill_template( 'admin/review_tool_migration_stages.mako', - migration_stages_dict=migration_stages_dict, - message=message, - status=status ) + migration_stages_dict[migration_stage] = (migration_info, repo_name_dependency_tups) + return trans.fill_template('admin/review_tool_migration_stages.mako', + migration_stages_dict=migration_stages_dict, + message=message, + status=status) @web.expose @web.require_admin - def tool_errors( self, trans, **kwd ): + def tool_errors(self, trans, **kwd): return trans.fill_template('admin/tool_errors.mako', tool_errors=global_tool_errors.error_stack) @web.expose @web.require_admin - def view_datatypes_registry( self, trans, **kwd ): - message = escape( galaxy.util.restore_text( kwd.get( 'message', '' ) ) ) - status = galaxy.util.restore_text( kwd.get( 'status', 'done' ) ) - return trans.fill_template( 'admin/view_datatypes_registry.mako', message=message, status=status ) + def view_datatypes_registry(self, trans, **kwd): + message = escape(util.restore_text(kwd.get('message', ''))) + status = util.restore_text(kwd.get('status', 'done')) + return trans.fill_template('admin/view_datatypes_registry.mako', message=message, status=status) @web.expose @web.require_admin - def view_tool_data_tables( self, trans, **kwd ): - message = escape( galaxy.util.restore_text( kwd.get( 'message', '' ) ) ) - status = galaxy.util.restore_text( kwd.get( 'status', 'done' ) ) - return trans.fill_template( 'admin/view_data_tables_registry.mako', message=message, status=status ) + def view_tool_data_tables(self, trans, **kwd): + message = escape(util.restore_text(kwd.get('message', ''))) + status = util.restore_text(kwd.get('status', 'done')) + return trans.fill_template('admin/view_data_tables_registry.mako', message=message, status=status) @web.expose @web.require_admin - def display_applications( self, trans, **kwd ): - return trans.fill_template( 'admin/view_display_applications.mako', display_applications=trans.app.datatypes_registry.display_applications ) + def display_applications(self, trans, **kwd): + return trans.fill_template('admin/view_display_applications.mako', display_applications=trans.app.datatypes_registry.display_applications) @web.expose @web.require_admin - def reload_display_application( self, trans, **kwd ): + def reload_display_application(self, trans, **kwd): galaxy.queue_worker.send_control_task(trans.app, 'reload_display_application', noop_self=True, - kwargs={'display_application_ids': kwd.get( 'id' )} ) - reloaded, failed = trans.app.datatypes_registry.reload_display_applications( kwd.get( 'id' ) ) + kwargs={'display_application_ids': kwd.get('id')}) + reloaded, failed = trans.app.datatypes_registry.reload_display_applications(kwd.get('id')) if not reloaded and failed: - return trans.show_error_message( 'Unable to reload any of the %i requested display applications ("%s").' - % ( len( failed ), '", "'.join( failed ) ) ) + return trans.show_error_message('Unable to reload any of the %i requested display applications ("%s").' + % (len(failed), '", "'.join(failed))) if failed: - return trans.show_warn_message( 'Reloaded %i display applications ("%s"), but failed to reload %i display applications ("%s").' - % ( len( reloaded ), '", "'.join( reloaded ), len( failed ), '", "'.join( failed ) ) ) + return trans.show_warn_message('Reloaded %i display applications ("%s"), but failed to reload %i display applications ("%s").' + % (len(reloaded), '", "'.join(reloaded), len(failed), '", "'.join(failed))) if not reloaded: - return trans.show_warn_message( 'You need to request at least one display application to reload.' ) - return trans.show_ok_message( 'Reloaded %i requested display applications ("%s").' % ( len( reloaded ), '", "'.join( reloaded ) ) ) + return trans.show_warn_message('You need to request at least one display application to reload.') + return trans.show_ok_message('Reloaded %i requested display applications ("%s").' % (len(reloaded), '", "'.join(reloaded))) + + @web.expose + @web.require_admin + def center(self, trans, **kwd): + message = escape(kwd.get('message', '')) + status = kwd.get('status', 'done') + is_repo_installed = trans.install_model.context.query(trans.install_model.ToolShedRepository).first() is not None + installing_repository_ids = repository_util.get_ids_of_tool_shed_repositories_being_installed(trans.app, as_string=True) + return trans.fill_template('/webapps/galaxy/admin/center.mako', + is_repo_installed=is_repo_installed, + installing_repository_ids=installing_repository_ids, + message=message, + status=status) + + @web.expose + @web.require_admin + def package_tool(self, trans, **kwd): + params = util.Params(kwd) + message = util.restore_text(params.get('message', '')) + toolbox = self.app.toolbox + tool_id = None + if params.get('package_tool_button', False): + tool_id = params.get('tool_id', None) + try: + tool_tarball = trans.app.toolbox.package_tool(trans, tool_id) + trans.response.set_content_type('application/x-gzip') + download_file = open(tool_tarball) + os.unlink(tool_tarball) + tarball_path, filename = os.path.split(tool_tarball) + trans.response.headers["Content-Disposition"] = 'attachment; filename="%s.tgz"' % (tool_id) + return download_file + except Exception: + return trans.fill_template('/admin/package_tool.mako', + tool_id=tool_id, + toolbox=toolbox, + message=message, + status='error') + + @web.expose + @web.require_admin + def reload_tool(self, trans, **kwd): + params = util.Params(kwd) + message = util.restore_text(params.get('message', '')) + status = params.get('status', 'done') + toolbox = self.app.toolbox + tool_id = None + if params.get('reload_tool_button', False): + tool_id = kwd.get('tool_id', None) + galaxy.queue_worker.send_control_task(trans.app, 'reload_tool', noop_self=True, kwargs={'tool_id': tool_id}) + message, status = trans.app.toolbox.reload_tool_by_id(tool_id) + return trans.fill_template('/admin/reload_tool.mako', + tool_id=tool_id, + toolbox=toolbox, + message=message, + status=status) + + @web.expose_api + @web.require_admin + def tool_versions_list(self, trans, **kwd): + kwd['dict_format'] = True + return self.tool_version_list_grid(trans, **kwd) @web.expose + @web.json + @web.require_admin + def roles_list(self, trans, **kwargs): + message = kwargs.get('message') + status = kwargs.get('status') + if 'operation' in kwargs: + id = kwargs.get('id', None) + if not id: + message, status = ('Invalid role id (%s) received.' % str(id), 'error') + ids = util.listify(id) + operation = kwargs['operation'].lower().replace('+', ' ') + if operation == 'delete': + message, status = self._delete_role(trans, ids) + elif operation == 'undelete': + message, status = self._undelete_role(trans, ids) + elif operation == 'purge': + message, status = self._purge_role(trans, ids) + kwargs['dict_format'] = True + if message and status: + kwargs['message'] = util.sanitize_text(message) + kwargs['status'] = status + return self.role_list_grid(trans, **kwargs) + + @web.expose_api + @web.require_admin + def create_role(self, trans, payload=None, **kwd): + if trans.request.method == 'GET': + all_users = [] + all_groups = [] + for user in trans.sa_session.query(trans.app.model.User) \ + .filter(trans.app.model.User.table.c.deleted == false()) \ + .order_by(trans.app.model.User.table.c.email): + all_users.append((user.email, trans.security.encode_id(user.id))) + for group in trans.sa_session.query(trans.app.model.Group) \ + .filter(trans.app.model.Group.table.c.deleted == false()) \ + .order_by(trans.app.model.Group.table.c.name): + all_groups.append((group.name, trans.security.encode_id(group.id))) + return { + 'title' : 'Create Role', + 'inputs' : [{ + 'name' : 'name', + 'label' : 'Name' + }, { + 'name' : 'description', + 'label' : 'Description' + }, + build_select_input('in_groups', 'Groups', all_groups, []), + build_select_input('in_users', 'Users', all_users, []), { + 'name' : 'create_group_for_role', + 'label' : 'Create a new role of the same name for this group:', + 'type' : 'boolean' + }]} + else: + name = util.restore_text(payload.get('name', '')) + description = util.restore_text(payload.get('description', '')) + auto_create_checked = payload.get('auto_create') == 'true' + in_users = [trans.sa_session.query(trans.app.model.User).get(trans.security.decode_id(x)) for x in util.listify(payload.get('in_users'))] + in_groups = [trans.sa_session.query(trans.app.model.Group).get(trans.security.decode_id(x)) for x in util.listify(payload.get('in_groups'))] + if not name or not description: + return message_exception(trans, 'Enter a valid name and a description.') + elif trans.sa_session.query(trans.app.model.Role).filter(trans.app.model.Role.table.c.name == name).first(): + return message_exception(trans, 'Role names must be unique and a role with that name already exists, so choose another name.') + elif None in in_users or None in in_groups: + return message_exception(trans, 'One or more invalid user/group id has been provided.') + else: + # Create the role + role = trans.app.model.Role(name=name, description=description, type=trans.app.model.Role.types.ADMIN) + trans.sa_session.add(role) + # Create the UserRoleAssociations + for user in in_users: + ura = trans.app.model.UserRoleAssociation(user, role) + trans.sa_session.add(ura) + # Create the GroupRoleAssociations + for group in in_groups: + gra = trans.app.model.GroupRoleAssociation(group, role) + trans.sa_session.add(gra) + if auto_create_checked: + # Check if role with same name already exists + if trans.sa_session.query(trans.app.model.Group).filter(trans.app.model.Group.table.c.name == name).first(): + return message_exception(trans, 'A group with that name already exists, so choose another name or disable group creation.') + # Create the group + group = trans.app.model.Group(name=name) + trans.sa_session.add(group) + # Associate the group with the role + gra = trans.model.GroupRoleAssociation(group, role) + trans.sa_session.add(gra) + num_in_groups = len(in_groups) + 1 + else: + num_in_groups = len(in_groups) + trans.sa_session.flush() + message = 'Role \'%s\' has been created with %d associated users and %d associated groups.' % (role.name, len(in_users), num_in_groups) + if auto_create_checked: + message += 'One of the groups associated with this role is the newly created group with the same name.' + return {'message' : message} + + @web.expose_api + @web.require_admin + def rename_role(self, trans, payload=None, **kwd): + id = kwd.get('id') + if not id: + return message_exception(trans, 'No role id received for renaming.') + role = get_role(trans, id) + if trans.request.method == 'GET': + return { + 'title' : 'Change role name and description for \'%s\'' % util.sanitize_text(role.name), + 'inputs' : [{ + 'name' : 'name', + 'label' : 'Name', + 'value' : role.name + }, { + 'name' : 'description', + 'label' : 'Description', + 'value' : role.description + }] + } + else: + old_name = role.name + new_name = util.restore_text(payload.get('name')) + new_description = util.restore_text(payload.get('description')) + if not new_name: + return message_exception(trans, 'Enter a valid role name.') + else: + existing_role = trans.sa_session.query(trans.app.model.Role).filter(trans.app.model.Role.table.c.name == new_name).first() + if existing_role and existing_role.id != role.id: + return message_exception(trans, 'A role with that name already exists.') + else: + if not (role.name == new_name and role.description == new_description): + role.name = new_name + role.description = new_description + trans.sa_session.add(role) + trans.sa_session.flush() + return {'message': 'Role \'%s\' has been renamed to \'%s\'.' % (old_name, new_name)} + + @web.expose_api + @web.require_admin + def manage_users_and_groups_for_role(self, trans, payload=None, **kwd): + role_id = kwd.get('id') + if not role_id: + return message_exception(trans, 'Invalid role id (%s) received' % str(role_id)) + role = get_role(trans, role_id) + if trans.request.method == 'GET': + in_users = [] + all_users = [] + in_groups = [] + all_groups = [] + for user in trans.sa_session.query(trans.app.model.User) \ + .filter(trans.app.model.User.table.c.deleted == false()) \ + .order_by(trans.app.model.User.table.c.email): + if user in [x.user for x in role.users]: + in_users.append(trans.security.encode_id(user.id)) + all_users.append((user.email, trans.security.encode_id(user.id))) + for group in trans.sa_session.query(trans.app.model.Group) \ + .filter(trans.app.model.Group.table.c.deleted == false()) \ + .order_by(trans.app.model.Group.table.c.name): + if group in [x.group for x in role.groups]: + in_groups.append(trans.security.encode_id(group.id)) + all_groups.append((group.name, trans.security.encode_id(group.id))) + return {'title' : 'Role \'%s\'' % role.name, + 'message': 'Role \'%s\' is currently associated with %d user(s) and %d group(s).' % + (role.name, len(in_users), len(in_groups)), + 'status' : 'info', + 'inputs' : [build_select_input('in_groups', 'Groups', all_groups, in_groups), + build_select_input('in_users', 'Users', all_users, in_users)]} + else: + in_users = [trans.sa_session.query(trans.app.model.User).get(trans.security.decode_id(x)) for x in util.listify(payload.get('in_users'))] + in_groups = [trans.sa_session.query(trans.app.model.Group).get(trans.security.decode_id(x)) for x in util.listify(payload.get('in_groups'))] + if None in in_users or None in in_groups: + return message_exception(trans, 'One or more invalid user/group id has been provided.') + for ura in role.users: + user = trans.sa_session.query(trans.app.model.User).get(ura.user_id) + if user not in in_users: + # Delete DefaultUserPermissions for previously associated users that have been removed from the role + for dup in user.default_permissions: + if role == dup.role: + trans.sa_session.delete(dup) + # Delete DefaultHistoryPermissions for previously associated users that have been removed from the role + for history in user.histories: + for dhp in history.default_permissions: + if role == dhp.role: + trans.sa_session.delete(dhp) + trans.sa_session.flush() + trans.app.security_agent.set_entity_role_associations(roles=[role], users=in_users, groups=in_groups) + trans.sa_session.refresh(role) + return {'message' : 'Role \'%s\' has been updated with %d associated users and %d associated groups.' % (role.name, len(in_users), len(in_groups))} + + def _delete_role(self, trans, ids): + message = 'Deleted %d roles: ' % len(ids) + for role_id in ids: + role = get_role(trans, role_id) + role.deleted = True + trans.sa_session.add(role) + trans.sa_session.flush() + message += ' %s ' % role.name + return (message, 'done') + + def _undelete_role(self, trans, ids): + count = 0 + undeleted_roles = "" + for role_id in ids: + role = get_role(trans, role_id) + if not role.deleted: + return ("Role '%s' has not been deleted, so it cannot be undeleted." % role.name, "error") + role.deleted = False + trans.sa_session.add(role) + trans.sa_session.flush() + count += 1 + undeleted_roles += " %s" % role.name + return ("Undeleted %d roles: %s" % (count, undeleted_roles), "done") + + def _purge_role(self, trans, ids): + # This method should only be called for a Role that has previously been deleted. + # Purging a deleted Role deletes all of the following from the database: + # - UserRoleAssociations where role_id == Role.id + # - DefaultUserPermissions where role_id == Role.id + # - DefaultHistoryPermissions where role_id == Role.id + # - GroupRoleAssociations where role_id == Role.id + # - DatasetPermissionss where role_id == Role.id + message = "Purged %d roles: " % len(ids) + for role_id in ids: + role = get_role(trans, role_id) + if not role.deleted: + return ("Role '%s' has not been deleted, so it cannot be purged." % role.name, "error") + # Delete UserRoleAssociations + for ura in role.users: + user = trans.sa_session.query(trans.app.model.User).get(ura.user_id) + # Delete DefaultUserPermissions for associated users + for dup in user.default_permissions: + if role == dup.role: + trans.sa_session.delete(dup) + # Delete DefaultHistoryPermissions for associated users + for history in user.histories: + for dhp in history.default_permissions: + if role == dhp.role: + trans.sa_session.delete(dhp) + trans.sa_session.delete(ura) + # Delete GroupRoleAssociations + for gra in role.groups: + trans.sa_session.delete(gra) + # Delete DatasetPermissionss + for dp in role.dataset_actions: + trans.sa_session.delete(dp) + trans.sa_session.flush() + message += " %s " % role.name + return (message, "done") + + @web.expose_api + @web.require_admin + def groups_list(self, trans, **kwargs): + message = kwargs.get('message') + status = kwargs.get('status') + if 'operation' in kwargs: + id = kwargs.get('id') + if not id: + return message_exception(trans, 'Invalid group id (%s) received.' % str(id)) + ids = util.listify(id) + operation = kwargs['operation'].lower().replace('+', ' ') + if operation == 'delete': + message, status = self._delete_group(trans, ids) + elif operation == 'undelete': + message, status = self._undelete_group(trans, ids) + elif operation == 'purge': + message, status = self._purge_group(trans, ids) + kwargs['dict_format'] = True + if message and status: + kwargs['message'] = util.sanitize_text(message) + kwargs['status'] = status + return self.group_list_grid(trans, **kwargs) + + @web.expose_api + @web.require_admin + def rename_group(self, trans, payload=None, **kwd): + id = kwd.get('id') + if not id: + return message_exception(trans, 'No group id received for renaming.') + group = get_group(trans, id) + if trans.request.method == 'GET': + return { + 'title' : 'Change group name for \'%s\'' % util.sanitize_text(group.name), + 'inputs' : [{ + 'name' : 'name', + 'label' : 'Name', + 'value' : group.name + }] + } + else: + old_name = group.name + new_name = util.restore_text(payload.get('name')) + if not new_name: + return message_exception(trans, 'Enter a valid group name.') + else: + existing_group = trans.sa_session.query(trans.app.model.Group).filter(trans.app.model.Group.table.c.name == new_name).first() + if existing_group and existing_group.id != group.id: + return message_exception(trans, 'A group with that name already exists.') + else: + if not (group.name == new_name): + group.name = new_name + trans.sa_session.add(group) + trans.sa_session.flush() + return {'message': 'Group \'%s\' has been renamed to \'%s\'.' % (old_name, new_name)} + + @web.expose_api + @web.require_admin + def manage_users_and_roles_for_group(self, trans, payload=None, **kwd): + group_id = kwd.get('id') + if not group_id: + return message_exception(trans, 'Invalid group id (%s) received' % str(group_id)) + group = get_group(trans, group_id) + if trans.request.method == 'GET': + in_users = [] + all_users = [] + in_roles = [] + all_roles = [] + for user in trans.sa_session.query(trans.app.model.User) \ + .filter(trans.app.model.User.table.c.deleted == false()) \ + .order_by(trans.app.model.User.table.c.email): + if user in [x.user for x in group.users]: + in_users.append(trans.security.encode_id(user.id)) + all_users.append((user.email, trans.security.encode_id(user.id))) + for role in trans.sa_session.query(trans.app.model.Role) \ + .filter(trans.app.model.Role.table.c.deleted == false()) \ + .order_by(trans.app.model.Role.table.c.name): + if role in [x.role for x in group.roles]: + in_roles.append(trans.security.encode_id(role.id)) + all_roles.append((role.name, trans.security.encode_id(role.id))) + return {'title' : 'Group \'%s\'' % group.name, + 'message': 'Group \'%s\' is currently associated with %d user(s) and %d role(s).' % + (group.name, len(in_users), len(in_roles)), + 'status' : 'info', + 'inputs' : [build_select_input('in_roles', 'Roles', all_roles, in_roles), + build_select_input('in_users', 'Users', all_users, in_users)]} + return {'message' : 'Not showing associated datasets, there are too many.', 'info' : 'info'} + else: + in_users = [trans.sa_session.query(trans.app.model.User).get(trans.security.decode_id(x)) for x in util.listify(payload.get('in_users'))] + in_roles = [trans.sa_session.query(trans.app.model.Role).get(trans.security.decode_id(x)) for x in util.listify(payload.get('in_roles'))] + if None in in_users or None in in_roles: + return message_exception(trans, 'One or more invalid user/role id has been provided.') + trans.app.security_agent.set_entity_group_associations(groups=[group], users=in_users, roles=in_roles) + trans.sa_session.refresh(group) + return {'message' : 'Group \'%s\' has been updated with %d associated users and %d associated roles.' % (group.name, len(in_users), len(in_roles))} + + @web.expose_api + @web.require_admin + def create_group(self, trans, payload=None, **kwd): + if trans.request.method == 'GET': + all_users = [] + all_roles = [] + for user in trans.sa_session.query(trans.app.model.User) \ + .filter(trans.app.model.User.table.c.deleted == false()) \ + .order_by(trans.app.model.User.table.c.email): + all_users.append((user.email, trans.security.encode_id(user.id))) + for role in trans.sa_session.query(trans.app.model.Role) \ + .filter(trans.app.model.Role.table.c.deleted == false()) \ + .order_by(trans.app.model.Role.table.c.name): + all_roles.append((role.name, trans.security.encode_id(role.id))) + return { + 'title' : 'Create Group', + 'inputs' : [{ + 'name' : 'name', + 'label' : 'Name' + }, + build_select_input('in_roles', 'Roles', all_roles, []), + build_select_input('in_users', 'Users', all_users, []), { + 'name' : 'auto_create', + 'label' : 'Create a new role of the same name for this group:', + 'type' : 'boolean' + }] + } + else: + name = util.restore_text(payload.get('name', '')) + auto_create_checked = payload.get('auto_create') == 'true' + in_users = [trans.sa_session.query(trans.app.model.User).get(trans.security.decode_id(x)) for x in util.listify(payload.get('in_users'))] + in_roles = [trans.sa_session.query(trans.app.model.Role).get(trans.security.decode_id(x)) for x in util.listify(payload.get('in_roles'))] + if not name: + return message_exception(trans, 'Enter a valid name.') + elif trans.sa_session.query(trans.app.model.Group).filter(trans.app.model.Group.table.c.name == name).first(): + return message_exception(trans, 'Group names must be unique and a group with that name already exists, so choose another name.') + elif None in in_users or None in in_roles: + return message_exception(trans, 'One or more invalid user/role id has been provided.') + else: + # Create the role + group = trans.app.model.Group(name=name) + trans.sa_session.add(group) + # Create the UserRoleAssociations + for user in in_users: + uga = trans.app.model.UserGroupAssociation(user, group) + trans.sa_session.add(uga) + # Create the GroupRoleAssociations + for role in in_roles: + gra = trans.app.model.GroupRoleAssociation(group, role) + trans.sa_session.add(gra) + if auto_create_checked: + # Check if role with same name already exists + if trans.sa_session.query(trans.app.model.Role).filter(trans.app.model.Role.table.c.name == name).first(): + return message_exception(trans, 'A role with that name already exists, so choose another name or disable role creation.') + # Create the role + role = trans.app.model.Role(name=name, description='Role for group %s' % name) + trans.sa_session.add(role) + # Associate the group with the role + gra = trans.model.GroupRoleAssociation(group, role) + trans.sa_session.add(gra) + num_in_roles = len(in_roles) + 1 + else: + num_in_roles = len(in_roles) + trans.sa_session.flush() + message = 'Group \'%s\' has been created with %d associated users and %d associated roles.' % (group.name, len(in_users), num_in_roles) + if auto_create_checked: + message += 'One of the roles associated with this group is the newly created role with the same name.' + return {'message' : message} + + def _delete_group(self, trans, ids): + message = 'Deleted %d groups: ' % len(ids) + for group_id in ids: + group = get_group(trans, group_id) + group.deleted = True + trans.sa_session.add(group) + trans.sa_session.flush() + message += ' %s ' % group.name + return (message, 'done') + + def _undelete_group(self, trans, ids): + count = 0 + undeleted_groups = "" + for group_id in ids: + group = get_group(trans, group_id) + if not group.deleted: + return ("Group '%s' has not been deleted, so it cannot be undeleted." % group.name, "error") + group.deleted = False + trans.sa_session.add(group) + trans.sa_session.flush() + count += 1 + undeleted_groups += " %s" % group.name + return ("Undeleted %d groups: %s" % (count, undeleted_groups), "done") + + def _purge_group(self, trans, ids): + message = "Purged %d groups: " % len(ids) + for group_id in ids: + group = get_group(trans, group_id) + if not group.deleted: + return ("Group '%s' has not been deleted, so it cannot be purged." % group.name, "error") + # Delete UserGroupAssociations + for uga in group.users: + trans.sa_session.delete(uga) + # Delete GroupRoleAssociations + for gra in group.roles: + trans.sa_session.delete(gra) + trans.sa_session.flush() + message += " %s " % group.name + return (message, "done") + + @web.expose + @web.require_admin + def create_new_user(self, trans, **kwd): + return trans.response.send_redirect(web.url_for(controller='user', + action='create', + cntrller='admin')) + + @web.expose_api @web.require_admin - def recalculate_user_disk_usage( self, trans, **kwd ): - user_id = kwd.get( 'id', None ) - user = trans.sa_session.query( trans.model.User ).get( trans.security.decode_id( user_id ) ) + def reset_user_password(self, trans, payload=None, **kwd): + users = {user_id: get_user(trans, user_id) for user_id in util.listify(kwd.get('id'))} + if users: + if trans.request.method == 'GET': + return { + 'message': 'Changes password(s) for: %s.' % ', '.join([user.email for user in users.itervalues()]), + 'status' : 'info', + 'inputs' : [{'name' : 'password', 'label' : 'New password', 'type' : 'password'}, + {'name' : 'confirm', 'label' : 'Confirm password', 'type' : 'password'}] + } + else: + password = payload.get('password') + confirm = payload.get('confirm') + if len(password) < 6: + return message_exception(trans, 'Use a password of at least 6 characters.') + elif password != confirm: + return message_exception(trans, 'Passwords do not match.') + for user in users.itervalues(): + user.set_password_cleartext(password) + trans.sa_session.add(user) + trans.sa_session.flush() + return {'message': 'Passwords reset for %d user(s).' % len(users)} + else: + return message_exception(trans, 'Please specify user ids.') + + def _delete_user(self, trans, ids): + message = 'Deleted %d users: ' % len(ids) + for user_id in ids: + user = get_user(trans, user_id) + user.deleted = True + trans.sa_session.add(user) + trans.sa_session.flush() + message += ' %s ' % user.email + return (message, 'done') + + def _undelete_user(self, trans, ids): + count = 0 + undeleted_users = "" + for user_id in ids: + user = get_user(trans, user_id) + if not user.deleted: + message = 'User \'%s\' has not been deleted, so it cannot be undeleted.' % user.email + return (message, 'error') + user.deleted = False + trans.sa_session.add(user) + trans.sa_session.flush() + count += 1 + undeleted_users += ' %s' % user.email + message = 'Undeleted %d users: %s' % (count, undeleted_users) + return (message, 'done') + + def _purge_user(self, trans, ids): + # This method should only be called for a User that has previously been deleted. + # We keep the User in the database ( marked as purged ), and stuff associated + # with the user's private role in case we want the ability to unpurge the user + # some time in the future. + # Purging a deleted User deletes all of the following: + # - History where user_id = User.id + # - HistoryDatasetAssociation where history_id = History.id + # - Dataset where HistoryDatasetAssociation.dataset_id = Dataset.id + # - UserGroupAssociation where user_id == User.id + # - UserRoleAssociation where user_id == User.id EXCEPT FOR THE PRIVATE ROLE + # - UserAddress where user_id == User.id + # Purging Histories and Datasets must be handled via the cleanup_datasets.py script + message = 'Purged %d users: ' % len(ids) + for user_id in ids: + user = get_user(trans, user_id) + if not user.deleted: + return ('User \'%s\' has not been deleted, so it cannot be purged.' % user.email, 'error') + private_role = trans.app.security_agent.get_private_user_role(user) + # Delete History + for h in user.active_histories: + trans.sa_session.refresh(h) + for hda in h.active_datasets: + # Delete HistoryDatasetAssociation + d = trans.sa_session.query(trans.app.model.Dataset).get(hda.dataset_id) + # Delete Dataset + if not d.deleted: + d.deleted = True + trans.sa_session.add(d) + hda.deleted = True + trans.sa_session.add(hda) + h.deleted = True + trans.sa_session.add(h) + # Delete UserGroupAssociations + for uga in user.groups: + trans.sa_session.delete(uga) + # Delete UserRoleAssociations EXCEPT FOR THE PRIVATE ROLE + for ura in user.roles: + if ura.role_id != private_role.id: + trans.sa_session.delete(ura) + # Delete UserAddresses + for address in user.addresses: + trans.sa_session.delete(address) + # Purge the user + user.purged = True + trans.sa_session.add(user) + trans.sa_session.flush() + message += '%s ' % user.email + return (message, 'done') + + def _recalculate_user(self, trans, user_id): + user = trans.sa_session.query(trans.model.User).get(trans.security.decode_id(user_id)) if not user: - return trans.show_error_message( "User not found for id (%s)" % sanitize_text( str( user_id ) ) ) + return ('User not found for id (%s)' % sanitize_text(str(user_id)), 'error') current = user.get_disk_usage() user.calculate_and_set_disk_usage() new = user.get_disk_usage() - if new in ( current, None ): - message = 'Usage is unchanged at %s.' % nice_size( current ) + if new in (current, None): + message = 'Usage is unchanged at %s.' % nice_size(current) + else: + message = 'Usage has changed by %s to %s.' % (nice_size(new - current), nice_size(new)) + return (message, 'done') + + @web.expose + @web.require_admin + def name_autocomplete_data(self, trans, q=None, limit=None, timestamp=None): + """Return autocomplete data for user emails""" + ac_data = "" + for user in trans.sa_session.query(trans.app.model.User).filter_by(deleted=False).filter(func.lower(trans.app.model.User.email).like(q.lower() + "%")): + ac_data = ac_data + user.email + "\n" + return ac_data + + @web.expose_api + @web.require_admin + def manage_roles_and_groups_for_user(self, trans, payload=None, **kwd): + user_id = kwd.get('id') + if not user_id: + return message_exception(trans, 'Invalid user id (%s) received' % str(user_id)) + user = get_user(trans, user_id) + if trans.request.method == 'GET': + in_roles = [] + all_roles = [] + in_groups = [] + all_groups = [] + for role in trans.sa_session.query(trans.app.model.Role).filter(trans.app.model.Role.table.c.deleted == false()) \ + .order_by(trans.app.model.Role.table.c.name): + if role in [x.role for x in user.roles]: + in_roles.append(trans.security.encode_id(role.id)) + if role.type != trans.app.model.Role.types.PRIVATE: + # There is a 1 to 1 mapping between a user and a PRIVATE role, so private roles should + # not be listed in the roles form fields, except for the currently selected user's private + # role, which should always be in in_roles. The check above is added as an additional + # precaution, since for a period of time we were including private roles in the form fields. + all_roles.append((role.name, trans.security.encode_id(role.id))) + for group in trans.sa_session.query(trans.app.model.Group).filter(trans.app.model.Group.table.c.deleted == false()) \ + .order_by(trans.app.model.Group.table.c.name): + if group in [x.group for x in user.groups]: + in_groups.append(trans.security.encode_id(group.id)) + all_groups.append((group.name, trans.security.encode_id(group.id))) + return {'title' : 'Roles and groups for \'%s\'' % user.email, + 'message': 'User \'%s\' is currently associated with %d role(s) and is a member of %d group(s).' % + (user.email, len(in_roles) - 1, len(in_groups)), + 'status' : 'info', + 'inputs' : [build_select_input('in_roles', 'Roles', all_roles, in_roles), + build_select_input('in_groups', 'Groups', all_groups, in_groups)]} else: - message = 'Usage has changed by %s to %s.' % ( nice_size( new - current ), nice_size( new ) ) - return trans.response.send_redirect( web.url_for( controller='admin', - action='users', - message=sanitize_text( message ), - status='info' ) ) + in_roles = [trans.sa_session.query(trans.app.model.Role).get(trans.security.decode_id(x)) for x in util.listify(payload.get('in_roles'))] + in_groups = [trans.sa_session.query(trans.app.model.Group).get(trans.security.decode_id(x)) for x in util.listify(payload.get('in_groups'))] + if None in in_groups or None in in_roles: + return message_exception(trans, 'One or more invalid role/group id has been provided.') + + # make sure the user is not dis-associating himself from his private role + private_role = trans.app.security_agent.get_private_user_role(user) + if private_role not in in_roles: + in_roles.append(private_role) + + trans.app.security_agent.set_entity_user_associations(users=[user], roles=in_roles, groups=in_groups) + trans.sa_session.refresh(user) + return {'message' : 'User \'%s\' has been updated with %d associated roles and %d associated groups (private roles are not displayed).' % (user.email, len(in_roles) - 1, len(in_groups))} + + @web.expose + @web.require_admin + def jobs(self, trans, stop=[], stop_msg=None, cutoff=180, job_lock=None, ajl_submit=None, **kwd): + deleted = [] + msg = None + status = None + job_ids = util.listify(stop) + if job_ids and stop_msg in [None, '']: + msg = 'Please enter an error message to display to the user describing why the job was terminated' + status = 'error' + elif job_ids: + if stop_msg[-1] not in PUNCTUATION: + stop_msg += '.' + for job_id in job_ids: + error_msg = "This job was stopped by an administrator: %s Contact support for additional help." \ + % (stop_msg, self.app.config.get("support_url", "https://galaxyproject.org/support/")) + if trans.app.config.track_jobs_in_database: + job = trans.sa_session.query(trans.app.model.Job).get(job_id) + job.stderr = error_msg + job.set_state(trans.app.model.Job.states.DELETED_NEW) + trans.sa_session.add(job) + else: + trans.app.job_manager.job_stop_queue.put(job_id, error_msg=error_msg) + deleted.append(str(job_id)) + if deleted: + msg = 'Queued job' + if len(deleted) > 1: + msg += 's' + msg += ' for deletion: ' + msg += ', '.join(deleted) + status = 'done' + trans.sa_session.flush() + if ajl_submit: + if job_lock == 'on': + galaxy.queue_worker.send_control_task(trans.app, 'admin_job_lock', + kwargs={'job_lock': True}) + job_lock = True + else: + galaxy.queue_worker.send_control_task(trans.app, 'admin_job_lock', + kwargs={'job_lock': False}) + job_lock = False + else: + job_lock = trans.app.job_manager.job_lock + cutoff_time = datetime.utcnow() - timedelta(seconds=int(cutoff)) + jobs = trans.sa_session.query(trans.app.model.Job) \ + .filter(and_(trans.app.model.Job.table.c.update_time < cutoff_time, + or_(trans.app.model.Job.state == trans.app.model.Job.states.NEW, + trans.app.model.Job.state == trans.app.model.Job.states.QUEUED, + trans.app.model.Job.state == trans.app.model.Job.states.RUNNING, + trans.app.model.Job.state == trans.app.model.Job.states.UPLOAD))) \ + .order_by(trans.app.model.Job.table.c.update_time.desc()).all() + recent_jobs = trans.sa_session.query(trans.app.model.Job) \ + .filter(and_(trans.app.model.Job.table.c.update_time > cutoff_time, + or_(trans.app.model.Job.state == trans.app.model.Job.states.ERROR, + trans.app.model.Job.state == trans.app.model.Job.states.OK))) \ + .order_by(trans.app.model.Job.table.c.update_time.desc()).all() + last_updated = {} + for job in jobs: + delta = datetime.utcnow() - job.update_time + if delta.days > 0: + last_updated[job.id] = '%s hours' % (delta.days * 24 + int(delta.seconds / 60 / 60)) + elif delta > timedelta(minutes=59): + last_updated[job.id] = '%s hours' % int(delta.seconds / 60 / 60) + else: + last_updated[job.id] = '%s minutes' % int(delta.seconds / 60) + finished = {} + for job in recent_jobs: + delta = datetime.utcnow() - job.update_time + if delta.days > 0: + finished[job.id] = '%s hours' % (delta.days * 24 + int(delta.seconds / 60 / 60)) + elif delta > timedelta(minutes=59): + finished[job.id] = '%s hours' % int(delta.seconds / 60 / 60) + else: + finished[job.id] = '%s minutes' % int(delta.seconds / 60) + return trans.fill_template('/admin/jobs.mako', + jobs=jobs, + recent_jobs=recent_jobs, + last_updated=last_updated, + finished=finished, + cutoff=cutoff, + msg=msg, + status=status, + job_lock=job_lock) + + @web.expose + @web.require_admin + def job_info(self, trans, jobid=None): + job = None + if jobid is not None: + job = trans.sa_session.query(trans.app.model.Job).get(jobid) + return trans.fill_template('/webapps/reports/job_info.mako', + job=job, + message="Back") + + @web.expose + @web.require_admin + def manage_tool_dependencies(self, + trans, + install_dependencies=False, + uninstall_dependencies=False, + remove_unused_dependencies=False, + selected_tool_ids=None, + selected_environments_to_uninstall=None, + viewkey='View tool-centric dependencies'): + if not selected_tool_ids: + selected_tool_ids = [] + if not selected_environments_to_uninstall: + selected_environments_to_uninstall = [] + tools_by_id = trans.app.toolbox.tools_by_id + view = six.next(six.itervalues(trans.app.toolbox.tools_by_id))._view + if selected_tool_ids: + # install the dependencies for the tools in the selected_tool_ids list + if not isinstance(selected_tool_ids, list): + selected_tool_ids = [selected_tool_ids] + requirements = set([tools_by_id[tid].tool_requirements for tid in selected_tool_ids]) + if install_dependencies: + [view.install_dependencies(r) for r in requirements] + elif uninstall_dependencies: + [view.uninstall_dependencies(index=None, requirements=r) for r in requirements] + if selected_environments_to_uninstall and remove_unused_dependencies: + if not isinstance(selected_environments_to_uninstall, list): + selected_environments_to_uninstall = [selected_environments_to_uninstall] + view.remove_unused_dependency_paths(selected_environments_to_uninstall) + return trans.fill_template('/webapps/galaxy/admin/manage_dependencies.mako', + tools=tools_by_id, + requirements_status=view.toolbox_requirements_status, + tool_ids_by_requirements=view.tool_ids_by_requirements, + unused_environments=view.unused_dependency_paths, + viewkey=viewkey) + + @web.expose + @web.require_admin + def sanitize_whitelist(self, trans, submit_whitelist=False, tools_to_whitelist=[]): + if submit_whitelist: + # write the configured sanitize_whitelist_file with new whitelist + # and update in-memory list. + with open(trans.app.config.sanitize_whitelist_file, 'wt') as f: + if isinstance(tools_to_whitelist, six.string_types): + tools_to_whitelist = [tools_to_whitelist] + new_whitelist = sorted([tid for tid in tools_to_whitelist if tid in trans.app.toolbox.tools_by_id]) + f.write("\n".join(new_whitelist)) + trans.app.config.sanitize_whitelist = new_whitelist + galaxy.queue_worker.send_control_task(trans.app, 'reload_sanitize_whitelist', noop_self=True) + # dispatch a message to reload list for other processes + return trans.fill_template('/webapps/galaxy/admin/sanitize_whitelist.mako', + sanitize_all=trans.app.config.sanitize_all_html, + tools=trans.app.toolbox.tools_by_id) + + +# ---- Utility methods ------------------------------------------------------- + +def build_select_input(name, label, options, value): + return {'type' : 'select', + 'multiple' : True, + 'optional' : True, + 'individual': True, + 'name' : name, + 'label' : label, + 'options' : options, + 'value' : value} + + +def message_exception(trans, message): + trans.response.status = 400 + return {'err_msg': sanitize_text(message)} + + +def get_user(trans, user_id): + """Get a User from the database by id.""" + user = trans.sa_session.query(trans.model.User).get(trans.security.decode_id(user_id)) + if not user: + return trans.show_error_message("User not found for id (%s)" % str(user_id)) + return user + + +def get_role(trans, id): + """Get a Role from the database by id.""" + # Load user from database + id = trans.security.decode_id(id) + role = trans.sa_session.query(trans.model.Role).get(id) + if not role: + return trans.show_error_message("Role not found for id (%s)" % str(id)) + return role + + +def get_group(trans, id): + """Get a Group from the database by id.""" + # Load user from database + id = trans.security.decode_id(id) + group = trans.sa_session.query(trans.model.Group).get(id) + if not group: + return trans.show_error_message("Group not found for id (%s)" % str(id)) + return group + + +def get_quota(trans, id): + """Get a Quota from the database by id.""" + # Load user from database + id = trans.security.decode_id(id) + quota = trans.sa_session.query(trans.model.Quota).get(id) + return quota diff --git a/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py b/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py index fc65ed1a50f1..18b9910e1446 100644 --- a/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py +++ b/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py @@ -31,10 +31,10 @@ from tool_shed.util import workflow_util from tool_shed.util.web_util import escape -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class AdminToolshed( AdminGalaxy ): +class AdminToolshed(AdminGalaxy): installed_repository_grid = admin_toolshed_grids.InstalledRepositoryGrid() repository_installation_grid = admin_toolshed_grids.RepositoryInstallationGrid() @@ -42,69 +42,69 @@ class AdminToolshed( AdminGalaxy ): @web.expose @web.require_admin - def activate_repository( self, trans, **kwd ): + def activate_repository(self, trans, **kwd): """Activate a repository that was deactivated but not uninstalled.""" - repository_id = kwd[ 'id' ] - repository = repository_util.get_installed_tool_shed_repository( trans.app, repository_id ) + repository_id = kwd['id'] + repository = repository_util.get_installed_tool_shed_repository(trans.app, repository_id) try: - trans.app.installed_repository_manager.activate_repository( repository ) + trans.app.installed_repository_manager.activate_repository(repository) except Exception as e: - error_message = "Error activating repository %s: %s" % ( escape( repository.name ), str( e ) ) - log.exception( error_message ) + error_message = "Error activating repository %s: %s" % (escape(repository.name), str(e)) + log.exception(error_message) message = '%s.
    You may be able to resolve this by uninstalling and then reinstalling the repository. Click here to uninstall the repository.' \ - % ( error_message, web.url_for( controller='admin_toolshed', action='deactivate_or_uninstall_repository', id=trans.security.encode_id( repository.id ) ) ) + % (error_message, web.url_for(controller='admin_toolshed', action='deactivate_or_uninstall_repository', id=trans.security.encode_id(repository.id))) status = 'error' - return trans.response.send_redirect( web.url_for( controller='admin_toolshed', - action='manage_repository', - id=repository_id, - message=message, - status=status ) ) - message = 'The %s repository has been activated.' % escape( repository.name ) + return trans.response.send_redirect(web.url_for(controller='admin_toolshed', + action='manage_repository', + id=repository_id, + message=message, + status=status)) + message = 'The %s repository has been activated.' % escape(repository.name) status = 'done' - return trans.response.send_redirect( web.url_for( controller='admin_toolshed', - action='browse_repositories', - message=message, - status=status ) ) + return trans.response.send_redirect(web.url_for(controller='admin_toolshed', + action='browse_repositories', + message=message, + status=status)) @web.expose @web.require_admin - def browse_repository( self, trans, **kwd ): - message = escape( kwd.get( 'message', '' ) ) - status = kwd.get( 'status', 'done' ) - repository = repository_util.get_installed_tool_shed_repository( trans.app, kwd[ 'id' ] ) - return trans.fill_template( '/admin/tool_shed_repository/browse_repository.mako', - repository=repository, - message=message, - status=status ) + def browse_repository(self, trans, **kwd): + message = escape(kwd.get('message', '')) + status = kwd.get('status', 'done') + repository = repository_util.get_installed_tool_shed_repository(trans.app, kwd['id']) + return trans.fill_template('/admin/tool_shed_repository/browse_repository.mako', + repository=repository, + message=message, + status=status) @web.expose @web.require_admin - def browse_repositories( self, trans, **kwd ): + def browse_repositories(self, trans, **kwd): if 'operation' in kwd: - operation = kwd.pop( 'operation' ).lower() + operation = kwd.pop('operation').lower() if operation == "manage_repository": - return trans.response.send_redirect( web.url_for( controller='admin_toolshed', - action='manage_repository', - **kwd ) ) + return trans.response.send_redirect(web.url_for(controller='admin_toolshed', + action='manage_repository', + **kwd)) if operation == "get updates": - return trans.response.send_redirect( web.url_for( controller='admin_toolshed', - action='check_for_updates', - **kwd ) ) + return trans.response.send_redirect(web.url_for(controller='admin_toolshed', + action='check_for_updates', + **kwd)) if operation == "update tool shed status": - return trans.response.send_redirect( web.url_for( controller='admin_toolshed', - action='update_tool_shed_status_for_installed_repository', - **kwd ) ) + return trans.response.send_redirect(web.url_for(controller='admin_toolshed', + action='update_tool_shed_status_for_installed_repository', + **kwd)) if operation == "reset to install": - kwd[ 'reset_repository' ] = True - return trans.response.send_redirect( web.url_for( controller='admin_toolshed', - action='reset_to_install', - **kwd ) ) + kwd['reset_repository'] = True + return trans.response.send_redirect(web.url_for(controller='admin_toolshed', + action='reset_to_install', + **kwd)) if operation == "purge": - return trans.response.send_redirect( web.url_for( controller='admin_toolshed', - action='purge_repository', - **kwd ) ) + return trans.response.send_redirect(web.url_for(controller='admin_toolshed', + action='purge_repository', + **kwd)) if operation == "activate or reinstall": - repository = repository_util.get_installed_tool_shed_repository( trans.app, kwd[ 'id' ] ) + repository = repository_util.get_installed_tool_shed_repository(trans.app, kwd['id']) if repository.uninstalled: # Since we're reinstalling the repository we need to find the latest changeset revision to which it can # be updated so that we can reset the metadata if necessary. This will ensure that information about @@ -112,122 +112,122 @@ def browse_repositories( self, trans, **kwd ): # in the tool panel if the repository was uninstalled and it contained tools that should be displayed in # the tool panel. changeset_revision_dict = \ - trans.app.update_repository_manager.get_update_to_changeset_revision_and_ctx_rev( repository ) - current_changeset_revision = changeset_revision_dict.get( 'changeset_revision', None ) - current_ctx_rev = changeset_revision_dict.get( 'ctx_rev', None ) + trans.app.update_repository_manager.get_update_to_changeset_revision_and_ctx_rev(repository) + current_changeset_revision = changeset_revision_dict.get('changeset_revision', None) + current_ctx_rev = changeset_revision_dict.get('ctx_rev', None) if current_changeset_revision and current_ctx_rev: if current_ctx_rev == repository.ctx_rev: # The uninstalled repository is current. - return trans.response.send_redirect( web.url_for( controller='admin_toolshed', - action='reselect_tool_panel_section', - **kwd ) ) + return trans.response.send_redirect(web.url_for(controller='admin_toolshed', + action='reselect_tool_panel_section', + **kwd)) else: # The uninstalled repository has updates available in the tool shed. updated_repo_info_dict = \ - self.get_updated_repository_information( trans=trans, - repository_id=trans.security.encode_id( repository.id ), - repository_name=repository.name, - repository_owner=repository.owner, - changeset_revision=current_changeset_revision ) - json_repo_info_dict = json.dumps( updated_repo_info_dict ) - encoded_repo_info_dict = encoding_util.tool_shed_encode( json_repo_info_dict ) - kwd[ 'latest_changeset_revision' ] = current_changeset_revision - kwd[ 'latest_ctx_rev' ] = current_ctx_rev - kwd[ 'updated_repo_info_dict' ] = encoded_repo_info_dict - return trans.response.send_redirect( web.url_for( controller='admin_toolshed', - action='reselect_tool_panel_section', - **kwd ) ) + self.get_updated_repository_information(trans=trans, + repository_id=trans.security.encode_id(repository.id), + repository_name=repository.name, + repository_owner=repository.owner, + changeset_revision=current_changeset_revision) + json_repo_info_dict = json.dumps(updated_repo_info_dict) + encoded_repo_info_dict = encoding_util.tool_shed_encode(json_repo_info_dict) + kwd['latest_changeset_revision'] = current_changeset_revision + kwd['latest_ctx_rev'] = current_ctx_rev + kwd['updated_repo_info_dict'] = encoded_repo_info_dict + return trans.response.send_redirect(web.url_for(controller='admin_toolshed', + action='reselect_tool_panel_section', + **kwd)) else: - message = "Unable to get latest revision for repository %s from " % escape( str( repository.name ) ) + message = "Unable to get latest revision for repository %s from " % escape(str(repository.name)) message += "the Tool Shed, so repository re-installation is not possible at this time." status = "error" - return trans.response.send_redirect( web.url_for( controller='admin_toolshed', - action='browse_repositories', - message=message, - status=status ) ) + return trans.response.send_redirect(web.url_for(controller='admin_toolshed', + action='browse_repositories', + message=message, + status=status)) else: - return trans.response.send_redirect( web.url_for( controller='admin_toolshed', - action='activate_repository', - **kwd ) ) + return trans.response.send_redirect(web.url_for(controller='admin_toolshed', + action='activate_repository', + **kwd)) if operation == "deactivate or uninstall": - return trans.response.send_redirect( web.url_for( controller='admin_toolshed', - action='deactivate_or_uninstall_repository', - **kwd ) ) + return trans.response.send_redirect(web.url_for(controller='admin_toolshed', + action='deactivate_or_uninstall_repository', + **kwd)) if operation == "install latest revision": - return trans.response.send_redirect( web.url_for( controller='admin_toolshed', - action='install_latest_repository_revision', - **kwd ) ) + return trans.response.send_redirect(web.url_for(controller='admin_toolshed', + action='install_latest_repository_revision', + **kwd)) if operation == 'install': # The user is attempting to install a white ghost. - kwd[ 'status' ] = 'error' - kwd[ 'message' ] = 'It seems you are attempting to install a "white ghost", which should instead be purged.' - return self.installed_repository_grid( trans, **kwd ) + kwd['status'] = 'error' + kwd['message'] = 'It seems you are attempting to install a "white ghost", which should instead be purged.' + return self.installed_repository_grid(trans, **kwd) @web.expose @web.require_admin - def browse_tool_dependency( self, trans, **kwd ): - message = escape( kwd.get( 'message', '' ) ) - status = kwd.get( 'status', 'done' ) - tool_dependency_ids = tool_dependency_util.get_tool_dependency_ids( as_string=False, **kwd ) - tool_dependency = tool_dependency_util.get_tool_dependency( trans.app, tool_dependency_ids[ 0 ] ) + def browse_tool_dependency(self, trans, **kwd): + message = escape(kwd.get('message', '')) + status = kwd.get('status', 'done') + tool_dependency_ids = tool_dependency_util.get_tool_dependency_ids(as_string=False, **kwd) + tool_dependency = tool_dependency_util.get_tool_dependency(trans.app, tool_dependency_ids[0]) if tool_dependency.in_error_state: message = "This tool dependency is not installed correctly (see the Tool dependency installation error below). " message += "Choose Uninstall this tool dependency from the Repository Actions menu, correct problems " message += "if necessary, and try installing the dependency again." status = "error" repository = tool_dependency.tool_shed_repository - return trans.fill_template( '/admin/tool_shed_repository/browse_tool_dependency.mako', - repository=repository, - tool_dependency=tool_dependency, - message=message, - status=status ) + return trans.fill_template('/admin/tool_shed_repository/browse_tool_dependency.mako', + repository=repository, + tool_dependency=tool_dependency, + message=message, + status=status) @web.expose @web.require_admin - def browse_tool_shed( self, trans, **kwd ): - tool_shed_url = kwd.get( 'tool_shed_url', '' ) - tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry( trans.app, tool_shed_url ) - params = dict( galaxy_url=web.url_for( '/', qualified=True ) ) - url = util.build_url( tool_shed_url, pathspec=[ 'repository', 'browse_valid_categories' ], params=params ) - return trans.response.send_redirect( url ) + def browse_tool_shed(self, trans, **kwd): + tool_shed_url = kwd.get('tool_shed_url', '') + tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry(trans.app, tool_shed_url) + params = dict(galaxy_url=web.url_for('/', qualified=True)) + url = util.build_url(tool_shed_url, pathspec=['repository', 'browse_valid_categories'], params=params) + return trans.response.send_redirect(url) @web.expose @web.require_admin - def browse_toolsheds( self, trans, **kwd ): + def browse_toolsheds(self, trans, **kwd): app = { 'jscript': "admin.toolshed" } - return trans.fill_template( 'galaxy.panels.mako', - config={ - 'title': 'Galaxy Tool Sheds', - 'app': app } ) + return trans.fill_template('galaxy.panels.mako', + config={ + 'title': 'Galaxy Tool Sheds', + 'app': app}) @web.expose @web.require_admin - def browse_tool_sheds( self, trans, **kwd ): - message = escape( kwd.get( 'message', '' ) ) - return trans.fill_template( '/webapps/galaxy/admin/tool_sheds.mako', - message=message, - status='error' ) + def browse_tool_sheds(self, trans, **kwd): + message = escape(kwd.get('message', '')) + return trans.fill_template('/webapps/galaxy/admin/tool_sheds.mako', + message=message, + status='error') @web.expose @web.require_admin - def check_for_updates( self, trans, **kwd ): + def check_for_updates(self, trans, **kwd): """Send a request to the relevant tool shed to see if there are any updates.""" - repository_id = kwd.get( 'id', None ) - repository = repository_util.get_installed_tool_shed_repository( trans.app, repository_id ) - tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry( trans.app, str( repository.tool_shed ) ) - params = dict( galaxy_url=web.url_for( '/', qualified=True ), - name=str( repository.name ), - owner=str( repository.owner ), - changeset_revision=str( repository.changeset_revision ) ) - pathspec = [ 'repository', 'check_for_updates' ] - url = util.build_url( tool_shed_url, pathspec=pathspec, params=params ) - return trans.response.send_redirect( url ) + repository_id = kwd.get('id', None) + repository = repository_util.get_installed_tool_shed_repository(trans.app, repository_id) + tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry(trans.app, str(repository.tool_shed)) + params = dict(galaxy_url=web.url_for('/', qualified=True), + name=str(repository.name), + owner=str(repository.owner), + changeset_revision=str(repository.changeset_revision)) + pathspec = ['repository', 'check_for_updates'] + url = util.build_url(tool_shed_url, pathspec=pathspec, params=params) + return trans.response.send_redirect(url) @web.expose @web.require_admin - def deactivate_or_uninstall_repository( self, trans, **kwd ): + def deactivate_or_uninstall_repository(self, trans, **kwd): """ Handle all changes when a tool shed repository is being deactivated or uninstalled. Notice that if the repository contents include a file named tool_data_table_conf.xml.sample, its @@ -236,44 +236,44 @@ def deactivate_or_uninstall_repository( self, trans, **kwd ): require the same entry. For now we'll never delete entries from config.shed_tool_data_table_config, but we may choose to do so in the future if it becomes necessary. """ - message = escape( kwd.get( 'message', '' ) ) - statuses = [ None, '' 'info', 'done', 'warning', 'error' ] - status = kwd.get( 'status', 'done' ) + message = escape(kwd.get('message', '')) + statuses = [None, '' 'info', 'done', 'warning', 'error'] + status = kwd.get('status', 'done') if status in statuses: - status = statuses.index( status ) + status = statuses.index(status) else: status = 1 - remove_from_disk = kwd.get( 'remove_from_disk', '' ) - remove_from_disk_checked = CheckboxField.is_checked( remove_from_disk ) - tool_shed_repositories = repository_util.get_installed_tool_shed_repository( trans.app, kwd[ 'id' ] ) - if not isinstance( tool_shed_repositories, list ): + remove_from_disk = kwd.get('remove_from_disk', '') + remove_from_disk_checked = CheckboxField.is_checked(remove_from_disk) + tool_shed_repositories = repository_util.get_installed_tool_shed_repository(trans.app, kwd['id']) + if not isinstance(tool_shed_repositories, list): tool_shed_repositories = [tool_shed_repositories] irm = InstalledRepositoryManager(app=trans.app) for tool_shed_repository in tool_shed_repositories: - if kwd.get( 'deactivate_or_uninstall_repository_button', False ): + if kwd.get('deactivate_or_uninstall_repository_button', False): errors = irm.uninstall_repository(repository=tool_shed_repository, remove_from_disk=remove_from_disk_checked) action = 'uninstalled' if remove_from_disk_checked else 'deactivated' - status = max( status, statuses.index( 'done' ) ) - message += 'The repository named %s has been %s. ' % (escape( tool_shed_repository.name ), action) + status = max(status, statuses.index('done')) + message += 'The repository named %s has been %s. ' % (escape(tool_shed_repository.name), action) if remove_from_disk_checked: if errors: message += 'Attempting to uninstall tool dependencies resulted in errors: %s' % errors - status = max( status, statuses.index( 'error' ) ) - status = statuses[ status ] - if kwd.get( 'deactivate_or_uninstall_repository_button', False ): - return trans.response.send_redirect( web.url_for( controller='admin_toolshed', - action='browse_repositories', - message=message, - status=status ) ) - remove_from_disk_check_box = CheckboxField( 'remove_from_disk', checked=remove_from_disk_checked ) - return trans.fill_template( '/admin/tool_shed_repository/deactivate_or_uninstall_repository.mako', - repository=tool_shed_repositories, - remove_from_disk_check_box=remove_from_disk_check_box, - message=message, - status=status ) + status = max(status, statuses.index('error')) + status = statuses[status] + if kwd.get('deactivate_or_uninstall_repository_button', False): + return trans.response.send_redirect(web.url_for(controller='admin_toolshed', + action='browse_repositories', + message=message, + status=status)) + remove_from_disk_check_box = CheckboxField('remove_from_disk', checked=remove_from_disk_checked) + return trans.fill_template('/admin/tool_shed_repository/deactivate_or_uninstall_repository.mako', + repository=tool_shed_repositories, + remove_from_disk_check_box=remove_from_disk_check_box, + message=message, + status=status) @web.expose - def display_image_in_repository( self, trans, **kwd ): + def display_image_in_repository(self, trans, **kwd): """ Open an image file that is contained in an installed tool shed repository or that is referenced by a URL for display. The image can be defined in either a README.rst file contained in the repository or the help section of a Galaxy tool config that @@ -285,140 +285,140 @@ def display_image_in_repository( self, trans, **kwd ): .. image:: some_image.jpg .. image:: /deep/some_image.png """ - repository_id = kwd.get( 'repository_id', None ) - relative_path_to_image_file = kwd.get( 'image_file', None ) + repository_id = kwd.get('repository_id', None) + relative_path_to_image_file = kwd.get('image_file', None) if repository_id and relative_path_to_image_file: - repository = repository_util.get_tool_shed_repository_by_id( trans.app, repository_id ) + repository = repository_util.get_tool_shed_repository_by_id(trans.app, repository_id) if repository: - repo_files_dir = repository.repo_files_directory( trans.app ) + repo_files_dir = repository.repo_files_directory(trans.app) # The following line sometimes returns None. TODO: Figure out why. - path_to_file = repository_util.get_absolute_path_to_file_in_repository( repo_files_dir, relative_path_to_image_file ) - if path_to_file and os.path.exists( path_to_file ): - file_name = os.path.basename( relative_path_to_image_file ) + path_to_file = repository_util.get_absolute_path_to_file_in_repository(repo_files_dir, relative_path_to_image_file) + if path_to_file and os.path.exists(path_to_file): + file_name = os.path.basename(relative_path_to_image_file) try: - extension = file_name.split( '.' )[ -1 ] + extension = file_name.split('.')[-1] except Exception: extension = None if extension: - mimetype = trans.app.datatypes_registry.get_mimetype_by_extension( extension ) + mimetype = trans.app.datatypes_registry.get_mimetype_by_extension(extension) if mimetype: - trans.response.set_content_type( mimetype ) - return open( path_to_file, 'r' ) + trans.response.set_content_type(mimetype) + return open(path_to_file, 'r') return None @web.expose @web.require_admin - def find_tools_in_tool_shed( self, trans, **kwd ): - tool_shed_url = kwd.get( 'tool_shed_url', '' ) - tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry( trans.app, tool_shed_url ) - params = dict( galaxy_url=web.url_for( '/', qualified=True ) ) - url = util.build_url( tool_shed_url, pathspec=[ 'repository', 'find_tools' ], params=params ) - return trans.response.send_redirect( url ) + def find_tools_in_tool_shed(self, trans, **kwd): + tool_shed_url = kwd.get('tool_shed_url', '') + tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry(trans.app, tool_shed_url) + params = dict(galaxy_url=web.url_for('/', qualified=True)) + url = util.build_url(tool_shed_url, pathspec=['repository', 'find_tools'], params=params) + return trans.response.send_redirect(url) @web.expose @web.require_admin - def find_workflows_in_tool_shed( self, trans, **kwd ): - tool_shed_url = kwd.get( 'tool_shed_url', '' ) - tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry( trans.app, tool_shed_url ) - params = dict( galaxy_url=web.url_for( '/', qualified=True ) ) - url = util.build_url( tool_shed_url, pathspec=[ 'repository', 'find_workflows' ], params=params ) - return trans.response.send_redirect( url ) + def find_workflows_in_tool_shed(self, trans, **kwd): + tool_shed_url = kwd.get('tool_shed_url', '') + tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry(trans.app, tool_shed_url) + params = dict(galaxy_url=web.url_for('/', qualified=True)) + url = util.build_url(tool_shed_url, pathspec=['repository', 'find_workflows'], params=params) + return trans.response.send_redirect(url) @web.expose @web.require_admin - def generate_workflow_image( self, trans, workflow_name, repository_id=None ): + def generate_workflow_image(self, trans, workflow_name, repository_id=None): """Return an svg image representation of a workflow dictionary created when the workflow was exported.""" - return workflow_util.generate_workflow_image( trans, workflow_name, repository_metadata_id=None, repository_id=repository_id ) + return workflow_util.generate_workflow_image(trans, workflow_name, repository_metadata_id=None, repository_id=repository_id) @web.json @web.require_admin - def get_file_contents( self, trans, file_path, repository_id ): + def get_file_contents(self, trans, file_path, repository_id): # Avoid caching trans.response.headers['Pragma'] = 'no-cache' trans.response.headers['Expires'] = '0' - return suc.get_repository_file_contents( trans.app, file_path, repository_id, is_admin=True ) + return suc.get_repository_file_contents(trans.app, file_path, repository_id, is_admin=True) @web.expose @web.require_admin - def get_tool_dependencies( self, trans, repository_id, repository_name, repository_owner, changeset_revision ): + def get_tool_dependencies(self, trans, repository_id, repository_name, repository_owner, changeset_revision): """ Send a request to the appropriate tool shed to retrieve the dictionary of tool dependencies defined for the received repository name, owner and changeset revision. The received repository_id is the encoded id of the installed tool shed repository in Galaxy. We need it so that we can derive the tool shed from which it was installed. """ - repository = repository_util.get_installed_tool_shed_repository( trans.app, repository_id ) - tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry( trans.app, str( repository.tool_shed ) ) + repository = repository_util.get_installed_tool_shed_repository(trans.app, repository_id) + tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry(trans.app, str(repository.tool_shed)) if tool_shed_url is None or repository_name is None or repository_owner is None or changeset_revision is None: message = "Unable to retrieve tool dependencies from the Tool Shed because one or more of the following required " message += "parameters is None: tool_shed_url: %s, repository_name: %s, repository_owner: %s, changeset_revision: %s " % \ - ( str( tool_shed_url ), str( repository_name ), str( repository_owner ), str( changeset_revision ) ) - raise Exception( message ) - params = dict( name=repository_name, owner=repository_owner, changeset_revision=changeset_revision ) - pathspec = [ 'repository', 'get_tool_dependencies' ] - raw_text = util.url_get( tool_shed_url, password_mgr=self.app.tool_shed_registry.url_auth( tool_shed_url ), pathspec=pathspec, params=params ) - if len( raw_text ) > 2: - encoded_text = json.loads( raw_text ) - text = encoding_util.tool_shed_decode( encoded_text ) + (str(tool_shed_url), str(repository_name), str(repository_owner), str(changeset_revision)) + raise Exception(message) + params = dict(name=repository_name, owner=repository_owner, changeset_revision=changeset_revision) + pathspec = ['repository', 'get_tool_dependencies'] + raw_text = util.url_get(tool_shed_url, password_mgr=self.app.tool_shed_registry.url_auth(tool_shed_url), pathspec=pathspec, params=params) + if len(raw_text) > 2: + encoded_text = json.loads(raw_text) + text = encoding_util.tool_shed_decode(encoded_text) else: text = '' return text @web.expose @web.require_admin - def get_updated_repository_information( self, trans, repository_id, repository_name, repository_owner, changeset_revision ): + def get_updated_repository_information(self, trans, repository_id, repository_name, repository_owner, changeset_revision): """ Send a request to the appropriate tool shed to retrieve the dictionary of information required to reinstall an updated revision of an uninstalled tool shed repository. """ - repository = repository_util.get_installed_tool_shed_repository( trans.app, repository_id ) - tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry( trans.app, str( repository.tool_shed ) ) + repository = repository_util.get_installed_tool_shed_repository(trans.app, repository_id) + tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry(trans.app, str(repository.tool_shed)) if tool_shed_url is None or repository_name is None or repository_owner is None or changeset_revision is None: message = "Unable to retrieve updated repository information from the Tool Shed because one or more of the following " message += "required parameters is None: tool_shed_url: %s, repository_name: %s, repository_owner: %s, changeset_revision: %s " % \ - ( str( tool_shed_url ), str( repository_name ), str( repository_owner ), str( changeset_revision ) ) - raise Exception( message ) - params = dict( name=str( repository_name ), - owner=str( repository_owner ), - changeset_revision=changeset_revision ) - pathspec = [ 'repository', 'get_updated_repository_information' ] - raw_text = util.url_get( tool_shed_url, password_mgr=self.app.tool_shed_registry.url_auth( tool_shed_url ), pathspec=pathspec, params=params ) - repo_information_dict = json.loads( raw_text ) + (str(tool_shed_url), str(repository_name), str(repository_owner), str(changeset_revision)) + raise Exception(message) + params = dict(name=str(repository_name), + owner=str(repository_owner), + changeset_revision=changeset_revision) + pathspec = ['repository', 'get_updated_repository_information'] + raw_text = util.url_get(tool_shed_url, password_mgr=self.app.tool_shed_registry.url_auth(tool_shed_url), pathspec=pathspec, params=params) + repo_information_dict = json.loads(raw_text) return repo_information_dict @web.expose @web.require_admin - def import_workflow( self, trans, workflow_name, repository_id, **kwd ): + def import_workflow(self, trans, workflow_name, repository_id, **kwd): """Import a workflow contained in an installed tool shed repository into Galaxy.""" - message = str( escape( kwd.get( 'message', '' ) ) ) - status = kwd.get( 'status', 'done' ) + message = str(escape(kwd.get('message', ''))) + status = kwd.get('status', 'done') if workflow_name: - workflow_name = encoding_util.tool_shed_decode( workflow_name ) - repository = repository_util.get_tool_shed_repository_by_id( trans.app, repository_id ) + workflow_name = encoding_util.tool_shed_decode(workflow_name) + repository = repository_util.get_tool_shed_repository_by_id(trans.app, repository_id) if repository: - workflow, status, message = workflow_util.import_workflow( trans, repository, workflow_name ) + workflow, status, message = workflow_util.import_workflow(trans, repository, workflow_name) if workflow: - workflow_name = encoding_util.tool_shed_encode( str( workflow.name ) ) + workflow_name = encoding_util.tool_shed_encode(str(workflow.name)) else: message += 'Unable to locate a workflow named %s within the installed tool shed repository named %s' % \ - ( escape( str( workflow_name ) ), escape( str( repository.name ) ) ) + (escape(str(workflow_name)), escape(str(repository.name))) status = 'error' else: - message = 'Invalid repository id %s received.' % str( repository_id ) + message = 'Invalid repository id %s received.' % str(repository_id) status = 'error' else: message = 'The value of workflow_name is required, but was not received.' status = 'error' - return trans.response.send_redirect( web.url_for( controller='admin_toolshed', - action='view_workflow', - workflow_name=workflow_name, - repository_id=repository_id, - message=message, - status=status ) ) + return trans.response.send_redirect(web.url_for(controller='admin_toolshed', + action='view_workflow', + workflow_name=workflow_name, + repository_id=repository_id, + message=message, + status=status)) @web.expose @web.require_admin - def initiate_tool_dependency_installation( self, trans, tool_dependencies, **kwd ): + def initiate_tool_dependency_installation(self, trans, tool_dependencies, **kwd): """ Install specified dependencies for repository tools. The received list of tool_dependencies are the database records for those dependencies defined in the tool_dependencies.xml file @@ -427,54 +427,54 @@ def initiate_tool_dependency_installation( self, trans, tool_dependencies, **kwd tool shed repository. """ # Get the tool_shed_repository from one of the tool_dependencies. - message = str( escape( kwd.get( 'message', '' ) ) ) - status = kwd.get( 'status', 'done' ) + message = str(escape(kwd.get('message', ''))) + status = kwd.get('status', 'done') err_msg = '' - tool_shed_repository = tool_dependencies[ 0 ].tool_shed_repository + tool_shed_repository = tool_dependencies[0].tool_shed_repository # Get the tool_dependencies.xml file from the repository. - tool_dependencies_config = hg_util.get_config_from_disk( rt_util.TOOL_DEPENDENCY_DEFINITION_FILENAME, - tool_shed_repository.repo_path( trans.app ) ) - itdm = install_manager.InstallToolDependencyManager( trans.app ) - installed_tool_dependencies = itdm.install_specified_tool_dependencies( tool_shed_repository=tool_shed_repository, - tool_dependencies_config=tool_dependencies_config, - tool_dependencies=tool_dependencies, - from_tool_migration_manager=False ) + tool_dependencies_config = hg_util.get_config_from_disk(rt_util.TOOL_DEPENDENCY_DEFINITION_FILENAME, + tool_shed_repository.repo_path(trans.app)) + itdm = install_manager.InstallToolDependencyManager(trans.app) + installed_tool_dependencies = itdm.install_specified_tool_dependencies(tool_shed_repository=tool_shed_repository, + tool_dependencies_config=tool_dependencies_config, + tool_dependencies=tool_dependencies, + from_tool_migration_manager=False) for installed_tool_dependency in installed_tool_dependencies: if installed_tool_dependency.status == trans.app.install_model.ToolDependency.installation_status.ERROR: - text = util.unicodify( installed_tool_dependency.error_message ) + text = util.unicodify(installed_tool_dependency.error_message) if text is not None: err_msg += ' %s' % text if err_msg: message += err_msg status = 'error' - message += "Installed tool dependencies: %s" % ', '.join( td.name for td in installed_tool_dependencies ) - td_ids = [ trans.security.encode_id( td.id ) for td in tool_shed_repository.tool_dependencies ] - return trans.response.send_redirect( web.url_for( controller='admin_toolshed', - action='manage_tool_dependencies', - tool_dependency_ids=td_ids, - message=message, - status=status ) ) + message += "Installed tool dependencies: %s" % ', '.join(td.name for td in installed_tool_dependencies) + td_ids = [trans.security.encode_id(td.id) for td in tool_shed_repository.tool_dependencies] + return trans.response.send_redirect(web.url_for(controller='admin_toolshed', + action='manage_tool_dependencies', + tool_dependency_ids=td_ids, + message=message, + status=status)) @web.expose @web.require_admin - def install_latest_repository_revision( self, trans, **kwd ): + def install_latest_repository_revision(self, trans, **kwd): """Install the latest installable revision of a repository that has been previously installed.""" - message = escape( kwd.get( 'message', '' ) ) - status = kwd.get( 'status', 'done' ) - repository_id = kwd.get( 'id', None ) + message = escape(kwd.get('message', '')) + status = kwd.get('status', 'done') + repository_id = kwd.get('id', None) if repository_id is not None: - repository = repository_util.get_installed_tool_shed_repository( trans.app, repository_id ) + repository = repository_util.get_installed_tool_shed_repository(trans.app, repository_id) if repository is not None: - tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry( trans.app, str( repository.tool_shed ) ) - name = str( repository.name ) - owner = str( repository.owner ) - params = dict( galaxy_url=web.url_for( '/', qualified=True ), - name=name, - owner=owner ) - pathspec = [ 'repository', 'get_latest_downloadable_changeset_revision' ] - raw_text = util.url_get( tool_shed_url, password_mgr=self.app.tool_shed_registry.url_auth( tool_shed_url ), pathspec=pathspec, params=params ) - url = util.build_url( tool_shed_url, pathspec=pathspec, params=params ) - latest_downloadable_revision = json.loads( raw_text ) + tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry(trans.app, str(repository.tool_shed)) + name = str(repository.name) + owner = str(repository.owner) + params = dict(galaxy_url=web.url_for('/', qualified=True), + name=name, + owner=owner) + pathspec = ['repository', 'get_latest_downloadable_changeset_revision'] + raw_text = util.url_get(tool_shed_url, password_mgr=self.app.tool_shed_registry.url_auth(tool_shed_url), pathspec=pathspec, params=params) + url = util.build_url(tool_shed_url, pathspec=pathspec, params=params) + latest_downloadable_revision = json.loads(raw_text) if latest_downloadable_revision == hg_util.INITIAL_CHANGELOG_HASH: message = 'Error retrieving the latest downloadable revision for this repository via the url %s.' % url status = 'error' @@ -484,81 +484,81 @@ def install_latest_repository_revision( self, trans, **kwd ): # appropriate repository revision if one exists. We need to create a temporary repo_info_tuple # with the following entries to handle this. # ( description, clone_url, changeset_revision, ctx_rev, owner, repository_dependencies, tool_dependencies ) - tmp_clone_url = util.build_url( tool_shed_url, pathspec=[ 'repos', owner, name ] ) - tmp_repo_info_tuple = ( None, tmp_clone_url, latest_downloadable_revision, None, owner, None, None ) + tmp_clone_url = util.build_url(tool_shed_url, pathspec=['repos', owner, name]) + tmp_repo_info_tuple = (None, tmp_clone_url, latest_downloadable_revision, None, owner, None, None) installed_repository, installed_changeset_revision = \ - repository_util.repository_was_previously_installed( trans.app, tool_shed_url, name, tmp_repo_info_tuple, from_tip=False ) + repository_util.repository_was_previously_installed(trans.app, tool_shed_url, name, tmp_repo_info_tuple, from_tip=False) if installed_repository: - current_changeset_revision = str( installed_repository.changeset_revision ) + current_changeset_revision = str(installed_repository.changeset_revision) message = 'Revision %s of repository %s owned by %s has already been installed.' % \ - ( latest_downloadable_revision, name, owner ) + (latest_downloadable_revision, name, owner) if current_changeset_revision != latest_downloadable_revision: message += ' The current changeset revision is %s.' % current_changeset_revision status = 'error' else: # Install the latest downloadable revision of the repository. - params = dict( name=name, - owner=owner, - changeset_revisions=str( latest_downloadable_revision ), - galaxy_url=web.url_for( '/', qualified=True ) ) - pathspec = [ 'repository', 'install_repositories_by_revision' ] - url = util.build_url( tool_shed_url, pathspec=pathspec, params=params ) - return trans.response.send_redirect( url ) + params = dict(name=name, + owner=owner, + changeset_revisions=str(latest_downloadable_revision), + galaxy_url=web.url_for('/', qualified=True)) + pathspec = ['repository', 'install_repositories_by_revision'] + url = util.build_url(tool_shed_url, pathspec=pathspec, params=params) + return trans.response.send_redirect(url) else: - message = 'Cannot locate installed tool shed repository with encoded id %s.' % str( repository_id ) + message = 'Cannot locate installed tool shed repository with encoded id %s.' % str(repository_id) status = 'error' else: message = 'The request parameters did not include the required encoded id of installed repository.' status = 'error' - return trans.response.send_redirect( web.url_for( controller='admin_toolshed', - action='browse_repositories', - message=message, - status=status ) ) + return trans.response.send_redirect(web.url_for(controller='admin_toolshed', + action='browse_repositories', + message=message, + status=status)) @web.expose @web.require_admin - def install_tool_dependencies_with_update( self, trans, **kwd ): + def install_tool_dependencies_with_update(self, trans, **kwd): """ Updating an installed tool shed repository where new tool dependencies but no new repository dependencies are included in the updated revision. """ - updating_repository_id = kwd.get( 'updating_repository_id', None ) - repository = repository_util.get_installed_tool_shed_repository( trans.app, updating_repository_id ) + updating_repository_id = kwd.get('updating_repository_id', None) + repository = repository_util.get_installed_tool_shed_repository(trans.app, updating_repository_id) # All received dependencies need to be installed - confirmed by the caller. - encoded_tool_dependencies_dict = kwd.get( 'encoded_tool_dependencies_dict', None ) + encoded_tool_dependencies_dict = kwd.get('encoded_tool_dependencies_dict', None) if encoded_tool_dependencies_dict is not None: - tool_dependencies_dict = encoding_util.tool_shed_decode( encoded_tool_dependencies_dict ) + tool_dependencies_dict = encoding_util.tool_shed_decode(encoded_tool_dependencies_dict) else: tool_dependencies_dict = {} - encoded_relative_install_dir = kwd.get( 'encoded_relative_install_dir', None ) + encoded_relative_install_dir = kwd.get('encoded_relative_install_dir', None) if encoded_relative_install_dir is not None: - relative_install_dir = encoding_util.tool_shed_decode( encoded_relative_install_dir ) + relative_install_dir = encoding_util.tool_shed_decode(encoded_relative_install_dir) else: relative_install_dir = '' - updating_to_changeset_revision = kwd.get( 'updating_to_changeset_revision', None ) - updating_to_ctx_rev = kwd.get( 'updating_to_ctx_rev', None ) - encoded_updated_metadata = kwd.get( 'encoded_updated_metadata', None ) - message = escape( kwd.get( 'message', '' ) ) - status = kwd.get( 'status', 'done' ) + updating_to_changeset_revision = kwd.get('updating_to_changeset_revision', None) + updating_to_ctx_rev = kwd.get('updating_to_ctx_rev', None) + encoded_updated_metadata = kwd.get('encoded_updated_metadata', None) + message = escape(kwd.get('message', '')) + status = kwd.get('status', 'done') if 'install_tool_dependencies_with_update_button' in kwd: # Now that the user has chosen whether to install tool dependencies or not, we can # update the repository record with the changes in the updated revision. if encoded_updated_metadata: - updated_metadata = encoding_util.tool_shed_decode( encoded_updated_metadata ) + updated_metadata = encoding_util.tool_shed_decode(encoded_updated_metadata) else: updated_metadata = None - repository = trans.app.update_repository_manager.update_repository_record( repository=repository, - updated_metadata_dict=updated_metadata, - updated_changeset_revision=updating_to_changeset_revision, - updated_ctx_rev=updating_to_ctx_rev ) + repository = trans.app.update_repository_manager.update_repository_record(repository=repository, + updated_metadata_dict=updated_metadata, + updated_changeset_revision=updating_to_changeset_revision, + updated_ctx_rev=updating_to_ctx_rev) if tool_dependencies_dict: - tool_dependencies = tool_dependency_util.create_tool_dependency_objects( trans.app, - repository, - relative_install_dir, - set_status=False ) + tool_dependencies = tool_dependency_util.create_tool_dependency_objects(trans.app, + repository, + relative_install_dir, + set_status=False) message = "The installed repository named '%s' has been updated to change set revision '%s'. " % \ - ( escape( str( repository.name ) ), updating_to_changeset_revision ) - self.initiate_tool_dependency_installation( trans, tool_dependencies, message=message, status=status ) + (escape(str(repository.name)), updating_to_changeset_revision) + self.initiate_tool_dependency_installation(trans, tool_dependencies, message=message, status=status) # Handle tool dependencies check box. if trans.app.config.tool_dependency_dir is None: if tool_dependencies_dict: @@ -569,76 +569,76 @@ def install_tool_dependencies_with_update( self, trans, **kwd ): install_tool_dependencies_check_box_checked = False else: install_tool_dependencies_check_box_checked = True - install_tool_dependencies_check_box = CheckboxField( 'install_tool_dependencies', - checked=install_tool_dependencies_check_box_checked ) + install_tool_dependencies_check_box = CheckboxField('install_tool_dependencies', + checked=install_tool_dependencies_check_box_checked) view = views.DependencyResolversView(self.app) if view.installable_resolvers: - install_resolver_dependencies_check_box = CheckboxField( 'install_resolver_dependencies', checked=True ) + install_resolver_dependencies_check_box = CheckboxField('install_resolver_dependencies', checked=True) else: install_resolver_dependencies_check_box = None - return trans.fill_template( '/admin/tool_shed_repository/install_tool_dependencies_with_update.mako', - repository=repository, - updating_repository_id=updating_repository_id, - updating_to_ctx_rev=updating_to_ctx_rev, - updating_to_changeset_revision=updating_to_changeset_revision, - encoded_updated_metadata=encoded_updated_metadata, - encoded_relative_install_dir=encoded_relative_install_dir, - encoded_tool_dependencies_dict=encoded_tool_dependencies_dict, - install_resolver_dependencies_check_box=install_resolver_dependencies_check_box, - install_tool_dependencies_check_box=install_tool_dependencies_check_box, - tool_dependencies_dict=tool_dependencies_dict, - message=message, - status=status ) + return trans.fill_template('/admin/tool_shed_repository/install_tool_dependencies_with_update.mako', + repository=repository, + updating_repository_id=updating_repository_id, + updating_to_ctx_rev=updating_to_ctx_rev, + updating_to_changeset_revision=updating_to_changeset_revision, + encoded_updated_metadata=encoded_updated_metadata, + encoded_relative_install_dir=encoded_relative_install_dir, + encoded_tool_dependencies_dict=encoded_tool_dependencies_dict, + install_resolver_dependencies_check_box=install_resolver_dependencies_check_box, + install_tool_dependencies_check_box=install_tool_dependencies_check_box, + tool_dependencies_dict=tool_dependencies_dict, + message=message, + status=status) @web.expose @web.require_admin - def manage_repositories( self, trans, **kwd ): - message = escape( kwd.get( 'message', '' ) ) - api_installation = util.asbool( kwd.get( 'api', 'false' ) ) + def manage_repositories(self, trans, **kwd): + message = escape(kwd.get('message', '')) + api_installation = util.asbool(kwd.get('api', 'false')) if api_installation: - tsr_ids = json.loads( kwd.get( 'tool_shed_repository_ids', '[]' ) ) - kwd[ 'tool_shed_repository_ids' ] = tsr_ids - tsridslist = common_util.get_tool_shed_repository_ids( **kwd ) + tsr_ids = json.loads(kwd.get('tool_shed_repository_ids', '[]')) + kwd['tool_shed_repository_ids'] = tsr_ids + tsridslist = common_util.get_tool_shed_repository_ids(**kwd) else: - tsridslist = common_util.get_tool_shed_repository_ids( **kwd ) + tsridslist = common_util.get_tool_shed_repository_ids(**kwd) if 'operation' in kwd: - operation = kwd[ 'operation' ].lower() + operation = kwd['operation'].lower() if not tsridslist: message = 'Select at least 1 tool shed repository to %s.' % operation - kwd[ 'message' ] = message - kwd[ 'status' ] = 'error' - del kwd[ 'operation' ] - return trans.response.send_redirect( web.url_for( controller='admin_toolshed', - action='manage_repositories', - **kwd ) ) + kwd['message'] = message + kwd['status'] = 'error' + del kwd['operation'] + return trans.response.send_redirect(web.url_for(controller='admin_toolshed', + action='manage_repositories', + **kwd)) if operation == 'browse': - return trans.response.send_redirect( web.url_for( controller='admin_toolshed', - action='browse_repository', - **kwd ) ) + return trans.response.send_redirect(web.url_for(controller='admin_toolshed', + action='browse_repository', + **kwd)) elif operation == 'uninstall': # TODO: I believe this block should be removed, but make sure.. repositories_for_uninstallation = [] for repository_id in tsridslist: - repository = trans.install_model.context.query( trans.install_model.ToolShedRepository ) \ - .get( trans.security.decode_id( repository_id ) ) - if repository.status in [ trans.install_model.ToolShedRepository.installation_status.INSTALLED, - trans.install_model.ToolShedRepository.installation_status.ERROR ]: - repositories_for_uninstallation.append( repository ) + repository = trans.install_model.context.query(trans.install_model.ToolShedRepository) \ + .get(trans.security.decode_id(repository_id)) + if repository.status in [trans.install_model.ToolShedRepository.installation_status.INSTALLED, + trans.install_model.ToolShedRepository.installation_status.ERROR]: + repositories_for_uninstallation.append(repository) if repositories_for_uninstallation: - return trans.response.send_redirect( web.url_for( controller='admin_toolshed', - action='uninstall_repositories', - **kwd ) ) + return trans.response.send_redirect(web.url_for(controller='admin_toolshed', + action='uninstall_repositories', + **kwd)) else: - kwd[ 'message' ] = 'All selected tool shed repositories are already uninstalled.' - kwd[ 'status' ] = 'error' + kwd['message'] = 'All selected tool shed repositories are already uninstalled.' + kwd['status'] = 'error' elif operation == "install": - irm = install_manager.InstallRepositoryManager( trans.app ) - reinstalling = util.string_as_bool( kwd.get( 'reinstalling', False ) ) - encoded_kwd = kwd[ 'encoded_kwd' ] - decoded_kwd = encoding_util.tool_shed_decode( encoded_kwd ) - install_resolver_dependencies = CheckboxField.is_checked( decoded_kwd.get( 'install_resolver_dependencies', '' ) ) - install_tool_dependencies = CheckboxField.is_checked( decoded_kwd.get( 'install_tool_dependencies', '' ) ) - tsr_ids = decoded_kwd[ 'tool_shed_repository_ids' ] + irm = install_manager.InstallRepositoryManager(trans.app) + reinstalling = util.string_as_bool(kwd.get('reinstalling', False)) + encoded_kwd = kwd['encoded_kwd'] + decoded_kwd = encoding_util.tool_shed_decode(encoded_kwd) + install_resolver_dependencies = CheckboxField.is_checked(decoded_kwd.get('install_resolver_dependencies', '')) + install_tool_dependencies = CheckboxField.is_checked(decoded_kwd.get('install_tool_dependencies', '')) + tsr_ids = decoded_kwd['tool_shed_repository_ids'] decoded_kwd['install_resolver_dependencies'] = install_resolver_dependencies decoded_kwd['install_tool_dependencies'] = install_tool_dependencies try: @@ -647,52 +647,52 @@ def manage_repositories( self, trans, **kwd ): decoded_kwd=decoded_kwd, reinstalling=reinstalling, ) - tsr_ids_for_monitoring = [ trans.security.encode_id( tsr.id ) for tsr in tool_shed_repositories ] + tsr_ids_for_monitoring = [trans.security.encode_id(tsr.id) for tsr in tool_shed_repositories] if api_installation: - return json.dumps( tsr_ids_for_monitoring ) + return json.dumps(tsr_ids_for_monitoring) else: - trans.response.send_redirect( web.url_for( controller='admin_toolshed', - action='monitor_repository_installation', - tool_shed_repository_ids=tsr_ids_for_monitoring ) ) + trans.response.send_redirect(web.url_for(controller='admin_toolshed', + action='monitor_repository_installation', + tool_shed_repository_ids=tsr_ids_for_monitoring)) except install_manager.RepositoriesInstalledException as e: - kwd[ 'message' ] = e.message - kwd[ 'status' ] = 'error' - return self.repository_installation_grid( trans, **kwd ) + kwd['message'] = e.message + kwd['status'] = 'error' + return self.repository_installation_grid(trans, **kwd) @web.expose @web.require_admin - def manage_repository( self, trans, **kwd ): - message = escape( kwd.get( 'message', '' ) ) - status = kwd.get( 'status', 'done' ) - repository_id = kwd.get( 'id', None ) + def manage_repository(self, trans, **kwd): + message = escape(kwd.get('message', '')) + status = kwd.get('status', 'done') + repository_id = kwd.get('id', None) if repository_id is None: - return trans.show_error_message( 'Missing required encoded repository id.' ) - operation = kwd.get( 'operation', None ) - repository = repository_util.get_installed_tool_shed_repository( trans.app, repository_id ) + return trans.show_error_message('Missing required encoded repository id.') + operation = kwd.get('operation', None) + repository = repository_util.get_installed_tool_shed_repository(trans.app, repository_id) if repository is None: - return trans.show_error_message( 'Invalid repository specified.' ) - tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry( trans.app, str( repository.tool_shed ) ) - name = str( repository.name ) - owner = str( repository.owner ) - installed_changeset_revision = str( repository.installed_changeset_revision ) - if repository.status in [ trans.install_model.ToolShedRepository.installation_status.CLONING ]: - tool_shed_repository_ids = [ repository_id ] - return trans.response.send_redirect( web.url_for( controller='admin_toolshed', - action='monitor_repository_installation', - tool_shed_repository_ids=tool_shed_repository_ids ) ) + return trans.show_error_message('Invalid repository specified.') + tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry(trans.app, str(repository.tool_shed)) + name = str(repository.name) + owner = str(repository.owner) + installed_changeset_revision = str(repository.installed_changeset_revision) + if repository.status in [trans.install_model.ToolShedRepository.installation_status.CLONING]: + tool_shed_repository_ids = [repository_id] + return trans.response.send_redirect(web.url_for(controller='admin_toolshed', + action='monitor_repository_installation', + tool_shed_repository_ids=tool_shed_repository_ids)) if repository.can_install and operation == 'install': # Send a request to the tool shed to install the repository. - params = dict( name=name, - owner=owner, - changeset_revisions=installed_changeset_revision, - galaxy_url=web.url_for( '/', qualified=True ) ) - pathspec = [ 'repository', 'install_repositories_by_revision' ] - url = util.build_url( tool_shed_url, pathspec=pathspec, params=params ) - return trans.response.send_redirect( url ) - description = kwd.get( 'description', repository.description ) - shed_tool_conf, tool_path, relative_install_dir = suc.get_tool_panel_config_tool_path_install_dir( trans.app, repository ) + params = dict(name=name, + owner=owner, + changeset_revisions=installed_changeset_revision, + galaxy_url=web.url_for('/', qualified=True)) + pathspec = ['repository', 'install_repositories_by_revision'] + url = util.build_url(tool_shed_url, pathspec=pathspec, params=params) + return trans.response.send_redirect(url) + description = kwd.get('description', repository.description) + shed_tool_conf, tool_path, relative_install_dir = suc.get_tool_panel_config_tool_path_install_dir(trans.app, repository) if relative_install_dir: - repo_files_dir = os.path.abspath( os.path.join( tool_path, relative_install_dir, name ) ) + repo_files_dir = os.path.abspath(os.path.join(tool_path, relative_install_dir, name)) else: repo_files_dir = None if repository.in_error_state: @@ -703,70 +703,70 @@ def manage_repository( self, trans, **kwd ): elif repository.can_install: message = "This repository is not installed. You can install it by choosing Install from the Repository Actions menu." status = "error" - elif kwd.get( 'edit_repository_button', False ): + elif kwd.get('edit_repository_button', False): if description != repository.description: repository.description = description - trans.install_model.context.add( repository ) + trans.install_model.context.add(repository) trans.install_model.context.flush() message = "The repository information has been updated." - dd = dependency_display.DependencyDisplayer( trans.app ) - containers_dict = dd.populate_containers_dict_from_repository_metadata( tool_shed_url=tool_shed_url, - tool_path=tool_path, - repository=repository, - reinstalling=False, - required_repo_info_dicts=None ) + dd = dependency_display.DependencyDisplayer(trans.app) + containers_dict = dd.populate_containers_dict_from_repository_metadata(tool_shed_url=tool_shed_url, + tool_path=tool_path, + repository=repository, + reinstalling=False, + required_repo_info_dicts=None) view = views.DependencyResolversView(self.app) tool_requirements_d = suc.get_requirements_from_repository(repository) requirements_status = view.get_requirements_status(tool_requirements_d, repository.installed_tool_dependencies) - return trans.fill_template( '/admin/tool_shed_repository/manage_repository.mako', - repository=repository, - description=description, - repo_files_dir=repo_files_dir, - containers_dict=containers_dict, - requirements_status=requirements_status, - message=message, - status=status ) + return trans.fill_template('/admin/tool_shed_repository/manage_repository.mako', + repository=repository, + description=description, + repo_files_dir=repo_files_dir, + containers_dict=containers_dict, + requirements_status=requirements_status, + message=message, + status=status) @web.expose @web.require_admin - def manage_repository_tool_dependencies( self, trans, **kwd ): - message = escape( kwd.get( 'message', '' ) ) - status = kwd.get( 'status', 'done' ) - tool_dependency_ids = tool_dependency_util.get_tool_dependency_ids( as_string=False, **kwd ) + def manage_repository_tool_dependencies(self, trans, **kwd): + message = escape(kwd.get('message', '')) + status = kwd.get('status', 'done') + tool_dependency_ids = tool_dependency_util.get_tool_dependency_ids(as_string=False, **kwd) if tool_dependency_ids: # We need a tool_shed_repository, so get it from one of the tool_dependencies. - tool_dependency = tool_dependency_util.get_tool_dependency( trans.app, tool_dependency_ids[ 0 ] ) + tool_dependency = tool_dependency_util.get_tool_dependency(trans.app, tool_dependency_ids[0]) tool_shed_repository = tool_dependency.tool_shed_repository else: # The user must be on the manage_repository_tool_dependencies page and clicked the button to either install or uninstall a # tool dependency, but they didn't check any of the available tool dependencies on which to perform the action. - repository_id = kwd.get( 'repository_id', None ) - tool_shed_repository = repository_util.get_tool_shed_repository_by_id( trans.app, repository_id ) + repository_id = kwd.get('repository_id', None) + tool_shed_repository = repository_util.get_tool_shed_repository_by_id(trans.app, repository_id) if 'operation' in kwd: - operation = kwd[ 'operation' ].lower() + operation = kwd['operation'].lower() if not tool_dependency_ids: message = 'Select at least 1 tool dependency to %s.' % operation - kwd[ 'message' ] = message - kwd[ 'status' ] = 'error' - del kwd[ 'operation' ] - return trans.response.send_redirect( web.url_for( controller='admin_toolshed', - action='manage_repository_tool_dependencies', - **kwd ) ) + kwd['message'] = message + kwd['status'] = 'error' + del kwd['operation'] + return trans.response.send_redirect(web.url_for(controller='admin_toolshed', + action='manage_repository_tool_dependencies', + **kwd)) if operation == 'browse': - return trans.response.send_redirect( web.url_for( controller='admin_toolshed', - action='browse_tool_dependency', - **kwd ) ) + return trans.response.send_redirect(web.url_for(controller='admin_toolshed', + action='browse_tool_dependency', + **kwd)) elif operation == 'uninstall': tool_dependencies_for_uninstallation = [] for tool_dependency_id in tool_dependency_ids: - tool_dependency = tool_dependency_util.get_tool_dependency( trans.app, tool_dependency_id ) - if tool_dependency.status in [ trans.install_model.ToolDependency.installation_status.INSTALLED, - trans.install_model.ToolDependency.installation_status.ERROR ]: - tool_dependencies_for_uninstallation.append( tool_dependency ) + tool_dependency = tool_dependency_util.get_tool_dependency(trans.app, tool_dependency_id) + if tool_dependency.status in [trans.install_model.ToolDependency.installation_status.INSTALLED, + trans.install_model.ToolDependency.installation_status.ERROR]: + tool_dependencies_for_uninstallation.append(tool_dependency) if tool_dependencies_for_uninstallation: - return trans.response.send_redirect( web.url_for( controller='admin_toolshed', - action='uninstall_tool_dependencies', - **kwd ) ) + return trans.response.send_redirect(web.url_for(controller='admin_toolshed', + action='uninstall_tool_dependencies', + **kwd)) else: message = 'No selected tool dependencies can be uninstalled, you may need to use the Repair repository feature.' status = 'error' @@ -774,123 +774,123 @@ def manage_repository_tool_dependencies( self, trans, **kwd ): if trans.app.config.tool_dependency_dir: tool_dependencies_for_installation = [] for tool_dependency_id in tool_dependency_ids: - tool_dependency = tool_dependency_util.get_tool_dependency( trans.app, tool_dependency_id ) - if tool_dependency.status in [ trans.install_model.ToolDependency.installation_status.NEVER_INSTALLED, - trans.install_model.ToolDependency.installation_status.UNINSTALLED ]: - tool_dependencies_for_installation.append( tool_dependency ) + tool_dependency = tool_dependency_util.get_tool_dependency(trans.app, tool_dependency_id) + if tool_dependency.status in [trans.install_model.ToolDependency.installation_status.NEVER_INSTALLED, + trans.install_model.ToolDependency.installation_status.UNINSTALLED]: + tool_dependencies_for_installation.append(tool_dependency) if tool_dependencies_for_installation: - self.initiate_tool_dependency_installation( trans, - tool_dependencies_for_installation, - message=message, - status=status ) + self.initiate_tool_dependency_installation(trans, + tool_dependencies_for_installation, + message=message, + status=status) else: message = 'All selected tool dependencies are already installed.' status = 'error' else: - message = 'Set the value of your tool_dependency_dir setting in your Galaxy config file (galaxy.ini) ' - message += ' and restart your Galaxy server to install tool dependencies.' - status = 'error' + message = 'Set the value of your tool_dependency_dir setting in your Galaxy config file (galaxy.ini) ' + message += ' and restart your Galaxy server to install tool dependencies.' + status = 'error' installed_tool_dependencies_select_field = \ - tool_dependency_util.build_tool_dependencies_select_field( trans.app, - tool_shed_repository=tool_shed_repository, - name='inst_td_ids', - uninstalled_only=False ) + tool_dependency_util.build_tool_dependencies_select_field(trans.app, + tool_shed_repository=tool_shed_repository, + name='inst_td_ids', + uninstalled_only=False) uninstalled_tool_dependencies_select_field = \ - tool_dependency_util.build_tool_dependencies_select_field( trans.app, - tool_shed_repository=tool_shed_repository, - name='uninstalled_tool_dependency_ids', - uninstalled_only=True ) - return trans.fill_template( '/admin/tool_shed_repository/manage_repository_tool_dependencies.mako', - repository=tool_shed_repository, - installed_tool_dependencies_select_field=installed_tool_dependencies_select_field, - uninstalled_tool_dependencies_select_field=uninstalled_tool_dependencies_select_field, - message=message, - status=status ) + tool_dependency_util.build_tool_dependencies_select_field(trans.app, + tool_shed_repository=tool_shed_repository, + name='uninstalled_tool_dependency_ids', + uninstalled_only=True) + return trans.fill_template('/admin/tool_shed_repository/manage_repository_tool_dependencies.mako', + repository=tool_shed_repository, + installed_tool_dependencies_select_field=installed_tool_dependencies_select_field, + uninstalled_tool_dependencies_select_field=uninstalled_tool_dependencies_select_field, + message=message, + status=status) @web.expose @web.require_admin - def manage_tool_dependencies( self, trans, **kwd ): + def manage_tool_dependencies(self, trans, **kwd): # This method is called when tool dependencies are being installed. See the related manage_repository_tool_dependencies # method for managing the tool dependencies for a specified installed tool shed repository. - message = escape( kwd.get( 'message', '' ) ) - status = kwd.get( 'status', 'done' ) - tool_dependency_ids = tool_dependency_util.get_tool_dependency_ids( as_string=False, **kwd ) - repository_id = kwd.get( 'repository_id', None ) + message = escape(kwd.get('message', '')) + status = kwd.get('status', 'done') + tool_dependency_ids = tool_dependency_util.get_tool_dependency_ids(as_string=False, **kwd) + repository_id = kwd.get('repository_id', None) if tool_dependency_ids: # We need a tool_shed_repository, so get it from one of the tool_dependencies. - tool_dependency = tool_dependency_util.get_tool_dependency( trans.app, tool_dependency_ids[ 0 ] ) + tool_dependency = tool_dependency_util.get_tool_dependency(trans.app, tool_dependency_ids[0]) tool_shed_repository = tool_dependency.tool_shed_repository else: # The user must be on the manage_repository_tool_dependencies page and clicked the button to either install or uninstall a # tool dependency, but they didn't check any of the available tool dependencies on which to perform the action. - tool_shed_repository = repository_util.get_tool_shed_repository_by_id( trans.app, repository_id ) - self.tool_dependency_grid.title = "Tool shed repository '%s' tool dependencies" % escape( tool_shed_repository.name ) + tool_shed_repository = repository_util.get_tool_shed_repository_by_id(trans.app, repository_id) + self.tool_dependency_grid.title = "Tool shed repository '%s' tool dependencies" % escape(tool_shed_repository.name) if 'operation' in kwd: - operation = kwd[ 'operation' ].lower() + operation = kwd['operation'].lower() if not tool_dependency_ids: message = 'Select at least 1 tool dependency to %s.' % operation - kwd[ 'message' ] = message - kwd[ 'status' ] = 'error' - del kwd[ 'operation' ] - return trans.response.send_redirect( web.url_for( controller='admin_toolshed', - action='manage_tool_dependencies', - **kwd ) ) + kwd['message'] = message + kwd['status'] = 'error' + del kwd['operation'] + return trans.response.send_redirect(web.url_for(controller='admin_toolshed', + action='manage_tool_dependencies', + **kwd)) if operation == 'browse': - return trans.response.send_redirect( web.url_for( controller='admin_toolshed', - action='browse_tool_dependency', - **kwd ) ) + return trans.response.send_redirect(web.url_for(controller='admin_toolshed', + action='browse_tool_dependency', + **kwd)) elif operation == "install": if trans.app.config.tool_dependency_dir: tool_dependencies_for_installation = [] for tool_dependency_id in tool_dependency_ids: - tool_dependency = tool_dependency_util.get_tool_dependency( trans.app, tool_dependency_id ) - if tool_dependency.status in [ trans.install_model.ToolDependency.installation_status.NEVER_INSTALLED, - trans.install_model.ToolDependency.installation_status.UNINSTALLED ]: - tool_dependencies_for_installation.append( tool_dependency ) + tool_dependency = tool_dependency_util.get_tool_dependency(trans.app, tool_dependency_id) + if tool_dependency.status in [trans.install_model.ToolDependency.installation_status.NEVER_INSTALLED, + trans.install_model.ToolDependency.installation_status.UNINSTALLED]: + tool_dependencies_for_installation.append(tool_dependency) if tool_dependencies_for_installation: - self.initiate_tool_dependency_installation( trans, - tool_dependencies_for_installation, - message=message, - status=status ) + self.initiate_tool_dependency_installation(trans, + tool_dependencies_for_installation, + message=message, + status=status) else: - kwd[ 'message' ] = 'All selected tool dependencies are already installed.' - kwd[ 'status' ] = 'error' + kwd['message'] = 'All selected tool dependencies are already installed.' + kwd['status'] = 'error' else: - message = 'Set the value of your tool_dependency_dir setting in your Galaxy config file (galaxy.ini) ' - message += ' and restart your Galaxy server to install tool dependencies.' - kwd[ 'message' ] = message - kwd[ 'status' ] = 'error' + message = 'Set the value of your tool_dependency_dir setting in your Galaxy config file (galaxy.ini) ' + message += ' and restart your Galaxy server to install tool dependencies.' + kwd['message'] = message + kwd['status'] = 'error' # Redirect if no tool dependencies are in the process of being installed. if tool_shed_repository.tool_dependencies_being_installed: - return self.tool_dependency_grid( trans, **kwd ) - return trans.response.send_redirect( web.url_for( controller='admin_toolshed', - action='manage_repository_tool_dependencies', - tool_dependency_ids=tool_dependency_ids, - repository_id=repository_id, - message=message, - status=status ) ) + return self.tool_dependency_grid(trans, **kwd) + return trans.response.send_redirect(web.url_for(controller='admin_toolshed', + action='manage_repository_tool_dependencies', + tool_dependency_ids=tool_dependency_ids, + repository_id=repository_id, + message=message, + status=status)) @web.expose @web.require_admin - def monitor_repository_installation( self, trans, **kwd ): - tsridslist = common_util.get_tool_shed_repository_ids( **kwd ) + def monitor_repository_installation(self, trans, **kwd): + tsridslist = common_util.get_tool_shed_repository_ids(**kwd) if not tsridslist: - tsridslist = repository_util.get_ids_of_tool_shed_repositories_being_installed( trans.app, as_string=False ) - kwd[ 'tool_shed_repository_ids' ] = tsridslist - return self.repository_installation_grid( trans, **kwd ) + tsridslist = repository_util.get_ids_of_tool_shed_repositories_being_installed(trans.app, as_string=False) + kwd['tool_shed_repository_ids'] = tsridslist + return self.repository_installation_grid(trans, **kwd) @web.json @web.require_admin - def open_folder( self, trans, folder_path, repository_id ): + def open_folder(self, trans, folder_path, repository_id): # Avoid caching trans.response.headers['Pragma'] = 'no-cache' trans.response.headers['Expires'] = '0' - return suc.open_repository_files_folder( trans.app, folder_path, repository_id, is_admin=True ) + return suc.open_repository_files_folder(trans.app, folder_path, repository_id, is_admin=True) @web.expose @web.require_admin - def prepare_for_install( self, trans, **kwd ): - if not suc.have_shed_tool_conf_for_install( trans.app ): + def prepare_for_install(self, trans, **kwd): + if not suc.have_shed_tool_conf_for_install(trans.app): message = 'The tool_config_file setting in galaxy.ini must include at least one ' message += 'shed tool configuration file name with a <toolbox> tag that includes a tool_path ' message += 'attribute value which is a directory relative to the Galaxy installation directory in order ' @@ -899,41 +899,41 @@ def prepare_for_install( self, trans, **kwd ): message += 'Installation ' message += 'of Galaxy Tool Shed repository tools into a local Galaxy instance section of the Galaxy Tool ' message += 'Shed wiki for all of the details.' - return trans.show_error_message( message ) - message = escape( kwd.get( 'message', '' ) ) - status = kwd.get( 'status', 'done' ) - shed_tool_conf = kwd.get( 'shed_tool_conf', None ) - tool_shed_url = kwd.get( 'tool_shed_url', '' ) - tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry( trans.app, tool_shed_url ) + return trans.show_error_message(message) + message = escape(kwd.get('message', '')) + status = kwd.get('status', 'done') + shed_tool_conf = kwd.get('shed_tool_conf', None) + tool_shed_url = kwd.get('tool_shed_url', '') + tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry(trans.app, tool_shed_url) # Handle repository dependencies, which do not include those that are required only for compiling a dependent # repository's tool dependencies. - has_repository_dependencies = util.string_as_bool( kwd.get( 'has_repository_dependencies', False ) ) - install_repository_dependencies = kwd.get( 'install_repository_dependencies', '' ) + has_repository_dependencies = util.string_as_bool(kwd.get('has_repository_dependencies', False)) + install_repository_dependencies = kwd.get('install_repository_dependencies', '') # Every repository will be installed into the same tool panel section or all will be installed outside of any sections. - new_tool_panel_section_label = kwd.get( 'new_tool_panel_section_label', '' ) - tool_panel_section_id = kwd.get( 'tool_panel_section_id', '' ) + new_tool_panel_section_label = kwd.get('new_tool_panel_section_label', '') + tool_panel_section_id = kwd.get('tool_panel_section_id', '') tool_panel_section_keys = [] # One or more repositories may include tools, but not necessarily all of them. - includes_tools = util.string_as_bool( kwd.get( 'includes_tools', False ) ) + includes_tools = util.string_as_bool(kwd.get('includes_tools', False)) # Some tools should not be displayed in the tool panel (e.g., DataManager tools and datatype converters). - includes_tools_for_display_in_tool_panel = util.string_as_bool( kwd.get( 'includes_tools_for_display_in_tool_panel', False ) ) - includes_tool_dependencies = util.string_as_bool( kwd.get( 'includes_tool_dependencies', False ) ) - install_resolver_dependencies = kwd.get( 'install_resolver_dependencies', '' ) - install_tool_dependencies = kwd.get( 'install_tool_dependencies', '' ) + includes_tools_for_display_in_tool_panel = util.string_as_bool(kwd.get('includes_tools_for_display_in_tool_panel', False)) + includes_tool_dependencies = util.string_as_bool(kwd.get('includes_tool_dependencies', False)) + install_resolver_dependencies = kwd.get('install_resolver_dependencies', '') + install_tool_dependencies = kwd.get('install_tool_dependencies', '') # In addition to installing new repositories, this method is called when updating an installed repository # to a new changeset_revision where the update includes newly defined repository dependencies. - updating = util.asbool( kwd.get( 'updating', False ) ) - updating_repository_id = kwd.get( 'updating_repository_id', None ) - updating_to_changeset_revision = kwd.get( 'updating_to_changeset_revision', None ) - updating_to_ctx_rev = kwd.get( 'updating_to_ctx_rev', None ) - encoded_updated_metadata = kwd.get( 'encoded_updated_metadata', None ) - encoded_repo_info_dicts = kwd.get( 'encoded_repo_info_dicts', '' ) + updating = util.asbool(kwd.get('updating', False)) + updating_repository_id = kwd.get('updating_repository_id', None) + updating_to_changeset_revision = kwd.get('updating_to_changeset_revision', None) + updating_to_ctx_rev = kwd.get('updating_to_ctx_rev', None) + encoded_updated_metadata = kwd.get('encoded_updated_metadata', None) + encoded_repo_info_dicts = kwd.get('encoded_repo_info_dicts', '') if encoded_repo_info_dicts: - encoded_repo_info_dicts = encoded_repo_info_dicts.split( encoding_util.encoding_sep ) + encoded_repo_info_dicts = encoded_repo_info_dicts.split(encoding_util.encoding_sep) if not encoded_repo_info_dicts: # The request originated in the tool shed via a tool search or from this controller's # update_to_changeset_revision() method. - repository_ids = kwd.get( 'repository_ids', None ) + repository_ids = kwd.get('repository_ids', None) if updating: # We have updated an installed repository where the updates included newly defined repository # and possibly tool dependencies. We will have arrived here only if the updates include newly @@ -942,138 +942,138 @@ def prepare_for_install( self, trans, **kwd ): # but the received repository id is from the Galaxy side (the caller is this controller's # update_to_changeset_revision() method. We need to get the id of the same repository from the # Tool Shed side. - repository = repository_util.get_tool_shed_repository_by_id( trans.app, updating_repository_id ) + repository = repository_util.get_tool_shed_repository_by_id(trans.app, updating_repository_id) # For backward compatibility to the 12/20/12 Galaxy release. try: - params = dict( name=str( repository.name ), owner=str( repository.owner ) ) - pathspec = [ 'repository', 'get_repository_id' ] - repository_ids = util.url_get( tool_shed_url, password_mgr=self.app.tool_shed_registry.url_auth( tool_shed_url ), pathspec=pathspec, params=params ) + params = dict(name=str(repository.name), owner=str(repository.owner)) + pathspec = ['repository', 'get_repository_id'] + repository_ids = util.url_get(tool_shed_url, password_mgr=self.app.tool_shed_registry.url_auth(tool_shed_url), pathspec=pathspec, params=params) except Exception as e: # The Tool Shed cannot handle the get_repository_id request, so the code must be older than the # 04/2014 Galaxy release when it was introduced. It will be safest to error out and let the # Tool Shed admin update the Tool Shed to a later release. - message = 'The updates available for the repository %s ' % escape( str( repository.name ) ) + message = 'The updates available for the repository %s ' % escape(str(repository.name)) message += 'include newly defined repository or tool dependency definitions, and attempting ' message += 'to update the repository resulted in the following error. Contact the Tool Shed ' - message += 'administrator if necessary.
    %s' % str( e ) + message += 'administrator if necessary.
    %s' % str(e) status = 'error' - return trans.response.send_redirect( web.url_for( controller='admin_toolshed', - action='browse_repositories', - message=message, - status=status ) ) + return trans.response.send_redirect(web.url_for(controller='admin_toolshed', + action='browse_repositories', + message=message, + status=status)) changeset_revisions = updating_to_changeset_revision else: - changeset_revisions = kwd.get( 'changeset_revisions', None ) + changeset_revisions = kwd.get('changeset_revisions', None) # Get the information necessary to install each repository. - params = dict( repository_ids=str( repository_ids ), changeset_revisions=str( changeset_revisions ) ) - pathspec = [ 'repository', 'get_repository_information' ] - raw_text = util.url_get( tool_shed_url, password_mgr=self.app.tool_shed_registry.url_auth( tool_shed_url ), pathspec=pathspec, params=params ) - repo_information_dict = json.loads( raw_text ) - for encoded_repo_info_dict in repo_information_dict.get( 'repo_info_dicts', [] ): - decoded_repo_info_dict = encoding_util.tool_shed_decode( encoded_repo_info_dict ) + params = dict(repository_ids=str(repository_ids), changeset_revisions=str(changeset_revisions)) + pathspec = ['repository', 'get_repository_information'] + raw_text = util.url_get(tool_shed_url, password_mgr=self.app.tool_shed_registry.url_auth(tool_shed_url), pathspec=pathspec, params=params) + repo_information_dict = json.loads(raw_text) + for encoded_repo_info_dict in repo_information_dict.get('repo_info_dicts', []): + decoded_repo_info_dict = encoding_util.tool_shed_decode(encoded_repo_info_dict) if not includes_tools: - includes_tools = util.string_as_bool( decoded_repo_info_dict.get( 'includes_tools', False ) ) + includes_tools = util.string_as_bool(decoded_repo_info_dict.get('includes_tools', False)) if not includes_tools_for_display_in_tool_panel: includes_tools_for_display_in_tool_panel = \ - util.string_as_bool( decoded_repo_info_dict.get( 'includes_tools_for_display_in_tool_panel', False ) ) + util.string_as_bool(decoded_repo_info_dict.get('includes_tools_for_display_in_tool_panel', False)) if not has_repository_dependencies: - has_repository_dependencies = util.string_as_bool( repo_information_dict.get( 'has_repository_dependencies', False ) ) + has_repository_dependencies = util.string_as_bool(repo_information_dict.get('has_repository_dependencies', False)) if not includes_tool_dependencies: - includes_tool_dependencies = util.string_as_bool( repo_information_dict.get( 'includes_tool_dependencies', False ) ) - encoded_repo_info_dicts = util.listify( repo_information_dict.get( 'repo_info_dicts', [] ) ) - repo_info_dicts = [ encoding_util.tool_shed_decode( encoded_repo_info_dict ) for encoded_repo_info_dict in encoded_repo_info_dicts ] - dd = dependency_display.DependencyDisplayer( trans.app ) - install_repository_manager = install_manager.InstallRepositoryManager( trans.app ) - if kwd.get( 'select_tool_panel_section_button', False ): + includes_tool_dependencies = util.string_as_bool(repo_information_dict.get('includes_tool_dependencies', False)) + encoded_repo_info_dicts = util.listify(repo_information_dict.get('repo_info_dicts', [])) + repo_info_dicts = [encoding_util.tool_shed_decode(encoded_repo_info_dict) for encoded_repo_info_dict in encoded_repo_info_dicts] + dd = dependency_display.DependencyDisplayer(trans.app) + install_repository_manager = install_manager.InstallRepositoryManager(trans.app) + if kwd.get('select_tool_panel_section_button', False): if updating: - repository = repository_util.get_tool_shed_repository_by_id( trans.app, updating_repository_id ) - decoded_updated_metadata = encoding_util.tool_shed_decode( encoded_updated_metadata ) + repository = repository_util.get_tool_shed_repository_by_id(trans.app, updating_repository_id) + decoded_updated_metadata = encoding_util.tool_shed_decode(encoded_updated_metadata) # Now that the user has decided whether they will handle dependencies, we can update # the repository to the latest revision. - repository = trans.app.update_repository_manager.update_repository_record( repository=repository, - updated_metadata_dict=decoded_updated_metadata, - updated_changeset_revision=updating_to_changeset_revision, - updated_ctx_rev=updating_to_ctx_rev ) - install_repository_dependencies = CheckboxField.is_checked( install_repository_dependencies ) + repository = trans.app.update_repository_manager.update_repository_record(repository=repository, + updated_metadata_dict=decoded_updated_metadata, + updated_changeset_revision=updating_to_changeset_revision, + updated_ctx_rev=updating_to_ctx_rev) + install_repository_dependencies = CheckboxField.is_checked(install_repository_dependencies) if includes_tool_dependencies: - install_tool_dependencies = CheckboxField.is_checked( install_tool_dependencies ) + install_tool_dependencies = CheckboxField.is_checked(install_tool_dependencies) else: install_tool_dependencies = False - install_resolver_dependencies = CheckboxField.is_checked( install_resolver_dependencies ) - tool_path = suc.get_tool_path_by_shed_tool_conf_filename( trans.app, shed_tool_conf ) - installation_dict = dict( install_repository_dependencies=install_repository_dependencies, - new_tool_panel_section_label=new_tool_panel_section_label, - no_changes_checked=False, - repo_info_dicts=repo_info_dicts, - tool_panel_section_id=tool_panel_section_id, - tool_path=tool_path, - tool_shed_url=tool_shed_url ) + install_resolver_dependencies = CheckboxField.is_checked(install_resolver_dependencies) + tool_path = suc.get_tool_path_by_shed_tool_conf_filename(trans.app, shed_tool_conf) + installation_dict = dict(install_repository_dependencies=install_repository_dependencies, + new_tool_panel_section_label=new_tool_panel_section_label, + no_changes_checked=False, + repo_info_dicts=repo_info_dicts, + tool_panel_section_id=tool_panel_section_id, + tool_path=tool_path, + tool_shed_url=tool_shed_url) created_or_updated_tool_shed_repositories, tool_panel_section_keys, repo_info_dicts, filtered_repo_info_dicts = \ - install_repository_manager.handle_tool_shed_repositories( installation_dict ) + install_repository_manager.handle_tool_shed_repositories(installation_dict) if created_or_updated_tool_shed_repositories: - installation_dict = dict( created_or_updated_tool_shed_repositories=created_or_updated_tool_shed_repositories, - filtered_repo_info_dicts=filtered_repo_info_dicts, - has_repository_dependencies=has_repository_dependencies, - includes_tool_dependencies=includes_tool_dependencies, - includes_tools=includes_tools, - includes_tools_for_display_in_tool_panel=includes_tools_for_display_in_tool_panel, - install_repository_dependencies=install_repository_dependencies, - install_resolver_dependencies=install_resolver_dependencies, - install_tool_dependencies=install_tool_dependencies, - message=message, - new_tool_panel_section_label=new_tool_panel_section_label, - shed_tool_conf=shed_tool_conf, - status=status, - tool_panel_section_id=tool_panel_section_id, - tool_panel_section_keys=tool_panel_section_keys, - tool_path=tool_path, - tool_shed_url=tool_shed_url ) + installation_dict = dict(created_or_updated_tool_shed_repositories=created_or_updated_tool_shed_repositories, + filtered_repo_info_dicts=filtered_repo_info_dicts, + has_repository_dependencies=has_repository_dependencies, + includes_tool_dependencies=includes_tool_dependencies, + includes_tools=includes_tools, + includes_tools_for_display_in_tool_panel=includes_tools_for_display_in_tool_panel, + install_repository_dependencies=install_repository_dependencies, + install_resolver_dependencies=install_resolver_dependencies, + install_tool_dependencies=install_tool_dependencies, + message=message, + new_tool_panel_section_label=new_tool_panel_section_label, + shed_tool_conf=shed_tool_conf, + status=status, + tool_panel_section_id=tool_panel_section_id, + tool_panel_section_keys=tool_panel_section_keys, + tool_path=tool_path, + tool_shed_url=tool_shed_url) encoded_kwd, query, tool_shed_repositories, encoded_repository_ids = \ - install_repository_manager.initiate_repository_installation( installation_dict ) - return trans.fill_template( 'admin/tool_shed_repository/initiate_repository_installation.mako', - encoded_kwd=encoded_kwd, - query=query, - tool_shed_repositories=tool_shed_repositories, - initiate_repository_installation_ids=encoded_repository_ids, - reinstalling=False ) + install_repository_manager.initiate_repository_installation(installation_dict) + return trans.fill_template('admin/tool_shed_repository/initiate_repository_installation.mako', + encoded_kwd=encoded_kwd, + query=query, + tool_shed_repositories=tool_shed_repositories, + initiate_repository_installation_ids=encoded_repository_ids, + reinstalling=False) else: - kwd[ 'message' ] = message - kwd[ 'status' ] = status - return trans.response.send_redirect( web.url_for( controller='admin_toolshed', - action='manage_repositories', - **kwd ) ) - shed_tool_conf_select_field = tool_util.build_shed_tool_conf_select_field( trans.app ) - tool_path = suc.get_tool_path_by_shed_tool_conf_filename( trans.app, shed_tool_conf ) - tool_panel_section_select_field = tool_util.build_tool_panel_section_select_field( trans.app ) + kwd['message'] = message + kwd['status'] = status + return trans.response.send_redirect(web.url_for(controller='admin_toolshed', + action='manage_repositories', + **kwd)) + shed_tool_conf_select_field = tool_util.build_shed_tool_conf_select_field(trans.app) + tool_path = suc.get_tool_path_by_shed_tool_conf_filename(trans.app, shed_tool_conf) + tool_panel_section_select_field = tool_util.build_tool_panel_section_select_field(trans.app) tool_requirements = suc.get_tool_shed_repo_requirements(app=trans.app, tool_shed_url=tool_shed_url, repo_info_dicts=repo_info_dicts) view = views.DependencyResolversView(self.app) requirements_status = view.get_requirements_status(tool_requirements) - if len( repo_info_dicts ) == 1: + if len(repo_info_dicts) == 1: # If we're installing or updating a single repository, see if it contains a readme or # dependencies that we can display. - repo_info_dict = repo_info_dicts[ 0 ] + repo_info_dict = repo_info_dicts[0] dependencies_for_repository_dict = \ - trans.app.installed_repository_manager.get_dependencies_for_repository( tool_shed_url, - repo_info_dict, - includes_tool_dependencies, - updating=updating ) + trans.app.installed_repository_manager.get_dependencies_for_repository(tool_shed_url, + repo_info_dict, + includes_tool_dependencies, + updating=updating) if not has_repository_dependencies: - has_repository_dependencies = dependencies_for_repository_dict.get( 'has_repository_dependencies', False ) + has_repository_dependencies = dependencies_for_repository_dict.get('has_repository_dependencies', False) if not includes_tool_dependencies: - includes_tool_dependencies = dependencies_for_repository_dict.get( 'includes_tool_dependencies', False ) + includes_tool_dependencies = dependencies_for_repository_dict.get('includes_tool_dependencies', False) if not includes_tools: - includes_tools = dependencies_for_repository_dict.get( 'includes_tools', False ) + includes_tools = dependencies_for_repository_dict.get('includes_tools', False) if not includes_tools_for_display_in_tool_panel: includes_tools_for_display_in_tool_panel = \ - dependencies_for_repository_dict.get( 'includes_tools_for_display_in_tool_panel', False ) - installed_repository_dependencies = dependencies_for_repository_dict.get( 'installed_repository_dependencies', None ) - installed_tool_dependencies = dependencies_for_repository_dict.get( 'installed_tool_dependencies', None ) - missing_repository_dependencies = dependencies_for_repository_dict.get( 'missing_repository_dependencies', None ) - missing_tool_dependencies = dependencies_for_repository_dict.get( 'missing_tool_dependencies', None ) - readme_files_dict = readme_util.get_readme_files_dict_for_display( trans.app, tool_shed_url, repo_info_dict ) + dependencies_for_repository_dict.get('includes_tools_for_display_in_tool_panel', False) + installed_repository_dependencies = dependencies_for_repository_dict.get('installed_repository_dependencies', None) + installed_tool_dependencies = dependencies_for_repository_dict.get('installed_tool_dependencies', None) + missing_repository_dependencies = dependencies_for_repository_dict.get('missing_repository_dependencies', None) + missing_tool_dependencies = dependencies_for_repository_dict.get('missing_tool_dependencies', None) + readme_files_dict = readme_util.get_readme_files_dict_for_display(trans.app, tool_shed_url, repo_info_dict) # We're handling 1 of 3 scenarios here: (1) we're installing a tool shed repository for the first time, so we've # retrieved the list of installed and missing repository dependencies from the database (2) we're handling the # scenario where an error occurred during the installation process, so we have a tool_shed_repository record in @@ -1084,36 +1084,36 @@ def prepare_for_install( self, trans, **kwd ): # defined repository (and possibly tool) dependencies. In this case, merging will result in newly defined # dependencies to be lost. We pass the updating parameter to make sure merging occurs only when appropriate. containers_dict = \ - dd.populate_containers_dict_for_new_install( tool_shed_url=tool_shed_url, - tool_path=tool_path, - readme_files_dict=readme_files_dict, - installed_repository_dependencies=installed_repository_dependencies, - missing_repository_dependencies=missing_repository_dependencies, - installed_tool_dependencies=installed_tool_dependencies, - missing_tool_dependencies=missing_tool_dependencies, - updating=updating ) + dd.populate_containers_dict_for_new_install(tool_shed_url=tool_shed_url, + tool_path=tool_path, + readme_files_dict=readme_files_dict, + installed_repository_dependencies=installed_repository_dependencies, + missing_repository_dependencies=missing_repository_dependencies, + installed_tool_dependencies=installed_tool_dependencies, + missing_tool_dependencies=missing_tool_dependencies, + updating=updating) else: # We're installing a list of repositories, each of which may have tool dependencies or repository dependencies. containers_dicts = [] for repo_info_dict in repo_info_dicts: dependencies_for_repository_dict = \ - trans.app.installed_repository_manager.get_dependencies_for_repository( tool_shed_url, - repo_info_dict, - includes_tool_dependencies, - updating=updating ) + trans.app.installed_repository_manager.get_dependencies_for_repository(tool_shed_url, + repo_info_dict, + includes_tool_dependencies, + updating=updating) if not has_repository_dependencies: - has_repository_dependencies = dependencies_for_repository_dict.get( 'has_repository_dependencies', False ) + has_repository_dependencies = dependencies_for_repository_dict.get('has_repository_dependencies', False) if not includes_tool_dependencies: - includes_tool_dependencies = dependencies_for_repository_dict.get( 'includes_tool_dependencies', False ) + includes_tool_dependencies = dependencies_for_repository_dict.get('includes_tool_dependencies', False) if not includes_tools: - includes_tools = dependencies_for_repository_dict.get( 'includes_tools', False ) + includes_tools = dependencies_for_repository_dict.get('includes_tools', False) if not includes_tools_for_display_in_tool_panel: includes_tools_for_display_in_tool_panel = \ - dependencies_for_repository_dict.get( 'includes_tools_for_display_in_tool_panel', False ) - installed_repository_dependencies = dependencies_for_repository_dict.get( 'installed_repository_dependencies', None ) - installed_tool_dependencies = dependencies_for_repository_dict.get( 'installed_tool_dependencies', None ) - missing_repository_dependencies = dependencies_for_repository_dict.get( 'missing_repository_dependencies', None ) - missing_tool_dependencies = dependencies_for_repository_dict.get( 'missing_tool_dependencies', None ) + dependencies_for_repository_dict.get('includes_tools_for_display_in_tool_panel', False) + installed_repository_dependencies = dependencies_for_repository_dict.get('installed_repository_dependencies', None) + installed_tool_dependencies = dependencies_for_repository_dict.get('installed_tool_dependencies', None) + missing_repository_dependencies = dependencies_for_repository_dict.get('missing_repository_dependencies', None) + missing_tool_dependencies = dependencies_for_repository_dict.get('missing_tool_dependencies', None) containers_dict = dd.populate_containers_dict_for_new_install( tool_shed_url=tool_shed_url, tool_path=tool_path, @@ -1124,9 +1124,9 @@ def prepare_for_install( self, trans, **kwd ): missing_tool_dependencies=missing_tool_dependencies, updating=updating ) - containers_dicts.append( containers_dict ) + containers_dicts.append(containers_dict) # Merge all containers into a single container. - containers_dict = dd.merge_containers_dicts_for_new_install( containers_dicts ) + containers_dict = dd.merge_containers_dicts_for_new_install(containers_dicts) # Handle tool dependencies check box. if trans.app.config.tool_dependency_dir is None: if includes_tool_dependencies: @@ -1137,99 +1137,99 @@ def prepare_for_install( self, trans, **kwd ): install_tool_dependencies_check_box_checked = False else: install_tool_dependencies_check_box_checked = True - install_tool_dependencies_check_box = CheckboxField( 'install_tool_dependencies', - checked=install_tool_dependencies_check_box_checked ) + install_tool_dependencies_check_box = CheckboxField('install_tool_dependencies', + checked=install_tool_dependencies_check_box_checked) # Handle repository dependencies check box. - install_repository_dependencies_check_box = CheckboxField( 'install_repository_dependencies', checked=True ) + install_repository_dependencies_check_box = CheckboxField('install_repository_dependencies', checked=True) view = views.DependencyResolversView(self.app) if view.installable_resolvers: - install_resolver_dependencies_check_box = CheckboxField( 'install_resolver_dependencies', checked=True ) + install_resolver_dependencies_check_box = CheckboxField('install_resolver_dependencies', checked=True) else: install_resolver_dependencies_check_box = None - encoded_repo_info_dicts = encoding_util.encoding_sep.join( encoded_repo_info_dicts ) - tool_shed_url = kwd[ 'tool_shed_url' ] - return trans.fill_template( '/admin/tool_shed_repository/select_tool_panel_section.mako', - encoded_repo_info_dicts=encoded_repo_info_dicts, - updating=updating, - updating_repository_id=updating_repository_id, - updating_to_ctx_rev=updating_to_ctx_rev, - updating_to_changeset_revision=updating_to_changeset_revision, - encoded_updated_metadata=encoded_updated_metadata, - includes_tools=includes_tools, - includes_tools_for_display_in_tool_panel=includes_tools_for_display_in_tool_panel, - includes_tool_dependencies=includes_tool_dependencies, - install_tool_dependencies_check_box=install_tool_dependencies_check_box, - install_resolver_dependencies_check_box=install_resolver_dependencies_check_box, - has_repository_dependencies=has_repository_dependencies, - install_repository_dependencies_check_box=install_repository_dependencies_check_box, - new_tool_panel_section_label=new_tool_panel_section_label, - containers_dict=containers_dict, - shed_tool_conf=shed_tool_conf, - shed_tool_conf_select_field=shed_tool_conf_select_field, - tool_panel_section_select_field=tool_panel_section_select_field, - tool_shed_url=tool_shed_url, - requirements_status=requirements_status, - message=message, - status=status ) + encoded_repo_info_dicts = encoding_util.encoding_sep.join(encoded_repo_info_dicts) + tool_shed_url = kwd['tool_shed_url'] + return trans.fill_template('/admin/tool_shed_repository/select_tool_panel_section.mako', + encoded_repo_info_dicts=encoded_repo_info_dicts, + updating=updating, + updating_repository_id=updating_repository_id, + updating_to_ctx_rev=updating_to_ctx_rev, + updating_to_changeset_revision=updating_to_changeset_revision, + encoded_updated_metadata=encoded_updated_metadata, + includes_tools=includes_tools, + includes_tools_for_display_in_tool_panel=includes_tools_for_display_in_tool_panel, + includes_tool_dependencies=includes_tool_dependencies, + install_tool_dependencies_check_box=install_tool_dependencies_check_box, + install_resolver_dependencies_check_box=install_resolver_dependencies_check_box, + has_repository_dependencies=has_repository_dependencies, + install_repository_dependencies_check_box=install_repository_dependencies_check_box, + new_tool_panel_section_label=new_tool_panel_section_label, + containers_dict=containers_dict, + shed_tool_conf=shed_tool_conf, + shed_tool_conf_select_field=shed_tool_conf_select_field, + tool_panel_section_select_field=tool_panel_section_select_field, + tool_shed_url=tool_shed_url, + requirements_status=requirements_status, + message=message, + status=status) @web.expose @web.require_admin - def purge_repository( self, trans, **kwd ): + def purge_repository(self, trans, **kwd): """Purge a "white ghost" repository from the database.""" - repository_id = kwd.get( 'id', None ) + repository_id = kwd.get('id', None) new_kwd = {} if repository_id is not None: - repository = repository_util.get_installed_tool_shed_repository( trans.app, repository_id ) + repository = repository_util.get_installed_tool_shed_repository(trans.app, repository_id) if repository: if repository.is_new: - if kwd.get( 'purge_repository_button', False ): + if kwd.get('purge_repository_button', False): irm = trans.app.installed_repository_manager - purge_status, purge_message = irm.purge_repository( repository ) + purge_status, purge_message = irm.purge_repository(repository) if purge_status == 'ok': - new_kwd[ 'status' ] = "done" + new_kwd['status'] = "done" else: - new_kwd[ 'status' ] = 'error' - new_kwd[ 'message' ] = purge_message + new_kwd['status'] = 'error' + new_kwd['message'] = purge_message else: - return trans.fill_template( 'admin/tool_shed_repository/purge_repository_confirmation.mako', - repository=repository ) + return trans.fill_template('admin/tool_shed_repository/purge_repository_confirmation.mako', + repository=repository) else: - new_kwd[ 'status' ] = 'error' - new_kwd[ 'message' ] = 'Repositories must have a New status in order to be purged.' + new_kwd['status'] = 'error' + new_kwd['message'] = 'Repositories must have a New status in order to be purged.' else: - new_kwd[ 'status' ] = 'error' - new_kwd[ 'message' ] = 'Cannot locate the database record for the repository with encoded id %s.' % str( repository_id ) + new_kwd['status'] = 'error' + new_kwd['message'] = 'Cannot locate the database record for the repository with encoded id %s.' % str(repository_id) else: - new_kwd[ 'status' ] = 'error' - new_kwd[ 'message' ] = 'Invalid repository id value "None" received for repository to be purged.' - return trans.response.send_redirect( web.url_for( controller='admin_toolshed', - action='browse_repositories', - **new_kwd ) ) + new_kwd['status'] = 'error' + new_kwd['message'] = 'Invalid repository id value "None" received for repository to be purged.' + return trans.response.send_redirect(web.url_for(controller='admin_toolshed', + action='browse_repositories', + **new_kwd)) @web.expose @web.require_admin - def reinstall_repository( self, trans, **kwd ): + def reinstall_repository(self, trans, **kwd): """ Reinstall a tool shed repository that has been previously uninstalled, making sure to handle all repository and tool dependencies of the repository. """ - rdim = repository_dependency_manager.RepositoryDependencyInstallManager( trans.app ) - message = escape( kwd.get( 'message', '' ) ) - status = kwd.get( 'status', 'done' ) - repository_id = kwd[ 'id' ] - tool_shed_repository = repository_util.get_installed_tool_shed_repository( trans.app, repository_id ) - no_changes = kwd.get( 'no_changes', '' ) - no_changes_checked = CheckboxField.is_checked( no_changes ) - install_repository_dependencies = CheckboxField.is_checked( kwd.get( 'install_repository_dependencies', '' ) ) - install_tool_dependencies = CheckboxField.is_checked( kwd.get( 'install_tool_dependencies', '' ) ) - install_resolver_dependencies = CheckboxField.is_checked( kwd.get( 'install_resolver_dependencies', '' ) ) + rdim = repository_dependency_manager.RepositoryDependencyInstallManager(trans.app) + message = escape(kwd.get('message', '')) + status = kwd.get('status', 'done') + repository_id = kwd['id'] + tool_shed_repository = repository_util.get_installed_tool_shed_repository(trans.app, repository_id) + no_changes = kwd.get('no_changes', '') + no_changes_checked = CheckboxField.is_checked(no_changes) + install_repository_dependencies = CheckboxField.is_checked(kwd.get('install_repository_dependencies', '')) + install_tool_dependencies = CheckboxField.is_checked(kwd.get('install_tool_dependencies', '')) + install_resolver_dependencies = CheckboxField.is_checked(kwd.get('install_resolver_dependencies', '')) shed_tool_conf, tool_path, relative_install_dir = \ - suc.get_tool_panel_config_tool_path_install_dir( trans.app, tool_shed_repository ) - repository_clone_url = common_util.generate_clone_url_for_installed_repository( trans.app, tool_shed_repository ) - tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry( trans.app, tool_shed_repository.tool_shed ) + suc.get_tool_panel_config_tool_path_install_dir(trans.app, tool_shed_repository) + repository_clone_url = common_util.generate_clone_url_for_installed_repository(trans.app, tool_shed_repository) + tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry(trans.app, tool_shed_repository.tool_shed) tool_section = None - tool_panel_section_id = kwd.get( 'tool_panel_section_id', '' ) - new_tool_panel_section_label = kwd.get( 'new_tool_panel_section_label', '' ) + tool_panel_section_id = kwd.get('tool_panel_section_id', '') + new_tool_panel_section_label = kwd.get('new_tool_panel_section_label', '') tool_panel_section_key = None tool_panel_section_keys = [] metadata = tool_shed_repository.metadata @@ -1237,17 +1237,17 @@ def reinstall_repository( self, trans, **kwd ): # its repository dependencies. includes_tool_dependencies = tool_shed_repository.includes_tool_dependencies if tool_shed_repository.includes_tools_for_display_in_tool_panel: - tpm = tool_panel_manager.ToolPanelManager( trans.app ) + tpm = tool_panel_manager.ToolPanelManager(trans.app) # Handle the selected tool panel location for loading tools included in the tool shed repository. tool_section, tool_panel_section_key = \ - tpm.handle_tool_panel_selection( toolbox=trans.app.toolbox, - metadata=metadata, - no_changes_checked=no_changes_checked, - tool_panel_section_id=tool_panel_section_id, - new_tool_panel_section_label=new_tool_panel_section_label ) + tpm.handle_tool_panel_selection(toolbox=trans.app.toolbox, + metadata=metadata, + no_changes_checked=no_changes_checked, + tool_panel_section_id=tool_panel_section_id, + new_tool_panel_section_label=new_tool_panel_section_label) if tool_section is not None: # Just in case the tool_section.id differs from tool_panel_section_id, which it shouldn't... - tool_panel_section_id = str( tool_section.id ) + tool_panel_section_id = str(tool_section.id) if tool_shed_repository.status == trans.install_model.ToolShedRepository.installation_status.UNINSTALLED: repository_type = suc.get_repository_type_from_tool_shed(trans.app, tool_shed_url, @@ -1266,181 +1266,181 @@ def reinstall_repository( self, trans, **kwd ): suc.clean_dependency_relationships(trans, new_meta, tool_shed_repository, tool_shed_url) # The repository's status must be updated from 'Uninstalled' to 'New' when initiating reinstall # so the repository_installation_updater will function. - tool_shed_repository = repository_util.create_or_update_tool_shed_repository( trans.app, - tool_shed_repository.name, - tool_shed_repository.description, - tool_shed_repository.installed_changeset_revision, - tool_shed_repository.ctx_rev, - repository_clone_url, - metadata, - trans.install_model.ToolShedRepository.installation_status.NEW, - tool_shed_repository.changeset_revision, - tool_shed_repository.owner, - tool_shed_repository.dist_to_shed ) - ctx_rev = suc.get_ctx_rev( trans.app, - tool_shed_url, - tool_shed_repository.name, - tool_shed_repository.owner, - tool_shed_repository.installed_changeset_revision ) + tool_shed_repository = repository_util.create_or_update_tool_shed_repository(trans.app, + tool_shed_repository.name, + tool_shed_repository.description, + tool_shed_repository.installed_changeset_revision, + tool_shed_repository.ctx_rev, + repository_clone_url, + metadata, + trans.install_model.ToolShedRepository.installation_status.NEW, + tool_shed_repository.changeset_revision, + tool_shed_repository.owner, + tool_shed_repository.dist_to_shed) + ctx_rev = suc.get_ctx_rev(trans.app, + tool_shed_url, + tool_shed_repository.name, + tool_shed_repository.owner, + tool_shed_repository.installed_changeset_revision) repo_info_dicts = [] - repo_info_dict = kwd.get( 'repo_info_dict', None ) + repo_info_dict = kwd.get('repo_info_dict', None) if repo_info_dict: - if isinstance( repo_info_dict, string_types ): - repo_info_dict = encoding_util.tool_shed_decode( repo_info_dict ) + if isinstance(repo_info_dict, string_types): + repo_info_dict = encoding_util.tool_shed_decode(repo_info_dict) else: # Entering this else block occurs only if the tool_shed_repository does not include any valid tools. if install_repository_dependencies: repository_dependencies = \ - rdim.get_repository_dependencies_for_installed_tool_shed_repository( trans.app, - tool_shed_repository ) + rdim.get_repository_dependencies_for_installed_tool_shed_repository(trans.app, + tool_shed_repository) else: repository_dependencies = None if metadata: - tool_dependencies = metadata.get( 'tool_dependencies', None ) + tool_dependencies = metadata.get('tool_dependencies', None) else: tool_dependencies = None - repo_info_dict = repository_util.create_repo_info_dict( trans.app, - repository_clone_url=repository_clone_url, - changeset_revision=tool_shed_repository.changeset_revision, - ctx_rev=ctx_rev, - repository_owner=tool_shed_repository.owner, - repository_name=tool_shed_repository.name, - tool_dependencies=tool_dependencies, - repository_dependencies=repository_dependencies ) + repo_info_dict = repository_util.create_repo_info_dict(trans.app, + repository_clone_url=repository_clone_url, + changeset_revision=tool_shed_repository.changeset_revision, + ctx_rev=ctx_rev, + repository_owner=tool_shed_repository.owner, + repository_name=tool_shed_repository.name, + tool_dependencies=tool_dependencies, + repository_dependencies=repository_dependencies) if repo_info_dict not in repo_info_dicts: - repo_info_dicts.append( repo_info_dict ) + repo_info_dicts.append(repo_info_dict) # Make sure all tool_shed_repository records exist. created_or_updated_tool_shed_repositories, tool_panel_section_keys, repo_info_dicts, filtered_repo_info_dicts = \ - rdim.create_repository_dependency_objects( tool_path=tool_path, - tool_shed_url=tool_shed_url, - repo_info_dicts=repo_info_dicts, - install_repository_dependencies=install_repository_dependencies, - no_changes_checked=no_changes_checked, - tool_panel_section_id=tool_panel_section_id ) + rdim.create_repository_dependency_objects(tool_path=tool_path, + tool_shed_url=tool_shed_url, + repo_info_dicts=repo_info_dicts, + install_repository_dependencies=install_repository_dependencies, + no_changes_checked=no_changes_checked, + tool_panel_section_id=tool_panel_section_id) # Default the selected tool panel location for loading tools included in each newly installed required # tool shed repository to the location selected for the repository selected for re-installation. - for index, tps_key in enumerate( tool_panel_section_keys ): + for index, tps_key in enumerate(tool_panel_section_keys): if tps_key is None: - tool_panel_section_keys[ index ] = tool_panel_section_key - encoded_repository_ids = [ trans.security.encode_id( r.id ) for r in created_or_updated_tool_shed_repositories ] - new_kwd = dict( includes_tool_dependencies=includes_tool_dependencies, - includes_tools=tool_shed_repository.includes_tools, - includes_tools_for_display_in_tool_panel=tool_shed_repository.includes_tools_for_display_in_tool_panel, - install_tool_dependencies=install_tool_dependencies, - install_resolver_dependencies=install_resolver_dependencies, - repo_info_dicts=filtered_repo_info_dicts, - message=message, - new_tool_panel_section_label=new_tool_panel_section_label, - shed_tool_conf=shed_tool_conf, - status=status, - tool_panel_section_id=tool_panel_section_id, - tool_path=tool_path, - tool_panel_section_keys=tool_panel_section_keys, - tool_shed_repository_ids=encoded_repository_ids, - tool_shed_url=tool_shed_url ) - encoded_kwd = encoding_util.tool_shed_encode( new_kwd ) - tsr_ids = [ r.id for r in created_or_updated_tool_shed_repositories ] + tool_panel_section_keys[index] = tool_panel_section_key + encoded_repository_ids = [trans.security.encode_id(r.id) for r in created_or_updated_tool_shed_repositories] + new_kwd = dict(includes_tool_dependencies=includes_tool_dependencies, + includes_tools=tool_shed_repository.includes_tools, + includes_tools_for_display_in_tool_panel=tool_shed_repository.includes_tools_for_display_in_tool_panel, + install_tool_dependencies=install_tool_dependencies, + install_resolver_dependencies=install_resolver_dependencies, + repo_info_dicts=filtered_repo_info_dicts, + message=message, + new_tool_panel_section_label=new_tool_panel_section_label, + shed_tool_conf=shed_tool_conf, + status=status, + tool_panel_section_id=tool_panel_section_id, + tool_path=tool_path, + tool_panel_section_keys=tool_panel_section_keys, + tool_shed_repository_ids=encoded_repository_ids, + tool_shed_url=tool_shed_url) + encoded_kwd = encoding_util.tool_shed_encode(new_kwd) + tsr_ids = [r.id for r in created_or_updated_tool_shed_repositories] tool_shed_repositories = [] for tsr_id in tsr_ids: - tsr = trans.install_model.context.query( trans.install_model.ToolShedRepository ).get( tsr_id ) - tool_shed_repositories.append( tsr ) + tsr = trans.install_model.context.query(trans.install_model.ToolShedRepository).get(tsr_id) + tool_shed_repositories.append(tsr) clause_list = [] for tsr_id in tsr_ids: - clause_list.append( trans.install_model.ToolShedRepository.table.c.id == tsr_id ) - query = trans.install_model.context.current.query( trans.install_model.ToolShedRepository ) \ - .filter( or_( *clause_list ) ) - return trans.fill_template( 'admin/tool_shed_repository/initiate_repository_installation.mako', - encoded_kwd=encoded_kwd, - query=query, - tool_shed_repositories=tool_shed_repositories, - initiate_repository_installation_ids=encoded_repository_ids, - reinstalling=True ) + clause_list.append(trans.install_model.ToolShedRepository.table.c.id == tsr_id) + query = trans.install_model.context.current.query(trans.install_model.ToolShedRepository) \ + .filter(or_(*clause_list)) + return trans.fill_template('admin/tool_shed_repository/initiate_repository_installation.mako', + encoded_kwd=encoded_kwd, + query=query, + tool_shed_repositories=tool_shed_repositories, + initiate_repository_installation_ids=encoded_repository_ids, + reinstalling=True) @web.expose @web.require_admin - def repair_repository( self, trans, **kwd ): + def repair_repository(self, trans, **kwd): """ Inspect the repository dependency hierarchy for a specified repository and attempt to make sure they are all properly installed as well as each repository's tool dependencies. """ - message = escape( kwd.get( 'message', '' ) ) - status = kwd.get( 'status', 'done' ) - repository_id = kwd.get( 'id', None ) + message = escape(kwd.get('message', '')) + status = kwd.get('status', 'done') + repository_id = kwd.get('id', None) if not repository_id: - message = 'Invalid installed tool shed repository id %s received.' % str( repository_id ) + message = 'Invalid installed tool shed repository id %s received.' % str(repository_id) status = 'error' - return trans.response.send_redirect( web.url_for( controller='admin_toolshed', - action='browse_repositories', - message=message, - status=status ) ) - tool_shed_repository = repository_util.get_installed_tool_shed_repository( trans.app, repository_id ) - rrm = RepairRepositoryManager( trans.app ) - if kwd.get( 'repair_repository_button', False ): - encoded_repair_dict = kwd.get( 'repair_dict', None ) + return trans.response.send_redirect(web.url_for(controller='admin_toolshed', + action='browse_repositories', + message=message, + status=status)) + tool_shed_repository = repository_util.get_installed_tool_shed_repository(trans.app, repository_id) + rrm = RepairRepositoryManager(trans.app) + if kwd.get('repair_repository_button', False): + encoded_repair_dict = kwd.get('repair_dict', None) if encoded_repair_dict: - repair_dict = encoding_util.tool_shed_decode( encoded_repair_dict ) + repair_dict = encoding_util.tool_shed_decode(encoded_repair_dict) else: repair_dict = None if not repair_dict: - repair_dict = rrm.get_repair_dict( tool_shed_repository ) - ordered_tsr_ids = repair_dict.get( 'ordered_tsr_ids', [] ) - ordered_repo_info_dicts = repair_dict.get( 'ordered_repo_info_dicts', [] ) + repair_dict = rrm.get_repair_dict(tool_shed_repository) + ordered_tsr_ids = repair_dict.get('ordered_tsr_ids', []) + ordered_repo_info_dicts = repair_dict.get('ordered_repo_info_dicts', []) if ordered_tsr_ids and ordered_repo_info_dicts: repositories_for_repair = [] for tsr_id in ordered_tsr_ids: - repository = trans.install_model.context.query( trans.install_model.ToolShedRepository ).get( trans.security.decode_id( tsr_id ) ) - repositories_for_repair.append( repository ) - return self.repair_tool_shed_repositories( trans, rrm, repositories_for_repair, ordered_repo_info_dicts ) - tool_shed_repository = repository_util.get_installed_tool_shed_repository( trans.app, repository_id ) - repair_dict = rrm.get_repair_dict( tool_shed_repository ) - encoded_repair_dict = encoding_util.tool_shed_encode( repair_dict ) - ordered_tsr_ids = repair_dict.get( 'ordered_tsr_ids', [] ) - ordered_repo_info_dicts = repair_dict.get( 'ordered_repo_info_dicts', [] ) - return trans.fill_template( 'admin/tool_shed_repository/repair_repository.mako', - repository=tool_shed_repository, - encoded_repair_dict=encoded_repair_dict, - repair_dict=repair_dict, - message=message, - status=status ) + repository = trans.install_model.context.query(trans.install_model.ToolShedRepository).get(trans.security.decode_id(tsr_id)) + repositories_for_repair.append(repository) + return self.repair_tool_shed_repositories(trans, rrm, repositories_for_repair, ordered_repo_info_dicts) + tool_shed_repository = repository_util.get_installed_tool_shed_repository(trans.app, repository_id) + repair_dict = rrm.get_repair_dict(tool_shed_repository) + encoded_repair_dict = encoding_util.tool_shed_encode(repair_dict) + ordered_tsr_ids = repair_dict.get('ordered_tsr_ids', []) + ordered_repo_info_dicts = repair_dict.get('ordered_repo_info_dicts', []) + return trans.fill_template('admin/tool_shed_repository/repair_repository.mako', + repository=tool_shed_repository, + encoded_repair_dict=encoded_repair_dict, + repair_dict=repair_dict, + message=message, + status=status) @web.expose @web.require_admin - def repair_tool_shed_repositories( self, trans, repair_repository_manager, tool_shed_repositories, repo_info_dicts, **kwd ): + def repair_tool_shed_repositories(self, trans, repair_repository_manager, tool_shed_repositories, repo_info_dicts, **kwd): """Repair specified tool shed repositories.""" # The received lists of tool_shed_repositories and repo_info_dicts are ordered. - for index, tool_shed_repository in enumerate( tool_shed_repositories ): - repo_info_dict = repo_info_dicts[ index ] - repair_repository_manager.repair_tool_shed_repository( tool_shed_repository, - encoding_util.tool_shed_encode( repo_info_dict ) ) - tsr_ids_for_monitoring = [ trans.security.encode_id( tsr.id ) for tsr in tool_shed_repositories ] - return trans.response.send_redirect( web.url_for( controller='admin_toolshed', - action='monitor_repository_installation', - tool_shed_repository_ids=tsr_ids_for_monitoring ) ) + for index, tool_shed_repository in enumerate(tool_shed_repositories): + repo_info_dict = repo_info_dicts[index] + repair_repository_manager.repair_tool_shed_repository(tool_shed_repository, + encoding_util.tool_shed_encode(repo_info_dict)) + tsr_ids_for_monitoring = [trans.security.encode_id(tsr.id) for tsr in tool_shed_repositories] + return trans.response.send_redirect(web.url_for(controller='admin_toolshed', + action='monitor_repository_installation', + tool_shed_repository_ids=tsr_ids_for_monitoring)) @web.json - def repository_installation_status_updates( self, trans, ids=None, status_list=None ): + def repository_installation_status_updates(self, trans, ids=None, status_list=None): # Avoid caching - trans.response.headers[ 'Pragma' ] = 'no-cache' - trans.response.headers[ 'Expires' ] = '0' + trans.response.headers['Pragma'] = 'no-cache' + trans.response.headers['Expires'] = '0' # Create new HTML for any ToolShedRepository records whose status that has changed. rval = [] if ids is not None and status_list is not None: - ids = util.listify( ids ) - status_list = util.listify( status_list ) - for tup in zip( ids, status_list ): + ids = util.listify(ids) + status_list = util.listify(status_list) + for tup in zip(ids, status_list): id, status = tup - repository = trans.install_model.context.query( trans.install_model.ToolShedRepository ).get( trans.security.decode_id( id ) ) + repository = trans.install_model.context.query(trans.install_model.ToolShedRepository).get(trans.security.decode_id(id)) if repository.status != status: - rval.append( dict( id=id, - status=repository.status, - html_status=unicode( trans.fill_template( "admin/tool_shed_repository/repository_installation_status.mako", - repository=repository ), - 'utf-8' ) ) ) + rval.append(dict(id=id, + status=repository.status, + html_status=unicode(trans.fill_template("admin/tool_shed_repository/repository_installation_status.mako", + repository=repository), + 'utf-8'))) return rval @web.expose @web.require_admin - def reselect_tool_panel_section( self, trans, **kwd ): + def reselect_tool_panel_section(self, trans, **kwd): """ Select or change the tool panel section to contain the tools included in the tool shed repository being reinstalled. If there are updates available for the repository in the tool shed, the @@ -1450,25 +1450,25 @@ def reselect_tool_panel_section( self, trans, **kwd ): """ message = '' status = 'done' - repository_id = kwd.get( 'id', None ) - latest_changeset_revision = kwd.get( 'latest_changeset_revision', None ) - latest_ctx_rev = kwd.get( 'latest_ctx_rev', None ) - tool_shed_repository = repository_util.get_installed_tool_shed_repository( trans.app, repository_id ) - repository_clone_url = common_util.generate_clone_url_for_installed_repository( trans.app, tool_shed_repository ) + repository_id = kwd.get('id', None) + latest_changeset_revision = kwd.get('latest_changeset_revision', None) + latest_ctx_rev = kwd.get('latest_ctx_rev', None) + tool_shed_repository = repository_util.get_installed_tool_shed_repository(trans.app, repository_id) + repository_clone_url = common_util.generate_clone_url_for_installed_repository(trans.app, tool_shed_repository) metadata = tool_shed_repository.metadata - tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry( trans.app, str( tool_shed_repository.tool_shed ) ) - tool_path = tool_shed_repository.get_tool_relative_path( trans.app )[0] + tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry(trans.app, str(tool_shed_repository.tool_shed)) + tool_path = tool_shed_repository.get_tool_relative_path(trans.app)[0] if latest_changeset_revision and latest_ctx_rev: # There are updates available in the tool shed for the repository, so use the received # dependency information which was retrieved from the tool shed. - encoded_updated_repo_info_dict = kwd.get( 'updated_repo_info_dict', None ) - updated_repo_info_dict = encoding_util.tool_shed_decode( encoded_updated_repo_info_dict ) - readme_files_dict = updated_repo_info_dict.get( 'readme_files_dict', None ) - includes_data_managers = updated_repo_info_dict.get( 'includes_data_managers', False ) - includes_datatypes = updated_repo_info_dict.get( 'includes_datatypes', False ) - includes_workflows = updated_repo_info_dict.get( 'includes_workflows', False ) - includes_tool_dependencies = updated_repo_info_dict.get( 'includes_tool_dependencies', False ) - repo_info_dict = updated_repo_info_dict[ 'repo_info_dict' ] + encoded_updated_repo_info_dict = kwd.get('updated_repo_info_dict', None) + updated_repo_info_dict = encoding_util.tool_shed_decode(encoded_updated_repo_info_dict) + readme_files_dict = updated_repo_info_dict.get('readme_files_dict', None) + includes_data_managers = updated_repo_info_dict.get('includes_data_managers', False) + includes_datatypes = updated_repo_info_dict.get('includes_datatypes', False) + includes_workflows = updated_repo_info_dict.get('includes_workflows', False) + includes_tool_dependencies = updated_repo_info_dict.get('includes_tool_dependencies', False) + repo_info_dict = updated_repo_info_dict['repo_info_dict'] else: # There are no updates available from the tool shed for the repository, so use its locally stored metadata. includes_data_managers = False @@ -1487,37 +1487,37 @@ def reselect_tool_panel_section( self, trans, **kwd ): if 'workflows' in metadata: includes_workflows = True # Since we're reinstalling, we need to send a request to the tool shed to get the README files. - params = dict( name=tool_shed_repository.name, - owner=tool_shed_repository.owner, - changeset_revision=tool_shed_repository.installed_changeset_revision ) - pathspec = [ 'repository', 'get_readme_files' ] - raw_text = util.url_get( tool_shed_url, password_mgr=self.app.tool_shed_registry.url_auth( tool_shed_url ), pathspec=pathspec, params=params ) - readme_files_dict = json.loads( raw_text ) - tool_dependencies = metadata.get( 'tool_dependencies', None ) - rdim = repository_dependency_manager.RepositoryDependencyInstallManager( trans.app ) + params = dict(name=tool_shed_repository.name, + owner=tool_shed_repository.owner, + changeset_revision=tool_shed_repository.installed_changeset_revision) + pathspec = ['repository', 'get_readme_files'] + raw_text = util.url_get(tool_shed_url, password_mgr=self.app.tool_shed_registry.url_auth(tool_shed_url), pathspec=pathspec, params=params) + readme_files_dict = json.loads(raw_text) + tool_dependencies = metadata.get('tool_dependencies', None) + rdim = repository_dependency_manager.RepositoryDependencyInstallManager(trans.app) repository_dependencies = \ - rdim.get_repository_dependencies_for_installed_tool_shed_repository( trans.app, - tool_shed_repository ) - repo_info_dict = repository_util.create_repo_info_dict( trans.app, - repository_clone_url=repository_clone_url, - changeset_revision=tool_shed_repository.installed_changeset_revision, - ctx_rev=tool_shed_repository.ctx_rev, - repository_owner=tool_shed_repository.owner, - repository_name=tool_shed_repository.name, - tool_dependencies=tool_dependencies, - repository_dependencies=repository_dependencies ) + rdim.get_repository_dependencies_for_installed_tool_shed_repository(trans.app, + tool_shed_repository) + repo_info_dict = repository_util.create_repo_info_dict(trans.app, + repository_clone_url=repository_clone_url, + changeset_revision=tool_shed_repository.installed_changeset_revision, + ctx_rev=tool_shed_repository.ctx_rev, + repository_owner=tool_shed_repository.owner, + repository_name=tool_shed_repository.name, + tool_dependencies=tool_dependencies, + repository_dependencies=repository_dependencies) irm = trans.app.installed_repository_manager - dependencies_for_repository_dict = irm.get_dependencies_for_repository( tool_shed_url, - repo_info_dict, - includes_tool_dependencies, - updating=True ) - includes_tool_dependencies = dependencies_for_repository_dict.get( 'includes_tool_dependencies', False ) - includes_tools = dependencies_for_repository_dict.get( 'includes_tools', False ) - includes_tools_for_display_in_tool_panel = dependencies_for_repository_dict.get( 'includes_tools_for_display_in_tool_panel', False ) - installed_repository_dependencies = dependencies_for_repository_dict.get( 'installed_repository_dependencies', None ) - installed_tool_dependencies = dependencies_for_repository_dict.get( 'installed_tool_dependencies', None ) - missing_repository_dependencies = dependencies_for_repository_dict.get( 'missing_repository_dependencies', None ) - missing_tool_dependencies = dependencies_for_repository_dict.get( 'missing_tool_dependencies', None ) + dependencies_for_repository_dict = irm.get_dependencies_for_repository(tool_shed_url, + repo_info_dict, + includes_tool_dependencies, + updating=True) + includes_tool_dependencies = dependencies_for_repository_dict.get('includes_tool_dependencies', False) + includes_tools = dependencies_for_repository_dict.get('includes_tools', False) + includes_tools_for_display_in_tool_panel = dependencies_for_repository_dict.get('includes_tools_for_display_in_tool_panel', False) + installed_repository_dependencies = dependencies_for_repository_dict.get('installed_repository_dependencies', None) + installed_tool_dependencies = dependencies_for_repository_dict.get('installed_tool_dependencies', None) + missing_repository_dependencies = dependencies_for_repository_dict.get('missing_repository_dependencies', None) + missing_tool_dependencies = dependencies_for_repository_dict.get('missing_tool_dependencies', None) if installed_repository_dependencies or missing_repository_dependencies: has_repository_dependencies = True else: @@ -1525,52 +1525,52 @@ def reselect_tool_panel_section( self, trans, **kwd ): if includes_tools_for_display_in_tool_panel: # Get the location in the tool panel in which the tools were originally loaded. if 'tool_panel_section' in metadata: - tool_panel_dict = metadata[ 'tool_panel_section' ] + tool_panel_dict = metadata['tool_panel_section'] if tool_panel_dict: - if tool_util.panel_entry_per_tool( tool_panel_dict ): + if tool_util.panel_entry_per_tool(tool_panel_dict): # The following forces everything to be loaded into 1 section (or no section) in the tool panel. - tool_section_dicts = tool_panel_dict[ tool_panel_dict.keys()[ 0 ] ] - tool_section_dict = tool_section_dicts[ 0 ] - original_section_name = tool_section_dict[ 'name' ] + tool_section_dicts = tool_panel_dict[tool_panel_dict.keys()[0]] + tool_section_dict = tool_section_dicts[0] + original_section_name = tool_section_dict['name'] else: - original_section_name = tool_panel_dict[ 'name' ] + original_section_name = tool_panel_dict['name'] else: original_section_name = '' else: original_section_name = '' - tool_panel_section_select_field = tool_util.build_tool_panel_section_select_field( trans.app ) - no_changes_check_box = CheckboxField( 'no_changes', checked=True ) + tool_panel_section_select_field = tool_util.build_tool_panel_section_select_field(trans.app) + no_changes_check_box = CheckboxField('no_changes', checked=True) if original_section_name: message += "The tools contained in your %s repository were last loaded into the tool panel section %s. " \ - % ( escape( tool_shed_repository.name ), original_section_name ) + % (escape(tool_shed_repository.name), original_section_name) message += "Uncheck the No changes check box and select a different tool panel section to load the tools in a " message += "different section in the tool panel. " status = 'warning' else: - message += "The tools contained in your %s repository were last loaded into the tool panel outside of any sections. " % escape( tool_shed_repository.name ) + message += "The tools contained in your %s repository were last loaded into the tool panel outside of any sections. " % escape(tool_shed_repository.name) message += "Uncheck the No changes check box and select a tool panel section to load the tools into that section. " status = 'warning' else: no_changes_check_box = None original_section_name = '' tool_panel_section_select_field = None - shed_tool_conf_select_field = tool_util.build_shed_tool_conf_select_field( trans.app ) - dd = dependency_display.DependencyDisplayer( trans.app ) + shed_tool_conf_select_field = tool_util.build_shed_tool_conf_select_field(trans.app) + dd = dependency_display.DependencyDisplayer(trans.app) containers_dict = \ - dd.populate_containers_dict_for_new_install( tool_shed_url=tool_shed_url, - tool_path=tool_path, - readme_files_dict=readme_files_dict, - installed_repository_dependencies=installed_repository_dependencies, - missing_repository_dependencies=missing_repository_dependencies, - installed_tool_dependencies=installed_tool_dependencies, - missing_tool_dependencies=missing_tool_dependencies, - updating=False ) + dd.populate_containers_dict_for_new_install(tool_shed_url=tool_shed_url, + tool_path=tool_path, + readme_files_dict=readme_files_dict, + installed_repository_dependencies=installed_repository_dependencies, + missing_repository_dependencies=missing_repository_dependencies, + installed_tool_dependencies=installed_tool_dependencies, + missing_tool_dependencies=missing_tool_dependencies, + updating=False) # Since we're reinstalling we'll merge the list of missing repository dependencies into the list of # installed repository dependencies since each displayed repository dependency will display a status, # whether installed or missing. - containers_dict = dd.merge_missing_repository_dependencies_to_installed_container( containers_dict ) + containers_dict = dd.merge_missing_repository_dependencies_to_installed_container(containers_dict) # Handle repository dependencies check box. - install_repository_dependencies_check_box = CheckboxField( 'install_repository_dependencies', checked=True ) + install_repository_dependencies_check_box = CheckboxField('install_repository_dependencies', checked=True) # Handle tool dependencies check box. if trans.app.config.tool_dependency_dir is None: if includes_tool_dependencies: @@ -1580,301 +1580,301 @@ def reselect_tool_panel_section( self, trans, **kwd ): install_tool_dependencies_check_box_checked = False else: install_tool_dependencies_check_box_checked = True - install_tool_dependencies_check_box = CheckboxField( 'install_tool_dependencies', checked=install_tool_dependencies_check_box_checked ) + install_tool_dependencies_check_box = CheckboxField('install_tool_dependencies', checked=install_tool_dependencies_check_box_checked) view = views.DependencyResolversView(self.app) if view.installable_resolvers: - install_resolver_dependencies_check_box = CheckboxField( 'install_resolver_dependencies', checked=True ) + install_resolver_dependencies_check_box = CheckboxField('install_resolver_dependencies', checked=True) else: install_resolver_dependencies_check_box = None - return trans.fill_template( '/admin/tool_shed_repository/reselect_tool_panel_section.mako', - repository=tool_shed_repository, - no_changes_check_box=no_changes_check_box, - original_section_name=original_section_name, - includes_data_managers=includes_data_managers, - includes_datatypes=includes_datatypes, - includes_tools=includes_tools, - includes_tools_for_display_in_tool_panel=includes_tools_for_display_in_tool_panel, - includes_tool_dependencies=includes_tool_dependencies, - includes_workflows=includes_workflows, - has_repository_dependencies=has_repository_dependencies, - install_repository_dependencies_check_box=install_repository_dependencies_check_box, - install_tool_dependencies_check_box=install_tool_dependencies_check_box, - install_resolver_dependencies_check_box=install_resolver_dependencies_check_box, - containers_dict=containers_dict, - tool_panel_section_select_field=tool_panel_section_select_field, - shed_tool_conf_select_field=shed_tool_conf_select_field, - encoded_repo_info_dict=encoding_util.tool_shed_encode( repo_info_dict ), - repo_info_dict=repo_info_dict, - message=message, - status=status ) + return trans.fill_template('/admin/tool_shed_repository/reselect_tool_panel_section.mako', + repository=tool_shed_repository, + no_changes_check_box=no_changes_check_box, + original_section_name=original_section_name, + includes_data_managers=includes_data_managers, + includes_datatypes=includes_datatypes, + includes_tools=includes_tools, + includes_tools_for_display_in_tool_panel=includes_tools_for_display_in_tool_panel, + includes_tool_dependencies=includes_tool_dependencies, + includes_workflows=includes_workflows, + has_repository_dependencies=has_repository_dependencies, + install_repository_dependencies_check_box=install_repository_dependencies_check_box, + install_tool_dependencies_check_box=install_tool_dependencies_check_box, + install_resolver_dependencies_check_box=install_resolver_dependencies_check_box, + containers_dict=containers_dict, + tool_panel_section_select_field=tool_panel_section_select_field, + shed_tool_conf_select_field=shed_tool_conf_select_field, + encoded_repo_info_dict=encoding_util.tool_shed_encode(repo_info_dict), + repo_info_dict=repo_info_dict, + message=message, + status=status) @web.expose @web.require_admin - def reset_metadata_on_selected_installed_repositories( self, trans, **kwd ): - irmm = InstalledRepositoryMetadataManager( trans.app ) + def reset_metadata_on_selected_installed_repositories(self, trans, **kwd): + irmm = InstalledRepositoryMetadataManager(trans.app) if 'reset_metadata_on_selected_repositories_button' in kwd: - message, status = irmm.reset_metadata_on_selected_repositories( trans.user, **kwd ) + message, status = irmm.reset_metadata_on_selected_repositories(trans.user, **kwd) else: - message = escape( kwd.get( 'message', '' ) ) - status = kwd.get( 'status', 'done' ) + message = escape(kwd.get('message', '')) + status = kwd.get('status', 'done') repositories_select_field = irmm.build_repository_ids_select_field() - return trans.fill_template( '/admin/tool_shed_repository/reset_metadata_on_selected_repositories.mako', - repositories_select_field=repositories_select_field, - message=message, - status=status ) + return trans.fill_template('/admin/tool_shed_repository/reset_metadata_on_selected_repositories.mako', + repositories_select_field=repositories_select_field, + message=message, + status=status) @web.expose @web.require_admin - def reset_repository_metadata( self, trans, id ): + def reset_repository_metadata(self, trans, id): """Reset all metadata on a single installed tool shed repository.""" - repository = repository_util.get_installed_tool_shed_repository( trans.app, id ) - repository_clone_url = common_util.generate_clone_url_for_installed_repository( trans.app, repository ) - tool_path, relative_install_dir = repository.get_tool_relative_path( trans.app ) + repository = repository_util.get_installed_tool_shed_repository(trans.app, id) + repository_clone_url = common_util.generate_clone_url_for_installed_repository(trans.app, repository) + tool_path, relative_install_dir = repository.get_tool_relative_path(trans.app) if relative_install_dir: original_metadata_dict = repository.metadata - irmm = InstalledRepositoryMetadataManager( app=trans.app, - repository=repository, - changeset_revision=repository.changeset_revision, - repository_clone_url=repository_clone_url, - shed_config_dict=repository.get_shed_config_dict( trans.app ), - relative_install_dir=relative_install_dir, - repository_files_dir=None, - resetting_all_metadata_on_repository=False, - updating_installed_repository=False, - persist=False ) + irmm = InstalledRepositoryMetadataManager(app=trans.app, + repository=repository, + changeset_revision=repository.changeset_revision, + repository_clone_url=repository_clone_url, + shed_config_dict=repository.get_shed_config_dict(trans.app), + relative_install_dir=relative_install_dir, + repository_files_dir=None, + resetting_all_metadata_on_repository=False, + updating_installed_repository=False, + persist=False) irmm.generate_metadata_for_changeset_revision() irmm_metadata_dict = irmm.get_metadata_dict() if irmm_metadata_dict != original_metadata_dict: repository.metadata = irmm_metadata_dict irmm.update_in_shed_tool_config() - trans.install_model.context.add( repository ) + trans.install_model.context.add(repository) trans.install_model.context.flush() - message = 'Metadata has been reset on repository %s.' % escape( repository.name ) + message = 'Metadata has been reset on repository %s.' % escape(repository.name) status = 'done' else: - message = 'Metadata did not need to be reset on repository %s.' % escape( repository.name ) + message = 'Metadata did not need to be reset on repository %s.' % escape(repository.name) status = 'done' else: - message = 'Error locating installation directory for repository %s.' % escape( repository.name ) + message = 'Error locating installation directory for repository %s.' % escape(repository.name) status = 'error' - return trans.response.send_redirect( web.url_for( controller='admin_toolshed', - action='manage_repository', - id=id, - message=message, - status=status ) ) + return trans.response.send_redirect(web.url_for(controller='admin_toolshed', + action='manage_repository', + id=id, + message=message, + status=status)) @web.expose @web.require_admin - def reset_to_install( self, trans, **kwd ): + def reset_to_install(self, trans, **kwd): """An error occurred while cloning the repository, so reset everything necessary to enable another attempt.""" - repository = repository_util.get_installed_tool_shed_repository( trans.app, kwd[ 'id' ] ) - if kwd.get( 'reset_repository', False ): - repository_util.set_repository_attributes( trans.app, - repository, - status=trans.install_model.ToolShedRepository.installation_status.NEW, - error_message=None, - deleted=False, - uninstalled=False, - remove_from_disk=True ) + repository = repository_util.get_installed_tool_shed_repository(trans.app, kwd['id']) + if kwd.get('reset_repository', False): + repository_util.set_repository_attributes(trans.app, + repository, + status=trans.install_model.ToolShedRepository.installation_status.NEW, + error_message=None, + deleted=False, + uninstalled=False, + remove_from_disk=True) new_kwd = {} - new_kwd[ 'message' ] = "You can now attempt to install the repository named %s again." % escape( str( repository.name ) ) - new_kwd[ 'status' ] = "done" - return trans.response.send_redirect( web.url_for( controller='admin_toolshed', - action='browse_repositories', - **new_kwd ) ) - return trans.response.send_redirect( web.url_for( controller='admin_toolshed', - action='manage_repository', - **kwd ) ) + new_kwd['message'] = "You can now attempt to install the repository named %s again." % escape(str(repository.name)) + new_kwd['status'] = "done" + return trans.response.send_redirect(web.url_for(controller='admin_toolshed', + action='browse_repositories', + **new_kwd)) + return trans.response.send_redirect(web.url_for(controller='admin_toolshed', + action='manage_repository', + **kwd)) @web.expose @web.require_admin - def set_tool_versions( self, trans, **kwd ): + def set_tool_versions(self, trans, **kwd): """ Get the tool_versions from the tool shed for each tool in the installed revision of a selected tool shed repository and update the metadata for the repository's revision in the Galaxy database. """ - repository = repository_util.get_installed_tool_shed_repository( trans.app, kwd[ 'id' ] ) - tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry( trans.app, str( repository.tool_shed ) ) - params = dict( name=repository.name, owner=repository.owner, changeset_revision=repository.changeset_revision ) - pathspec = [ 'repository', 'get_tool_versions' ] - text = util.url_get( tool_shed_url, password_mgr=self.app.tool_shed_registry.url_auth( tool_shed_url ), pathspec=pathspec, params=params ) + repository = repository_util.get_installed_tool_shed_repository(trans.app, kwd['id']) + tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry(trans.app, str(repository.tool_shed)) + params = dict(name=repository.name, owner=repository.owner, changeset_revision=repository.changeset_revision) + pathspec = ['repository', 'get_tool_versions'] + text = util.url_get(tool_shed_url, password_mgr=self.app.tool_shed_registry.url_auth(tool_shed_url), pathspec=pathspec, params=params) if text: - tool_version_dicts = json.loads( text ) - tvm = tool_version_manager.ToolVersionManager( trans.app ) - tvm.handle_tool_versions( tool_version_dicts, repository ) + tool_version_dicts = json.loads(text) + tvm = tool_version_manager.ToolVersionManager(trans.app) + tvm.handle_tool_versions(tool_version_dicts, repository) message = "Tool versions have been set for all included tools." status = 'done' else: message = ("Version information for the tools included in the %s repository is missing. " "Reset all of this reppository's metadata in the tool shed, then set the installed tool versions " - "from the installed repository's Repository Actions menu. " % escape( repository.name )) + "from the installed repository's Repository Actions menu. " % escape(repository.name)) status = 'error' - shed_tool_conf, tool_path, relative_install_dir = suc.get_tool_panel_config_tool_path_install_dir( trans.app, repository ) - repo_files_dir = os.path.abspath( os.path.join( relative_install_dir, repository.name ) ) - dd = dependency_display.DependencyDisplayer( trans.app ) - containers_dict = dd.populate_containers_dict_from_repository_metadata( tool_shed_url=tool_shed_url, - tool_path=tool_path, - repository=repository, - reinstalling=False, - required_repo_info_dicts=None ) - return trans.fill_template( '/admin/tool_shed_repository/manage_repository.mako', - repository=repository, - description=repository.description, - repo_files_dir=repo_files_dir, - containers_dict=containers_dict, - message=message, - status=status ) + shed_tool_conf, tool_path, relative_install_dir = suc.get_tool_panel_config_tool_path_install_dir(trans.app, repository) + repo_files_dir = os.path.abspath(os.path.join(relative_install_dir, repository.name)) + dd = dependency_display.DependencyDisplayer(trans.app) + containers_dict = dd.populate_containers_dict_from_repository_metadata(tool_shed_url=tool_shed_url, + tool_path=tool_path, + repository=repository, + reinstalling=False, + required_repo_info_dicts=None) + return trans.fill_template('/admin/tool_shed_repository/manage_repository.mako', + repository=repository, + description=repository.description, + repo_files_dir=repo_files_dir, + containers_dict=containers_dict, + message=message, + status=status) @web.json - def tool_dependency_status_updates( self, trans, ids=None, status_list=None ): + def tool_dependency_status_updates(self, trans, ids=None, status_list=None): # Avoid caching - trans.response.headers[ 'Pragma' ] = 'no-cache' - trans.response.headers[ 'Expires' ] = '0' + trans.response.headers['Pragma'] = 'no-cache' + trans.response.headers['Expires'] = '0' # Create new HTML for any ToolDependency records whose status that has changed. rval = [] if ids is not None and status_list is not None: - ids = util.listify( ids ) - status_list = util.listify( status_list ) - for tup in zip( ids, status_list ): + ids = util.listify(ids) + status_list = util.listify(status_list) + for tup in zip(ids, status_list): id, status = tup - tool_dependency = trans.install_model.context.query( trans.install_model.ToolDependency ).get( trans.security.decode_id( id ) ) + tool_dependency = trans.install_model.context.query(trans.install_model.ToolDependency).get(trans.security.decode_id(id)) if tool_dependency.status != status: - rval.append( dict( id=id, - status=tool_dependency.status, - html_status=unicode( trans.fill_template( "admin/tool_shed_repository/tool_dependency_installation_status.mako", - tool_dependency=tool_dependency ), - 'utf-8' ) ) ) + rval.append(dict(id=id, + status=tool_dependency.status, + html_status=unicode(trans.fill_template("admin/tool_shed_repository/tool_dependency_installation_status.mako", + tool_dependency=tool_dependency), + 'utf-8'))) return rval @web.expose @web.require_admin - def uninstall_tool_dependencies( self, trans, **kwd ): - message = escape( kwd.get( 'message', '' ) ) - status = kwd.get( 'status', 'done' ) - tool_dependency_ids = tool_dependency_util.get_tool_dependency_ids( as_string=False, **kwd ) + def uninstall_tool_dependencies(self, trans, **kwd): + message = escape(kwd.get('message', '')) + status = kwd.get('status', 'done') + tool_dependency_ids = tool_dependency_util.get_tool_dependency_ids(as_string=False, **kwd) if not tool_dependency_ids: - tool_dependency_ids = util.listify( kwd.get( 'id', None ) ) + tool_dependency_ids = util.listify(kwd.get('id', None)) tool_dependencies = [] for tool_dependency_id in tool_dependency_ids: - tool_dependency = tool_dependency_util.get_tool_dependency( trans.app, tool_dependency_id ) - tool_dependencies.append( tool_dependency ) - tool_shed_repository = tool_dependencies[ 0 ].tool_shed_repository - if kwd.get( 'uninstall_tool_dependencies_button', False ): + tool_dependency = tool_dependency_util.get_tool_dependency(trans.app, tool_dependency_id) + tool_dependencies.append(tool_dependency) + tool_shed_repository = tool_dependencies[0].tool_shed_repository + if kwd.get('uninstall_tool_dependencies_button', False): errors = False # Filter tool dependencies to only those that are installed but in an error state. tool_dependencies_for_uninstallation = [] for tool_dependency in tool_dependencies: if tool_dependency.can_uninstall: - tool_dependencies_for_uninstallation.append( tool_dependency ) + tool_dependencies_for_uninstallation.append(tool_dependency) for tool_dependency in tool_dependencies_for_uninstallation: - uninstalled, error_message = tool_dependency_util.remove_tool_dependency( trans.app, tool_dependency ) + uninstalled, error_message = tool_dependency_util.remove_tool_dependency(trans.app, tool_dependency) if error_message: errors = True - message = '%s %s' % ( message, error_message ) + message = '%s %s' % (message, error_message) if errors: message = "Error attempting to uninstall tool dependencies: %s" % message status = 'error' else: message = "These tool dependencies have been uninstalled: %s" % \ - ','.join( td.name for td in tool_dependencies_for_uninstallation ) - td_ids = [ trans.security.encode_id( td.id ) for td in tool_shed_repository.tool_dependencies ] - return trans.response.send_redirect( web.url_for( controller='admin_toolshed', - action='manage_repository_tool_dependencies', - tool_dependency_ids=td_ids, - status=status, - message=message ) ) - return trans.fill_template( '/admin/tool_shed_repository/uninstall_tool_dependencies.mako', - repository=tool_shed_repository, - tool_dependency_ids=tool_dependency_ids, - tool_dependencies=tool_dependencies, - message=message, - status=status ) + ','.join(td.name for td in tool_dependencies_for_uninstallation) + td_ids = [trans.security.encode_id(td.id) for td in tool_shed_repository.tool_dependencies] + return trans.response.send_redirect(web.url_for(controller='admin_toolshed', + action='manage_repository_tool_dependencies', + tool_dependency_ids=td_ids, + status=status, + message=message)) + return trans.fill_template('/admin/tool_shed_repository/uninstall_tool_dependencies.mako', + repository=tool_shed_repository, + tool_dependency_ids=tool_dependency_ids, + tool_dependencies=tool_dependencies, + message=message, + status=status) @web.expose @web.require_admin - def update_to_changeset_revision( self, trans, **kwd ): + def update_to_changeset_revision(self, trans, **kwd): """Update a cloned repository to the latest revision possible.""" - message = escape( kwd.get( 'message', '' ) ) - status = kwd.get( 'status', 'done' ) - tool_shed_url = kwd.get( 'tool_shed_url', '' ) + message = escape(kwd.get('message', '')) + status = kwd.get('status', 'done') + tool_shed_url = kwd.get('tool_shed_url', '') # Handle protocol changes over time. - tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry( trans.app, tool_shed_url ) - name = kwd.get( 'name', None ) - owner = kwd.get( 'owner', None ) - changeset_revision = kwd.get( 'changeset_revision', None ) - latest_changeset_revision = kwd.get( 'latest_changeset_revision', None ) - latest_ctx_rev = kwd.get( 'latest_ctx_rev', None ) - repository = repository_util.get_installed_repository( trans.app, - tool_shed=tool_shed_url, - name=name, - owner=owner, - changeset_revision=changeset_revision ) + tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry(trans.app, tool_shed_url) + name = kwd.get('name', None) + owner = kwd.get('owner', None) + changeset_revision = kwd.get('changeset_revision', None) + latest_changeset_revision = kwd.get('latest_changeset_revision', None) + latest_ctx_rev = kwd.get('latest_ctx_rev', None) + repository = repository_util.get_installed_repository(trans.app, + tool_shed=tool_shed_url, + name=name, + owner=owner, + changeset_revision=changeset_revision) original_metadata_dict = repository.metadata - original_repository_dependencies_dict = original_metadata_dict.get( 'repository_dependencies', {} ) - original_repository_dependencies = original_repository_dependencies_dict.get( 'repository_dependencies', [] ) - original_tool_dependencies_dict = original_metadata_dict.get( 'tool_dependencies', {} ) + original_repository_dependencies_dict = original_metadata_dict.get('repository_dependencies', {}) + original_repository_dependencies = original_repository_dependencies_dict.get('repository_dependencies', []) + original_tool_dependencies_dict = original_metadata_dict.get('tool_dependencies', {}) if changeset_revision and latest_changeset_revision and latest_ctx_rev: if changeset_revision == latest_changeset_revision: message = "The installed repository named '%s' is current, there are no updates available. " % name else: - shed_tool_conf, tool_path, relative_install_dir = suc.get_tool_panel_config_tool_path_install_dir( trans.app, repository ) + shed_tool_conf, tool_path, relative_install_dir = suc.get_tool_panel_config_tool_path_install_dir(trans.app, repository) if relative_install_dir: if tool_path: - repo_files_dir = os.path.abspath( os.path.join( tool_path, relative_install_dir, name ) ) + repo_files_dir = os.path.abspath(os.path.join(tool_path, relative_install_dir, name)) else: - repo_files_dir = os.path.abspath( os.path.join( relative_install_dir, name ) ) - repo = hg_util.get_repo_for_repository( trans.app, - repository=None, - repo_path=repo_files_dir, - create=False ) - repository_clone_url = os.path.join( tool_shed_url, 'repos', owner, name ) - hg_util.pull_repository( repo, repository_clone_url, latest_ctx_rev ) - hg_util.update_repository( repo, latest_ctx_rev ) + repo_files_dir = os.path.abspath(os.path.join(relative_install_dir, name)) + repo = hg_util.get_repo_for_repository(trans.app, + repository=None, + repo_path=repo_files_dir, + create=False) + repository_clone_url = os.path.join(tool_shed_url, 'repos', owner, name) + hg_util.pull_repository(repo, repository_clone_url, latest_ctx_rev) + hg_util.update_repository(repo, latest_ctx_rev) # Remove old Data Manager entries if repository.includes_data_managers: - dmh = data_manager.DataManagerHandler( trans.app ) - dmh.remove_from_data_manager( repository ) + dmh = data_manager.DataManagerHandler(trans.app) + dmh.remove_from_data_manager(repository) # Update the repository metadata. - tpm = tool_panel_manager.ToolPanelManager( trans.app ) - irmm = InstalledRepositoryMetadataManager( app=trans.app, - tpm=tpm, - repository=repository, - changeset_revision=latest_changeset_revision, - repository_clone_url=repository_clone_url, - shed_config_dict=repository.get_shed_config_dict( trans.app ), - relative_install_dir=relative_install_dir, - repository_files_dir=None, - resetting_all_metadata_on_repository=False, - updating_installed_repository=True, - persist=True ) + tpm = tool_panel_manager.ToolPanelManager(trans.app) + irmm = InstalledRepositoryMetadataManager(app=trans.app, + tpm=tpm, + repository=repository, + changeset_revision=latest_changeset_revision, + repository_clone_url=repository_clone_url, + shed_config_dict=repository.get_shed_config_dict(trans.app), + relative_install_dir=relative_install_dir, + repository_files_dir=None, + resetting_all_metadata_on_repository=False, + updating_installed_repository=True, + persist=True) irmm.generate_metadata_for_changeset_revision() irmm_metadata_dict = irmm.get_metadata_dict() if 'tools' in irmm_metadata_dict: - tool_panel_dict = irmm_metadata_dict.get( 'tool_panel_section', None ) + tool_panel_dict = irmm_metadata_dict.get('tool_panel_section', None) if tool_panel_dict is None: - tool_panel_dict = tpm.generate_tool_panel_dict_from_shed_tool_conf_entries( repository ) + tool_panel_dict = tpm.generate_tool_panel_dict_from_shed_tool_conf_entries(repository) repository_tools_tups = irmm.get_repository_tools_tups() - tpm.add_to_tool_panel( repository_name=str( repository.name ), - repository_clone_url=repository_clone_url, - changeset_revision=str( repository.installed_changeset_revision ), - repository_tools_tups=repository_tools_tups, - owner=str( repository.owner ), - shed_tool_conf=shed_tool_conf, - tool_panel_dict=tool_panel_dict, - new_install=False ) + tpm.add_to_tool_panel(repository_name=str(repository.name), + repository_clone_url=repository_clone_url, + changeset_revision=str(repository.installed_changeset_revision), + repository_tools_tups=repository_tools_tups, + owner=str(repository.owner), + shed_tool_conf=shed_tool_conf, + tool_panel_dict=tool_panel_dict, + new_install=False) # Add new Data Manager entries if 'data_manager' in irmm_metadata_dict: - dmh = data_manager.DataManagerHandler( trans.app ) - dmh.install_data_managers( trans.app.config.shed_data_manager_config_file, - irmm_metadata_dict, - repository.get_shed_config_dict( trans.app ), - os.path.join( relative_install_dir, name ), - repository, - repository_tools_tups ) + dmh = data_manager.DataManagerHandler(trans.app) + dmh.install_data_managers(trans.app.config.shed_data_manager_config_file, + irmm_metadata_dict, + repository.get_shed_config_dict(trans.app), + os.path.join(relative_install_dir, name), + repository, + repository_tools_tups) if 'repository_dependencies' in irmm_metadata_dict or 'tool_dependencies' in irmm_metadata_dict: - new_repository_dependencies_dict = irmm_metadata_dict.get( 'repository_dependencies', {} ) - new_repository_dependencies = new_repository_dependencies_dict.get( 'repository_dependencies', [] ) - new_tool_dependencies_dict = irmm_metadata_dict.get( 'tool_dependencies', {} ) + new_repository_dependencies_dict = irmm_metadata_dict.get('repository_dependencies', {}) + new_repository_dependencies = new_repository_dependencies_dict.get('repository_dependencies', []) + new_tool_dependencies_dict = irmm_metadata_dict.get('tool_dependencies', {}) if new_repository_dependencies: # [[http://localhost:9009', package_picard_1_56_0', devteam', 910b0b056666', False', False']] proceed_to_install = False @@ -1885,25 +1885,25 @@ def update_to_changeset_revision( self, trans, **kwd ): # giving them the option to install them or not. This is the same behavior as when initially # installing and when re-installing. new_tool_shed, new_name, new_owner, new_changeset_revision, new_pir, new_oicct = \ - common_util.parse_repository_dependency_tuple( new_repository_tup ) + common_util.parse_repository_dependency_tuple(new_repository_tup) # Mock up a repo_info_tupe that has the information needed to see if the repository dependency # was previously installed. - repo_info_tuple = ( '', new_tool_shed, new_changeset_revision, '', new_owner, [], [] ) + repo_info_tuple = ('', new_tool_shed, new_changeset_revision, '', new_owner, [], []) # Since the value of new_changeset_revision came from a repository dependency # definition, it may occur earlier in the Tool Shed's repository changelog than # the Galaxy tool_shed_repository.installed_changeset_revision record value, so # we set from_tip to True to make sure we get the entire set of changeset revisions # from the Tool Shed. new_repository_db_record, installed_changeset_revision = \ - repository_util.repository_was_previously_installed( trans.app, - tool_shed_url, - new_name, - repo_info_tuple, - from_tip=True ) + repository_util.repository_was_previously_installed(trans.app, + tool_shed_url, + new_name, + repo_info_tuple, + from_tip=True) if new_repository_db_record: - if new_repository_db_record.status in [ trans.install_model.ToolShedRepository.installation_status.ERROR, - trans.install_model.ToolShedRepository.installation_status.NEW, - trans.install_model.ToolShedRepository.installation_status.UNINSTALLED ]: + if new_repository_db_record.status in [trans.install_model.ToolShedRepository.installation_status.ERROR, + trans.install_model.ToolShedRepository.installation_status.NEW, + trans.install_model.ToolShedRepository.installation_status.UNINSTALLED]: proceed_to_install = True break else: @@ -1913,13 +1913,13 @@ def update_to_changeset_revision( self, trans, **kwd ): # Updates received include newly defined repository dependencies, so allow the user # the option of installting them. We cannot update the repository with the changes # until that happens, so we have to send them along. - new_kwd = dict( tool_shed_url=tool_shed_url, - updating_repository_id=trans.security.encode_id( repository.id ), - updating_to_ctx_rev=latest_ctx_rev, - updating_to_changeset_revision=latest_changeset_revision, - encoded_updated_metadata=encoding_util.tool_shed_encode( irmm_metadata_dict ), - updating=True ) - return self.prepare_for_install( trans, **new_kwd ) + new_kwd = dict(tool_shed_url=tool_shed_url, + updating_repository_id=trans.security.encode_id(repository.id), + updating_to_ctx_rev=latest_ctx_rev, + updating_to_changeset_revision=latest_changeset_revision, + encoded_updated_metadata=encoding_util.tool_shed_encode(irmm_metadata_dict), + updating=True) + return self.prepare_for_install(trans, **new_kwd) # Updates received did not include any newly defined repository dependencies but did include # newly defined tool dependencies. If the newly defined tool dependencies are not the same # as the originally defined tool dependencies, we need to install them. @@ -1928,102 +1928,102 @@ def update_to_changeset_revision( self, trans, **kwd ): if new_key not in original_tool_dependencies_dict: proceed_to_install = True break - original_val = original_tool_dependencies_dict[ new_key ] + original_val = original_tool_dependencies_dict[new_key] if new_val != original_val: proceed_to_install = True break if proceed_to_install: - encoded_tool_dependencies_dict = encoding_util.tool_shed_encode( irmm_metadata_dict.get( 'tool_dependencies', {} ) ) - encoded_relative_install_dir = encoding_util.tool_shed_encode( relative_install_dir ) - new_kwd = dict( updating_repository_id=trans.security.encode_id( repository.id ), - updating_to_ctx_rev=latest_ctx_rev, - updating_to_changeset_revision=latest_changeset_revision, - encoded_updated_metadata=encoding_util.tool_shed_encode( irmm_metadata_dict ), - encoded_relative_install_dir=encoded_relative_install_dir, - encoded_tool_dependencies_dict=encoded_tool_dependencies_dict, - message=message, - status=status ) - return self.install_tool_dependencies_with_update( trans, **new_kwd ) + encoded_tool_dependencies_dict = encoding_util.tool_shed_encode(irmm_metadata_dict.get('tool_dependencies', {})) + encoded_relative_install_dir = encoding_util.tool_shed_encode(relative_install_dir) + new_kwd = dict(updating_repository_id=trans.security.encode_id(repository.id), + updating_to_ctx_rev=latest_ctx_rev, + updating_to_changeset_revision=latest_changeset_revision, + encoded_updated_metadata=encoding_util.tool_shed_encode(irmm_metadata_dict), + encoded_relative_install_dir=encoded_relative_install_dir, + encoded_tool_dependencies_dict=encoded_tool_dependencies_dict, + message=message, + status=status) + return self.install_tool_dependencies_with_update(trans, **new_kwd) # Updates received did not include any newly defined repository dependencies or newly defined # tool dependencies that need to be installed. - repository = trans.app.update_repository_manager.update_repository_record( repository=repository, - updated_metadata_dict=irmm_metadata_dict, - updated_changeset_revision=latest_changeset_revision, - updated_ctx_rev=latest_ctx_rev ) + repository = trans.app.update_repository_manager.update_repository_record(repository=repository, + updated_metadata_dict=irmm_metadata_dict, + updated_changeset_revision=latest_changeset_revision, + updated_ctx_rev=latest_ctx_rev) message = "The installed repository named '%s' has been updated to change set revision '%s'. " % \ - ( name, latest_changeset_revision ) + (name, latest_changeset_revision) else: message = "The directory containing the installed repository named '%s' cannot be found. " % name status = 'error' else: message = "The latest changeset revision could not be retrieved for the installed repository named '%s'. " % name status = 'error' - return trans.response.send_redirect( web.url_for( controller='admin_toolshed', - action='manage_repository', - id=trans.security.encode_id( repository.id ), - message=message, - status=status ) ) + return trans.response.send_redirect(web.url_for(controller='admin_toolshed', + action='manage_repository', + id=trans.security.encode_id(repository.id), + message=message, + status=status)) @web.expose @web.require_admin - def update_tool_shed_status_for_installed_repository( self, trans, **kwd ): - message, status = repository_util.check_for_updates( trans.app, trans.install_model, kwd.get( 'id', None ) ) - return trans.response.send_redirect( web.url_for( controller='admin_toolshed', - action='browse_repositories', - message=message, - status=status ) ) + def update_tool_shed_status_for_installed_repository(self, trans, **kwd): + message, status = repository_util.check_for_updates(trans.app, trans.install_model, kwd.get('id', None)) + return trans.response.send_redirect(web.url_for(controller='admin_toolshed', + action='browse_repositories', + message=message, + status=status)) @web.expose @web.require_admin - def view_tool_metadata( self, trans, repository_id, tool_id, **kwd ): - message = escape( kwd.get( 'message', '' ) ) - status = kwd.get( 'status', 'done' ) - repository = repository_util.get_installed_tool_shed_repository( trans.app, repository_id ) + def view_tool_metadata(self, trans, repository_id, tool_id, **kwd): + message = escape(kwd.get('message', '')) + status = kwd.get('status', 'done') + repository = repository_util.get_installed_tool_shed_repository(trans.app, repository_id) repository_metadata = repository.metadata - shed_config_dict = repository.get_shed_config_dict( trans.app ) + shed_config_dict = repository.get_shed_config_dict(trans.app) tool_metadata = {} tool_lineage = [] tool = None if 'tools' in repository_metadata: - for tool_metadata_dict in repository_metadata[ 'tools' ]: - if tool_metadata_dict[ 'id' ] == tool_id: + for tool_metadata_dict in repository_metadata['tools']: + if tool_metadata_dict['id'] == tool_id: tool_metadata = tool_metadata_dict - tool_config = tool_metadata[ 'tool_config' ] - if shed_config_dict and shed_config_dict.get( 'tool_path' ): - tool_config = os.path.join( shed_config_dict.get( 'tool_path' ), tool_config ) - tool = trans.app.toolbox.get_tool(tool_id=tool_metadata[ 'guid' ], exact=True) + tool_config = tool_metadata['tool_config'] + if shed_config_dict and shed_config_dict.get('tool_path'): + tool_config = os.path.join(shed_config_dict.get('tool_path'), tool_config) + tool = trans.app.toolbox.get_tool(tool_id=tool_metadata['guid'], exact=True) if not tool: - tool = trans.app.toolbox.load_tool( os.path.abspath( tool_config ), guid=tool_metadata[ 'guid' ] ) + tool = trans.app.toolbox.load_tool(os.path.abspath(tool_config), guid=tool_metadata['guid']) if tool: - tool._lineage = trans.app.toolbox._lineage_map.register( tool ) + tool._lineage = trans.app.toolbox._lineage_map.register(tool) if tool: tool_lineage = tool.lineage.get_version_ids(reverse=True) break - return trans.fill_template( "/admin/tool_shed_repository/view_tool_metadata.mako", - repository=repository, - repository_metadata=repository_metadata, - tool=tool, - tool_metadata=tool_metadata, - tool_lineage=tool_lineage, - message=message, - status=status ) + return trans.fill_template("/admin/tool_shed_repository/view_tool_metadata.mako", + repository=repository, + repository_metadata=repository_metadata, + tool=tool, + tool_metadata=tool_metadata, + tool_lineage=tool_lineage, + message=message, + status=status) @web.expose @web.require_admin - def view_workflow( self, trans, workflow_name=None, repository_id=None, **kwd ): + def view_workflow(self, trans, workflow_name=None, repository_id=None, **kwd): """Retrieve necessary information about a workflow from the database so that it can be displayed in an svg image.""" - message = escape( kwd.get( 'message', '' ) ) - status = kwd.get( 'status', 'done' ) + message = escape(kwd.get('message', '')) + status = kwd.get('status', 'done') if workflow_name: - workflow_name = encoding_util.tool_shed_decode( workflow_name ) - repository = repository_util.get_tool_shed_repository_by_id( trans.app, repository_id ) + workflow_name = encoding_util.tool_shed_decode(workflow_name) + repository = repository_util.get_tool_shed_repository_by_id(trans.app, repository_id) changeset_revision = repository.changeset_revision metadata = repository.metadata - return trans.fill_template( "/admin/tool_shed_repository/view_workflow.mako", - repository=repository, - changeset_revision=changeset_revision, - repository_id=repository_id, - workflow_name=workflow_name, - metadata=metadata, - message=message, - status=status ) + return trans.fill_template("/admin/tool_shed_repository/view_workflow.mako", + repository=repository, + changeset_revision=changeset_revision, + repository_id=repository_id, + workflow_name=workflow_name, + metadata=metadata, + message=message, + status=status) diff --git a/lib/galaxy/webapps/galaxy/controllers/async.py b/lib/galaxy/webapps/galaxy/controllers/async.py index c0dca96de903..7db4af7667f4 100644 --- a/lib/galaxy/webapps/galaxy/controllers/async.py +++ b/lib/galaxy/webapps/galaxy/controllers/async.py @@ -10,15 +10,15 @@ from galaxy.util.hash_util import hmac_new from galaxy.web.base.controller import BaseUIController -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class ASync( BaseUIController ): +class ASync(BaseUIController): @web.expose def default(self, trans, tool_id=None, data_id=None, data_secret=None, **kwd): """Catches the tool id and redirects as needed""" - return self.index( trans, tool_id=tool_id, data_id=data_id, data_secret=data_secret, **kwd) + return self.index(trans, tool_id=tool_id, data_id=data_id, data_secret=data_secret, **kwd) @web.expose def index(self, trans, tool_id=None, data_secret=None, **kwd): @@ -30,7 +30,7 @@ def index(self, trans, tool_id=None, data_secret=None, **kwd): # redirect to main when getting no parameters if not kwd: - return trans.response.send_redirect( "/index" ) + return trans.response.send_redirect("/index") params = Params(kwd, sanitize=False) STATUS = params.STATUS @@ -38,11 +38,11 @@ def index(self, trans, tool_id=None, data_secret=None, **kwd): data_id = params.data_id log.debug('async dataid -> %s' % data_id) - trans.log_event( 'Async dataid -> %s' % str(data_id) ) + trans.log_event('Async dataid -> %s' % str(data_id)) # initialize the tool toolbox = self.get_toolbox() - tool = toolbox.get_tool( tool_id ) + tool = toolbox.get_tool(tool_id) if not tool: return "Tool with id %s not found" % tool_id @@ -52,28 +52,41 @@ def index(self, trans, tool_id=None, data_secret=None, **kwd): if data_id: if not URL: return "No URL parameter was submitted for data %s" % data_id - data = trans.sa_session.query( trans.model.HistoryDatasetAssociation ).get( data_id ) + data = trans.sa_session.query(trans.model.HistoryDatasetAssociation).get(data_id) if not data: return "Data %s does not exist or has already been deleted" % data_id if STATUS == 'OK': - key = hmac_new( trans.app.config.tool_secret, "%d:%d" % ( data.id, data.history_id ) ) + key = hmac_new(trans.app.config.tool_secret, "%d:%d" % (data.id, data.history_id)) if key != data_secret: return "You do not have permission to alter data %s." % data_id # push the job into the queue data.state = data.blurb = data.states.RUNNING log.debug('executing tool %s' % tool.id) - trans.log_event( 'Async executing tool %s' % tool.id, tool_id=tool.id ) - galaxy_url = trans.request.base + '/async/%s/%s/%s' % ( tool_id, data.id, key ) + trans.log_event('Async executing tool %s' % tool.id, tool_id=tool.id) + galaxy_url = trans.request.base + '/async/%s/%s/%s' % (tool_id, data.id, key) galaxy_url = params.get("GALAXY_URL", galaxy_url) - params = dict( URL=URL, GALAXY_URL=galaxy_url, name=data.name, info=data.info, dbkey=data.dbkey, data_type=data.ext ) + params = dict(URL=URL, GALAXY_URL=galaxy_url, name=data.name, info=data.info, dbkey=data.dbkey, data_type=data.ext) + # Assume there is exactly one output file possible - params[tool.outputs.keys()[0]] = data.id - tool.execute( trans, incoming=params ) + TOOL_OUTPUT_TYPE = None + for idx, obj in enumerate(tool.outputs.values()): + try: + TOOL_OUTPUT_TYPE = obj.format + params[tool.outputs.keys()[idx]] = data.id + break + except Exception: + # exclude outputs different from ToolOutput (e.g. collections) from the previous assumption + continue + if TOOL_OUTPUT_TYPE is None: + raise Exception("Error: ToolOutput object not found") + + original_history = trans.sa_session.query(trans.app.model.History).get(data.history_id) + tool.execute(trans, incoming=params, history=original_history) else: log.debug('async error -> %s' % STATUS) - trans.log_event( 'Async error -> %s' % STATUS ) + trans.log_event('Async error -> %s' % STATUS) data.state = data.blurb = jobs.JOB_ERROR data.info = "Error -> %s" % STATUS @@ -84,12 +97,29 @@ def index(self, trans, tool_id=None, data_secret=None, **kwd): # # no data_id must be parameter submission # + GALAXY_TYPE = None if params.data_type: GALAXY_TYPE = params.data_type elif params.galaxyFileFormat == 'wig': # this is an undocumented legacy special case GALAXY_TYPE = 'wig' + elif params.GALAXY_TYPE: + GALAXY_TYPE = params.GALAXY_TYPE else: - GALAXY_TYPE = params.GALAXY_TYPE or tool.outputs.values()[0].format + # Assume there is exactly one output + outputs_count = 0 + for obj in tool.outputs.values(): + try: + GALAXY_TYPE = obj.format + outputs_count += 1 + except Exception: + # exclude outputs different from ToolOutput (e.g. collections) from the previous assumption + # a collection object does not have the 'format' attribute, so it will throw an exception + continue + if outputs_count > 1: + raise Exception("Error: the tool should have just one output") + + if GALAXY_TYPE is None: + raise Exception("Error: ToolOutput object not found") GALAXY_NAME = params.name or params.GALAXY_NAME or '%s query' % tool.name GALAXY_INFO = params.info or params.GALAXY_INFO or params.galaxyDescription or '' @@ -103,24 +133,25 @@ def index(self, trans, tool_id=None, data_secret=None, **kwd): # data.state = jobs.JOB_OK # history.datasets.add_dataset( data ) - data = trans.app.model.HistoryDatasetAssociation( create_dataset=True, sa_session=trans.sa_session, extension=GALAXY_TYPE ) - trans.app.security_agent.set_all_dataset_permissions( data.dataset, trans.app.security_agent.history_get_default_permissions( trans.history ) ) + data = trans.app.model.HistoryDatasetAssociation(create_dataset=True, sa_session=trans.sa_session, extension=GALAXY_TYPE) + trans.app.security_agent.set_all_dataset_permissions(data.dataset, trans.app.security_agent.history_get_default_permissions(trans.history)) data.name = GALAXY_NAME data.dbkey = GALAXY_BUILD data.info = GALAXY_INFO - trans.sa_session.add( data ) # Need to add data to session before setting state (setting state requires that the data object is in the session, but this may change) + trans.sa_session.add(data) # Need to add data to session before setting state (setting state requires that the data object is in the session, but this may change) data.state = data.states.NEW - open( data.file_name, 'wb' ).close() # create the file - trans.history.add_dataset( data, genome_build=GALAXY_BUILD ) - trans.sa_session.add( trans.history ) + open(data.file_name, 'wb').close() # create the file + trans.history.add_dataset(data, genome_build=GALAXY_BUILD) + trans.sa_session.add(trans.history) trans.sa_session.flush() - trans.log_event( "Added dataset %d to history %d" % (data.id, trans.history.id ), tool_id=tool_id ) + trans.log_event("Added dataset %d to history %d" % (data.id, trans.history.id), tool_id=tool_id) try: - key = hmac_new( trans.app.config.tool_secret, "%d:%d" % ( data.id, data.history_id ) ) - galaxy_url = trans.request.base + '/async/%s/%s/%s' % ( tool_id, data.id, key ) - params.update( { 'GALAXY_URL': galaxy_url } ) - params.update( { 'data_id': data.id } ) + key = hmac_new(trans.app.config.tool_secret, "%d:%d" % (data.id, data.history_id)) + galaxy_url = trans.request.base + '/async/%s/%s/%s' % (tool_id, data.id, key) + params.update({'GALAXY_URL': galaxy_url}) + params.update({'data_id': data.id}) + # Use provided URL or fallback to tool action url = URL or tool.action # Does url already have query params? @@ -128,13 +159,13 @@ def index(self, trans, tool_id=None, data_secret=None, **kwd): url_join_char = '&' else: url_join_char = '?' - url = "%s%s%s" % ( url, url_join_char, urllib.urlencode( params.flatten() ) ) + url = "%s%s%s" % (url, url_join_char, urllib.urlencode(params.flatten())) log.debug("connecting to -> %s" % url) - trans.log_event( "Async connecting to -> %s" % url ) + trans.log_event("Async connecting to -> %s" % url) text = urllib.urlopen(url).read(-1) text = text.strip() if not text.endswith('OK'): - raise Exception( text ) + raise Exception(text) data.state = data.blurb = data.states.RUNNING except Exception as e: data.info = str(e) @@ -142,4 +173,4 @@ def index(self, trans, tool_id=None, data_secret=None, **kwd): trans.sa_session.flush() - return trans.fill_template( 'root/tool_runner.mako', out_data={}, num_jobs=1, job_errors=[] ) + return trans.fill_template('root/tool_runner.mako', out_data={}, num_jobs=1, job_errors=[]) diff --git a/lib/galaxy/webapps/galaxy/controllers/biostar.py b/lib/galaxy/webapps/galaxy/controllers/biostar.py index bb5523ea8686..1708384bcf1d 100644 --- a/lib/galaxy/webapps/galaxy/controllers/biostar.py +++ b/lib/galaxy/webapps/galaxy/controllers/biostar.py @@ -6,98 +6,98 @@ from galaxy.util import biostar -class BiostarController( BaseUIController ): +class BiostarController(BaseUIController): """ Provides integration with Biostar through external authentication, see: http://liondb.com/help/x/ """ @web.expose - def biostar_redirect( self, trans, payload=None, biostar_action=None ): + def biostar_redirect(self, trans, payload=None, biostar_action=None): """ Generate a redirect to a Biostar site using external authentication to pass Galaxy user information and optional information about a specific tool. """ try: - url, payload = biostar.get_biostar_url( trans.app, payload=payload, biostar_action=biostar_action ) + url, payload = biostar.get_biostar_url(trans.app, payload=payload, biostar_action=biostar_action) except Exception as e: - return error( str( e ) ) + return error(str(e)) # Only create/log in biostar user if is registered Galaxy user if trans.user: - biostar.create_cookie( trans, trans.app.config.biostar_key_name, trans.app.config.biostar_key, trans.user.email ) + biostar.create_cookie(trans, trans.app.config.biostar_key_name, trans.app.config.biostar_key, trans.user.email) if payload: - return trans.fill_template( "biostar/post_redirect.mako", post_url=url, form_inputs=payload ) - return trans.response.send_redirect( url ) + return trans.fill_template("biostar/post_redirect.mako", post_url=url, form_inputs=payload) + return trans.response.send_redirect(url) @web.expose - def biostar_tool_tag_redirect( self, trans, tool_id=None ): + def biostar_tool_tag_redirect(self, trans, tool_id=None): """ Generate a redirect to a Biostar site using tag for tool. """ # tool_id is required if tool_id is None: - return error( "No tool_id provided" ) + return error("No tool_id provided") # Load the tool tool_version_select_field, tools, tool = \ - self.app.toolbox.get_tool_components( tool_id, tool_version=None, get_loaded_tools_by_lineage=False, set_selected=True ) + self.app.toolbox.get_tool_components(tool_id, tool_version=None, get_loaded_tools_by_lineage=False, set_selected=True) # No matching tool, unlikely if not tool: - return error( "No tool found matching '%s'" % tool_id ) + return error("No tool found matching '%s'" % tool_id) # Tool specific information for payload - payload = biostar.populate_tag_payload( tool=tool ) + payload = biostar.populate_tag_payload(tool=tool) # Pass on to standard redirect method - return self.biostar_redirect( trans, payload=payload, biostar_action='show_tags' ) + return self.biostar_redirect(trans, payload=payload, biostar_action='show_tags') @web.expose - def biostar_question_redirect( self, trans, payload=None ): + def biostar_question_redirect(self, trans, payload=None): """ Generate a redirect to a Biostar site using external authentication to pass Galaxy user information and information about a specific tool. """ # Pass on to standard redirect method - return self.biostar_redirect( trans, payload=payload, biostar_action='new_post' ) + return self.biostar_redirect(trans, payload=payload, biostar_action='new_post') @web.expose - def biostar_tool_question_redirect( self, trans, tool_id=None ): + def biostar_tool_question_redirect(self, trans, tool_id=None): """ Generate a redirect to a Biostar site using external authentication to pass Galaxy user information and information about a specific tool. """ # tool_id is required if tool_id is None: - return error( "No tool_id provided" ) + return error("No tool_id provided") # Load the tool tool_version_select_field, tools, tool = \ - self.app.toolbox.get_tool_components( tool_id, tool_version=None, get_loaded_tools_by_lineage=False, set_selected=True ) + self.app.toolbox.get_tool_components(tool_id, tool_version=None, get_loaded_tools_by_lineage=False, set_selected=True) # No matching tool, unlikely if not tool: - return error( "No tool found matching '%s'" % tool_id ) + return error("No tool found matching '%s'" % tool_id) # Tool specific information for payload - payload = biostar.populate_tool_payload( tool=tool ) + payload = biostar.populate_tool_payload(tool=tool) # Pass on to regular question method - return self.biostar_question_redirect( trans, payload ) + return self.biostar_question_redirect(trans, payload) @web.expose - def biostar_tool_bug_report( self, trans, hda=None, email=None, message=None ): + def biostar_tool_bug_report(self, trans, hda=None, email=None, message=None): """ Generate a redirect to a Biostar site using external authentication to pass Galaxy user information and information about a specific tool error. """ try: - error_reporter = biostar.BiostarErrorReporter( hda, trans.app ) - payload = error_reporter.send_report( trans.user, email=email, message=message ) + error_reporter = biostar.BiostarErrorReporter(hda, trans.app) + payload = error_reporter.send_report(trans.user, email=email, message=message) except Exception as e: - return error( str( e ) ) - return self.biostar_redirect( trans, payload=payload, biostar_action='new_post' ) + return error(str(e)) + return self.biostar_redirect(trans, payload=payload, biostar_action='new_post') @web.expose - def biostar_logout( self, trans ): + def biostar_logout(self, trans): """ Log out of biostar """ try: - url = biostar.biostar_log_out( trans ) + url = biostar.biostar_log_out(trans) except Exception as e: - return error( str( e ) ) + return error(str(e)) if url: - return trans.response.send_redirect( url ) - return error( "Could not determine Biostar logout URL." ) + return trans.response.send_redirect(url) + return error("Could not determine Biostar logout URL.") diff --git a/lib/galaxy/webapps/galaxy/controllers/data_manager.py b/lib/galaxy/webapps/galaxy/controllers/data_manager.py index 28056ee0219a..9229842d2890 100644 --- a/lib/galaxy/webapps/galaxy/controllers/data_manager.py +++ b/lib/galaxy/webapps/galaxy/controllers/data_manager.py @@ -9,110 +9,110 @@ from galaxy import web from galaxy.web.base.controller import BaseUIController -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class DataManager( BaseUIController ): +class DataManager(BaseUIController): @web.expose - def index( self, trans, **kwd ): + def index(self, trans, **kwd): not_is_admin = not trans.user_is_admin() if not_is_admin and not trans.app.config.enable_data_manager_user_view: - raise paste.httpexceptions.HTTPUnauthorized( "This Galaxy instance is not configured to allow non-admins to view the data manager." ) - message = escape( kwd.get( 'message', '' ) ) - status = escape( kwd.get( 'status', 'info' ) ) - return trans.fill_template( "data_manager/index.mako", data_managers=trans.app.data_managers, tool_data_tables=trans.app.tool_data_tables, view_only=not_is_admin, message=message, status=status ) + raise paste.httpexceptions.HTTPUnauthorized("This Galaxy instance is not configured to allow non-admins to view the data manager.") + message = escape(kwd.get('message', '')) + status = escape(kwd.get('status', 'info')) + return trans.fill_template("data_manager/index.mako", data_managers=trans.app.data_managers, tool_data_tables=trans.app.tool_data_tables, view_only=not_is_admin, message=message, status=status) @web.expose - def manage_data_manager( self, trans, **kwd ): + def manage_data_manager(self, trans, **kwd): not_is_admin = not trans.user_is_admin() if not_is_admin and not trans.app.config.enable_data_manager_user_view: - raise paste.httpexceptions.HTTPUnauthorized( "This Galaxy instance is not configured to allow non-admins to view the data manager." ) - message = escape( kwd.get( 'message', '' ) ) - status = escape( kwd.get( 'status', 'info' ) ) - data_manager_id = kwd.get( 'id', None ) - data_manager = trans.app.data_managers.get_manager( data_manager_id ) + raise paste.httpexceptions.HTTPUnauthorized("This Galaxy instance is not configured to allow non-admins to view the data manager.") + message = escape(kwd.get('message', '')) + status = escape(kwd.get('status', 'info')) + data_manager_id = kwd.get('id', None) + data_manager = trans.app.data_managers.get_manager(data_manager_id) if data_manager is None: - return trans.response.send_redirect( web.url_for( controller="data_manager", action="index", message="Invalid Data Manager (%s) was requested" % data_manager_id, status="error" ) ) - jobs = list( reversed( [ assoc.job for assoc in trans.sa_session.query( trans.app.model.DataManagerJobAssociation ).filter_by( data_manager_id=data_manager_id ) ] ) ) - return trans.fill_template( "data_manager/manage_data_manager.mako", data_manager=data_manager, jobs=jobs, view_only=not_is_admin, message=message, status=status ) + return trans.response.send_redirect(web.url_for(controller="data_manager", action="index", message="Invalid Data Manager (%s) was requested" % data_manager_id, status="error")) + jobs = list(reversed([assoc.job for assoc in trans.sa_session.query(trans.app.model.DataManagerJobAssociation).filter_by(data_manager_id=data_manager_id)])) + return trans.fill_template("data_manager/manage_data_manager.mako", data_manager=data_manager, jobs=jobs, view_only=not_is_admin, message=message, status=status) @web.expose - def view_job( self, trans, **kwd ): + def view_job(self, trans, **kwd): not_is_admin = not trans.user_is_admin() if not_is_admin and not trans.app.config.enable_data_manager_user_view: - raise paste.httpexceptions.HTTPUnauthorized( "This Galaxy instance is not configured to allow non-admins to view the data manager." ) - message = escape( kwd.get( 'message', '' ) ) - status = escape( kwd.get( 'status', 'info' ) ) - job_id = kwd.get( 'id', None ) + raise paste.httpexceptions.HTTPUnauthorized("This Galaxy instance is not configured to allow non-admins to view the data manager.") + message = escape(kwd.get('message', '')) + status = escape(kwd.get('status', 'info')) + job_id = kwd.get('id', None) try: - job_id = trans.security.decode_id( job_id ) - job = trans.sa_session.query( trans.app.model.Job ).get( job_id ) + job_id = trans.security.decode_id(job_id) + job = trans.sa_session.query(trans.app.model.Job).get(job_id) except Exception as e: job = None - log.error( "Bad job id (%s) passed to view_job: %s" % ( job_id, e ) ) + log.error("Bad job id (%s) passed to view_job: %s" % (job_id, e)) if not job: - return trans.response.send_redirect( web.url_for( controller="data_manager", action="index", message="Invalid job (%s) was requested" % job_id, status="error" ) ) + return trans.response.send_redirect(web.url_for(controller="data_manager", action="index", message="Invalid job (%s) was requested" % job_id, status="error")) data_manager_id = job.data_manager_association.data_manager_id - data_manager = trans.app.data_managers.get_manager( data_manager_id ) - hdas = [ assoc.dataset for assoc in job.get_output_datasets() ] + data_manager = trans.app.data_managers.get_manager(data_manager_id) + hdas = [assoc.dataset for assoc in job.get_output_datasets()] data_manager_output = [] error_messages = [] for hda in hdas: try: - data_manager_json = loads( open( hda.get_file_name() ).read() ) + data_manager_json = loads(open(hda.get_file_name()).read()) except Exception as e: data_manager_json = {} - error_messages.append( escape( "Unable to obtain data_table info for hda (%s): %s" % ( hda.id, e ) ) ) + error_messages.append(escape("Unable to obtain data_table info for hda (%s): %s" % (hda.id, e))) values = [] - for key, value in data_manager_json.get( 'data_tables', {} ).iteritems(): - values.append( ( key, value ) ) - data_manager_output.append( values ) - return trans.fill_template( "data_manager/view_job.mako", data_manager=data_manager, job=job, view_only=not_is_admin, hdas=hdas, data_manager_output=data_manager_output, message=message, status=status, error_messages=error_messages ) + for key, value in data_manager_json.get('data_tables', {}).iteritems(): + values.append((key, value)) + data_manager_output.append(values) + return trans.fill_template("data_manager/view_job.mako", data_manager=data_manager, job=job, view_only=not_is_admin, hdas=hdas, data_manager_output=data_manager_output, message=message, status=status, error_messages=error_messages) @web.expose - def manage_data_table( self, trans, **kwd ): + def manage_data_table(self, trans, **kwd): not_is_admin = not trans.user_is_admin() if not_is_admin and not trans.app.config.enable_data_manager_user_view: - raise paste.httpexceptions.HTTPUnauthorized( "This Galaxy instance is not configured to allow non-admins to view the data manager." ) - message = escape( kwd.get( 'message', '' ) ) - status = escape( kwd.get( 'status', 'info' ) ) - data_table_name = kwd.get( 'table_name', None ) + raise paste.httpexceptions.HTTPUnauthorized("This Galaxy instance is not configured to allow non-admins to view the data manager.") + message = escape(kwd.get('message', '')) + status = escape(kwd.get('status', 'info')) + data_table_name = kwd.get('table_name', None) if not data_table_name: - return trans.response.send_redirect( web.url_for( controller="data_manager", action="index" ) ) - data_table = trans.app.tool_data_tables.get( data_table_name, None ) + return trans.response.send_redirect(web.url_for(controller="data_manager", action="index")) + data_table = trans.app.tool_data_tables.get(data_table_name, None) if data_table is None: - return trans.response.send_redirect( web.url_for( controller="data_manager", action="index", message="Invalid Data table (%s) was requested" % data_table_name, status="error" ) ) - return trans.fill_template( "data_manager/manage_data_table.mako", data_table=data_table, view_only=not_is_admin, message=message, status=status ) + return trans.response.send_redirect(web.url_for(controller="data_manager", action="index", message="Invalid Data table (%s) was requested" % data_table_name, status="error")) + return trans.fill_template("data_manager/manage_data_table.mako", data_table=data_table, view_only=not_is_admin, message=message, status=status) @web.expose @web.require_admin - def reload_tool_data_tables( self, trans, table_name=None, **kwd ): - if table_name and isinstance( table_name, string_types ): - table_name = table_name.split( "," ) + def reload_tool_data_tables(self, trans, table_name=None, **kwd): + if table_name and isinstance(table_name, string_types): + table_name = table_name.split(",") # Reload the tool data tables - table_names = self.app.tool_data_tables.reload_tables( table_names=table_name ) + table_names = self.app.tool_data_tables.reload_tables(table_names=table_name) galaxy.queue_worker.send_control_task(trans.app, 'reload_tool_data_tables', noop_self=True, - kwargs={'table_name': table_name} ) + kwargs={'table_name': table_name}) redirect_url = None if table_names: status = 'done' - if len( table_names ) == 1: + if len(table_names) == 1: message = "The data table '%s' has been reloaded." % table_names[0] - redirect_url = web.url_for( controller='data_manager', - action='manage_data_table', - table_name=table_names[0], - message=message, - status=status ) + redirect_url = web.url_for(controller='data_manager', + action='manage_data_table', + table_name=table_names[0], + message=message, + status=status) else: - message = "The data tables '%s' have been reloaded." % ', '.join( table_names ) + message = "The data tables '%s' have been reloaded." % ', '.join(table_names) else: message = "No data tables have been reloaded." status = 'error' if redirect_url is None: - redirect_url = web.url_for( controller='admin', - action='view_tool_data_tables', - message=message, - status=status ) - return trans.response.send_redirect( redirect_url ) + redirect_url = web.url_for(controller='admin', + action='view_tool_data_tables', + message=message, + status=status) + return trans.response.send_redirect(redirect_url) diff --git a/lib/galaxy/webapps/galaxy/controllers/dataset.py b/lib/galaxy/webapps/galaxy/controllers/dataset.py index 2eff5f295bab..74e32c5254d9 100644 --- a/lib/galaxy/webapps/galaxy/controllers/dataset.py +++ b/lib/galaxy/webapps/galaxy/controllers/dataset.py @@ -1,6 +1,7 @@ import logging import os import urllib +import json from markupsafe import escape import paste.httpexceptions @@ -14,40 +15,40 @@ from galaxy.model.item_attrs import UsesAnnotations, UsesItemRatings from galaxy.util import inflector, smart_str from galaxy.util.sanitize_html import sanitize_html +from galaxy.web import form_builder from galaxy.web.base.controller import BaseUIController, ERROR, SUCCESS, url_for, UsesExtendedMetadataMixin from galaxy.web.framework.helpers import grids, iff, time_ago, to_unicode -from galaxy.tools.errors import EmailErrorReporter -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) comptypes = [] try: import zlib # noqa: F401 - comptypes.append( 'zip' ) + comptypes.append('zip') except ImportError: pass -class HistoryDatasetAssociationListGrid( grids.Grid ): +class HistoryDatasetAssociationListGrid(grids.Grid): # Custom columns for grid. - class HistoryColumn( grids.GridColumn ): - def get_value( self, trans, grid, hda): + class HistoryColumn(grids.GridColumn): + def get_value(self, trans, grid, hda): return escape(hda.history.name) - class StatusColumn( grids.GridColumn ): - def get_value( self, trans, grid, hda ): + class StatusColumn(grids.GridColumn): + def get_value(self, trans, grid, hda): if hda.deleted: return "deleted" return "" - def get_accepted_filters( self ): + def get_accepted_filters(self): """ Returns a list of accepted filters for this column. """ - accepted_filter_labels_and_vals = { "Active" : "False", "Deleted" : "True", "All": "All" } + accepted_filter_labels_and_vals = {"Active" : "False", "Deleted" : "True", "All": "All"} accepted_filters = [] for label, val in accepted_filter_labels_and_vals.items(): - args = { self.key: val } - accepted_filters.append( grids.GridColumnFilter( label, args) ) + args = {self.key: val} + accepted_filters.append(grids.GridColumnFilter(label, args)) return accepted_filters # Grid definition @@ -55,121 +56,109 @@ def get_accepted_filters( self ): model_class = model.HistoryDatasetAssociation default_sort_key = "-update_time" columns = [ - grids.TextColumn( "Name", key="name", - # Link name to dataset's history. - link=( lambda item: iff( item.history.deleted, None, dict( operation="switch", id=item.id ) ) ), filterable="advanced", attach_popup=True ), - HistoryColumn( "History", key="history", sortable=False, target="inbound", - link=( lambda item: iff( item.history.deleted, None, dict( operation="switch_history", id=item.id ) ) ) ), - grids.IndividualTagsColumn( "Tags", key="tags", model_tag_association_class=model.HistoryDatasetAssociationTagAssociation, filterable="advanced", grid_name="HistoryDatasetAssocationListGrid" ), - StatusColumn( "Status", key="deleted", attach_popup=False ), - grids.GridColumn( "Last Updated", key="update_time", format=time_ago ), + grids.TextColumn("Name", key="name", + # Link name to dataset's history. + link=(lambda item: iff(item.history.deleted, None, dict(operation="switch", id=item.id))), filterable="advanced", attach_popup=True), + HistoryColumn("History", key="history", sortable=False, target="inbound", + link=(lambda item: iff(item.history.deleted, None, dict(operation="switch_history", id=item.id)))), + grids.IndividualTagsColumn("Tags", key="tags", model_tag_association_class=model.HistoryDatasetAssociationTagAssociation, filterable="advanced", grid_name="HistoryDatasetAssocationListGrid"), + StatusColumn("Status", key="deleted", attach_popup=False), + grids.GridColumn("Last Updated", key="update_time", format=time_ago), ] columns.append( grids.MulticolFilterColumn( "Search", - cols_to_filter=[ columns[0], columns[2] ], - key="free-text-search", visible=False, filterable="standard" ) + cols_to_filter=[columns[0], columns[2]], + key="free-text-search", visible=False, filterable="standard") ) operations = [ - grids.GridOperation( "Copy to current history", condition=( lambda item: not item.deleted ), async_compatible=True ), + grids.GridOperation("Copy to current history", condition=(lambda item: not item.deleted), async_compatible=True), ] standard_filters = [] - default_filter = dict( name="All", deleted="False", tags="All" ) + default_filter = dict(name="All", deleted="False", tags="All") preserve_state = False use_async = True use_paging = True num_rows_per_page = 50 - def build_initial_query( self, trans, **kwargs ): + def build_initial_query(self, trans, **kwargs): # Show user's datasets that are not deleted, not in deleted histories, and not hidden. # To filter HDAs by user, need to join model class/HDA and History table so that it is # possible to filter by user. However, for dictionary-based filtering to work, need a # primary table for the query. - return trans.sa_session.query( self.model_class ).select_from( self.model_class.table.join( model.History.table ) ) \ - .filter( model.History.user == trans.user ) \ - .filter( self.model_class.deleted == false() ) \ - .filter( model.History.deleted == false() ) \ - .filter( self.model_class.visible == true() ) + return trans.sa_session.query(self.model_class).select_from(self.model_class.table.join(model.History.table)) \ + .filter(model.History.user == trans.user) \ + .filter(self.model_class.deleted == false()) \ + .filter(model.History.deleted == false()) \ + .filter(self.model_class.visible == true()) -class DatasetInterface( BaseUIController, UsesAnnotations, UsesItemRatings, UsesExtendedMetadataMixin ): +class DatasetInterface(BaseUIController, UsesAnnotations, UsesItemRatings, UsesExtendedMetadataMixin): stored_list_grid = HistoryDatasetAssociationListGrid() - def __init__( self, app ): - super( DatasetInterface, self ).__init__( app ) - self.history_manager = managers.histories.HistoryManager( app ) - self.hda_manager = managers.hdas.HDAManager( app ) + def __init__(self, app): + super(DatasetInterface, self).__init__(app) + self.history_manager = managers.histories.HistoryManager(app) + self.hda_manager = managers.hdas.HDAManager(app) - def _get_job_for_dataset( self, trans, dataset_id ): + def _get_job_for_dataset(self, trans, dataset_id): ''' Return the job for the given dataset. This will throw an error if the dataset is either nonexistent or inaccessible to the user. ''' - hda = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( self.decode_id( dataset_id ) ) - assert hda and self._can_access_dataset( trans, hda ) + hda = trans.sa_session.query(trans.app.model.HistoryDatasetAssociation).get(self.decode_id(dataset_id)) + assert hda and self._can_access_dataset(trans, hda) return hda.creating_job - def _can_access_dataset( self, trans, dataset_association, allow_admin=True, additional_roles=None ): + def _can_access_dataset(self, trans, dataset_association, allow_admin=True, additional_roles=None): roles = trans.get_current_user_roles() if additional_roles: roles = roles + additional_roles - return ( allow_admin and trans.user_is_admin() ) or trans.app.security_agent.can_access_dataset( roles, dataset_association.dataset ) + return (allow_admin and trans.user_is_admin()) or trans.app.security_agent.can_access_dataset(roles, dataset_association.dataset) @web.expose - def errors( self, trans, id ): - hda = trans.sa_session.query( model.HistoryDatasetAssociation ).get( self.decode_id( id ) ) + def errors(self, trans, id): + hda = trans.sa_session.query(model.HistoryDatasetAssociation).get(self.decode_id(id)) - if not hda or not self._can_access_dataset( trans, hda ): - return trans.show_error_message( "Either this dataset does not exist or you do not have permission to access it." ) - return trans.fill_template( "dataset/errors.mako", hda=hda ) + if not hda or not self._can_access_dataset(trans, hda): + return trans.show_error_message("Either this dataset does not exist or you do not have permission to access it.") + return trans.fill_template("dataset/errors.mako", hda=hda) @web.expose - def stdout( self, trans, dataset_id=None, **kwargs ): - trans.response.set_content_type( 'text/plain' ) + def stdout(self, trans, dataset_id=None, **kwargs): + trans.response.set_content_type('text/plain') stdout = "" try: - job = self._get_job_for_dataset( trans, dataset_id ) + job = self._get_job_for_dataset(trans, dataset_id) stdout = job.stdout except: stdout = "Invalid dataset ID or you are not allowed to access this dataset" - return smart_str( stdout ) + return smart_str(stdout) @web.expose # TODO: Migrate stderr and stdout to use _get_job_for_dataset; it wasn't tested. - def stderr( self, trans, dataset_id=None, **kwargs ): - trans.response.set_content_type( 'text/plain' ) + def stderr(self, trans, dataset_id=None, **kwargs): + trans.response.set_content_type('text/plain') stderr = "" try: - job = self._get_job_for_dataset( trans, dataset_id ) + job = self._get_job_for_dataset(trans, dataset_id) stderr = job.stderr except: stderr = "Invalid dataset ID or you are not allowed to access this dataset" - return smart_str( stderr ) + return smart_str(stderr) @web.expose - def exit_code( self, trans, dataset_id=None, **kwargs ): - trans.response.set_content_type( 'text/plain' ) + def exit_code(self, trans, dataset_id=None, **kwargs): + trans.response.set_content_type('text/plain') exit_code = "" try: - job = self._get_job_for_dataset( trans, dataset_id ) + job = self._get_job_for_dataset(trans, dataset_id) exit_code = job.exit_code except: exit_code = "Invalid dataset ID or you are not allowed to access this dataset" return exit_code - @web.expose - def report_error( self, trans, id, email='', message="", **kwd ): - biostar_report = 'biostar' in str( kwd.get( 'submit_error_report') ).lower() - if biostar_report: - return trans.response.send_redirect( url_for( controller='biostar', action='biostar_tool_bug_report', hda=id, email=email, message=message ) ) - try: - error_reporter = EmailErrorReporter( id, trans.app ) - error_reporter.send_report( user=trans.user, email=email, message=message ) - return trans.show_ok_message( "Your error report has been sent" ) - except Exception as e: - return trans.show_error_message( "An error occurred sending the report by email: %s" % str( e ) ) - @web.expose def default(self, trans, dataset_id=None, **kwd): return 'This link may not be followed from within Galaxy.' @@ -177,9 +166,9 @@ def default(self, trans, dataset_id=None, **kwd): @web.expose def get_metadata_file(self, trans, hda_id, metadata_name): """ Allows the downloading of metadata files associated with datasets (eg. bai index for bam files) """ - data = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( self.decode_id( hda_id ) ) - if not data or not self._can_access_dataset( trans, data ): - return trans.show_error_message( "You are not allowed to access this dataset" ) + data = trans.sa_session.query(trans.app.model.HistoryDatasetAssociation).get(self.decode_id(hda_id)) + if not data or not self._can_access_dataset(trans, data): + return trans.show_error_message("You are not allowed to access this dataset") fname = ''.join(c in util.FILENAME_VALID_CHARS and c or '_' for c in data.name)[0:150] @@ -191,24 +180,24 @@ def get_metadata_file(self, trans, hda_id, metadata_name): def _check_dataset(self, trans, hda_id): # DEPRECATION: We still support unencoded ids for backward compatibility try: - data = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( self.decode_id( hda_id) ) + data = trans.sa_session.query(trans.app.model.HistoryDatasetAssociation).get(self.decode_id(hda_id)) if data is None: - raise ValueError( 'Invalid reference dataset id: %s.' % hda_id) + raise ValueError('Invalid reference dataset id: %s.' % hda_id) except: try: - data = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( int( hda_id ) ) + data = trans.sa_session.query(trans.app.model.HistoryDatasetAssociation).get(int(hda_id)) except: data = None if not data: - raise paste.httpexceptions.HTTPRequestRangeNotSatisfiable( "Invalid reference dataset id: %s." % str( hda_id ) ) - if not self._can_access_dataset( trans, data ): - return trans.show_error_message( "You are not allowed to access this dataset" ) + raise paste.httpexceptions.HTTPRequestRangeNotSatisfiable("Invalid reference dataset id: %s." % str(hda_id)) + if not self._can_access_dataset(trans, data): + return trans.show_error_message("You are not allowed to access this dataset") if data.purged: - return trans.show_error_message( "The dataset you are attempting to view has been purged." ) - if data.deleted and not ( trans.user_is_admin() or ( data.history and trans.get_user() == data.history.user ) ): - return trans.show_error_message( "The dataset you are attempting to view has been deleted." ) + return trans.show_error_message("The dataset you are attempting to view has been purged.") + if data.deleted and not (trans.user_is_admin() or (data.history and trans.get_user() == data.history.user)): + return trans.show_error_message("The dataset you are attempting to view has been deleted.") if data.state == trans.model.Dataset.states.UPLOAD: - return trans.show_error_message( "Please wait until this dataset finishes uploading before attempting to view it." ) + return trans.show_error_message("Please wait until this dataset finishes uploading before attempting to view it.") return data @web.expose @@ -217,9 +206,9 @@ def transfer_status(self, trans, dataset_id, filename=None): """ Primarily used for the S3ObjectStore - get the status of data transfer if the file is not in cache """ data = self._check_dataset(trans, dataset_id) - if isinstance( data, string_types ): + if isinstance(data, string_types): return data - log.debug( "Checking transfer status for dataset %s..." % data.dataset.id ) + log.debug("Checking transfer status for dataset %s..." % data.dataset.id) # Pulling files in extra_files_path into cache is not handled via this # method but that's primarily because those files are typically linked to @@ -233,13 +222,13 @@ def transfer_status(self, trans, dataset_id, filename=None): @web.expose def display(self, trans, dataset_id=None, preview=False, filename=None, to_ext=None, offset=None, ck_size=None, **kwd): data = self._check_dataset(trans, dataset_id) - if not isinstance( data, trans.app.model.DatasetInstance ): + if not isinstance(data, trans.app.model.DatasetInstance): return data if "hdca" in kwd: raise RequestParameterInvalidException("Invalid request parameter 'hdca' encountered.") hdca_id = kwd.get("hdca_id", None) if hdca_id: - hdca = self.app.dataset_collections_service.get_dataset_collection_instance( trans, "history", hdca_id ) + hdca = self.app.dataset_collections_service.get_dataset_collection_instance(trans, "history", hdca_id) del kwd["hdca_id"] kwd["hdca"] = hdca # Ensure offset is an integer before passing through to datatypes. @@ -251,97 +240,99 @@ def display(self, trans, dataset_id=None, preview=False, filename=None, to_ext=N return data.datatype.display_data(trans, data, preview, filename, to_ext, offset=offset, ck_size=ck_size, **kwd) @web.expose + @web.json def edit(self, trans, dataset_id=None, filename=None, hid=None, **kwd): """Allows user to modify parameters of an HDA.""" message = None status = 'done' - refresh_frames = [] error = False - def __ok_to_edit_metadata( dataset_id ): + def __ok_to_edit_metadata(dataset_id): # prevent modifying metadata when dataset is queued or running as input/output # This code could be more efficient, i.e. by using mappers, but to prevent slowing down loading a History panel, we'll leave the code here for now for job_to_dataset_association in trans.sa_session.query( - self.app.model.JobToInputDatasetAssociation ) \ - .filter_by( dataset_id=dataset_id ) \ + self.app.model.JobToInputDatasetAssociation) \ + .filter_by(dataset_id=dataset_id) \ .all() \ - + trans.sa_session.query( self.app.model.JobToOutputDatasetAssociation ) \ - .filter_by( dataset_id=dataset_id ) \ + + trans.sa_session.query(self.app.model.JobToOutputDatasetAssociation) \ + .filter_by(dataset_id=dataset_id) \ .all(): - if job_to_dataset_association.job.state not in [ job_to_dataset_association.job.states.OK, job_to_dataset_association.job.states.ERROR, job_to_dataset_association.job.states.DELETED ]: + if job_to_dataset_association.job.state not in [job_to_dataset_association.job.states.OK, job_to_dataset_association.job.states.ERROR, job_to_dataset_association.job.states.DELETED]: return False return True if hid is not None: history = trans.get_history() # TODO: hid handling - data = history.datasets[ int( hid ) - 1 ] + data = history.datasets[int(hid) - 1] id = None elif dataset_id is not None: - id = self.decode_id( dataset_id ) - data = trans.sa_session.query( self.app.model.HistoryDatasetAssociation ).get( id ) + id = self.decode_id(dataset_id) + data = trans.sa_session.query(self.app.model.HistoryDatasetAssociation).get(id) else: - trans.log_event( "dataset_id and hid are both None, cannot load a dataset to edit" ) - return trans.show_error_message( "You must provide a history dataset id to edit" ) + trans.log_event("dataset_id and hid are both None, cannot load a dataset to edit") + return { + status: 'error', + message: 'You must provide a history dataset id to edit.' + } if data is None: - trans.log_event( "Problem retrieving dataset (encoded: %s, decoded: %s) with history id %s." % ( str( dataset_id ), str( id ), str( hid ) ) ) - return trans.show_error_message( "History dataset id is invalid" ) + trans.log_event("Problem retrieving dataset (encoded: %s, decoded: %s) with history id %s." % (str(dataset_id), str(id), str(hid))) + return { + status: 'error', + message: "History dataset id is invalid." + } if dataset_id is not None and data.history.user is not None and data.history.user != trans.user: - trans.log_event( "User attempted to edit an HDA they do not own (encoded: %s, decoded: %s)" % ( dataset_id, id ) ) + trans.log_event("User attempted to edit an HDA they do not own (encoded: %s, decoded: %s)." % (dataset_id, id)) # Do not reveal the dataset's existence - return trans.show_error_message( "History dataset id is invalid" ) + return { + status: 'error', + message: "History dataset id is invalid." + } current_user_roles = trans.get_current_user_roles() - if data.history.user and not data.dataset.has_manage_permissions_roles( trans ): + if data.history.user and not data.dataset.has_manage_permissions_roles(trans): # Permission setting related to DATASET_MANAGE_PERMISSIONS was broken for a period of time, # so it is possible that some Datasets have no roles associated with the DATASET_MANAGE_PERMISSIONS # permission. In this case, we'll reset this permission to the hda user's private role. - manage_permissions_action = trans.app.security_agent.get_action( trans.app.security_agent.permitted_actions.DATASET_MANAGE_PERMISSIONS.action ) - permissions = { manage_permissions_action : [ trans.app.security_agent.get_private_user_role( data.history.user ) ] } - trans.app.security_agent.set_dataset_permission( data.dataset, permissions ) - if self._can_access_dataset( trans, data ): + manage_permissions_action = trans.app.security_agent.get_action(trans.app.security_agent.permitted_actions.DATASET_MANAGE_PERMISSIONS.action) + permissions = {manage_permissions_action : [trans.app.security_agent.get_private_user_role(data.history.user)]} + trans.app.security_agent.set_dataset_permission(data.dataset, permissions) + if self._can_access_dataset(trans, data): if data.state == trans.model.Dataset.states.UPLOAD: - return trans.show_error_message( "Please wait until this dataset finishes uploading before attempting to edit its metadata." ) - params = util.Params( kwd, sanitize=False ) + return { + status: 'error', + message: "Please wait until this dataset finishes uploading before attempting to edit its metadata." + } + params = util.Params(kwd, sanitize=False) if params.change: # The user clicked the Save button on the 'Change data type' form - if data.datatype.allow_datatype_change and trans.app.datatypes_registry.get_datatype_by_extension( params.datatype ).allow_datatype_change: + if data.datatype.allow_datatype_change and trans.app.datatypes_registry.get_datatype_by_extension(params.datatype).allow_datatype_change: # prevent modifying datatype when dataset is queued or running as input/output - if not __ok_to_edit_metadata( data.id ): + if not __ok_to_edit_metadata(data.id): message = "This dataset is currently being used as input or output. You cannot change datatype until the jobs have completed or you have canceled them." error = True else: - trans.app.datatypes_registry.change_datatype( data, params.datatype ) + trans.app.datatypes_registry.change_datatype(data, params.datatype) trans.sa_session.flush() - trans.app.datatypes_registry.set_external_metadata_tool.tool_action.execute( trans.app.datatypes_registry.set_external_metadata_tool, trans, incoming={ 'input1': data }, overwrite=False ) # overwrite is False as per existing behavior - message = "Changed the type of dataset '%s' to %s" % ( to_unicode( data.name ), params.datatype ) - refresh_frames = ['history'] + trans.app.datatypes_registry.set_external_metadata_tool.tool_action.execute(trans.app.datatypes_registry.set_external_metadata_tool, trans, incoming={'input1': data}, overwrite=False) # overwrite is False as per existing behavior + message = "Changed the type of dataset %s to %s." % (to_unicode(data.name), params.datatype) else: - message = "You are unable to change datatypes in this manner. Changing %s to %s is not allowed." % ( data.extension, params.datatype ) + message = "You are unable to change datatypes in this manner. Changing %s to %s is not allowed." % (data.extension, params.datatype) error = True elif params.save: # The user clicked the Save button on the 'Edit Attributes' form data.name = params.name if params.name else '' data.info = params.info if params.info else '' message = '' - if __ok_to_edit_metadata( data.id ): + if __ok_to_edit_metadata(data.id): # The following for loop will save all metadata_spec items for name, spec in data.datatype.metadata_spec.items(): if spec.get("readonly"): continue - optional = params.get("is_" + name, None) - other = params.get("or_" + name, None) - if optional and optional == '__NOTHING__': - # optional element... == '__NOTHING__' actually means it is NOT checked (and therefore omitted) - setattr(data.metadata, name, None) - else: - if other: - setattr( data.metadata, name, other ) - else: - setattr( data.metadata, name, spec.unwrap( params.get(name, None) ) ) - data.datatype.after_setting_metadata( data ) + setattr(data.metadata, name, spec.unwrap(params.get(name, None))) + data.datatype.after_setting_metadata(data) # Sanitize annotation before adding it. if params.annotation: - annotation = sanitize_html( params.annotation, 'utf-8', 'text/html' ) - self.add_item_annotation( trans.sa_session, trans.get_user(), data, annotation ) + annotation = sanitize_html(params.annotation, 'utf-8', 'text/html') + self.add_item_annotation(trans.sa_session, trans.get_user(), data, annotation) # This block on controller code is inactive until the 'extended_metadata' edit box is added back into the UI # Add or delete extended metadata # if params.extended_metadata: @@ -379,58 +370,61 @@ def __ok_to_edit_metadata( dataset_id ): if data._state == trans.model.Dataset.states.FAILED_METADATA and not data.missing_meta(): data._state = None trans.sa_session.flush() - message = "Attributes updated%s" % message - refresh_frames = ['history'] + if message: + message = "Attributes updated. %s" % message + else: + message = "Attributes updated." else: trans.sa_session.flush() message = "Attributes updated, but metadata could not be changed because this dataset is currently being used as input or output. You must cancel or wait for these jobs to complete before changing metadata." status = "warning" - refresh_frames = ['history'] elif params.detect: # The user clicked the Auto-detect button on the 'Edit Attributes' form # prevent modifying metadata when dataset is queued or running as input/output - if not __ok_to_edit_metadata( data.id ): + if not __ok_to_edit_metadata(data.id): message = "This dataset is currently being used as input or output. You cannot change metadata until the jobs have completed or you have canceled them." error = True else: for name, spec in data.metadata.spec.items(): # We need to be careful about the attributes we are resetting - if name not in [ 'name', 'info', 'dbkey', 'base_name' ]: - if spec.get( 'default' ): - setattr( data.metadata, name, spec.unwrap( spec.get( 'default' ) ) ) - message = 'Attributes have been queued to be updated' - trans.app.datatypes_registry.set_external_metadata_tool.tool_action.execute( trans.app.datatypes_registry.set_external_metadata_tool, trans, incoming={ 'input1': data } ) + if name not in ['name', 'info', 'dbkey', 'base_name']: + if spec.get('default'): + setattr(data.metadata, name, spec.unwrap(spec.get('default'))) + message = 'Attributes have been queued to be updated.' + trans.app.datatypes_registry.set_external_metadata_tool.tool_action.execute(trans.app.datatypes_registry.set_external_metadata_tool, trans, incoming={'input1': data}) trans.sa_session.flush() - refresh_frames = ['history'] elif params.convert_data: target_type = kwd.get("target_type", None) if target_type: message = data.datatype.convert_dataset(trans, data, target_type) - refresh_frames = ['history'] elif params.update_roles_button: if not trans.user: - return trans.show_error_message( "You must be logged in if you want to change permissions." ) - if trans.app.security_agent.can_manage_dataset( current_user_roles, data.dataset ): - access_action = trans.app.security_agent.get_action( trans.app.security_agent.permitted_actions.DATASET_ACCESS.action ) - manage_permissions_action = trans.app.security_agent.get_action( trans.app.security_agent.permitted_actions.DATASET_MANAGE_PERMISSIONS.action ) + return { + status: 'error', + message: "You must be logged in if you want to change permissions." + } + if trans.app.security_agent.can_manage_dataset(current_user_roles, data.dataset): + permitted_actions = trans.app.model.Dataset.permitted_actions.items() + payload_permissions = json.loads(params.permissions) # The user associated the DATASET_ACCESS permission on the dataset with 1 or more roles. We # need to ensure that they did not associate roles that would cause accessibility problems. permissions, in_roles, error, message = \ - trans.app.security_agent.derive_roles_from_access( trans, data.dataset.id, 'root', **kwd ) + trans.app.security_agent.derive_roles_from_access(trans, data.dataset.id, 'root', **payload_permissions) if error: # Keep the original role associations for the DATASET_ACCESS permission on the dataset. - permissions[ access_action ] = data.dataset.get_access_roles( trans ) + access_action = trans.app.security_agent.get_action(trans.app.security_agent.permitted_actions.DATASET_ACCESS.action) + permissions[access_action] = data.dataset.get_access_roles(trans) status = 'error' else: - error = trans.app.security_agent.set_all_dataset_permissions( data.dataset, permissions ) + error = trans.app.security_agent.set_all_dataset_permissions(data.dataset, permissions) if error: message += error status = 'error' else: message = 'Your changes completed successfully.' - trans.sa_session.refresh( data.dataset ) + trans.sa_session.refresh(data.dataset) else: - message = "You are not authorized to change this dataset's permissions" + message = "You are not authorized to change this dataset's permissions." error = True else: if "dbkey" in data.datatype.metadata_spec and not data.metadata.dbkey: @@ -443,34 +437,171 @@ def __ok_to_edit_metadata( dataset_id ): data.metadata.dbkey = data.dbkey # let's not overwrite the imported datatypes module with the variable datatypes? # the built-in 'id' is overwritten in lots of places as well - ldatatypes = [ dtype_name for dtype_name, dtype_value in trans.app.datatypes_registry.datatypes_by_extension.iteritems() if dtype_value.allow_datatype_change ] + ldatatypes = [(dtype_name, dtype_name) for dtype_name, dtype_value in trans.app.datatypes_registry.datatypes_by_extension.iteritems() if dtype_value.allow_datatype_change] ldatatypes.sort() - all_roles = trans.app.security_agent.get_legitimate_roles( trans, data.dataset, 'root' ) + all_roles = trans.app.security_agent.get_legitimate_roles(trans, data.dataset, 'root') + data_metadata = [(name, spec) for name, spec in data.metadata.spec.items()] + converters_collection = [(key, value.name) for key, value in data.get_converter_types().items()] + can_manage_dataset = trans.app.security_agent.can_manage_dataset(current_user_roles, data.dataset) if error: status = 'error' - return trans.fill_template( "/dataset/edit_attributes.mako", - data=data, - data_annotation=self.get_item_annotation_str( trans.sa_session, trans.user, data ), - datatypes=ldatatypes, - current_user_roles=current_user_roles, - all_roles=all_roles, - message=message, - status=status, - dataset_id=dataset_id, - refresh_frames=refresh_frames ) + edit_attributes_inputs = list() + convert_inputs = list() + convert_datatype_inputs = list() + permission_inputs = list() + + edit_attributes_inputs.append({ + 'name' : 'name', + 'type' : 'text', + 'label': 'Name:', + 'value': data.get_display_name() + }) + + edit_attributes_inputs.append({ + 'name' : 'info', + 'type' : 'text', + 'label': 'Info:', + 'value': data.info + }) + + edit_attributes_inputs.append({ + 'name' : 'annotation', + 'type' : 'text', + 'label': 'Annotation', + 'value': self.get_item_annotation_str(trans.sa_session, trans.user, data), + 'help' : 'Add an annotation or notes to a dataset; annotations are available when a history is viewed.' + }) + + for name, spec in data_metadata: + if spec.visible: + attributes = data.metadata.get_metadata_parameter(name, trans=trans) + if type(attributes) is form_builder.SelectField: + edit_attributes_inputs.append({ + 'type': 'select', + 'multiple': attributes.multiple, + 'optional': attributes.optional, + 'name': name, + 'label': spec.desc, + 'options': attributes.options, + 'value': attributes.value if attributes.multiple else [attributes.value] + }) + elif type(attributes) is form_builder.TextField: + edit_attributes_inputs.append({ + 'type': 'text', + 'name': name, + 'label': spec.desc, + 'value': attributes.value, + 'readonly': spec.get('readonly') + }) + + if data.missing_meta(): + edit_attributes_inputs.append({ + 'name' : 'errormsg', + 'type' : 'text', + 'label': 'Error Message', + 'value': 'Required metadata values are missing. Some of these values may not be editable by the user. Selecting "Auto-detect" will attempt to fix these values.', + 'class' : 'errormessagesmall', + 'readonly' : True + }) + + convert_inputs.append({ + 'type': 'select', + 'name': 'target_type', + 'label': 'Name:', + 'help': 'This will create a new dataset with the contents of this dataset converted to a new format.', + 'options': [(convert_name, convert_id) for convert_id, convert_name in converters_collection] + }) + + convert_datatype_inputs.append({ + 'type': 'select', + 'name': 'datatype', + 'label': 'New Type:', + 'help': 'This will change the datatype of the existing dataset but not modify its contents. Use this if Galaxy has incorrectly guessed the type of your dataset.', + 'options': [(ext_name, ext_id) for ext_id, ext_name in ldatatypes], + 'value': [ext_id for ext_id, ext_name in ldatatypes if ext_id == data.ext] + }) + + if can_manage_dataset: + permitted_actions = trans.app.model.Dataset.permitted_actions.items() + saved_role_ids = {} + for action, roles in trans.app.security_agent.get_permissions(data.dataset).items(): + for role in roles: + saved_role_ids[action.action] = role.id + + for index, action in permitted_actions: + if action == trans.app.security_agent.permitted_actions.DATASET_ACCESS: + help_text = action.description + '
    NOTE: Users must have every role associated with this dataset in order to access it.' + else: + help_text = action.description + permission_inputs.append({ + 'type': 'select', + 'multiple': True, + 'optional': True, + 'name': index, + 'label': action.action, + 'help': help_text, + 'options': [(r.name, r.id) for r in all_roles], + 'value': saved_role_ids[action.action] if action.action in saved_role_ids else [] + }) + elif trans.user: + if data.dataset.actions: + for action, roles in trans.app.security_agent.get_permissions(data.dataset).items(): + if roles: + role_inputs = list() + for role in roles: + role_inputs.append({ + 'name': role.name + action.action, + 'type': 'text', + 'label': action.description, + 'value': role.name, + 'readonly': True + }) + view_permissions = {'name': action.action, 'label': action.action, 'type': 'section', 'inputs': role_inputs} + permission_inputs.append(view_permissions) + else: + permission_inputs.append({ + 'name': 'access_public', + 'type': 'text', + 'label': 'Public access', + 'value': 'This dataset is accessible by everyone (it is public).', + 'readonly': True + }) + else: + permission_inputs.append({ + 'name': 'no_access', + 'type': 'text', + 'label': 'No access', + 'value': 'Permissions not available (not logged in).', + 'readonly': True + }) + + return { + 'display_name': data.get_display_name(), + 'message': message, + 'status': status, + 'dataset_id': dataset_id, + 'can_manage_dataset': can_manage_dataset, + 'edit_attributes_inputs': edit_attributes_inputs, + 'convert_inputs': convert_inputs, + 'convert_datatype_inputs': convert_datatype_inputs, + 'permission_inputs': permission_inputs + } else: - return trans.show_error_message( "You do not have permission to edit this dataset's ( id: %s ) information." % str( dataset_id ) ) + return { + status: 'error', + message: "You do not have permission to edit this dataset's ( id: %s ) information." % str(dataset_id) + } @web.expose @web.json - @web.require_login( "see all available datasets" ) - def list( self, trans, **kwargs ): + @web.require_login("see all available datasets") + def list(self, trans, **kwargs): """List all available datasets""" status = message = None if 'operation' in kwargs: operation = kwargs['operation'].lower() - hda_ids = util.listify( kwargs.get( 'id', [] ) ) + hda_ids = util.listify(kwargs.get('id', [])) # Display no message by default status, message = None, None @@ -478,15 +609,15 @@ def list( self, trans, **kwargs ): # Load the hdas and ensure they all belong to the current user hdas = [] for encoded_hda_id in hda_ids: - hda_id = self.decode_id( encoded_hda_id ) - hda = trans.sa_session.query( model.HistoryDatasetAssociation ).filter_by( id=hda_id ).first() + hda_id = self.decode_id(encoded_hda_id) + hda = trans.sa_session.query(model.HistoryDatasetAssociation).filter_by(id=hda_id).first() if hda: # Ensure history is owned by current user if hda.history.user_id is not None and trans.user: assert trans.user.id == hda.history.user_id, "HistoryDatasetAssocation does not belong to current user" - hdas.append( hda ) + hdas.append(hda) else: - log.warning( "Invalid history_dataset_association id '%r' passed to list", hda_id ) + log.warning("Invalid history_dataset_association id '%r' passed to list", hda_id) if hdas: if operation == "switch" or operation == "switch_history": @@ -495,110 +626,110 @@ def list( self, trans, **kwargs ): # Convert hda to histories. histories = [] for hda in hdas: - histories.append( hda.history ) + histories.append(hda.history) # Use history controller to switch the history. TODO: is this reasonable? - status, message = trans.webapp.controllers['history']._list_switch( trans, histories ) + status, message = trans.webapp.controllers['history']._list_switch(trans, histories) # Current history changed, refresh history frame; if switching to a dataset, set hda seek. kwargs['refresh_frames'] = ['history'] if operation == "switch": - hda_ids = [ trans.security.encode_id( hda.id ) for hda in hdas ] + hda_ids = [trans.security.encode_id(hda.id) for hda in hdas] # TODO: Highlighting does not work, has to be revisited - trans.template_context[ 'seek_hda_ids' ] = hda_ids + trans.template_context['seek_hda_ids'] = hda_ids elif operation == "copy to current history": # # Copy datasets to the current history. # - target_histories = [ trans.get_history() ] + target_histories = [trans.get_history()] # Reverse HDAs so that they appear in the history in the order they are provided. hda_ids.reverse() - status, message = self._copy_datasets( trans, hda_ids, target_histories ) + status, message = self._copy_datasets(trans, hda_ids, target_histories) # Current history changed, refresh history frame. kwargs['refresh_frames'] = ['history'] # Render the list view - kwargs[ 'dict_format' ] = True - return self.stored_list_grid( trans, status=status, message=message, **kwargs ) + kwargs['dict_format'] = True + return self.stored_list_grid(trans, status=status, message=message, **kwargs) @web.expose - def imp( self, trans, dataset_id=None, **kwd ): + def imp(self, trans, dataset_id=None, **kwd): """ Import another user's dataset via a shared URL; dataset is added to user's current history. """ # Set referer message. referer = trans.request.referer if referer: referer_message = "return to the previous page" % escape(referer) else: - referer_message = "go to Galaxy's start page" % url_for( '/' ) + referer_message = "go to Galaxy's start page" % url_for('/') # Error checking. if not dataset_id: - return trans.show_error_message( "You must specify a dataset to import. You can %s." % referer_message, use_panels=True ) + return trans.show_error_message("You must specify a dataset to import. You can %s." % referer_message, use_panels=True) # Do import. - cur_history = trans.get_history( create=True ) - status, message = self._copy_datasets( trans, [ dataset_id ], [ cur_history ], imported=True ) - message = "Dataset imported.
    You can start using the dataset or %s." % ( url_for('/'), referer_message ) - return trans.show_message( message, type=status, use_panels=True ) + cur_history = trans.get_history(create=True) + status, message = self._copy_datasets(trans, [dataset_id], [cur_history], imported=True) + message = "Dataset imported.
    You can start using the dataset or %s." % (url_for('/'), referer_message) + return trans.show_message(message, type=status, use_panels=True) @web.expose @web.json - @web.require_login( "use Galaxy datasets" ) - def get_name_and_link_async( self, trans, id=None ): + @web.require_login("use Galaxy datasets") + def get_name_and_link_async(self, trans, id=None): """ Returns dataset's name and link. """ - decoded_id = self.decode_id( id ) - dataset = self.hda_manager.get_accessible( decoded_id, trans.user ) - dataset = self.hda_manager.error_if_uploading( dataset ) - return_dict = { "name" : dataset.name, "link" : url_for( controller='dataset', action="display_by_username_and_slug", username=dataset.history.user.username, slug=trans.security.encode_id( dataset.id ) ) } + decoded_id = self.decode_id(id) + dataset = self.hda_manager.get_accessible(decoded_id, trans.user) + dataset = self.hda_manager.error_if_uploading(dataset) + return_dict = {"name" : dataset.name, "link" : url_for(controller='dataset', action="display_by_username_and_slug", username=dataset.history.user.username, slug=trans.security.encode_id(dataset.id))} return return_dict @web.expose - def get_embed_html_async( self, trans, id ): + def get_embed_html_async(self, trans, id): """ Returns HTML for embedding a dataset in a page. """ - decoded_id = self.decode_id( id ) - dataset = self.hda_manager.get_accessible( decoded_id, trans.user ) - dataset = self.hda_manager.error_if_uploading( dataset ) + decoded_id = self.decode_id(id) + dataset = self.hda_manager.get_accessible(decoded_id, trans.user) + dataset = self.hda_manager.error_if_uploading(dataset) if dataset: return "Embedded Dataset '%s'" % dataset.name @web.expose - @web.require_login( "use Galaxy datasets" ) - def set_accessible_async( self, trans, id=None, accessible=False ): + @web.require_login("use Galaxy datasets") + def set_accessible_async(self, trans, id=None, accessible=False): """ Does nothing because datasets do not have an importable/accessible attribute. This method could potentially set another attribute. """ return @web.expose - @web.require_login( "rate items" ) + @web.require_login("rate items") @web.json - def rate_async( self, trans, id, rating ): + def rate_async(self, trans, id, rating): """ Rate a dataset asynchronously and return updated community data. """ - decoded_id = self.decode_id( id ) - dataset = self.hda_manager.get_accessible( decoded_id, trans.user ) - dataset = self.hda_manager.error_if_uploading( dataset ) + decoded_id = self.decode_id(id) + dataset = self.hda_manager.get_accessible(decoded_id, trans.user) + dataset = self.hda_manager.error_if_uploading(dataset) if not dataset: - return trans.show_error_message( "The specified dataset does not exist." ) + return trans.show_error_message("The specified dataset does not exist.") # Rate dataset. - self.rate_item( trans.sa_session, trans.get_user(), dataset, rating ) + self.rate_item(trans.sa_session, trans.get_user(), dataset, rating) - return self.get_ave_item_rating_data( trans.sa_session, dataset ) + return self.get_ave_item_rating_data(trans.sa_session, dataset) @web.expose - def display_by_username_and_slug( self, trans, username, slug, filename=None, preview=True ): + def display_by_username_and_slug(self, trans, username, slug, filename=None, preview=True): """ Display dataset by username and slug; because datasets do not yet have slugs, the slug is the dataset's id. """ id = slug - decoded_id = self.decode_id( id ) - dataset = self.hda_manager.get_accessible( decoded_id, trans.user ) - dataset = self.hda_manager.error_if_uploading( dataset ) + decoded_id = self.decode_id(id) + dataset = self.hda_manager.get_accessible(decoded_id, trans.user) + dataset = self.hda_manager.error_if_uploading(dataset) if dataset: # Filename used for composite types. if filename: - return self.display( trans, dataset_id=slug, filename=filename) + return self.display(trans, dataset_id=slug, filename=filename) - truncated, dataset_data = self.hda_manager.text_data( dataset, preview ) - dataset.annotation = self.get_item_annotation_str( trans.sa_session, dataset.history.user, dataset ) + truncated, dataset_data = self.hda_manager.text_data(dataset, preview) + dataset.annotation = self.get_item_annotation_str(trans.sa_session, dataset.history.user, dataset) # If dataset is chunkable, get first chunk. first_chunk = None @@ -608,105 +739,105 @@ def display_by_username_and_slug( self, trans, username, slug, filename=None, pr # If data is binary or an image, stream without template; otherwise, use display template. # TODO: figure out a way to display images in display template. if isinstance(dataset.datatype, datatypes.binary.Binary) or isinstance(dataset.datatype, datatypes.images.Image) or isinstance(dataset.datatype, datatypes.text.Html): - trans.response.set_content_type( dataset.get_mime() ) - return open( dataset.file_name ) + trans.response.set_content_type(dataset.get_mime()) + return open(dataset.file_name) else: # Get rating data. user_item_rating = 0 if trans.get_user(): - user_item_rating = self.get_user_item_rating( trans.sa_session, trans.get_user(), dataset ) + user_item_rating = self.get_user_item_rating(trans.sa_session, trans.get_user(), dataset) if user_item_rating: user_item_rating = user_item_rating.rating else: user_item_rating = 0 - ave_item_rating, num_ratings = self.get_ave_item_rating_data( trans.sa_session, dataset ) + ave_item_rating, num_ratings = self.get_ave_item_rating_data(trans.sa_session, dataset) - return trans.fill_template_mako( "/dataset/display.mako", item=dataset, item_data=dataset_data, - truncated=truncated, user_item_rating=user_item_rating, - ave_item_rating=ave_item_rating, num_ratings=num_ratings, - first_chunk=first_chunk ) + return trans.fill_template_mako("/dataset/display.mako", item=dataset, item_data=dataset_data, + truncated=truncated, user_item_rating=user_item_rating, + ave_item_rating=ave_item_rating, num_ratings=num_ratings, + first_chunk=first_chunk) else: raise web.httpexceptions.HTTPNotFound() @web.expose - def get_item_content_async( self, trans, id ): + def get_item_content_async(self, trans, id): """ Returns item content in HTML format. """ - decoded_id = self.decode_id( id ) - dataset = self.hda_manager.get_accessible( decoded_id, trans.user ) - dataset = self.hda_manager.error_if_uploading( dataset ) + decoded_id = self.decode_id(id) + dataset = self.hda_manager.get_accessible(decoded_id, trans.user) + dataset = self.hda_manager.error_if_uploading(dataset) if dataset is None: raise web.httpexceptions.HTTPNotFound() - truncated, dataset_data = self.hda_manager.text_data( dataset, preview=True ) + truncated, dataset_data = self.hda_manager.text_data(dataset, preview=True) # Get annotation. - dataset.annotation = self.get_item_annotation_str( trans.sa_session, trans.user, dataset ) - return trans.stream_template_mako( "/dataset/item_content.mako", item=dataset, item_data=dataset_data, truncated=truncated ) + dataset.annotation = self.get_item_annotation_str(trans.sa_session, trans.user, dataset) + return trans.stream_template_mako("/dataset/item_content.mako", item=dataset, item_data=dataset_data, truncated=truncated) @web.expose - def annotate_async( self, trans, id, new_annotation=None, **kwargs ): + def annotate_async(self, trans, id, new_annotation=None, **kwargs): # TODO:?? why is this an access check only? - decoded_id = self.decode_id( id ) - dataset = self.hda_manager.get_accessible( decoded_id, trans.user ) - dataset = self.hda_manager.error_if_uploading( dataset ) + decoded_id = self.decode_id(id) + dataset = self.hda_manager.get_accessible(decoded_id, trans.user) + dataset = self.hda_manager.error_if_uploading(dataset) if not dataset: web.httpexceptions.HTTPNotFound() if dataset and new_annotation: # Sanitize annotation before adding it. - new_annotation = sanitize_html( new_annotation, 'utf-8', 'text/html' ) - self.add_item_annotation( trans.sa_session, trans.get_user(), dataset, new_annotation ) + new_annotation = sanitize_html(new_annotation, 'utf-8', 'text/html') + self.add_item_annotation(trans.sa_session, trans.get_user(), dataset, new_annotation) trans.sa_session.flush() return new_annotation @web.expose - def get_annotation_async( self, trans, id ): - decoded_id = self.decode_id( id ) - dataset = self.hda_manager.get_accessible( decoded_id, trans.user ) - dataset = self.hda_manager.error_if_uploading( dataset ) + def get_annotation_async(self, trans, id): + decoded_id = self.decode_id(id) + dataset = self.hda_manager.get_accessible(decoded_id, trans.user) + dataset = self.hda_manager.error_if_uploading(dataset) if not dataset: web.httpexceptions.HTTPNotFound() - annotation = self.get_item_annotation_str( trans.sa_session, trans.user, dataset ) - if annotation and isinstance( annotation, text_type ): - annotation = annotation.encode( 'ascii', 'replace' ) # paste needs ascii here + annotation = self.get_item_annotation_str(trans.sa_session, trans.user, dataset) + if annotation and isinstance(annotation, text_type): + annotation = annotation.encode('ascii', 'replace') # paste needs ascii here return annotation @web.expose - def display_at( self, trans, dataset_id, filename=None, **kwd ): + def display_at(self, trans, dataset_id, filename=None, **kwd): """Sets up a dataset permissions so it is viewable at an external site""" if not trans.app.config.enable_old_display_applications: - return trans.show_error_message( "This method of accessing external display applications has been disabled by a Galaxy administrator." ) + return trans.show_error_message("This method of accessing external display applications has been disabled by a Galaxy administrator.") site = filename - data = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( dataset_id ) + data = trans.sa_session.query(trans.app.model.HistoryDatasetAssociation).get(dataset_id) if not data: - raise paste.httpexceptions.HTTPRequestRangeNotSatisfiable( "Invalid reference dataset id: %s." % str( dataset_id ) ) + raise paste.httpexceptions.HTTPRequestRangeNotSatisfiable("Invalid reference dataset id: %s." % str(dataset_id)) if 'display_url' not in kwd or 'redirect_url' not in kwd: - return trans.show_error_message( 'Invalid parameters specified for "display at" link, please contact a Galaxy administrator' ) + return trans.show_error_message('Invalid parameters specified for "display at" link, please contact a Galaxy administrator') try: - redirect_url = kwd['redirect_url'] % urllib.quote_plus( kwd['display_url'] ) + redirect_url = kwd['redirect_url'] % urllib.quote_plus(kwd['display_url']) except: redirect_url = kwd['redirect_url'] # not all will need custom text - if trans.app.security_agent.dataset_is_public( data.dataset ): - return trans.response.send_redirect( redirect_url ) # anon access already permitted by rbac - if self._can_access_dataset( trans, data ): - trans.app.host_security_agent.set_dataset_permissions( data, trans.user, site ) - return trans.response.send_redirect( redirect_url ) + if trans.app.security_agent.dataset_is_public(data.dataset): + return trans.response.send_redirect(redirect_url) # anon access already permitted by rbac + if self._can_access_dataset(trans, data): + trans.app.host_security_agent.set_dataset_permissions(data, trans.user, site) + return trans.response.send_redirect(redirect_url) else: - return trans.show_error_message( "You are not allowed to view this dataset at external sites. Please contact your Galaxy administrator to acquire management permissions for this dataset." ) + return trans.show_error_message("You are not allowed to view this dataset at external sites. Please contact your Galaxy administrator to acquire management permissions for this dataset.") @web.expose - def display_application( self, trans, dataset_id=None, user_id=None, app_name=None, link_name=None, app_action=None, action_param=None, action_param_extra=None, **kwds ): + def display_application(self, trans, dataset_id=None, user_id=None, app_name=None, link_name=None, app_action=None, action_param=None, action_param_extra=None, **kwds): """Access to external display applications""" # Build list of parameters to pass in to display application logic (app_kwds) app_kwds = {} for name, value in dict(kwds).iteritems(): # clone kwds because we remove stuff as we go. - if name.startswith( "app_" ): - app_kwds[ name[ len( "app_" ): ] ] = value - del kwds[ name ] + if name.startswith("app_"): + app_kwds[name[len("app_"):]] = value + del kwds[name] if kwds: - log.debug( "Unexpected Keywords passed to display_application: %s" % kwds ) # route memory? + log.debug("Unexpected Keywords passed to display_application: %s" % kwds) # route memory? # decode ids - data, user = decode_dataset_user( trans, dataset_id, user_id ) + data, user = decode_dataset_user(trans, dataset_id, user_id) if not data: - raise paste.httpexceptions.HTTPRequestRangeNotSatisfiable( "Invalid reference dataset id: %s." % str( dataset_id ) ) + raise paste.httpexceptions.HTTPRequestRangeNotSatisfiable("Invalid reference dataset id: %s." % str(dataset_id)) if user is None: user = trans.user if user: @@ -714,104 +845,104 @@ def display_application( self, trans, dataset_id=None, user_id=None, app_name=No else: user_roles = [] # Decode application name and link name - app_name = urllib.unquote_plus( app_name ) - link_name = urllib.unquote_plus( link_name ) - if None in [ app_name, link_name ]: - return trans.show_error_message( "A display application name and link name must be provided." ) - if self._can_access_dataset( trans, data, additional_roles=user_roles ): + app_name = urllib.unquote_plus(app_name) + link_name = urllib.unquote_plus(link_name) + if None in [app_name, link_name]: + return trans.show_error_message("A display application name and link name must be provided.") + if self._can_access_dataset(trans, data, additional_roles=user_roles): msg = [] preparable_steps = [] refresh = False - display_app = trans.app.datatypes_registry.display_applications.get( app_name ) + display_app = trans.app.datatypes_registry.display_applications.get(app_name) if not display_app: - log.debug( "Unknown display application has been requested: %s", app_name ) - return paste.httpexceptions.HTTPNotFound( "The requested display application (%s) is not available." % ( app_name ) ) - dataset_hash, user_hash = encode_dataset_user( trans, data, user ) + log.debug("Unknown display application has been requested: %s", app_name) + return paste.httpexceptions.HTTPNotFound("The requested display application (%s) is not available." % (app_name)) + dataset_hash, user_hash = encode_dataset_user(trans, data, user) try: - display_link = display_app.get_link( link_name, data, dataset_hash, user_hash, trans, app_kwds ) + display_link = display_app.get_link(link_name, data, dataset_hash, user_hash, trans, app_kwds) except Exception as e: - log.debug( "Error generating display_link: %s", e ) + log.debug("Error generating display_link: %s", e) # User can sometimes recover from, e.g. conversion errors by fixing input metadata, so use conflict - return paste.httpexceptions.HTTPConflict( "Error generating display_link: %s" % e ) + return paste.httpexceptions.HTTPConflict("Error generating display_link: %s" % e) if not display_link: - log.debug( "Unknown display link has been requested: %s", link_name ) - return paste.httpexceptions.HTTPNotFound( "Unknown display link has been requested: %s" % link_name ) + log.debug("Unknown display link has been requested: %s", link_name) + return paste.httpexceptions.HTTPNotFound("Unknown display link has been requested: %s" % link_name) if data.state == data.states.ERROR: - msg.append( ( 'This dataset is in an error state, you cannot view it at an external display application.', 'error' ) ) + msg.append(('This dataset is in an error state, you cannot view it at an external display application.', 'error')) elif data.deleted: - msg.append( ( 'This dataset has been deleted, you cannot view it at an external display application.', 'error' ) ) + msg.append(('This dataset has been deleted, you cannot view it at an external display application.', 'error')) elif data.state != data.states.OK: - msg.append( ( 'You must wait for this dataset to be created before you can view it at an external display application.', 'info' ) ) + msg.append(('You must wait for this dataset to be created before you can view it at an external display application.', 'info')) refresh = True else: # We have permissions, dataset is not deleted and is in OK state, allow access if display_link.display_ready(): - if app_action in [ 'data', 'param' ]: + if app_action in ['data', 'param']: assert action_param, "An action param must be provided for a data or param action" # data is used for things with filenames that could be passed off to a proxy # in case some display app wants all files to be in the same 'directory', # data can be forced to param, but not the other way (no filename for other direction) # get param name from url param name try: - action_param = display_link.get_param_name_by_url( action_param ) + action_param = display_link.get_param_name_by_url(action_param) except ValueError as e: - log.debug( e ) - return paste.httpexceptions.HTTPNotFound( str( e ) ) - value = display_link.get_param_value( action_param ) + log.debug(e) + return paste.httpexceptions.HTTPNotFound(str(e)) + value = display_link.get_param_value(action_param) assert value, "An invalid parameter name was provided: %s" % action_param assert value.parameter.viewable, "This parameter is not viewable." if value.parameter.type == 'data': try: if action_param_extra: - assert value.parameter.allow_extra_files_access, "Extra file content requested (%s), but allow_extra_files_access is False." % ( action_param_extra ) - file_name = os.path.join( value.extra_files_path, action_param_extra ) + assert value.parameter.allow_extra_files_access, "Extra file content requested (%s), but allow_extra_files_access is False." % (action_param_extra) + file_name = os.path.join(value.extra_files_path, action_param_extra) else: file_name = value.file_name - content_length = os.path.getsize( file_name ) - rval = open( file_name ) + content_length = os.path.getsize(file_name) + rval = open(file_name) except OSError as e: - log.debug( "Unable to access requested file in display application: %s", e ) - return paste.httpexceptions.HTTPNotFound( "This file is no longer available." ) + log.debug("Unable to access requested file in display application: %s", e) + return paste.httpexceptions.HTTPNotFound("This file is no longer available.") else: - rval = str( value ) - content_length = len( rval ) - trans.response.set_content_type( value.mime_type( action_param_extra=action_param_extra ) ) - trans.response.headers[ 'Content-Length' ] = content_length + rval = str(value) + content_length = len(rval) + trans.response.set_content_type(value.mime_type(action_param_extra=action_param_extra)) + trans.response.headers['Content-Length'] = content_length return rval elif app_action is None: # redirect user to url generated by display link # Fix for Safari caching display links, which can change if the underlying dataset has an attribute change, e.g. name, metadata, etc - trans.response.headers[ 'Cache-Control' ] = [ 'no-cache', 'max-age=0', 'no-store', 'must-revalidate' ] - return trans.response.send_redirect( display_link.display_url() ) + trans.response.headers['Cache-Control'] = ['no-cache', 'max-age=0', 'no-store', 'must-revalidate'] + return trans.response.send_redirect(display_link.display_url()) else: - msg.append( ( 'Invalid action provided: %s' % app_action, 'error' ) ) + msg.append(('Invalid action provided: %s' % app_action, 'error')) else: if app_action is None: if trans.history != data.history: - msg.append( ( 'You must import this dataset into your current history before you can view it at the desired display application.', 'error' ) ) + msg.append(('You must import this dataset into your current history before you can view it at the desired display application.', 'error')) else: refresh = True - msg.append( ( 'Launching this display application required additional datasets to be generated, you can view the status of these jobs below. ', 'info' ) ) + msg.append(('Launching this display application required additional datasets to be generated, you can view the status of these jobs below. ', 'info')) if not display_link.preparing_display(): display_link.prepare_display() preparable_steps = display_link.get_prepare_steps() else: - raise Exception( 'Attempted a view action (%s) on a non-ready display application' % app_action ) - return trans.fill_template_mako( "dataset/display_application/display.mako", - msg=msg, - display_app=display_app, - display_link=display_link, - refresh=refresh, - preparable_steps=preparable_steps ) - return trans.show_error_message( 'You do not have permission to view this dataset at an external display application.' ) - - def _delete( self, trans, dataset_id ): + raise Exception('Attempted a view action (%s) on a non-ready display application' % app_action) + return trans.fill_template_mako("dataset/display_application/display.mako", + msg=msg, + display_app=display_app, + display_link=display_link, + refresh=refresh, + preparable_steps=preparable_steps) + return trans.show_error_message('You do not have permission to view this dataset at an external display application.') + + def _delete(self, trans, dataset_id): message = None status = 'done' id = None try: - id = self.decode_id( dataset_id ) - hda = trans.sa_session.query( self.app.model.HistoryDatasetAssociation ).get( id ) + id = self.decode_id(dataset_id) + hda = trans.sa_session.query(self.app.model.HistoryDatasetAssociation).get(id) assert hda, 'Invalid HDA: %s' % id # Walk up parent datasets to find the containing history topmost_parent = hda @@ -821,25 +952,25 @@ def _delete( self, trans, dataset_id ): # Mark deleted and cleanup hda.mark_deleted() hda.clear_associated_files() - trans.log_event( "Dataset id %s marked as deleted" % str(id) ) - self.hda_manager.stop_creating_job( hda ) + trans.log_event("Dataset id %s marked as deleted" % str(id)) + self.hda_manager.stop_creating_job(hda) trans.sa_session.flush() except Exception as e: - msg = 'HDA deletion failed (encoded: %s, decoded: %s)' % ( dataset_id, id ) - log.exception( msg + ': ' + str( e ) ) - trans.log_event( msg ) + msg = 'HDA deletion failed (encoded: %s, decoded: %s)' % (dataset_id, id) + log.exception(msg + ': ' + str(e)) + trans.log_event(msg) message = 'Dataset deletion failed' status = 'error' - return ( message, status ) + return (message, status) - def _undelete( self, trans, dataset_id ): + def _undelete(self, trans, dataset_id): message = None status = 'done' id = None try: - id = self.decode_id( dataset_id ) + id = self.decode_id(dataset_id) history = trans.get_history() - hda = trans.sa_session.query( self.app.model.HistoryDatasetAssociation ).get( id ) + hda = trans.sa_session.query(self.app.model.HistoryDatasetAssociation).get(id) assert hda and hda.undeletable, 'Invalid HDA: %s' % id # Walk up parent datasets to find the containing history topmost_parent = hda @@ -849,22 +980,22 @@ def _undelete( self, trans, dataset_id ): # Mark undeleted hda.mark_undeleted() trans.sa_session.flush() - trans.log_event( "Dataset id %s has been undeleted" % str(id) ) + trans.log_event("Dataset id %s has been undeleted" % str(id)) except Exception: - msg = 'HDA undeletion failed (encoded: %s, decoded: %s)' % ( dataset_id, id ) - log.exception( msg ) - trans.log_event( msg ) + msg = 'HDA undeletion failed (encoded: %s, decoded: %s)' % (dataset_id, id) + log.exception(msg) + trans.log_event(msg) message = 'Dataset undeletion failed' status = 'error' - return ( message, status ) + return (message, status) - def _unhide( self, trans, dataset_id ): + def _unhide(self, trans, dataset_id): try: - id = self.decode_id( dataset_id ) + id = self.decode_id(dataset_id) except: return False history = trans.get_history() - hda = trans.sa_session.query( self.app.model.HistoryDatasetAssociation ).get( id ) + hda = trans.sa_session.query(self.app.model.HistoryDatasetAssociation).get(id) if hda: # Walk up parent datasets to find the containing history topmost_parent = hda @@ -874,17 +1005,17 @@ def _unhide( self, trans, dataset_id ): # Mark undeleted hda.mark_unhidden() trans.sa_session.flush() - trans.log_event( "Dataset id %s has been unhidden" % str(id) ) + trans.log_event("Dataset id %s has been unhidden" % str(id)) return True return False - def _purge( self, trans, dataset_id ): + def _purge(self, trans, dataset_id): message = None status = 'done' try: - id = self.decode_id( dataset_id ) + id = self.decode_id(dataset_id) user = trans.get_user() - hda = trans.sa_session.query( self.app.model.HistoryDatasetAssociation ).get( id ) + hda = trans.sa_session.query(self.app.model.HistoryDatasetAssociation).get(id) # Invalid HDA assert hda, 'Invalid history dataset ID' @@ -908,8 +1039,8 @@ def _purge( self, trans, dataset_id ): user.adjust_total_disk_usage(-hda.quota_amount(user)) # Mark purged hda.purged = True - trans.sa_session.add( hda ) - trans.log_event( "HDA id %s has been purged" % hda.id ) + trans.sa_session.add(hda) + trans.log_event("HDA id %s has been purged" % hda.id) trans.sa_session.flush() # Don't delete anything if there are active HDAs or any LDDAs, even if # the LDDAs are deleted. Let the cleanup scripts get it in the latter @@ -917,85 +1048,85 @@ def _purge( self, trans, dataset_id ): if hda.dataset.user_can_purge: try: hda.dataset.full_delete() - trans.log_event( "Dataset id %s has been purged upon the the purge of HDA id %s" % ( hda.dataset.id, hda.id ) ) - trans.sa_session.add( hda.dataset ) + trans.log_event("Dataset id %s has been purged upon the the purge of HDA id %s" % (hda.dataset.id, hda.id)) + trans.sa_session.add(hda.dataset) except: - log.exception( 'Unable to purge dataset (%s) on purge of HDA (%s):' % ( hda.dataset.id, hda.id ) ) + log.exception('Unable to purge dataset (%s) on purge of HDA (%s):' % (hda.dataset.id, hda.id)) trans.sa_session.flush() except Exception as exc: - msg = 'HDA purge failed (encoded: %s, decoded: %s): %s' % ( dataset_id, id, exc ) - log.exception( msg ) - trans.log_event( msg ) + msg = 'HDA purge failed (encoded: %s, decoded: %s): %s' % (dataset_id, id, exc) + log.exception(msg) + trans.log_event(msg) message = 'Dataset removal from disk failed' status = 'error' - return ( message, status ) + return (message, status) @web.expose - def delete( self, trans, dataset_id, filename, show_deleted_on_refresh=False ): - message, status = self._delete( trans, dataset_id ) - return trans.response.send_redirect( web.url_for( controller='root', action='history', show_deleted=show_deleted_on_refresh, message=message, status=status ) ) + def delete(self, trans, dataset_id, filename, show_deleted_on_refresh=False): + message, status = self._delete(trans, dataset_id) + return trans.response.send_redirect(web.url_for(controller='root', action='history', show_deleted=show_deleted_on_refresh, message=message, status=status)) @web.expose - def delete_async( self, trans, dataset_id, filename ): - message, status = self._delete( trans, dataset_id ) + def delete_async(self, trans, dataset_id, filename): + message, status = self._delete(trans, dataset_id) if status == 'done': return "OK" else: - raise Exception( message ) + raise Exception(message) @web.expose - def undelete( self, trans, dataset_id, filename ): - message, status = self._undelete( trans, dataset_id ) - return trans.response.send_redirect( web.url_for( controller='root', action='history', show_deleted=True, message=message, status=status ) ) + def undelete(self, trans, dataset_id, filename): + message, status = self._undelete(trans, dataset_id) + return trans.response.send_redirect(web.url_for(controller='root', action='history', show_deleted=True, message=message, status=status)) @web.expose - def undelete_async( self, trans, dataset_id, filename ): - message, status = self._undelete( trans, dataset_id ) + def undelete_async(self, trans, dataset_id, filename): + message, status = self._undelete(trans, dataset_id) if status == 'done': return "OK" else: - raise Exception( message ) + raise Exception(message) @web.expose - def unhide( self, trans, dataset_id, filename ): - if self._unhide( trans, dataset_id ): - return trans.response.send_redirect( web.url_for( controller='root', action='history', show_hidden=True ) ) - raise Exception( "Error unhiding" ) + def unhide(self, trans, dataset_id, filename): + if self._unhide(trans, dataset_id): + return trans.response.send_redirect(web.url_for(controller='root', action='history', show_hidden=True)) + raise Exception("Error unhiding") @web.expose - def purge( self, trans, dataset_id, filename, show_deleted_on_refresh=False ): + def purge(self, trans, dataset_id, filename, show_deleted_on_refresh=False): if trans.app.config.allow_user_dataset_purge: - message, status = self._purge( trans, dataset_id ) + message, status = self._purge(trans, dataset_id) else: message = "Removal of datasets by users is not allowed in this Galaxy instance. Please contact your Galaxy administrator." status = 'error' - return trans.response.send_redirect( web.url_for( controller='root', action='history', show_deleted=show_deleted_on_refresh, message=message, status=status ) ) + return trans.response.send_redirect(web.url_for(controller='root', action='history', show_deleted=show_deleted_on_refresh, message=message, status=status)) @web.expose - def purge_async( self, trans, dataset_id, filename ): + def purge_async(self, trans, dataset_id, filename): if trans.app.config.allow_user_dataset_purge: - message, status = self._purge( trans, dataset_id ) + message, status = self._purge(trans, dataset_id) else: message = "Removal of datasets by users is not allowed in this Galaxy instance. Please contact your Galaxy administrator." status = 'error' if status == 'done': return "OK" else: - raise Exception( message ) + raise Exception(message) @web.expose - def show_params( self, trans, dataset_id=None, from_noframe=None, **kwd ): + def show_params(self, trans, dataset_id=None, from_noframe=None, **kwd): """ Show the parameters used for the job associated with an HDA """ try: - hda = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( self.decode_id( dataset_id ) ) + hda = trans.sa_session.query(trans.app.model.HistoryDatasetAssociation).get(self.decode_id(dataset_id)) except ValueError: hda = None if not hda: - raise paste.httpexceptions.HTTPRequestRangeNotSatisfiable( "Invalid reference dataset id: %s." % escape( str( dataset_id ) ) ) - if not self._can_access_dataset( trans, hda ): - return trans.show_error_message( "You are not allowed to access this dataset" ) + raise paste.httpexceptions.HTTPRequestRangeNotSatisfiable("Invalid reference dataset id: %s." % escape(str(dataset_id))) + if not self._can_access_dataset(trans, hda): + return trans.show_error_message("You are not allowed to access this dataset") # Get the associated job, if any. If this hda was copied from another, # we need to find the job that created the origial dataset association. @@ -1016,50 +1147,50 @@ def show_params( self, trans, dataset_id=None, from_noframe=None, **kwd ): try: # Load the tool toolbox = self.get_toolbox() - tool = toolbox.get_tool( job.tool_id ) + tool = toolbox.get_tool(job.tool_id, job.tool_version) assert tool is not None, 'Requested tool has not been loaded.' # Load parameter objects, if a parameter type has changed, it's possible for the value to no longer be valid try: - params_objects = job.get_param_values( trans.app, ignore_errors=False ) + params_objects = job.get_param_values(trans.app, ignore_errors=False) except: - params_objects = job.get_param_values( trans.app, ignore_errors=True ) + params_objects = job.get_param_values(trans.app, ignore_errors=True) # use different param_objects in the following line, since we want to display original values as much as possible - upgrade_messages = tool.check_and_update_param_values( job.get_param_values( trans.app, ignore_errors=True ), - trans, - update_values=False ) + upgrade_messages = tool.check_and_update_param_values(job.get_param_values(trans.app, ignore_errors=True), + trans, + update_values=False) has_parameter_errors = True except: pass if job is None: - return trans.show_error_message( "Job information is not available for this dataset." ) + return trans.show_error_message("Job information is not available for this dataset.") # TODO: we should provide the basic values along with the objects, in order to better handle reporting of old values during upgrade - return trans.fill_template( "show_params.mako", - inherit_chain=inherit_chain, - history=trans.get_history(), - hda=hda, - job=job, - tool=tool, - params_objects=params_objects, - upgrade_messages=upgrade_messages, - has_parameter_errors=has_parameter_errors ) + return trans.fill_template("show_params.mako", + inherit_chain=inherit_chain, + history=trans.get_history(), + hda=hda, + job=job, + tool=tool, + params_objects=params_objects, + upgrade_messages=upgrade_messages, + has_parameter_errors=has_parameter_errors) @web.expose - def copy_datasets( self, trans, source_history=None, source_content_ids="", target_history_id=None, target_history_ids="", new_history_name="", do_copy=False, **kwd ): + def copy_datasets(self, trans, source_history=None, source_content_ids="", target_history_id=None, target_history_ids="", new_history_name="", do_copy=False, **kwd): user = trans.get_user() if source_history is not None: - decoded_source_history_id = self.decode_id( source_history ) - history = self.history_manager.get_owned( decoded_source_history_id, trans.user, current_history=trans.history ) + decoded_source_history_id = self.decode_id(source_history) + history = self.history_manager.get_owned(decoded_source_history_id, trans.user, current_history=trans.history) current_history = trans.get_history() else: history = current_history = trans.get_history() refresh_frames = [] if source_content_ids: - if not isinstance( source_content_ids, list ): + if not isinstance(source_content_ids, list): source_content_ids = source_content_ids.split(",") - encoded_dataset_collection_ids = [ s[ len("dataset_collection|"): ] for s in source_content_ids if s.startswith("dataset_collection|") ] - encoded_dataset_ids = [ s[ len("dataset|"): ] for s in source_content_ids if s.startswith("dataset|") ] - decoded_dataset_collection_ids = set(map( self.decode_id, encoded_dataset_collection_ids )) - decoded_dataset_ids = set(map( self.decode_id, encoded_dataset_ids )) + encoded_dataset_collection_ids = [s[len("dataset_collection|"):] for s in source_content_ids if s.startswith("dataset_collection|")] + encoded_dataset_ids = [s[len("dataset|"):] for s in source_content_ids if s.startswith("dataset|")] + decoded_dataset_collection_ids = set(map(self.decode_id, encoded_dataset_collection_ids)) + decoded_dataset_ids = set(map(self.decode_id, encoded_dataset_ids)) else: decoded_dataset_collection_ids = [] decoded_dataset_ids = [] @@ -1067,35 +1198,35 @@ def copy_datasets( self, trans, source_history=None, source_content_ids="", targ target_history_ids = [] else: if target_history_id: - target_history_ids = [ self.decode_id(target_history_id) ] + target_history_ids = [self.decode_id(target_history_id)] elif target_history_ids: - if not isinstance( target_history_ids, list ): + if not isinstance(target_history_ids, list): target_history_ids = target_history_ids.split(",") - target_history_ids = list(set([ self.decode_id(h) for h in target_history_ids if h ])) + target_history_ids = list(set([self.decode_id(h) for h in target_history_ids if h])) else: target_history_ids = [] done_msg = error_msg = "" new_history = None if do_copy: invalid_contents = 0 - if not ( decoded_dataset_ids or decoded_dataset_collection_ids ) or not ( target_history_ids or new_history_name ): + if not (decoded_dataset_ids or decoded_dataset_collection_ids) or not (target_history_ids or new_history_name): error_msg = "You must provide both source datasets and target histories. " else: if new_history_name: new_history = trans.app.model.History() new_history.name = new_history_name new_history.user = user - trans.sa_session.add( new_history ) + trans.sa_session.add(new_history) trans.sa_session.flush() - target_history_ids.append( new_history.id ) + target_history_ids.append(new_history.id) if user: - target_histories = [ hist for hist in map( trans.sa_session.query( trans.app.model.History ).get, target_history_ids ) if hist is not None and hist.user == user ] + target_histories = [hist for hist in map(trans.sa_session.query(trans.app.model.History).get, target_history_ids) if hist is not None and hist.user == user] else: - target_histories = [ history ] - if len( target_histories ) != len( target_history_ids ): - error_msg = error_msg + "You do not have permission to add datasets to %i requested histories. " % ( len( target_history_ids ) - len( target_histories ) ) - source_contents = map( trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get, decoded_dataset_ids ) - source_contents.extend( map( trans.sa_session.query( trans.app.model.HistoryDatasetCollectionAssociation ).get, decoded_dataset_collection_ids ) ) + target_histories = [history] + if len(target_histories) != len(target_history_ids): + error_msg = error_msg + "You do not have permission to add datasets to %i requested histories. " % (len(target_history_ids) - len(target_histories)) + source_contents = map(trans.sa_session.query(trans.app.model.HistoryDatasetAssociation).get, decoded_dataset_ids) + source_contents.extend(map(trans.sa_session.query(trans.app.model.HistoryDatasetCollectionAssociation).get, decoded_dataset_collection_ids)) source_contents.sort(key=lambda content: content.hid) for content in source_contents: if content is None: @@ -1107,42 +1238,42 @@ def copy_datasets( self, trans, source_history=None, source_content_ids="", targ else: for hist in target_histories: if content.history_content_type == "dataset": - hist.add_dataset( content.copy( copy_children=True ) ) + hist.add_dataset(content.copy(copy_children=True)) else: copy_collected_datasets = True copy_kwds = {} if copy_collected_datasets: copy_kwds["element_destination"] = hist - hist.add_dataset_collection( content.copy( **copy_kwds ) ) + hist.add_dataset_collection(content.copy(**copy_kwds)) if current_history in target_histories: refresh_frames = ['history'] trans.sa_session.flush() - hist_names_str = ", ".join( ['%s' % - ( url_for( controller="history", action="switch_to_history", - hist_id=trans.security.encode_id( hist.id ) ), escape(hist.name) ) - for hist in target_histories ] ) - num_source = len( source_content_ids ) - invalid_contents + hist_names_str = ", ".join(['%s' % + (url_for(controller="history", action="switch_to_history", + hist_id=trans.security.encode_id(hist.id)), escape(hist.name)) + for hist in target_histories]) + num_source = len(source_content_ids) - invalid_contents num_target = len(target_histories) - done_msg = "%i %s copied to %i %s: %s." % (num_source, inflector.cond_plural(num_source, "dataset"), num_target, inflector.cond_plural(num_target, "history"), hist_names_str ) - trans.sa_session.refresh( history ) + done_msg = "%i %s copied to %i %s: %s." % (num_source, inflector.cond_plural(num_source, "dataset"), num_target, inflector.cond_plural(num_target, "history"), hist_names_str) + trans.sa_session.refresh(history) source_contents = history.active_contents target_histories = [history] if user: target_histories = user.active_histories - return trans.fill_template( "/dataset/copy_view.mako", - source_history=history, - current_history=current_history, - source_content_ids=source_content_ids, - target_history_id=target_history_id, - target_history_ids=target_history_ids, - source_contents=source_contents, - target_histories=target_histories, - new_history_name=new_history_name, - done_msg=done_msg, - error_msg=error_msg, - refresh_frames=refresh_frames ) - - def _copy_datasets( self, trans, dataset_ids, target_histories, imported=False ): + return trans.fill_template("/dataset/copy_view.mako", + source_history=history, + current_history=current_history, + source_content_ids=source_content_ids, + target_history_id=target_history_id, + target_history_ids=target_history_ids, + source_contents=source_contents, + target_histories=target_histories, + new_history_name=new_history_name, + done_msg=done_msg, + error_msg=error_msg, + refresh_frames=refresh_frames) + + def _copy_datasets(self, trans, dataset_ids, target_histories, imported=False): """ Helper method for copying datasets. """ user = trans.get_user() done_msg = error_msg = "" @@ -1154,26 +1285,26 @@ def _copy_datasets( self, trans, dataset_ids, target_histories, imported=False ) # User must own target histories to copy datasets to them. for history in target_histories: if user != history.user: - error_msg = error_msg + "You do not have permission to add datasets to %i requested histories. " % ( len( target_histories ) ) + error_msg = error_msg + "You do not have permission to add datasets to %i requested histories. " % (len(target_histories)) for dataset_id in dataset_ids: - decoded_id = self.decode_id( dataset_id ) - data = self.hda_manager.get_accessible( decoded_id, trans.user ) - data = self.hda_manager.error_if_uploading( data ) + decoded_id = self.decode_id(dataset_id) + data = self.hda_manager.get_accessible(decoded_id, trans.user) + data = self.hda_manager.error_if_uploading(data) if data is None: error_msg = error_msg + "You tried to copy a dataset that does not exist or that you do not have access to. " invalid_datasets += 1 else: for hist in target_histories: - dataset_copy = data.copy( copy_children=True ) + dataset_copy = data.copy(copy_children=True) if imported: dataset_copy.name = "imported: " + dataset_copy.name - hist.add_dataset( dataset_copy ) + hist.add_dataset(dataset_copy) trans.sa_session.flush() - num_datasets_copied = len( dataset_ids ) - invalid_datasets + num_datasets_copied = len(dataset_ids) - invalid_datasets done_msg = "%i dataset%s copied to %i histor%s." % \ - ( num_datasets_copied, iff( num_datasets_copied == 1, "", "s"), len( target_histories ), iff( len( target_histories ) == 1, "y", "ies") ) - trans.sa_session.refresh( history ) + (num_datasets_copied, iff(num_datasets_copied == 1, "", "s"), len(target_histories), iff(len(target_histories) == 1, "y", "ies")) + trans.sa_session.refresh(history) if error_msg != "": status = ERROR diff --git a/lib/galaxy/webapps/galaxy/controllers/error.py b/lib/galaxy/webapps/galaxy/controllers/error.py index e0e8099f1a29..25dd69d1896e 100644 --- a/lib/galaxy/webapps/galaxy/controllers/error.py +++ b/lib/galaxy/webapps/galaxy/controllers/error.py @@ -1,7 +1,7 @@ from galaxy.web.base.controller import BaseUIController, web -class Error( BaseUIController ): +class Error(BaseUIController): @web.expose - def index( self, trans ): - raise Exception( "Fake error" ) + def index(self, trans): + raise Exception("Fake error") diff --git a/lib/galaxy/webapps/galaxy/controllers/external_service.py b/lib/galaxy/webapps/galaxy/controllers/external_service.py index dbf1b7faf6da..bb78257241d4 100644 --- a/lib/galaxy/webapps/galaxy/controllers/external_service.py +++ b/lib/galaxy/webapps/galaxy/controllers/external_service.py @@ -9,19 +9,19 @@ from galaxy.web.framework.helpers import time_ago, iff, grids from .requests_common import invalid_id_redirect -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -class ExternalServiceGrid( grids.Grid ): +class ExternalServiceGrid(grids.Grid): # Custom column types - class NameColumn( grids.TextColumn ): + class NameColumn(grids.TextColumn): def get_value(self, trans, grid, external_service): return escape(external_service.name) - class ExternalServiceTypeColumn( grids.TextColumn ): + class ExternalServiceTypeColumn(grids.TextColumn): def get_value(self, trans, grid, external_service): try: - return trans.app.external_service_types.all_external_service_types[ external_service.external_service_type_id ].name + return trans.app.external_service_types.all_external_service_types[external_service.external_service_type_id].name except KeyError: return 'Error in loading external_service type: %s' % external_service.external_service_type_id @@ -33,337 +33,337 @@ def get_value(self, trans, grid, external_service): num_rows_per_page = 50 preserve_state = True use_paging = True - default_filter = dict( deleted="False" ) + default_filter = dict(deleted="False") columns = [ - NameColumn( "Name", - key="name", - link=( lambda item: iff( item.deleted, None, dict( operation="view", id=item.id ) ) ), - attach_popup=True, - filterable="advanced" ), - grids.TextColumn( "Description", - key='description', - filterable="advanced" ), - ExternalServiceTypeColumn( "External Service Type" ), - grids.GridColumn( "Last Updated", - key="update_time", - format=time_ago ), - grids.DeletedColumn( "Deleted", - key="deleted", - visible=False, - filterable="advanced" ) + NameColumn("Name", + key="name", + link=(lambda item: iff(item.deleted, None, dict(operation="view", id=item.id))), + attach_popup=True, + filterable="advanced"), + grids.TextColumn("Description", + key='description', + filterable="advanced"), + ExternalServiceTypeColumn("External Service Type"), + grids.GridColumn("Last Updated", + key="update_time", + format=time_ago), + grids.DeletedColumn("Deleted", + key="deleted", + visible=False, + filterable="advanced") ] - columns.append( grids.MulticolFilterColumn( "Search", - cols_to_filter=[ columns[0], columns[1] ], - key="free-text-search", - visible=False, - filterable="standard" ) ) + columns.append(grids.MulticolFilterColumn("Search", + cols_to_filter=[columns[0], columns[1]], + key="free-text-search", + visible=False, + filterable="standard")) operations = [ - grids.GridOperation( "Edit", allow_multiple=False, condition=( lambda item: not item.deleted ) ), - grids.GridOperation( "Delete", allow_multiple=True, condition=( lambda item: not item.deleted ) ), - grids.GridOperation( "Undelete", condition=( lambda item: item.deleted ) ), + grids.GridOperation("Edit", allow_multiple=False, condition=(lambda item: not item.deleted)), + grids.GridOperation("Delete", allow_multiple=True, condition=(lambda item: not item.deleted)), + grids.GridOperation("Undelete", condition=(lambda item: item.deleted)), ] global_actions = [ - grids.GridAction( "Reload external service types", dict( controller='external_service', action='reload_external_service_types' ) ), - grids.GridAction( "Create new external service", dict( controller='external_service', action='create_external_service' ) ) + grids.GridAction("Reload external service types", dict(controller='external_service', action='reload_external_service_types')), + grids.GridAction("Create new external service", dict(controller='external_service', action='create_external_service')) ] -class ExternalService( BaseUIController, UsesFormDefinitionsMixin ): +class ExternalService(BaseUIController, UsesFormDefinitionsMixin): external_service_grid = ExternalServiceGrid() @web.expose @web.require_admin - def browse_external_services( self, trans, **kwd ): + def browse_external_services(self, trans, **kwd): if 'operation' in kwd: operation = kwd['operation'].lower() if operation == "view": - return self.view_external_service( trans, **kwd ) + return self.view_external_service(trans, **kwd) elif operation == "edit": - return self.edit_external_service( trans, **kwd ) + return self.edit_external_service(trans, **kwd) elif operation == "delete": - return self.delete_external_service( trans, **kwd ) + return self.delete_external_service(trans, **kwd) elif operation == "undelete": - return self.undelete_external_service( trans, **kwd ) + return self.undelete_external_service(trans, **kwd) # Render the grid view - return self.external_service_grid( trans, **kwd ) + return self.external_service_grid(trans, **kwd) @web.expose @web.require_admin - def create_external_service( self, trans, **kwd ): - params = util.Params( kwd ) - message = util.restore_text( params.get( 'message', '' ) ) - status = params.get( 'status', 'done' ) - external_service_type_id = params.get( 'external_service_type_id', 'none' ) - widgets = self.__build_external_service_widgets( trans, external_service=None, **kwd ) + def create_external_service(self, trans, **kwd): + params = util.Params(kwd) + message = util.restore_text(params.get('message', '')) + status = params.get('status', 'done') + external_service_type_id = params.get('external_service_type_id', 'none') + widgets = self.__build_external_service_widgets(trans, external_service=None, **kwd) external_service_type = None error = False if not trans.app.external_service_types.visible_external_service_types: error = True message = 'There are no visible external_service types in the external_service types config file' - elif params.get( 'create_external_service_button', False ): + elif params.get('create_external_service_button', False): if external_service_type_id == 'none': error = True message = 'Provide an external_service_type_id to create a new external service.' else: - self.__save_external_service( trans, **kwd ) + self.__save_external_service(trans, **kwd) message = 'The external_service has been created.' - return trans.response.send_redirect( web.url_for( controller='external_service', - action='browse_external_services', - message=message, - status=status ) ) + return trans.response.send_redirect(web.url_for(controller='external_service', + action='browse_external_services', + message=message, + status=status)) elif external_service_type_id != 'none': # Form submission via refresh_on_change - trans.app.external_service_types.reload( external_service_type_id ) - external_service_type = self.get_external_service_type( trans, external_service_type_id ) - widgets.extend( external_service_type.form_definition.get_widgets( trans.user, **kwd ) ) + trans.app.external_service_types.reload(external_service_type_id) + external_service_type = self.get_external_service_type(trans, external_service_type_id) + widgets.extend(external_service_type.form_definition.get_widgets(trans.user, **kwd)) if error: - return trans.response.send_redirect( web.url_for( controller='external_service', - action='browse_external_services', - message=message, - status='error' ) ) - return trans.fill_template( '/admin/external_service/create_external_service.mako', - widgets=widgets, - message=message, - status=status, - external_service_type=external_service_type ) + return trans.response.send_redirect(web.url_for(controller='external_service', + action='browse_external_services', + message=message, + status='error')) + return trans.fill_template('/admin/external_service/create_external_service.mako', + widgets=widgets, + message=message, + status=status, + external_service_type=external_service_type) @web.expose @web.require_admin - def view_external_service( self, trans, **kwd ): - external_service_id = kwd.get( 'id', None ) + def view_external_service(self, trans, **kwd): + external_service_id = kwd.get('id', None) try: - external_service = trans.sa_session.query( trans.model.ExternalService ).get( trans.security.decode_id( external_service_id ) ) + external_service = trans.sa_session.query(trans.model.ExternalService).get(trans.security.decode_id(external_service_id)) except: - return invalid_id_redirect( trans, 'external_service', external_service_id, 'external_service', action='browse_external_services' ) - external_service_type = self.get_external_service_type( trans, external_service.external_service_type_id ) - return trans.fill_template( '/admin/external_service/view_external_service.mako', - external_service=external_service, - external_service_type=external_service_type ) + return invalid_id_redirect(trans, 'external_service', external_service_id, 'external_service', action='browse_external_services') + external_service_type = self.get_external_service_type(trans, external_service.external_service_type_id) + return trans.fill_template('/admin/external_service/view_external_service.mako', + external_service=external_service, + external_service_type=external_service_type) @web.expose @web.require_admin - def edit_external_service( self, trans, **kwd ): - params = util.Params( kwd ) - message = util.restore_text( params.get( 'message', '' ) ) - status = params.get( 'status', 'done' ) - external_service_id = params.get( 'id', None ) + def edit_external_service(self, trans, **kwd): + params = util.Params(kwd) + message = util.restore_text(params.get('message', '')) + status = params.get('status', 'done') + external_service_id = params.get('id', None) try: - external_service = trans.sa_session.query( trans.model.ExternalService ).get( trans.security.decode_id( external_service_id ) ) + external_service = trans.sa_session.query(trans.model.ExternalService).get(trans.security.decode_id(external_service_id)) except: - return invalid_id_redirect( trans, 'external_service', external_service_id, 'external_service', action='browse_external_services' ) - if params.get( 'edit_external_service_button', False ): - external_service = self.__save_external_service( trans, **kwd ) - trans.sa_session.refresh( external_service ) + return invalid_id_redirect(trans, 'external_service', external_service_id, 'external_service', action='browse_external_services') + if params.get('edit_external_service_button', False): + external_service = self.__save_external_service(trans, **kwd) + trans.sa_session.refresh(external_service) message = 'Changes made to external service (%s) have been saved' % external_service.name - widgets = self.__build_external_service_widgets( trans, external_service, **kwd ) - widgets.extend( external_service.form_definition.get_widgets( trans.user, external_service.form_values.content, **kwd ) ) - external_service_type = self.get_external_service_type( trans, external_service.external_service_type_id ) - return trans.fill_template( '/admin/external_service/edit_external_service.mako', - external_service=external_service, - widgets=widgets, - message=message, - status=status, - external_service_type=external_service_type ) + widgets = self.__build_external_service_widgets(trans, external_service, **kwd) + widgets.extend(external_service.form_definition.get_widgets(trans.user, external_service.form_values.content, **kwd)) + external_service_type = self.get_external_service_type(trans, external_service.external_service_type_id) + return trans.fill_template('/admin/external_service/edit_external_service.mako', + external_service=external_service, + widgets=widgets, + message=message, + status=status, + external_service_type=external_service_type) - def __save_external_service( self, trans, **kwd ): + def __save_external_service(self, trans, **kwd): # Here we save a newly created external_service or save changed # attributes of an existing external_service. - params = util.Params( kwd ) - external_service_id = params.get( 'id', None ) - name = util.restore_text( params.get( 'external_service_name', '' ) ) - description = util.restore_text( params.get( 'external_service_description', '' ) ) - version = util.restore_text( params.get( 'external_service_version', '' ) ) - external_service_type_id = params.get( 'external_service_type_id', '' ) + params = util.Params(kwd) + external_service_id = params.get('id', None) + name = util.restore_text(params.get('external_service_name', '')) + description = util.restore_text(params.get('external_service_description', '')) + version = util.restore_text(params.get('external_service_version', '')) + external_service_type_id = params.get('external_service_type_id', '') if external_service_id: # We're saving changed attributes of an existing external_service. - external_service = trans.sa_session.query( trans.model.ExternalService ).get( trans.security.decode_id( external_service_id ) ) + external_service = trans.sa_session.query(trans.model.ExternalService).get(trans.security.decode_id(external_service_id)) external_service.name = name external_service.description = description external_service.version = version - external_service.form_values.content = self.get_form_values( trans, trans.user, external_service.form_definition, **kwd ) - trans.sa_session.add( external_service ) - trans.sa_session.add( external_service.form_values ) + external_service.form_values.content = self.get_form_values(trans, trans.user, external_service.form_definition, **kwd) + trans.sa_session.add(external_service) + trans.sa_session.add(external_service.form_values) trans.sa_session.flush() else: # We're saving a newly created external_service - external_service_type = self.get_external_service_type( trans, external_service_type_id ) - external_service = trans.model.ExternalService( name, description, external_service_type_id, version ) + external_service_type = self.get_external_service_type(trans, external_service_type_id) + external_service = trans.model.ExternalService(name, description, external_service_type_id, version) external_service.form_definition = external_service_type.form_definition # Get the form values from kwd, some of which may be different than the defaults in the external service # type config because the user could have overwritten them. - values = self.get_form_values( trans, trans.user, external_service.form_definition, **kwd ) - external_service.form_values = trans.model.FormValues( external_service.form_definition, values ) - trans.sa_session.add( external_service ) - trans.sa_session.add( external_service.form_definition ) - trans.sa_session.add( external_service.form_values ) + values = self.get_form_values(trans, trans.user, external_service.form_definition, **kwd) + external_service.form_values = trans.model.FormValues(external_service.form_definition, values) + trans.sa_session.add(external_service) + trans.sa_session.add(external_service.form_definition) + trans.sa_session.add(external_service.form_values) trans.sa_session.flush() return external_service @web.expose @web.require_admin - def edit_external_service_form_definition( self, trans, **kwd ): - util.Params( kwd ) - external_service_id = kwd.get( 'id', None ) + def edit_external_service_form_definition(self, trans, **kwd): + util.Params(kwd) + external_service_id = kwd.get('id', None) try: - external_service = trans.sa_session.query( trans.model.ExternalService ).get( trans.security.decode_id( external_service_id ) ) + external_service = trans.sa_session.query(trans.model.ExternalService).get(trans.security.decode_id(external_service_id)) except: - return invalid_id_redirect( trans, 'external_service', external_service_id, 'external_service', action='browse_external_services' ) - vars = dict( id=trans.security.encode_id( external_service.form_definition.form_definition_current_id ), - response_redirect=web.url_for( controller='external_service', - action='update_external_service_form_definition', - **kwd ) ) - return trans.response.send_redirect( web.url_for( controller='forms', action='edit_form_definition', **vars ) ) + return invalid_id_redirect(trans, 'external_service', external_service_id, 'external_service', action='browse_external_services') + vars = dict(id=trans.security.encode_id(external_service.form_definition.form_definition_current_id), + response_redirect=web.url_for(controller='external_service', + action='update_external_service_form_definition', + **kwd)) + return trans.response.send_redirect(web.url_for(controller='forms', action='edit_form_definition', **vars)) @web.expose @web.require_admin - def update_external_service_form_definition( self, trans, **kwd ): - util.Params( kwd ) - external_service_id = kwd.get( 'id', None ) + def update_external_service_form_definition(self, trans, **kwd): + util.Params(kwd) + external_service_id = kwd.get('id', None) try: - external_service = trans.sa_session.query( trans.model.ExternalService ).get( trans.security.decode_id( external_service_id ) ) + external_service = trans.sa_session.query(trans.model.ExternalService).get(trans.security.decode_id(external_service_id)) except: - return invalid_id_redirect( trans, 'external_service', external_service_id, 'external_service', action='browse_external_services' ) + return invalid_id_redirect(trans, 'external_service', external_service_id, 'external_service', action='browse_external_services') external_service.form_definition = external_service.form_definition.current.latest_form - trans.sa_session.add( external_service ) + trans.sa_session.add(external_service) trans.sa_session.flush() message = "The form definition for the '%s' external service has been updated with your changes." % external_service.name - return trans.response.send_redirect( web.url_for( controller='external_service', - action='edit_external_service', - message=message, - status='done', - **kwd ) ) + return trans.response.send_redirect(web.url_for(controller='external_service', + action='edit_external_service', + message=message, + status='done', + **kwd)) @web.expose @web.require_admin - def delete_external_service( self, trans, **kwd ): - external_service_id = kwd.get( 'id', '' ) - external_service_id_list = util.listify( external_service_id ) + def delete_external_service(self, trans, **kwd): + external_service_id = kwd.get('id', '') + external_service_id_list = util.listify(external_service_id) for external_service_id in external_service_id_list: try: - external_service = trans.sa_session.query( trans.model.ExternalService ).get( trans.security.decode_id( external_service_id ) ) + external_service = trans.sa_session.query(trans.model.ExternalService).get(trans.security.decode_id(external_service_id)) except: - return invalid_id_redirect( trans, 'external_service', external_service_id, 'external_service', action='browse_external_services' ) + return invalid_id_redirect(trans, 'external_service', external_service_id, 'external_service', action='browse_external_services') external_service.deleted = True - trans.sa_session.add( external_service ) + trans.sa_session.add(external_service) trans.sa_session.flush() - message = '%i external services has been deleted' % len( external_service_id_list ) - return trans.response.send_redirect( web.url_for( controller='external_service', - action='browse_external_services', - message=message, - status='done' ) ) + message = '%i external services has been deleted' % len(external_service_id_list) + return trans.response.send_redirect(web.url_for(controller='external_service', + action='browse_external_services', + message=message, + status='done')) @web.expose @web.require_admin - def undelete_external_service( self, trans, **kwd ): - external_service_id = kwd.get( 'id', '' ) - external_service_id_list = util.listify( external_service_id ) + def undelete_external_service(self, trans, **kwd): + external_service_id = kwd.get('id', '') + external_service_id_list = util.listify(external_service_id) for external_service_id in external_service_id_list: try: - external_service = trans.sa_session.query( trans.model.ExternalService ).get( trans.security.decode_id( external_service_id ) ) + external_service = trans.sa_session.query(trans.model.ExternalService).get(trans.security.decode_id(external_service_id)) except: - return invalid_id_redirect( trans, 'external_service', external_service_id, 'external_service', action='browse_external_services' ) + return invalid_id_redirect(trans, 'external_service', external_service_id, 'external_service', action='browse_external_services') external_service.deleted = False - trans.sa_session.add( external_service ) + trans.sa_session.add(external_service) trans.sa_session.flush() status = 'done' - message = '%i external services have been undeleted' % len( external_service_id_list ) - return trans.response.send_redirect( web.url_for( controller='external_service', - action='browse_external_services', - message=message, - status=status ) ) + message = '%i external services have been undeleted' % len(external_service_id_list) + return trans.response.send_redirect(web.url_for(controller='external_service', + action='browse_external_services', + message=message, + status=status)) @web.expose @web.require_admin - def reload_external_service_types( self, trans, **kwd ): - params = util.Params( kwd ) - message = util.restore_text( params.get( 'message', '' ) ) - status = params.get( 'status', 'done' ) - external_service_type_id = params.get( 'external_service_type_id', 'none' ) - if params.get( 'reload_external_service_type_button', False ): - new_external_service_type = trans.app.external_service_types.reload( external_service_type_id ) + def reload_external_service_types(self, trans, **kwd): + params = util.Params(kwd) + message = util.restore_text(params.get('message', '')) + status = params.get('status', 'done') + external_service_type_id = params.get('external_service_type_id', 'none') + if params.get('reload_external_service_type_button', False): + new_external_service_type = trans.app.external_service_types.reload(external_service_type_id) status = 'done' message = 'Reloaded external service type: %s' % new_external_service_type.name - external_service_type_select_field = self.__build_external_service_type_select_field( trans, - external_service_type_id, - refresh_on_change=False, - visible_external_service_types_only=False ) + external_service_type_select_field = self.__build_external_service_type_select_field(trans, + external_service_type_id, + refresh_on_change=False, + visible_external_service_types_only=False) if not trans.app.external_service_types.visible_external_service_types: message = 'There are no visible external service types in the external service types config file.' status = 'error' - return trans.response.send_redirect( web.url_for( controller='external_service', - action='browse_external_services', - message=message, - status=status ) ) - return trans.fill_template( '/admin/external_service/reload_external_service_types.mako', - external_service_type_select_field=external_service_type_select_field, - message=message, - status=status ) + return trans.response.send_redirect(web.url_for(controller='external_service', + action='browse_external_services', + message=message, + status=status)) + return trans.fill_template('/admin/external_service/reload_external_service_types.mako', + external_service_type_select_field=external_service_type_select_field, + message=message, + status=status) def get_external_service_type(self, trans, external_service_type_id, action='browse_external_services'): try: - return trans.app.external_service_types.all_external_service_types[ external_service_type_id ] + return trans.app.external_service_types.all_external_service_types[external_service_type_id] except KeyError: message = 'Error in loading external service type: %s' % external_service_type_id - return trans.response.send_redirect( web.url_for( controller='external_service', - action=action, - message=message, - status='error' ) ) + return trans.response.send_redirect(web.url_for(controller='external_service', + action=action, + message=message, + status='error')) # ===== Methods for building SelectFields used on various admin_requests forms - def __build_external_service_widgets( self, trans, external_service=None, **kwd ): - params = util.Params( kwd ) + def __build_external_service_widgets(self, trans, external_service=None, **kwd): + params = util.Params(kwd) if external_service: name = external_service.name description = external_service.description version = external_service.version seq_type = external_service.external_service_type_id else: - name = util.restore_text( params.get( 'external_service_name', '' ) ) - description = util.restore_text( params.get( 'external_service_description', '' ) ) - version = util.restore_text( params.get( 'external_service_version', '' ) ) - selected_seq_type = params.get( 'external_service_type_id', '' ) + name = util.restore_text(params.get('external_service_name', '')) + description = util.restore_text(params.get('external_service_description', '')) + version = util.restore_text(params.get('external_service_version', '')) + selected_seq_type = params.get('external_service_type_id', '') if selected_seq_type in trans.app.external_service_types.all_external_service_types: - seq_type = trans.app.external_service_types.all_external_service_types[ selected_seq_type ].id + seq_type = trans.app.external_service_types.all_external_service_types[selected_seq_type].id else: seq_type = 'none' - widgets = [ dict( label='Name', - widget=TextField( 'external_service_name', 40, name ), - helptext='' ), - dict( label='Description', - widget=TextField( 'external_service_description', 40, description ), - helptext='' ), - dict( label='Version', - widget=TextField( 'external_service_version', 40, version ), - helptext='' ) ] + widgets = [dict(label='Name', + widget=TextField('external_service_name', 40, name), + helptext=''), + dict(label='Description', + widget=TextField('external_service_description', 40, description), + helptext=''), + dict(label='Version', + widget=TextField('external_service_version', 40, version), + helptext='')] # Do not show the external_service_type selectfield when editing a external_service if not external_service: - widgets.append( dict( label='External service type', - widget=self.__build_external_service_type_select_field( trans, seq_type, visible_external_service_types_only=True ), - helptext='') ) + widgets.append(dict(label='External service type', + widget=self.__build_external_service_type_select_field(trans, seq_type, visible_external_service_types_only=True), + helptext='')) return widgets - def __build_external_service_type_select_field( self, trans, selected_value, refresh_on_change=True, visible_external_service_types_only=False ): + def __build_external_service_type_select_field(self, trans, selected_value, refresh_on_change=True, visible_external_service_types_only=False): external_service_types = trans.app.external_service_types.all_external_service_types if visible_external_service_types_only: - objs_list = [ external_service_types[ seq_type_id ] for seq_type_id in trans.app.external_service_types.visible_external_service_types ] + objs_list = [external_service_types[seq_type_id] for seq_type_id in trans.app.external_service_types.visible_external_service_types] else: objs_list = external_service_types.values() - refresh_on_change_values = [ 'none' ] - refresh_on_change_values.extend( [ trans.security.encode_id( obj.id ) for obj in objs_list] ) - select_external_service_type = SelectField( 'external_service_type_id', - refresh_on_change=refresh_on_change, - refresh_on_change_values=refresh_on_change_values ) + refresh_on_change_values = ['none'] + refresh_on_change_values.extend([trans.security.encode_id(obj.id) for obj in objs_list]) + select_external_service_type = SelectField('external_service_type_id', + refresh_on_change=refresh_on_change, + refresh_on_change_values=refresh_on_change_values) if selected_value == 'none': - select_external_service_type.add_option( 'Select one', 'none', selected=True ) + select_external_service_type.add_option('Select one', 'none', selected=True) else: - select_external_service_type.add_option( 'Select one', 'none' ) + select_external_service_type.add_option('Select one', 'none') for seq_type in objs_list: if seq_type.version: - option_name = " ".join( [ seq_type.name, "version", seq_type.version ] ) + option_name = " ".join([seq_type.name, "version", seq_type.version]) else: option_name = seq_type.name if selected_value == seq_type.id: - select_external_service_type.add_option( option_name, seq_type.id, selected=True ) + select_external_service_type.add_option(option_name, seq_type.id, selected=True) else: - select_external_service_type.add_option( option_name, seq_type.id ) + select_external_service_type.add_option(option_name, seq_type.id) return select_external_service_type diff --git a/lib/galaxy/webapps/galaxy/controllers/external_services.py b/lib/galaxy/webapps/galaxy/controllers/external_services.py index cb1b37a72e85..af51eb5ba233 100644 --- a/lib/galaxy/webapps/galaxy/controllers/external_services.py +++ b/lib/galaxy/webapps/galaxy/controllers/external_services.py @@ -4,25 +4,25 @@ from galaxy.model import ExternalService, Sample from galaxy.web.base.controller import BaseUIController -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) class_name_to_class = {} for model_class in [Sample]: - class_name_to_class[ model_class.__name__ ] = model_class + class_name_to_class[model_class.__name__] = model_class -class ExternalServiceController( BaseUIController ): +class ExternalServiceController(BaseUIController): @web.expose @web.require_admin - def access_action( self, trans, external_service_action, item, item_type, **kwd ): + def access_action(self, trans, external_service_action, item, item_type, **kwd): if item_type in class_name_to_class: - item_type = class_name_to_class.get( item_type ) - item = item_type.get( item ) - external_service_action_parsed = external_service_action.split( '|' ) - populated_external_service = ExternalService.get( external_service_action_parsed.pop( 0 ) ).populate_actions( trans, item ) - populated_action = populated_external_service.perform_action_by_name( external_service_action_parsed ) - results = populated_action.handle_results( trans ) + item_type = class_name_to_class.get(item_type) + item = item_type.get(item) + external_service_action_parsed = external_service_action.split('|') + populated_external_service = ExternalService.get(external_service_action_parsed.pop(0)).populate_actions(trans, item) + populated_action = populated_external_service.perform_action_by_name(external_service_action_parsed) + results = populated_action.handle_results(trans) return results else: - raise Exception( 'unknown item class type' ) + raise Exception('unknown item class type') diff --git a/lib/galaxy/webapps/galaxy/controllers/forms.py b/lib/galaxy/webapps/galaxy/controllers/forms.py index 27bdcac6a8ad..cc4657f787ea 100644 --- a/lib/galaxy/webapps/galaxy/controllers/forms.py +++ b/lib/galaxy/webapps/galaxy/controllers/forms.py @@ -8,22 +8,22 @@ from galaxy.web.form_builder import FileField, TextField, HiddenField, SelectField from galaxy.web.framework.helpers import iff, grids -log = logging.getLogger( __name__ ) +log = logging.getLogger(__name__) -VALID_FIELDNAME_RE = re.compile( "^[a-zA-Z0-9\_]+$" ) +VALID_FIELDNAME_RE = re.compile("^[a-zA-Z0-9\_]+$") -class FormsGrid( grids.Grid ): +class FormsGrid(grids.Grid): # Custom column types - class NameColumn( grids.TextColumn ): + class NameColumn(grids.TextColumn): def get_value(self, trans, grid, form): return escape(form.latest_form.name) - class DescriptionColumn( grids.TextColumn ): + class DescriptionColumn(grids.TextColumn): def get_value(self, trans, grid, form): return escape(form.latest_form.desc) - class TypeColumn( grids.TextColumn ): + class TypeColumn(grids.TextColumn): def get_value(self, trans, grid, form): return form.latest_form.type # Grid definition @@ -34,159 +34,159 @@ def get_value(self, trans, grid, form): num_rows_per_page = 50 preserve_state = True use_paging = True - default_filter = dict( deleted="False" ) + default_filter = dict(deleted="False") columns = [ - NameColumn( "Name", - key="name", - model_class=model.FormDefinition, - link=( lambda item: iff( item.deleted, None, dict( operation="view_latest_form_definition", - id=item.id ) ) ), - attach_popup=True, - filterable="advanced" ), - DescriptionColumn( "Description", - key='desc', - model_class=model.FormDefinition, - filterable="advanced" ), - TypeColumn( "Type" ), - grids.DeletedColumn( "Deleted", - key="deleted", - visible=False, - filterable="advanced" ) + NameColumn("Name", + key="name", + model_class=model.FormDefinition, + link=(lambda item: iff(item.deleted, None, dict(operation="view_latest_form_definition", + id=item.id))), + attach_popup=True, + filterable="advanced"), + DescriptionColumn("Description", + key='desc', + model_class=model.FormDefinition, + filterable="advanced"), + TypeColumn("Type"), + grids.DeletedColumn("Deleted", + key="deleted", + visible=False, + filterable="advanced") ] - columns.append( grids.MulticolFilterColumn( "Search", - cols_to_filter=[ columns[0], columns[1] ], - key="free-text-search", - visible=False, - filterable="standard" ) ) + columns.append(grids.MulticolFilterColumn("Search", + cols_to_filter=[columns[0], columns[1]], + key="free-text-search", + visible=False, + filterable="standard")) operations = [ - grids.GridOperation( "Edit", allow_multiple=False, condition=( lambda item: not item.deleted ) ), - grids.GridOperation( "Delete", allow_multiple=True, condition=( lambda item: not item.deleted ) ), - grids.GridOperation( "Undelete", condition=( lambda item: item.deleted ) ), + grids.GridOperation("Edit", allow_multiple=False, condition=(lambda item: not item.deleted)), + grids.GridOperation("Delete", allow_multiple=True, condition=(lambda item: not item.deleted)), + grids.GridOperation("Undelete", condition=(lambda item: item.deleted)), ] global_actions = [ - grids.GridAction( "Create new form", dict( controller='forms', action='create_form_definition' ) ) + grids.GridAction("Create new form", dict(controller='forms', action='create_form_definition')) ] - def build_initial_query( self, trans, **kwargs ): - return trans.sa_session.query( self.model_class ).join(model.FormDefinition, self.model_class.latest_form_id == model.FormDefinition.id) + def build_initial_query(self, trans, **kwargs): + return trans.sa_session.query(self.model_class).join(model.FormDefinition, self.model_class.latest_form_id == model.FormDefinition.id) -class Forms( BaseUIController ): +class Forms(BaseUIController): # Empty TextField - empty_field = { 'name': '', - 'label': '', - 'helptext': '', - 'visible': True, - 'required': False, - 'type': model.TextField.__name__, - 'selectlist': [], - 'layout': 'none', - 'default': '' } + empty_field = {'name': '', + 'label': '', + 'helptext': '', + 'visible': True, + 'required': False, + 'type': model.TextField.__name__, + 'selectlist': [], + 'layout': 'none', + 'default': ''} forms_grid = FormsGrid() @web.expose @web.require_admin - def browse_form_definitions( self, trans, **kwd ): + def browse_form_definitions(self, trans, **kwd): if 'operation' in kwd: operation = kwd['operation'].lower() - if not kwd.get( 'id', None ): - return trans.response.send_redirect( web.url_for( controller='forms', - action='browse_form_definitions', - status='error', - message="Invalid form ID") ) + if not kwd.get('id', None): + return trans.response.send_redirect(web.url_for(controller='forms', + action='browse_form_definitions', + status='error', + message="Invalid form ID")) if operation == "view_latest_form_definition": - return self.view_latest_form_definition( trans, **kwd ) + return self.view_latest_form_definition(trans, **kwd) elif operation == "delete": - return self.delete_form_definition( trans, **kwd ) + return self.delete_form_definition(trans, **kwd) elif operation == "undelete": - return self.undelete_form_definition( trans, **kwd ) + return self.undelete_form_definition(trans, **kwd) elif operation == "edit": - return self.edit_form_definition( trans, **kwd ) - return self.forms_grid( trans, **kwd ) + return self.edit_form_definition(trans, **kwd) + return self.forms_grid(trans, **kwd) @web.expose @web.require_admin - def view_latest_form_definition( self, trans, **kwd ): + def view_latest_form_definition(self, trans, **kwd): '''Displays the layout of the latest version of the form definition''' - form_definition_current_id = kwd.get( 'id', None ) + form_definition_current_id = kwd.get('id', None) try: - form_definition_current = trans.sa_session.query( trans.app.model.FormDefinitionCurrent ) \ - .get( trans.security.decode_id( form_definition_current_id ) ) + form_definition_current = trans.sa_session.query(trans.app.model.FormDefinitionCurrent) \ + .get(trans.security.decode_id(form_definition_current_id)) except: - return trans.response.send_redirect( web.url_for( controller='forms', - action='browse_form_definitions', - message='Invalid form', - status='error' ) ) - return trans.fill_template( '/admin/forms/view_form_definition.mako', - form_definition=form_definition_current.latest_form ) + return trans.response.send_redirect(web.url_for(controller='forms', + action='browse_form_definitions', + message='Invalid form', + status='error')) + return trans.fill_template('/admin/forms/view_form_definition.mako', + form_definition=form_definition_current.latest_form) @web.expose @web.require_admin - def create_form_definition( self, trans, **kwd ): - params = util.Params( kwd ) - message = util.restore_text( params.get( 'message', '' ) ) - status = params.get( 'status', 'done' ) + def create_form_definition(self, trans, **kwd): + params = util.Params(kwd) + message = util.restore_text(params.get('message', '')) + status = params.get('status', 'done') self.__imported_from_file = False - if params.get( 'create_form_button', False ): - form_definition, message = self.save_form_definition( trans, form_definition_current_id=None, **kwd ) + if params.get('create_form_button', False): + form_definition, message = self.save_form_definition(trans, form_definition_current_id=None, **kwd) if not form_definition: - return trans.response.send_redirect( web.url_for( controller='forms', - action='create_form_definition', - message=message, - status='error', - name=util.restore_text( params.get( 'name', '' ) ), - description=util.restore_text( params.get( 'description', '' ) ) )) + return trans.response.send_redirect(web.url_for(controller='forms', + action='create_form_definition', + message=message, + status='error', + name=util.restore_text(params.get('name', '')), + description=util.restore_text(params.get('description', '')))) if self.__imported_from_file: - return trans.response.send_redirect( web.url_for( controller='forms', - action='edit_form_definition', - id=trans.security.encode_id( form_definition.current.id )) ) + return trans.response.send_redirect(web.url_for(controller='forms', + action='edit_form_definition', + id=trans.security.encode_id(form_definition.current.id))) else: - return trans.response.send_redirect( web.url_for( controller='forms', - action='edit_form_definition', - id=trans.security.encode_id( form_definition.current.id ), - add_field_button='Add field', - name=form_definition.name, - description=form_definition.desc, - form_type_select_field=form_definition.type ) ) - inputs = [ ( 'Name', TextField( 'name', 40, util.restore_text( params.get( 'name', '' ) ) ) ), - ( 'Description', TextField( 'description', 40, util.restore_text( params.get( 'description', '' ) ) ) ), - ( 'Type', self.__build_form_types_widget( trans, selected=params.get( 'form_type', 'none' ) ) ), - ( 'Import from csv file (Optional)', FileField( 'file_data', 40, '' ) ) ] - return trans.fill_template( '/admin/forms/create_form.mako', - inputs=inputs, - message=message, - status=status ) + return trans.response.send_redirect(web.url_for(controller='forms', + action='edit_form_definition', + id=trans.security.encode_id(form_definition.current.id), + add_field_button='Add field', + name=form_definition.name, + description=form_definition.desc, + form_type_select_field=form_definition.type)) + inputs = [('Name', TextField('name', 40, util.restore_text(params.get('name', '')))), + ('Description', TextField('description', 40, util.restore_text(params.get('description', '')))), + ('Type', self.__build_form_types_widget(trans, selected=params.get('form_type', 'none'))), + ('Import from csv file (Optional)', FileField('file_data', 40, ''))] + return trans.fill_template('/admin/forms/create_form.mako', + inputs=inputs, + message=message, + status=status) @web.expose @web.require_admin - def edit_form_definition( self, trans, response_redirect=None, **kwd ): + def edit_form_definition(self, trans, response_redirect=None, **kwd): ''' This callback method is for handling form editing. The value of response_redirect should be an URL that is defined by the caller. This allows for redirecting as desired when the form changes have been saved. For an example of how this works, see the edit_template() method in the base controller. ''' - params = util.Params( kwd ) - message = util.restore_text( params.get( 'message', '' ) ) - status = params.get( 'status', 'done' ) + params = util.Params(kwd) + message = util.restore_text(params.get('message', '')) + status = params.get('status', 'done') try: - form_definition_current = trans.sa_session.query( trans.app.model.FormDefinitionCurrent ).get( trans.security.decode_id(kwd['id']) ) + form_definition_current = trans.sa_session.query(trans.app.model.FormDefinitionCurrent).get(trans.security.decode_id(kwd['id'])) except: - return trans.response.send_redirect( web.url_for( controller='forms', - action='browse_form_definitions', - message='Invalid form', - status='error' ) ) + return trans.response.send_redirect(web.url_for(controller='forms', + action='browse_form_definitions', + message='Invalid form', + status='error')) form_definition = form_definition_current.latest_form # TODO: eliminate the need for this refresh param. - if params.get( 'refresh', False ): + if params.get('refresh', False): # Refresh - current_form = self.get_current_form( trans, **kwd ) + current_form = self.get_current_form(trans, **kwd) else: # Show the saved form for editing - current_form = self.get_saved_form( form_definition ) + current_form = self.get_saved_form(form_definition) # Save changes - if params.get( 'save_changes_button', False ): - new_form_definition, message = self.save_form_definition( trans, form_definition_current_id=form_definition.form_definition_current.id, **kwd ) + if params.get('save_changes_button', False): + new_form_definition, message = self.save_form_definition(trans, form_definition_current_id=form_definition.form_definition_current.id, **kwd) # if validation error encountered while saving the form, show the # unsaved form, with the error message if not new_form_definition: @@ -195,92 +195,92 @@ def edit_form_definition( self, trans, response_redirect=None, **kwd ): # everything went fine. form saved successfully. Show the saved form or redirect # to response_redirect if appropriate. if response_redirect: - return trans.response.send_redirect( response_redirect ) + return trans.response.send_redirect(response_redirect) form_definition = new_form_definition - current_form = self.get_saved_form( form_definition ) + current_form = self.get_saved_form(form_definition) message = "The form '%s' has been updated with the changes." % form_definition.name # Add a layout grid - elif params.get( 'add_layout_grid_button', False ): - current_form[ 'layout' ].append( '' ) + elif params.get('add_layout_grid_button', False): + current_form['layout'].append('') # Delete a layout grid - elif params.get( 'remove_layout_grid_button', False ): - index = int( kwd[ 'remove_layout_grid_button' ].split( ' ' )[2] ) - 1 - del current_form[ 'layout' ][index] + elif params.get('remove_layout_grid_button', False): + index = int(kwd['remove_layout_grid_button'].split(' ')[2]) - 1 + del current_form['layout'][index] # Add a field - elif params.get( 'add_field_button', False ): - field_index = len( current_form[ 'fields' ] ) + 1 - self.empty_field[ 'name' ] = '%i_field_name' % field_index - self.empty_field[ 'label' ] = 'Field label %i' % field_index - current_form[ 'fields' ].append( self.empty_field ) + elif params.get('add_field_button', False): + field_index = len(current_form['fields']) + 1 + self.empty_field['name'] = '%i_field_name' % field_index + self.empty_field['label'] = 'Field label %i' % field_index + current_form['fields'].append(self.empty_field) # Delete a field - elif params.get( 'remove_button', False ): + elif params.get('remove_button', False): # find the index of the field to be removed from the remove button label - index = int( kwd[ 'remove_button' ].split( ' ' )[2] ) - 1 - del current_form[ 'fields' ][ index ] + index = int(kwd['remove_button'].split(' ')[2]) - 1 + del current_form['fields'][index] # Add SelectField option elif 'Add' in kwd.values(): - current_form, status, message = self.__add_select_field_option( trans=trans, - current_form=current_form, - **kwd) + current_form, status, message = self.__add_select_field_option(trans=trans, + current_form=current_form, + **kwd) # Remove SelectField option elif 'Remove' in kwd.values(): - current_form, status, message = self.__remove_select_field_option( trans=trans, - current_form=current_form, - **kwd) - return self.show_editable_form_definition( trans=trans, - form_definition=form_definition, - current_form=current_form, - message=message, - status=status, - response_redirect=response_redirect, - **kwd ) + current_form, status, message = self.__remove_select_field_option(trans=trans, + current_form=current_form, + **kwd) + return self.show_editable_form_definition(trans=trans, + form_definition=form_definition, + current_form=current_form, + message=message, + status=status, + response_redirect=response_redirect, + **kwd) - def get_saved_form( self, form_definition ): + def get_saved_form(self, form_definition): ''' This retrieves the saved form and returns a dictionary containing the name, desc, type, layout & fields of the form ''' if form_definition.type == form_definition.types.SAMPLE: - return dict( name=form_definition.name, - desc=form_definition.desc, - type=form_definition.type, - layout=list( copy.deepcopy( form_definition.layout ) ), - fields=list( copy.deepcopy( form_definition.fields ) ) ) - return dict( name=form_definition.name, - desc=form_definition.desc, - type=form_definition.type, - layout=[], - fields=list( copy.deepcopy( form_definition.fields ) ) ) + return dict(name=form_definition.name, + desc=form_definition.desc, + type=form_definition.type, + layout=list(copy.deepcopy(form_definition.layout)), + fields=list(copy.deepcopy(form_definition.fields))) + return dict(name=form_definition.name, + desc=form_definition.desc, + type=form_definition.type, + layout=[], + fields=list(copy.deepcopy(form_definition.fields))) - def get_current_form( self, trans, **kwd ): + def get_current_form(self, trans, **kwd): ''' This method gets all the unsaved user-entered form details and returns a dictionary containing the name, desc, type, layout & fields of the form ''' - params = util.Params( kwd ) - name = util.restore_text( params.name ) - desc = util.restore_text( params.description ) or "" - form_type = util.restore_text( params.form_type_select_field ) + params = util.Params(kwd) + name = util.restore_text(params.name) + desc = util.restore_text(params.description) or "" + form_type = util.restore_text(params.form_type_select_field) # get the user entered layout grids in it is a sample form definition layout = [] if form_type == trans.model.FormDefinition.types.SAMPLE: index = 0 while True: if 'grid_layout%i' % index in kwd: - grid_name = util.restore_text( params.get( 'grid_layout%i' % index, '' ) ) - layout.append( grid_name ) + grid_name = util.restore_text(params.get('grid_layout%i' % index, '')) + layout.append(grid_name) index = index + 1 else: break # for csv file import - csv_file = params.get( 'file_data', '' ) + csv_file = params.get('file_data', '') fields = [] if csv_file == '': # get the user entered fields index = 0 while True: if 'field_label_%i' % index in kwd: - fields.append( self.__get_field( index, **kwd ) ) + fields.append(self.__get_field(index, **kwd)) index = index + 1 else: break @@ -293,217 +293,217 @@ def get_current_form( self, trans, **kwd ): layout=layout, fields=fields) - def save_form_definition( self, trans, form_definition_current_id=None, **kwd ): + def save_form_definition(self, trans, form_definition_current_id=None, **kwd): ''' This method saves the current form ''' # check the form for invalid inputs - flag, message = self.__validate_form( **kwd ) + flag, message = self.__validate_form(**kwd) if not flag: return None, message - current_form = self.get_current_form( trans, **kwd ) + current_form = self.get_current_form(trans, **kwd) # validate fields field_names_dict = {} - for field in current_form[ 'fields' ]: - if not field[ 'label' ]: + for field in current_form['fields']: + if not field['label']: return None, "All the field labels must be completed." - if not VALID_FIELDNAME_RE.match( field[ 'name' ] ): - return None, "'%s' is not a valid field name." % field[ 'name' ] - if field[ 'name' ] in field_names_dict: - return None, "Each field name must be unique in the form definition. '%s' is not unique." % field[ 'name' ] + if not VALID_FIELDNAME_RE.match(field['name']): + return None, "'%s' is not a valid field name." % field['name'] + if field['name'] in field_names_dict: + return None, "Each field name must be unique in the form definition. '%s' is not unique." % field['name'] else: - field_names_dict[ field[ 'name' ] ] = 1 + field_names_dict[field['name']] = 1 # if type is sample form, it should have at least one layout grid - if current_form[ 'type' ] == trans.app.model.FormDefinition.types.SAMPLE and not len( current_form[ 'layout' ] ): - current_form[ 'layout' ] = [ 'Layout1' ] + if current_form['type'] == trans.app.model.FormDefinition.types.SAMPLE and not len(current_form['layout']): + current_form['layout'] = ['Layout1'] # create a new form definition - form_definition = trans.app.model.FormDefinition( name=current_form[ 'name' ], - desc=current_form[ 'desc' ], - fields=current_form[ 'fields' ], - form_definition_current=None, - form_type=current_form[ 'type' ], - layout=current_form[ 'layout' ] ) + form_definition = trans.app.model.FormDefinition(name=current_form['name'], + desc=current_form['desc'], + fields=current_form['fields'], + form_definition_current=None, + form_type=current_form['type'], + layout=current_form['layout']) if form_definition_current_id: # save changes to the existing form # change the pointer in the form_definition_current table to point # to this new record - form_definition_current = trans.sa_session.query( trans.app.model.FormDefinitionCurrent ).get( form_definition_current_id ) + form_definition_current = trans.sa_session.query(trans.app.model.FormDefinitionCurrent).get(form_definition_current_id) else: # create a new form form_definition_current = trans.app.model.FormDefinitionCurrent() # create corresponding row in the form_definition_current table form_definition.form_definition_current = form_definition_current form_definition_current.latest_form = form_definition - trans.sa_session.add( form_definition_current ) + trans.sa_session.add(form_definition_current) trans.sa_session.flush() message = "The new form named '%s' has been created. " % (form_definition.name) return form_definition, message - def show_editable_form_definition( self, trans, form_definition, current_form, message='', status='done', response_redirect=None, **kwd ): + def show_editable_form_definition(self, trans, form_definition, current_form, message='', status='done', response_redirect=None, **kwd): """ Displays the form and any of the changes made to it in edit mode. In this method all the widgets are build for all name, description and all the fields of a form definition. """ - util.Params( kwd ) + util.Params(kwd) # name & description - form_details = [ ( 'Name', TextField( 'name', 40, current_form[ 'name' ] ) ), - ( 'Description', TextField( 'description', 40, current_form[ 'desc' ] ) ), - ( 'Type', HiddenField( 'form_type_select_field', current_form['type']) ) ] + form_details = [('Name', TextField('name', 40, current_form['name'])), + ('Description', TextField('description', 40, current_form['desc'])), + ('Type', HiddenField('form_type_select_field', current_form['type']))] form_layout = [] - if current_form[ 'type' ] == trans.app.model.FormDefinition.types.SAMPLE: - for index, layout_name in enumerate( current_form[ 'layout' ] ): - form_layout.append( TextField( 'grid_layout%i' % index, 40, layout_name )) + if current_form['type'] == trans.app.model.FormDefinition.types.SAMPLE: + for index, layout_name in enumerate(current_form['layout']): + form_layout.append(TextField('grid_layout%i' % index, 40, layout_name)) # fields field_details = [] - for field_index, field in enumerate( current_form[ 'fields' ] ): - field_widgets = self.build_form_definition_field_widgets( trans=trans, - layout_grids=current_form['layout'], - field_index=field_index, - field=field, - form_type=current_form['type'] ) - field_details.append( field_widgets ) - return trans.fill_template( '/admin/forms/edit_form_definition.mako', - form_details=form_details, - field_details=field_details, - form_definition=form_definition, - field_types=trans.model.FormDefinition.supported_field_types, - message=message, - status=status, - current_form_type=current_form[ 'type' ], - layout_grids=form_layout, - response_redirect=response_redirect ) + for field_index, field in enumerate(current_form['fields']): + field_widgets = self.build_form_definition_field_widgets(trans=trans, + layout_grids=current_form['layout'], + field_index=field_index, + field=field, + form_type=current_form['type']) + field_details.append(field_widgets) + return trans.fill_template('/admin/forms/edit_form_definition.mako', + form_details=form_details, + field_details=field_details, + form_definition=form_definition, + field_types=trans.model.FormDefinition.supported_field_types, + message=message, + status=status, + current_form_type=current_form['type'], + layout_grids=form_layout, + response_redirect=response_redirect) @web.expose @web.require_admin - def delete_form_definition( self, trans, **kwd ): - id_list = util.listify( kwd['id'] ) + def delete_form_definition(self, trans, **kwd): + id_list = util.listify(kwd['id']) for id in id_list: try: - form_definition_current = trans.sa_session.query( trans.app.model.FormDefinitionCurrent ).get( trans.security.decode_id(id) ) + form_definition_current = trans.sa_session.query(trans.app.model.FormDefinitionCurrent).get(trans.security.decode_id(id)) except: - return trans.response.send_redirect( web.url_for( controller='forms', - action='browse_form_definitions', - message='Invalid form', - status='error' ) ) + return trans.response.send_redirect(web.url_for(controller='forms', + action='browse_form_definitions', + message='Invalid form', + status='error')) form_definition_current.deleted = True - trans.sa_session.add( form_definition_current ) + trans.sa_session.add(form_definition_current) trans.sa_session.flush() - return trans.response.send_redirect( web.url_for( controller='forms', - action='browse_form_definitions', - message='%i forms have been deleted.' % len(id_list), - status='done') ) + return trans.response.send_redirect(web.url_for(controller='forms', + action='browse_form_definitions', + message='%i forms have been deleted.' % len(id_list), + status='done')) @web.expose @web.require_admin - def undelete_form_definition( self, trans, **kwd ): - id_list = util.listify( kwd['id'] ) + def undelete_form_definition(self, trans, **kwd): + id_list = util.listify(kwd['id']) for id in id_list: try: - form_definition_current = trans.sa_session.query( trans.app.model.FormDefinitionCurrent ).get( trans.security.decode_id(id) ) + form_definition_current = trans.sa_session.query(trans.app.model.FormDefinitionCurrent).get(trans.security.decode_id(id)) except: - return trans.response.send_redirect( web.url_for( controller='forms', - action='browse_form_definitions', - message='Invalid form', - status='error' ) ) + return trans.response.send_redirect(web.url_for(controller='forms', + action='browse_form_definitions', + message='Invalid form', + status='error')) form_definition_current.deleted = False - trans.sa_session.add( form_definition_current ) + trans.sa_session.add(form_definition_current) trans.sa_session.flush() - return trans.response.send_redirect( web.url_for( controller='forms', - action='browse_form_definitions', - message='%i forms have been undeleted.' % len(id_list), - status='done') ) + return trans.response.send_redirect(web.url_for(controller='forms', + action='browse_form_definitions', + message='%i forms have been undeleted.' % len(id_list), + status='done')) - def build_form_definition_field_widgets( self, trans, layout_grids, field_index, field, form_type ): + def build_form_definition_field_widgets(self, trans, layout_grids, field_index, field, form_type): ''' This method returns a list of widgets which describes a form definition field. This includes the field label, helptext, type, selectfield options, required/optional & layout ''' # field label - label = TextField( 'field_label_' + str( field_index ), 40, field['label'] ) + label = TextField('field_label_' + str(field_index), 40, field['label']) # help text - helptext = TextField( 'field_helptext_' + str( field_index ), 40, field['helptext'] ) + helptext = TextField('field_helptext_' + str(field_index), 40, field['helptext']) # field type - field_type_select_field = SelectField( 'field_type_' + str( field_index ), - refresh_on_change=True, - refresh_on_change_values=[ SelectField.__name__ ] ) + field_type_select_field = SelectField('field_type_' + str(field_index), + refresh_on_change=True, + refresh_on_change_values=[SelectField.__name__]) # fill up the field type selectfield options field_type_options = [] # if the form is for defining samples, then use the sample field types # which does not include TextArea & AddressField if form_type == trans.model.FormDefinition.types.SAMPLE: for supported_field_type in trans.model.Sample.supported_field_types: - if supported_field_type.__name__ == field[ 'type' ]: - field_type_select_field.add_option( supported_field_type.__name__, - supported_field_type.__name__, - selected=True ) + if supported_field_type.__name__ == field['type']: + field_type_select_field.add_option(supported_field_type.__name__, + supported_field_type.__name__, + selected=True) if supported_field_type.__name__ == SelectField.__name__: # when field type is Selectfield, add option Textfields - field_type_options = self.__build_field_type_select_field_options( field, field_index ) + field_type_options = self.__build_field_type_select_field_options(field, field_index) else: - field_type_select_field.add_option( supported_field_type.__name__, - supported_field_type.__name__ ) + field_type_select_field.add_option(supported_field_type.__name__, + supported_field_type.__name__) else: for supported_field_type in trans.model.FormDefinition.supported_field_types: - if supported_field_type.__name__ == field[ 'type' ]: - field_type_select_field.add_option( supported_field_type.__name__, - supported_field_type.__name__, - selected=True ) + if supported_field_type.__name__ == field['type']: + field_type_select_field.add_option(supported_field_type.__name__, + supported_field_type.__name__, + selected=True) if supported_field_type.__name__ == SelectField.__name__: # when field type is Selectfield, add option Textfields - field_type_options = self.__build_field_type_select_field_options( field, field_index ) + field_type_options = self.__build_field_type_select_field_options(field, field_index) else: - field_type_select_field.add_option( supported_field_type.__name__, - supported_field_type.__name__ ) + field_type_select_field.add_option(supported_field_type.__name__, + supported_field_type.__name__) # required/optional radio button - required = SelectField( 'field_required_' + str(field_index), display='radio' ) - if field[ 'required' ] == 'required': - required.add_option( 'Required', 'required', selected=True ) - required.add_option( 'Optional', 'optional' ) + required = SelectField('field_required_' + str(field_index), display='radio') + if field['required'] == 'required': + required.add_option('Required', 'required', selected=True) + required.add_option('Optional', 'optional') else: - required.add_option( 'Required', 'required' ) - required.add_option( 'Optional', 'optional', selected=True ) + required.add_option('Required', 'required') + required.add_option('Optional', 'optional', selected=True) # layout grid option select_field if layout_grids and form_type == trans.model.FormDefinition.types.SAMPLE: - layout_select_field = SelectField( 'field_layout_' + str( field_index ) ) - for index, grid_name in enumerate( layout_grids ): - if str( field.get( 'layout', None ) ) == str( index ): # existing behavior: integer indexes are stored as strings. + layout_select_field = SelectField('field_layout_' + str(field_index)) + for index, grid_name in enumerate(layout_grids): + if str(field.get('layout', None)) == str(index): # existing behavior: integer indexes are stored as strings. grid_selected = True else: grid_selected = False - layout_select_field.add_option("%i. %s" % ( index + 1, grid_name ), index, selected=grid_selected ) + layout_select_field.add_option("%i. %s" % (index + 1, grid_name), index, selected=grid_selected) # default value - default_value = TextField( 'field_default_' + str(field_index), - 40, - field.get( 'default', '' ) ) + default_value = TextField('field_default_' + str(field_index), + 40, + field.get('default', '')) # field name - name = TextField( 'field_name_' + str( field_index ), 40, field[ 'name' ] ) + name = TextField('field_name_' + str(field_index), 40, field['name']) name_helptext = "The field name must be unique for each field and must contain only alphanumeric characters and underscore ." if layout_grids and form_type == trans.model.FormDefinition.types.SAMPLE: - return [ ( 'Field label', label ), - ( 'Help text', helptext ), - ( 'Type', field_type_select_field, "Add options below", field_type_options ), - ( 'Default value', default_value ), - ( '', required ), - ( 'Select the grid layout to place this field', layout_select_field ), - ( 'Field name', name, name_helptext ) ] - return [ ( 'Field label', label ), - ( 'Help text', helptext ), - ( 'Type', field_type_select_field, "Add options below", field_type_options), - ( 'Default value', default_value ), - ( '', required), - ( 'Field name', name, name_helptext ) ] + return [('Field label', label), + ('Help text', helptext), + ('Type', field_type_select_field, "Add options below", field_type_options), + ('Default value', default_value), + ('', required), + ('Select the grid layout to place this field', layout_select_field), + ('Field name', name, name_helptext)] + return [('Field label', label), + ('Help text', helptext), + ('Type', field_type_select_field, "Add options below", field_type_options), + ('Default value', default_value), + ('', required), + ('Field name', name, name_helptext)] - def __build_field_type_select_field_options( self, field, field_index ): + def __build_field_type_select_field_options(self, field, field_index): ''' Returns a list of TextFields, one for each select field option ''' field_type_options = [] - if field[ 'selectlist' ]: - for ctr, option in enumerate( field[ 'selectlist' ] ): - option_textfield = TextField( 'field_' + str( field_index ) + '_option_' + str( ctr ), 40, option ) - field_type_options.append( ( 'Option ' + str( ctr + 1 ), option_textfield ) ) + if field['selectlist']: + for ctr, option in enumerate(field['selectlist']): + option_textfield = TextField('field_' + str(field_index) + '_option_' + str(ctr), 40, option) + field_type_options.append(('Option ' + str(ctr + 1), option_textfield)) return field_type_options - def __add_select_field_option( self, trans, current_form, **kwd ): + def __add_select_field_option(self, trans, current_form, **kwd): ''' This method adds a select_field option. The kwd dict searched for the field index which needs to be removed @@ -523,10 +523,10 @@ def __add_select_field_option( self, trans, current_form, **kwd ): status = 'error', return current_form, status, message # add an empty option - current_form[ 'fields' ][ index ][ 'selectlist' ].append( '' ) + current_form['fields'][index]['selectlist'].append('') return current_form, status, message - def __remove_select_field_option( self, trans, current_form, **kwd ): + def __remove_select_field_option(self, trans, current_form, **kwd): ''' This method removes a select_field option. The kwd dict searched for the field index and option index which needs to be removed @@ -538,8 +538,8 @@ def __remove_select_field_option( self, trans, current_form, **kwd ): if v == 'Remove': # extract the field & option indices from the # button name of format: 'removeoption__