diff --git a/.travis.yml b/.travis.yml index 94156c2c969..bde4abd6f49 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,7 +16,6 @@ env: matrix: allow_failures: - env: TOX_ENV=check-python-dependencies - - env: TOX_ENV=qunit addons: apt: @@ -25,7 +24,6 @@ addons: install: - pip install tox - - if [ "$TOX_ENV" == "qunit" ]; then bash -c 'cd test/qunit && npm install'; fi - if [ "$TOX_ENV" == "first_startup" ]; then bash -c "bash scripts/common_startup.sh && wget -q https://github.com/jmchilton/galaxy-downloads/raw/master/db_gx_rev_0127.sqlite && mv db_gx_rev_0127.sqlite database/universe.sqlite && bash manage_db.sh -c ./config/galaxy.ini.sample upgrade"; fi script: tox -e $TOX_ENV diff --git a/Makefile b/Makefile index 00809e431ea..5e02eef04d2 100644 --- a/Makefile +++ b/Makefile @@ -141,9 +141,12 @@ client-watch: node-deps ## A useful target for parallel development building. cd client && yarn run watch @echo "Remember to 'make client' when finished developing!" -client-test: +client-test: client ## Run qunit tests via Karma cd client && yarn run test +client-test-watch: client ## Watch and run qunit tests on changes via Karma + cd client && yarn run test-watch + charts: node-deps ## Rebuild charts cd client && yarn run build-charts diff --git a/client/galaxy/scripts/apps/analysis.js b/client/galaxy/scripts/apps/analysis.js index cd78f61b4ba..230266a5f51 100644 --- a/client/galaxy/scripts/apps/analysis.js +++ b/client/galaxy/scripts/apps/analysis.js @@ -13,6 +13,7 @@ import Tours from "mvc/tours"; import GridView from "mvc/grid/grid-view"; import GridShared from "mvc/grid/grid-shared"; import Workflows from "mvc/workflow/workflow"; +import HistoryImport from "components/history-import.vue"; import HistoryList from "mvc/history/history-list"; import ToolFormComposite from "mvc/tool/tool-form-composite"; import QueryStringParsing from "utils/query-string-parsing"; @@ -46,20 +47,22 @@ window.app = function app(options, bootstrapped) { "(/)tours(/)(:tour_id)": "show_tours", "(/)user(/)": "show_user", "(/)user(/)(:form_id)": "show_user_form", - "(/)workflow(/)": "show_workflows", - "(/)workflow/run(/)": "show_run", "(/)pages(/)create(/)": "show_pages_create", "(/)pages(/)edit(/)": "show_pages_edit", "(/)pages(/)(:action_id)": "show_pages", "(/)visualizations(/)edit(/)": "show_visualizations_edit", "(/)visualizations/(:action_id)": "show_visualizations", + "(/)workflows/import_workflow": "show_import_workflow", + "(/)workflows/run(/)": "show_run", + "(/)workflows(/)list": "show_workflows", "(/)workflows/list_published(/)": "show_workflows_published", + "(/)workflows/create(/)": "show_workflows_create", "(/)histories(/)citations(/)": "show_history_citations", "(/)histories(/)rename(/)": "show_histories_rename", + "(/)histories(/)import(/)": "show_histories_import", "(/)histories(/)permissions(/)": "show_histories_permissions", "(/)histories(/)(:action_id)": "show_histories", "(/)datasets(/)list(/)": "show_datasets", - "(/)workflow/import_workflow": "show_import_workflow", "(/)custom_builds": "show_custom_builds", "(/)datasets/edit": "show_dataset_edit_attributes", "(/)datasets/error": "show_dataset_error" @@ -138,6 +141,13 @@ window.app = function app(options, bootstrapped) { ); }, + show_histories_import: function() { + var historyImportInstance = Vue.extend(HistoryImport); + var vm = document.createElement("div"); + this.page.display(vm); + new historyImportInstance().$mount(vm); + }, + show_histories_permissions: function() { this.page.display( new FormWrapper.View({ @@ -188,6 +198,15 @@ window.app = function app(options, bootstrapped) { this.page.display(new Workflows.View()); }, + show_workflows_create: function() { + this.page.display( + new FormWrapper.View({ + url: `workflow/create`, + redirect: "workflow/editor" + }) + ); + }, + show_run: function() { this._loadWorkflow(); }, diff --git a/client/galaxy/scripts/apps/panels/tool-panel.js b/client/galaxy/scripts/apps/panels/tool-panel.js index d3e1d543dc1..8573a44ff1a 100644 --- a/client/galaxy/scripts/apps/panels/tool-panel.js +++ b/client/galaxy/scripts/apps/panels/tool-panel.js @@ -66,7 +66,7 @@ var ToolPanel = Backbone.View.extend({ self.$("#internal-workflows").append( self._templateWorkflowLink({ title: menu_entry.stored_workflow.name, - href: `workflow/run?id=${menu_entry.encoded_stored_workflow_id}` + href: `workflows/run?id=${menu_entry.encoded_stored_workflow_id}` }) ); }); diff --git a/client/galaxy/scripts/components/history-import.vue b/client/galaxy/scripts/components/history-import.vue new file mode 100644 index 00000000000..a4b316786d8 --- /dev/null +++ b/client/galaxy/scripts/components/history-import.vue @@ -0,0 +1,63 @@ + + + \ No newline at end of file diff --git a/client/galaxy/scripts/layout/menu.js b/client/galaxy/scripts/layout/menu.js index 7cf1634aa5e..d848465994e 100644 --- a/client/galaxy/scripts/layout/menu.js +++ b/client/galaxy/scripts/layout/menu.js @@ -39,7 +39,7 @@ var Collection = Backbone.Collection.extend({ title: _l("Workflow"), tooltip: _l("Chain tools into workflows"), disabled: !Galaxy.user.id, - url: "workflow" + url: "workflows/list" }); // diff --git a/client/galaxy/scripts/mvc/form/form-wrapper.js b/client/galaxy/scripts/mvc/form/form-wrapper.js index e5dac4b849c..c7b835a48c7 100644 --- a/client/galaxy/scripts/mvc/form/form-wrapper.js +++ b/client/galaxy/scripts/mvc/form/form-wrapper.js @@ -58,13 +58,18 @@ var View = Backbone.View.extend({ contentType: "application/json" }) .done(response => { - var success_message = { - message: response.message, - status: "success", - persistent: false - }; + var params = {}; + if (response.id) { + params.id = response.id; + } else { + params = { + message: response.message, + status: "success", + persistent: false + } + } if (self.redirect) { - window.location = `${Galaxy.root + self.redirect}?${$.param(success_message)}`; + window.location = `${Galaxy.root + self.redirect}?${$.param(params)}`; } else { form.data.matchModel(response, (input, input_id) => { form.field_list[input_id].value(input.value); diff --git a/client/galaxy/scripts/mvc/history/options-menu.js b/client/galaxy/scripts/mvc/history/options-menu.js index d1c0c22b955..e8f280ebf40 100644 --- a/client/galaxy/scripts/mvc/history/options-menu.js +++ b/client/galaxy/scripts/mvc/history/options-menu.js @@ -169,7 +169,8 @@ var menu = [ }, { html: _l("Import from File"), - href: "history/import_archive" + href: "histories/import", + target: "_top" } ]; diff --git a/client/galaxy/scripts/mvc/tag.js b/client/galaxy/scripts/mvc/tag.js index 937ace60eac..769841d0920 100644 --- a/client/galaxy/scripts/mvc/tag.js +++ b/client/galaxy/scripts/mvc/tag.js @@ -103,7 +103,7 @@ var TagsEditor = Backbone.View.extend(baseMVC.LoggableMixin) _renderTags: function() { var tags = this.model.get("tags"); - var addButton = "static/images/fugue/tag--plus.png"; + var addButton = `${Galaxy.root}static/images/fugue/tag--plus.png`; var renderedArray = []; _.each(tags, tag => { tag = tag.indexOf("name:") == 0 ? tag.slice(5) : tag; diff --git a/client/galaxy/scripts/mvc/workflow/workflow-view.js b/client/galaxy/scripts/mvc/workflow/workflow-view.js index 35175a6dc56..938cccdbe95 100644 --- a/client/galaxy/scripts/mvc/workflow/workflow-view.js +++ b/client/galaxy/scripts/mvc/workflow/workflow-view.js @@ -296,7 +296,7 @@ export default Backbone.View.extend({ Save: save_current_workflow, "Save As": workflow_save_as, Run: function() { - window.location = `${Galaxy.root}workflow/run?id=${self.options.id}`; + window.location = `${Galaxy.root}workflows/run?id=${self.options.id}`; }, "Edit Attributes": function() { self.workflow.clear_active_node(); diff --git a/client/galaxy/scripts/mvc/workflow/workflow.js b/client/galaxy/scripts/mvc/workflow/workflow.js index ede0d4b44d5..efc0c963de4 100644 --- a/client/galaxy/scripts/mvc/workflow/workflow.js +++ b/client/galaxy/scripts/mvc/workflow/workflow.js @@ -36,7 +36,7 @@ var WorkflowItemView = Backbone.View.extend({ this.model.save(); // This reloads the whole page, so that the workflow appears in the tool panel. // Ideally we would notify only the tool panel of a change - window.location = `${Galaxy.root}workflow`; + window.location = `${Galaxy.root}workflows/list`; }, removeWorkflow: function() { @@ -127,7 +127,7 @@ var WorkflowItemView = Backbone.View.extend({ if (this.model.get("owner") === Galaxy.user.attributes.username) { return ``; + }workflows/import_workflow">`; }, /** Template for workflow table */ diff --git a/client/galaxy/scripts/qunit/test.js b/client/galaxy/scripts/qunit/test.js new file mode 100644 index 00000000000..9279d9ba095 --- /dev/null +++ b/client/galaxy/scripts/qunit/test.js @@ -0,0 +1,3 @@ +// Load all qunit tests into a single bundle. +var testsContext = require.context(".", true, /_tests$/); +testsContext.keys().forEach(testsContext); diff --git a/client/galaxy/style/less/ui.less b/client/galaxy/style/less/ui.less index e5499963c56..29d362d86b8 100644 --- a/client/galaxy/style/less/ui.less +++ b/client/galaxy/style/less/ui.less @@ -507,7 +507,7 @@ position: relative; clear: both; width: auto; - height: 100%; + height: auto; .portlet-header { background: @form-heading-bg; border-bottom: solid @form-border 1px; @@ -550,8 +550,8 @@ width : 100%; } .portlet-buttons { - margin-top: @ui-margin-vertical; - margin-bottom: @ui-margin-vertical; + margin-top: @ui-margin-vertical-large; + margin-bottom: @ui-margin-vertical-large; > .btn { margin-right: 5px; } diff --git a/client/karma.config.js b/client/karma.config.js index 29a1d9cfe56..02d45662958 100644 --- a/client/karma.config.js +++ b/client/karma.config.js @@ -1,48 +1,58 @@ + var webpackConfig = require('./webpack.config'); // CommonsChunkPlugin not compatible with karma. // https://github.com/webpack-contrib/karma-webpack/issues/24 webpackConfig.plugins.splice(0, 1); +// Don't build Galaxy bundles - we build per-test bundles. +webpackConfig.entry = function() { return {}; }; + +// Single pack mode runs whole test suite much more quickly - but requires +// running the whole test suite so it would be slower for one-off tests. +var single_pack_mode = function(){ + return process.env.GALAXY_TEST_AS_SINGLE_PACK || false; +}; + +var TESTS_SEPARATE_PACKS = [ + {pattern: 'galaxy/scripts/qunit/tests/list-of-pairs-collection-creator.js', watched: false}, + {pattern: 'galaxy/scripts/qunit/tests/galaxy-app-base.js', watched: false}, + {pattern: 'galaxy/scripts/qunit/tests/graph.js', watched: false}, + {pattern: 'galaxy/scripts/qunit/tests/hda-base.js', watched: false}, + {pattern: 'galaxy/scripts/qunit/tests/history_contents_model_tests.js', watched: false}, + {pattern: 'galaxy/scripts/qunit/tests/job-dag.js', watched: false}, + {pattern: 'galaxy/scripts/qunit/tests/metrics-logger.js', watched: false}, + {pattern: 'galaxy/scripts/qunit/tests/popover_tests.js', watched: false}, + {pattern: 'galaxy/scripts/qunit/tests/utils_test.js', watched: false}, + {pattern: 'galaxy/scripts/qunit/tests/page_tests.js', watched: false}, + {pattern: 'galaxy/scripts/qunit/tests/workflow_editor_tests.js', watched: false}, + // The following tests don't work for state reasons: + + // Error: Following test works on its own or with rest but not with + // list-of-pairs-collection-creator in the same suite. Not as much isolation + // as seperate page setup of previous runner. + // 'galaxy/scripts/qunit/tests/ui_tests.js', + // Error: things displayed wrong I guess cause assertions fail on CSS stuff + // 'galaxy/scripts/qunit/tests/modal_tests.js', + // 'galaxy/scripts/qunit/tests/upload_dialog_tests.js', + // Error: Cannot find module "libs/bibtexParse" + // 'galaxy/scripts/qunit/tests/form_tests.js', + // 'galaxy/scripts/qunit/tests/masthead_tests.js', +]; + +var TESTS_AS_SINGLE_PACK = [ + {pattern: 'galaxy/scripts/qunit/test.js', watched: false}, +]; + // karma.conf.js module.exports = function(config) { config.set({ basepath: '.', - files: [ - // Broken into uncommented working tests and commented out broken tests, - // this can be replaced with a glob when they all just work. - - // Working tests: - 'galaxy/scripts/qunit/tests/list-of-pairs-collection-creator.js', - 'galaxy/scripts/qunit/tests/galaxy-app-base.js', - 'galaxy/scripts/qunit/tests/graph.js', - 'galaxy/scripts/qunit/tests/hda-base.js', - 'galaxy/scripts/qunit/tests/history_contents_model_tests.js', - 'galaxy/scripts/qunit/tests/job-dag.js', - 'galaxy/scripts/qunit/tests/metrics-logger.js', - 'galaxy/scripts/qunit/tests/popover_tests.js', - 'galaxy/scripts/qunit/tests/utils_test.js', - 'galaxy/scripts/qunit/tests/page_tests.js', - 'galaxy/scripts/qunit/tests/workflow_editor_tests.js', - - // The following tests don't work for state reasons: - - // Error: Following test works on its own or with rest but not with - // list-of-pairs-collection-creator in the same suite. Not as much isolation - // as seperate page setup of previous runner. - // 'galaxy/scripts/qunit/tests/ui_tests.js', - // Error: things displayed wrong I guess cause assertions fail on CSS stuff - // 'galaxy/scripts/qunit/tests/modal_tests.js', - // 'galaxy/scripts/qunit/tests/upload_dialog_tests.js', - // Error: Cannot find module "libs/bibtexParse" - // 'galaxy/scripts/qunit/tests/form_tests.js', - // 'galaxy/scripts/qunit/tests/masthead_tests.js', - + files: (single_pack_mode() ? TESTS_AS_SINGLE_PACK : TESTS_SEPARATE_PACKS).concat([ // Non-test assets that will be served by web server. // CSS needed by tests. 'galaxy/scripts/qunit/assets/*.css', - ], - + ]), plugins: [ 'karma-webpack', 'karma-qunit', @@ -50,7 +60,7 @@ module.exports = function(config) { 'karma-phantomjs-launcher', ], polyfill: [ 'Object.assign' ], - browsers: [ 'PhantomJS' ], + // browsers: [ 'PhantomJS' ], // logLevel: config.LOG_DEBUG, // Tried to do some awesome babel, ES6 stuff in here but I was encountering @@ -58,13 +68,12 @@ module.exports = function(config) { // that we have a working setup. preprocessors: { // add webpack as preprocessor - 'galaxy/scripts/qunit/tests/*.js': ['webpack'], + 'galaxy/scripts/qunit/**/*.js': ['webpack'] }, frameworks: ['polyfill', 'qunit'], webpack: webpackConfig, webpackMiddleware: { noInfo: false } - }); }; diff --git a/client/package.json b/client/package.json index 4a8e3216c07..3ef445cc12c 100644 --- a/client/package.json +++ b/client/package.json @@ -39,7 +39,8 @@ "webpack-production-maps": "GXY_BUILD_SOURCEMAPS=1 webpack -p", "style": "grunt style", "style-watch": "grunt watch-style", - "test": "karma start --verbose --single-run --no-auto-watch karma.config.js", + "test": "karma start --verbose --single-run --browsers PhantomJS karma.config.js", + "test-watch": "karma start --verbose --browsers PhantomJS karma.config.js", "gulp": "gulp", "gulp-production": "NODE_ENV=production gulp", "gulp-production-maps": "GXY_BUILD_SOURCEMAPS=1 NODE_ENV=production gulp", diff --git a/config/datatypes_conf.xml.sample b/config/datatypes_conf.xml.sample index d1b1500c955..2d2b01ba7f6 100644 --- a/config/datatypes_conf.xml.sample +++ b/config/datatypes_conf.xml.sample @@ -643,6 +643,19 @@ + + + + + + + + + + + + + + + + diff --git a/lib/galaxy/app.py b/lib/galaxy/app.py index f076a5a4bf1..ddd905fee8d 100644 --- a/lib/galaxy/app.py +++ b/lib/galaxy/app.py @@ -55,12 +55,14 @@ class UniverseApplication(object, config.ConfiguresGalaxyMixin): self.name = 'galaxy' self.startup_timer = ExecutionTimer() self.new_installation = False - self.application_stack = application_stack_instance() # Read config file and check for errors self.config = config.Configuration(**kwargs) self.config.check() config.configure_logging(self.config) self.configure_fluent_log() + # A lot of postfork initialization depends on the server name, ensure it is set immediately after forking before other postfork functions + self.application_stack = application_stack_instance(app=self) + self.application_stack.register_postfork_function(self.application_stack.set_postfork_server_name, self) self.config.reload_sanitize_whitelist(explicit='sanitize_whitelist_file' in kwargs) self.amqp_internal_connection_obj = galaxy.queues.connection_from_config(self.config) # control_worker *can* be initialized with a queue, but here we don't @@ -188,10 +190,7 @@ class UniverseApplication(object, config.ConfiguresGalaxyMixin): # Start the job manager from galaxy.jobs import manager self.job_manager = manager.JobManager(self) - self.job_manager.start() - # FIXME: These are exposed directly for backward compatibility - self.job_queue = self.job_manager.job_queue - self.job_stop_queue = self.job_manager.job_stop_queue + self.application_stack.register_postfork_function(self.job_manager.start) self.proxy_manager = ProxyManager(self.config) # Initialize the external service types self.external_service_types = external_service_types.ExternalServiceTypesCollection( @@ -208,11 +207,15 @@ class UniverseApplication(object, config.ConfiguresGalaxyMixin): handlers[signal.SIGUSR1] = self.heartbeat.dump_signal_handler self._configure_signal_handlers(handlers) + # Start web stack message handling + self.application_stack.register_postfork_function(self.application_stack.start) + self.model.engine.dispose() self.server_starttime = int(time.time()) # used for cachebusting log.info("Galaxy app startup finished %s" % self.startup_timer) def shutdown(self): + log.debug('Shutting down') exception = None try: self.watchers.shutdown() @@ -258,8 +261,16 @@ class UniverseApplication(object, config.ConfiguresGalaxyMixin): exception = exception or e log.exception("Failed to shutdown SA database engine cleanly") + try: + self.application_stack.shutdown() + except Exception as e: + exception = exception or e + log.exception("Failed to shutdown application stack interface cleanly") + if exception: raise exception + else: + log.debug('Finished shutting down') def configure_fluent_log(self): if self.config.fluent_log: @@ -268,5 +279,6 @@ class UniverseApplication(object, config.ConfiguresGalaxyMixin): else: self.trace_logger = None + @property def is_job_handler(self): - return (self.config.track_jobs_in_database and self.job_config.is_handler(self.config.server_name)) or not self.config.track_jobs_in_database + return (self.config.track_jobs_in_database and self.job_config.is_handler) or not self.config.track_jobs_in_database diff --git a/lib/galaxy/config.py b/lib/galaxy/config.py index 13305fb4d83..af2a9aa0142 100644 --- a/lib/galaxy/config.py +++ b/lib/galaxy/config.py @@ -30,7 +30,7 @@ from galaxy.util import unicodify from galaxy.util.dbkeys import GenomeBuilds from galaxy.util.logging import LOGLV_TRACE from galaxy.web.formatting import expand_pretty_datetime_format -from galaxy.web.stack import register_postfork_function +from galaxy.web.stack import get_stack_facts, register_postfork_function from .version import VERSION_MAJOR log = logging.getLogger(__name__) @@ -79,6 +79,49 @@ PATH_LIST_DEFAULTS = dict( 'config/tool_conf.xml.sample,config/shed_tool_conf.xml'] ) +LOGGING_CONFIG_DEFAULT = { + 'version': 1, + 'root': { + 'handlers': ['console'], + 'level': 'INFO', + }, + 'loggers': { + 'galaxy': { + 'handlers': ['console'], + 'level': 'DEBUG', + 'propagate': 0, + 'qualname': 'galaxy', + }, + 'paste.httpserver.ThreadPool': { + 'level': 'WARN', + 'qualname': 'paste.httpserver.ThreadPool', + }, + 'routes.middleware': { + 'level': 'WARN', + 'qualname': 'routes.middleware', + }, + }, + 'filters': { + 'stack': { + '()': 'galaxy.web.stack.application_stack_log_filter', + }, + }, + 'handlers': { + 'console': { + 'class': 'logging.StreamHandler', + 'formatter': 'stack', + 'level': 'DEBUG', + 'stream': 'ext://sys.stderr', + 'filters': ['stack'], + }, + }, + 'formatters': { + 'stack': { + '()': 'galaxy.web.stack.application_stack_log_formatter', + }, + }, +} + def resolve_path(path, root): """If 'path' is relative make absolute by prepending 'root'""" @@ -245,8 +288,6 @@ class Configuration(object): self.collect_outputs_from = [x.strip() for x in kwargs.get('collect_outputs_from', 'new_file_path,job_working_directory').lower().split(',')] self.template_path = resolve_path(kwargs.get("template_path", "templates"), self.root) self.template_cache = resolve_path(kwargs.get("template_cache_path", "database/compiled_templates"), self.root) - self.local_job_queue_workers = int(kwargs.get("local_job_queue_workers", "5")) - self.cluster_job_queue_workers = int(kwargs.get("cluster_job_queue_workers", "3")) self.job_queue_cleanup_interval = int(kwargs.get("job_queue_cleanup_interval", "5")) self.cluster_files_directory = os.path.abspath(kwargs.get("cluster_files_directory", "database/pbs")) @@ -269,11 +310,6 @@ class Configuration(object): self.output_size_limit = int(kwargs.get('output_size_limit', 0)) self.retry_job_output_collection = int(kwargs.get('retry_job_output_collection', 0)) self.check_job_script_integrity = string_as_bool(kwargs.get("check_job_script_integrity", True)) - self.job_walltime = kwargs.get('job_walltime', None) - self.job_walltime_delta = None - if self.job_walltime is not None: - h, m, s = [int(v) for v in self.job_walltime.split(':')] - self.job_walltime_delta = timedelta(0, s, 0, 0, m, h) self.admin_users = kwargs.get("admin_users", "") self.admin_users_list = [u.strip() for u in self.admin_users.split(',') if u] self.mailing_join_addr = kwargs.get('mailing_join_addr', 'galaxy-announce-join@bx.psu.edu') @@ -306,7 +342,6 @@ class Configuration(object): self.smtp_password = kwargs.get('smtp_password', None) self.smtp_ssl = kwargs.get('smtp_ssl', None) self.track_jobs_in_database = string_as_bool(kwargs.get('track_jobs_in_database', 'True')) - self.start_job_runners = listify(kwargs.get('start_job_runners', '')) self.expose_dataset_path = string_as_bool(kwargs.get('expose_dataset_path', 'False')) self.expose_potentially_sensitive_job_metrics = string_as_bool(kwargs.get('expose_potentially_sensitive_job_metrics', 'False')) self.enable_communication_server = string_as_bool(kwargs.get('enable_communication_server', 'False')) @@ -354,12 +389,7 @@ class Configuration(object): self.parallelize_workflow_scheduling_within_histories = string_as_bool(kwargs.get('parallelize_workflow_scheduling_within_histories', 'False')) self.maximum_workflow_invocation_duration = int(kwargs.get("maximum_workflow_invocation_duration", 2678400)) - # Per-user Job concurrency limitations self.cache_user_job_count = string_as_bool(kwargs.get('cache_user_job_count', False)) - self.user_job_limit = int(kwargs.get('user_job_limit', 0)) - self.registered_user_job_limit = int(kwargs.get('registered_user_job_limit', self.user_job_limit)) - self.anonymous_user_job_limit = int(kwargs.get('anonymous_user_job_limit', self.user_job_limit)) - self.default_cluster_job_runner = kwargs.get('default_cluster_job_runner', 'local:///') self.pbs_application_server = kwargs.get('pbs_application_server', "") self.pbs_dataset_server = kwargs.get('pbs_dataset_server', "") self.pbs_dataset_path = kwargs.get('pbs_dataset_path', "") @@ -517,10 +547,12 @@ class Configuration(object): # Crummy, but PasteScript does not give you a way to determine this if arg.lower().startswith('--server-name='): self.server_name = arg.split('=', 1)[-1] - # Allow explicit override of server name in confg params + # Allow explicit override of server name in config params if "server_name" in kwargs: self.server_name = kwargs.get("server_name") - # Store all configured server names + # The application stack code may manipulate the server name + self.base_server_name = self.server_name + # Store all configured server names for the message queue routing self.server_names = [] for section in global_conf_parser.sections(): if section.startswith('server:'): @@ -549,12 +581,8 @@ class Configuration(object): self.galaxy_infrastructure_url_set = galaxy_infrastructure_url_set # Store advanced job management config - self.job_manager = kwargs.get('job_manager', self.server_name).strip() self.job_handlers = [x.strip() for x in kwargs.get('job_handlers', self.server_name).split(',')] self.default_job_handlers = [x.strip() for x in kwargs.get('default_job_handlers', ','.join(self.job_handlers)).split(',')] - # Store per-tool runner configs - self.tool_handlers = self.__read_tool_job_config(global_conf_parser, 'galaxy:tool_handlers', 'name') - self.tool_runners = self.__read_tool_job_config(global_conf_parser, 'galaxy:tool_runners', 'url') # Galaxy messaging (AMQP) configuration options self.amqp = {} try: @@ -590,6 +618,8 @@ class Configuration(object): self.api_folders = string_as_bool(kwargs.get('api_folders', False)) # This is for testing new library browsing capabilities. self.new_lib_browse = string_as_bool(kwargs.get('new_lib_browse', False)) + # Logging configuration with logging.config.configDict: + self.logging = kwargs.get('logging', None) # Error logging with sentry self.sentry_dsn = kwargs.get('sentry_dsn', None) # Statistics and profiling with statsd @@ -689,41 +719,6 @@ class Configuration(object): self.datatypes_config = self.datatypes_config_file self.tool_configs = self.tool_config_file - def __read_tool_job_config(self, global_conf_parser, section, key): - try: - tool_runners_config = global_conf_parser.items(section) - - # Process config to group multiple configs for the same tool. - rval = {} - for entry in tool_runners_config: - tool_config, val = entry - tool = None - runner_dict = {} - if tool_config.find("[") != -1: - # Found tool with additional params; put params in dict. - tool, params = tool_config[:-1].split("[") - param_dict = {} - for param in params.split(","): - name, value = param.split("@") - param_dict[name] = value - runner_dict['params'] = param_dict - else: - tool = tool_config - - # Add runner URL. - runner_dict[key] = val - - # Create tool entry if necessary. - if tool not in rval: - rval[tool] = [] - - # Add entry to runners. - rval[tool].append(runner_dict) - - return rval - except configparser.NoSectionError: - return {} - def get(self, key, default): return self.config_dict.get(key, default) @@ -891,34 +886,18 @@ def configure_logging(config): paste_configures_logging = False auto_configure_logging = not paste_configures_logging and string_as_bool(config.get("auto_configure_logging", "True")) if auto_configure_logging: - format = config.get("log_format", "%(name)s %(levelname)s %(asctime)s %(message)s") - level = logging._levelNames[config.get("log_level", "DEBUG")] - destination = config.get("log_destination", "stdout") - log.info("Logging at '%s' level to '%s'" % (level, destination)) - # Set level - root.setLevel(level) - - disable_chatty_loggers = string_as_bool(config.get("auto_configure_logging_disable_chatty", "True")) - if disable_chatty_loggers: - # Turn down paste httpserver logging - if level <= logging.DEBUG: - for chatty_logger in ["paste.httpserver.ThreadPool", "routes.middleware"]: - logging.getLogger(chatty_logger).setLevel(logging.WARN) - - # Remove old handlers - for h in root.handlers[:]: - root.removeHandler(h) - # Create handler - if destination == "stdout": - handler = logging.StreamHandler(sys.stdout) - else: - handler = logging.FileHandler(destination) - # Create formatter - formatter = logging.Formatter(format) - # Hook everything up - handler.setFormatter(formatter) - root.addHandler(handler) - # If sentry is configured, also log to it + logging_conf = config.get('logging', None) + if logging_conf is None: + # if using the default logging config, honor the log_level setting + logging_conf = LOGGING_CONFIG_DEFAULT + if config.get('log_level', 'DEBUG') != 'DEBUG': + logging_conf['handlers']['console']['level'] = config.get('log_level', 'DEBUG') + # configure logging with logging dict in config, template *FileHandler handler filenames with the `filename_template` option + for name, conf in logging_conf.get('handlers', {}).items(): + if conf['class'].startswith('logging.') and conf['class'].endswith('FileHandler') and 'filename_template' in conf: + conf['filename'] = conf.pop('filename_template').format(**get_stack_facts(config=config)) + logging_conf['handlers'][name] = conf + logging.config.dictConfig(logging_conf) if getattr(config, "sentry_dsn", None): from raven.handlers.logging import SentryHandler sentry_handler = SentryHandler(config.sentry_dsn) diff --git a/lib/galaxy/datatypes/annotation.py b/lib/galaxy/datatypes/annotation.py index 9a0717b5218..d5dd97cde80 100644 --- a/lib/galaxy/datatypes/annotation.py +++ b/lib/galaxy/datatypes/annotation.py @@ -14,7 +14,7 @@ class SnapHmm(Text): 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) dataset.blurb = "SNAP HMM model" else: dataset.peek = 'file does not exist' diff --git a/lib/galaxy/datatypes/binary.py b/lib/galaxy/datatypes/binary.py index 1d69cbba5fb..e3a99c931bb 100644 --- a/lib/galaxy/datatypes/binary.py +++ b/lib/galaxy/datatypes/binary.py @@ -1088,7 +1088,7 @@ class TwoBit(Binary): dataset.peek = "Binary TwoBit format nucleotide file" dataset.blurb = nice_size(dataset.get_size()) else: - return super(TwoBit, self).set_peek(dataset, is_multi_byte) + return super(TwoBit, self).set_peek(dataset) def display_peek(self, dataset): try: diff --git a/lib/galaxy/datatypes/blast.py b/lib/galaxy/datatypes/blast.py index 107202610f7..480c1ef1d67 100644 --- a/lib/galaxy/datatypes/blast.py +++ b/lib/galaxy/datatypes/blast.py @@ -53,7 +53,7 @@ class BlastXml(GenericXml): def set_peek(self, dataset, is_multi_byte=False): """Set the peek and blurb text""" 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) dataset.blurb = 'NCBI Blast XML data' else: dataset.peek = 'file does not exist' diff --git a/lib/galaxy/datatypes/constructive_solid_geometry.py b/lib/galaxy/datatypes/constructive_solid_geometry.py index 40325ffa5a0..9a31979afb3 100644 --- a/lib/galaxy/datatypes/constructive_solid_geometry.py +++ b/lib/galaxy/datatypes/constructive_solid_geometry.py @@ -102,7 +102,7 @@ class Ply(object): 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) dataset.blurb = "Faces: %s, Vertices: %s" % (str(dataset.metadata.face), str(dataset.metadata.vertex)) else: dataset.peek = 'File does not exist' @@ -429,7 +429,7 @@ class Vtk(object): 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) dataset.blurb = self.get_blurb(dataset) else: dataset.peek = 'File does not exist' diff --git a/lib/galaxy/datatypes/data.py b/lib/galaxy/datatypes/data.py index 1ae1b583f17..0931e2bd005 100644 --- a/lib/galaxy/datatypes/data.py +++ b/lib/galaxy/datatypes/data.py @@ -195,7 +195,12 @@ class Data(object): max_optional_metadata_filesize = property(get_max_optional_metadata_filesize, set_max_optional_metadata_filesize) def set_peek(self, dataset, is_multi_byte=False): - """Set the peek and blurb text""" + """ + Set the peek and blurb text + + :param is_multi_byte: deprecated + :type is_multi_byte: bool + """ if not dataset.dataset.purged: dataset.peek = '' dataset.blurb = 'data' @@ -838,7 +843,7 @@ class Text(Data): """ 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, 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: @@ -1046,7 +1051,10 @@ def get_test_fname(fname): 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 + Returns the first LINE_COUNT lines wrapped to WIDTH. + + :param is_multi_byte: deprecated + :type is_multi_byte: bool >>> fname = get_test_fname('4.bed') >>> get_file_peek(fname, LINE_COUNT=1) @@ -1061,20 +1069,12 @@ def get_file_peek(file_name, is_multi_byte=False, WIDTH=256, LINE_COUNT=5, skipc skipchars = [] lines = [] count = 0 - file_type = None - data_checked = False with compression_utils.get_fileobj(file_name, "U") as temp: while count < LINE_COUNT: - 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: - file_type = 'binary' - break - data_checked = True - if file_type == 'binary': - break + try: + line = temp.readline(WIDTH) + except UnicodeDecodeError: + return "binary file" if not line_wrap: if line.endswith('\n'): line = line[:-1] @@ -1091,11 +1091,4 @@ def get_file_peek(file_name, is_multi_byte=False, WIDTH=256, LINE_COUNT=5, skipc if not skip_line: lines.append(line) count += 1 - if file_type == 'binary': - text = "%s file" % file_type - else: - try: - text = util.unicodify('\n'.join(lines)) - except UnicodeDecodeError: - text = "binary/unknown file" - return text + return '\n'.join(lines) diff --git a/lib/galaxy/datatypes/genetics.py b/lib/galaxy/datatypes/genetics.py index d14da2823ed..895fb0c5011 100644 --- a/lib/galaxy/datatypes/genetics.py +++ b/lib/galaxy/datatypes/genetics.py @@ -20,6 +20,7 @@ from cgi import escape from six.moves.urllib.parse import quote_plus from galaxy.datatypes import metadata +from galaxy.datatypes.data import Text from galaxy.datatypes.metadata import MetadataElement from galaxy.datatypes.tabular import Tabular from galaxy.datatypes.text import Html @@ -31,6 +32,7 @@ verbose = False # https://genome.ucsc.edu/goldenpath/help/hgGenomeHelp.html VALID_GENOME_GRAPH_MARKERS = re.compile('^(chr.*|RH.*|rs.*|SNP_.*|CN.*|A_.*)') +VALID_GENOTYPES_LINE = re.compile('^([a-zA-Z0-9]+)(\\s([0-9]{2}|[A-Z]{2}|NC|\?\?))+\\s*$') class GenomeGraphs(Tabular): @@ -827,6 +829,281 @@ class MAlist(RexpBase): substitute_name_with_metadata='base_name', is_binary=True) +class LinkageStudies(Text): + """ + superclass for classical linkage analysis suites + """ + test_files = [ + 'linkstudies.allegro_fparam', 'linkstudies.alohomora_gts', + 'linkstudies.linkage_datain', 'linkstudies.linkage_map' + ] + + def __init__(self, **kwd): + Text.__init__(self, **kwd) + self.max_lines = 10 + + +class GenotypeMatrix(LinkageStudies): + """ + Sample matrix of genotypes + - GTs as columns + """ + file_ext = "alohomora_gts" + + def __init__(self, **kwd): + super(GenotypeMatrix, self).__init__(**kwd) + self.num_cols = -1 + + def header_check(self, fio): + header_elems = fio.readline().split('\t') + + if header_elems[0] != "Name": + return False + + try: + return all([int(sid) > 0 for sid in header_elems[1:]]) + except ValueError: + return False + + return True + + def sniff(self, filename): + """ + >>> classname = GenotypeMatrix + >>> from galaxy.datatypes.sniff import get_test_fname + >>> extn_true = classname().file_ext + >>> file_true = get_test_fname("linkstudies." + extn_true) + >>> classname().sniff(file_true) + True + + >>> false_files = list(LinkageStudies.test_files) + >>> false_files.remove("linkstudies." + extn_true) + >>> result_true = [] + >>> for fname in false_files: + ... file_false = get_test_fname(fname) + ... res = classname().sniff(file_false) + ... if res: + ... result_true.append(fname) + >>> + >>> result_true + [] + """ + with open(filename, "r") as fio: + + if not self.header_check(fio): + return False + + for lcount, line in enumerate(fio): + if lcount > self.max_lines: + return True + + tokens = line.split('\t') + + if self.num_cols == -1: + self.num_cols = len(tokens) + elif self.num_cols != len(tokens): + return False + if not VALID_GENOTYPES_LINE.match(line): + return False + + return True + + +class MarkerMap(LinkageStudies): + """ + Map of genetic markers including physical and genetic distance + Common input format for linkage programs + + chrom, genetic pos, markername, physical pos, Nr + """ + file_ext = "linkage_map" + + def header_check(self, fio): + headers = fio.readline().split() + + if len(headers) == 5 and headers[0] == "#Chr": + return True + + return False + + def sniff(self, filename): + """ + >>> classname = MarkerMap + >>> from galaxy.datatypes.sniff import get_test_fname + >>> extn_true = classname().file_ext + >>> file_true = get_test_fname("linkstudies." + extn_true) + >>> classname().sniff(file_true) + True + + >>> false_files = list(LinkageStudies.test_files) + >>> false_files.remove("linkstudies." + extn_true) + >>> result_true = [] + >>> for fname in false_files: + ... file_false = get_test_fname(fname) + ... res = classname().sniff(file_false) + ... if res: + ... result_true.append(fname) + >>> + >>> result_true + [] + """ + with open(filename, "r") as fio: + + if not self.header_check(fio): + return False + + for lcount, line in enumerate(fio): + if lcount > self.max_lines: + return True + + try: + chrm, gpos, nam, bpos, row = line.split() + float(gpos) + int(bpos) + + try: + int(chrm) + except ValueError: + if not chrm.lower()[0] in ('x', 'y', 'm'): + return False + + except ValueError: + return False + + return True + + +class DataIn(LinkageStudies): + """ + Common linkage input file for intermarker distances + and recombination rates + """ + file_ext = "linkage_datain" + + def __init__(self, **kwd): + super(DataIn, self).__init__(**kwd) + self.num_markers = None + self.intermarkers = 0 + + def eof_function(self): + return self.intermarkers > 0 + + def sniff(self, filename): + """ + >>> classname = DataIn + >>> from galaxy.datatypes.sniff import get_test_fname + >>> extn_true = classname().file_ext + >>> file_true = get_test_fname("linkstudies." + extn_true) + >>> classname().sniff(file_true) + True + + >>> false_files = list(LinkageStudies.test_files) + >>> false_files.remove("linkstudies." + extn_true) + >>> result_true = [] + >>> for fname in false_files: + ... file_false = get_test_fname(fname) + ... res = classname().sniff(file_false) + ... if res: + ... result_true.append(fname) + >>> + >>> result_true + [] + """ + with open(filename, "r") as fio: + + for lcount, line in enumerate(fio): + if lcount > self.max_lines: + return self.eof_function() + + tokens = line.split() + try: + if lcount == 0: + self.num_markers = int(tokens[0]) + map(int, tokens[1:]) + elif lcount == 1: + map(float, tokens) + + if len(tokens) != 4: + return False + elif lcount == 2: + map(int, tokens) + last_token = int(tokens[-1]) + + if self.num_markers is None: + return False + if len(tokens) != last_token: + return False + if self.num_markers != last_token: + return False + elif tokens[0] == "3" and tokens[1] == "2": + self.intermarkers += 1 + + except (ValueError, IndexError): + return False + + return self.eof_function() + + +class AllegroLOD(LinkageStudies): + """ + Allegro output format for LOD scores + """ + file_ext = "allegro_fparam" + + def header_check(self, fio): + header = fio.readline().splitlines()[0].split() + if len(header) == 4 and header == [ + "family", "location", "LOD", "marker" + ]: + return True + + return False + + def sniff(self, filename): + """ + >>> classname = AllegroLOD + >>> from galaxy.datatypes.sniff import get_test_fname + >>> extn_true = classname().file_ext + >>> file_true = get_test_fname("linkstudies." + extn_true) + >>> classname().sniff(file_true) + True + + >>> false_files = list(LinkageStudies.test_files) + >>> false_files.remove("linkstudies." + extn_true) + >>> result_true = [] + >>> for fname in false_files: + ... file_false = get_test_fname(fname) + ... res = classname().sniff(file_false) + ... if res: + ... result_true.append(fname) + >>> + >>> result_true + [] + """ + with open(filename, "r") as fio: + + if not self.header_check(fio): + return False + + for lcount, line in enumerate(fio): + if lcount > self.max_lines: + return True + + tokens = line.split() + + try: + int(tokens[0]) + float(tokens[1]) + + if tokens[2] != "-inf": + float(tokens[2]) + + except (ValueError, IndexError): + return False + + return True + + if __name__ == '__main__': import doctest doctest.testmod(sys.modules[__name__]) diff --git a/lib/galaxy/datatypes/graph.py b/lib/galaxy/datatypes/graph.py index cb50fb81d29..e8448fd155e 100644 --- a/lib/galaxy/datatypes/graph.py +++ b/lib/galaxy/datatypes/graph.py @@ -27,7 +27,7 @@ class Xgmml(xml.GenericXml): 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) dataset.blurb = 'XGMML data' else: dataset.peek = 'file does not exist' @@ -73,7 +73,7 @@ class Sif(tabular.Tabular): 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) dataset.blurb = 'SIF data' else: dataset.peek = 'file does not exist' diff --git a/lib/galaxy/datatypes/images.py b/lib/galaxy/datatypes/images.py index 6c7e457a9ee..46659607d41 100644 --- a/lib/galaxy/datatypes/images.py +++ b/lib/galaxy/datatypes/images.py @@ -7,7 +7,6 @@ import zipfile from six.moves.urllib.parse import quote_plus from galaxy.datatypes.binary import Binary -from galaxy.datatypes.sniff import get_headers from galaxy.datatypes.text import Html as HtmlFromText from galaxy.util import nice_size from galaxy.util.image_util import check_image_type @@ -160,14 +159,8 @@ class Pdf(Image): def sniff(self, filename): """Determine if the file is in pdf format.""" - headers = get_headers(filename, None, 1) - try: - if headers[0][0].startswith("%PDF"): - return True - else: - return False - except IndexError: - return False + with open(filename, 'rb') as fh: + return fh.read(4) == b"%PDF" Binary.register_sniffable_binary_format("pdf", "pdf", Pdf) diff --git a/lib/galaxy/datatypes/molecules.py b/lib/galaxy/datatypes/molecules.py index e7ee4734804..c3e00f33229 100644 --- a/lib/galaxy/datatypes/molecules.py +++ b/lib/galaxy/datatypes/molecules.py @@ -61,12 +61,11 @@ class GenericMolFile(data.Text): 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) if (dataset.metadata.number_of_molecules == 1): dataset.blurb = "1 molecule" else: dataset.blurb = "%s molecules" % dataset.metadata.number_of_molecules - dataset.peek = data.get_file_peek(dataset.file_name, is_multi_byte=is_multi_byte) + dataset.peek = get_file_peek(dataset.file_name) else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disk' @@ -471,7 +470,7 @@ class PHAR(GenericMolFile): 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) dataset.blurb = "pharmacophore" else: dataset.peek = 'file does not exist' @@ -524,7 +523,7 @@ class PDB(GenericMolFile): if not dataset.dataset.purged: atom_numbers = count_special_lines("^ATOM", dataset.file_name) hetatm_numbers = count_special_lines("^HETATM", dataset.file_name) - dataset.peek = get_file_peek(dataset.file_name, is_multi_byte=is_multi_byte) + dataset.peek = get_file_peek(dataset.file_name) dataset.blurb = "%s atoms and %s HET-atoms" % (atom_numbers, hetatm_numbers) else: dataset.peek = 'file does not exist' @@ -575,7 +574,7 @@ class PDBQT(GenericMolFile): if not dataset.dataset.purged: root_numbers = count_special_lines("^ROOT", dataset.file_name) branch_numbers = count_special_lines("^BRANCH", dataset.file_name) - dataset.peek = get_file_peek(dataset.file_name, is_multi_byte=is_multi_byte) + dataset.peek = get_file_peek(dataset.file_name) dataset.blurb = "%s roots and %s branches" % (root_numbers, branch_numbers) else: dataset.peek = 'file does not exist' @@ -587,7 +586,7 @@ class grd(data.Text): 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) dataset.blurb = "grids for docking" else: dataset.peek = 'file does not exist' @@ -621,12 +620,11 @@ class InChI(Tabular): 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) if (dataset.metadata.number_of_molecules == 1): dataset.blurb = "1 molecule" else: dataset.blurb = "%s molecules" % dataset.metadata.number_of_molecules - dataset.peek = data.get_file_peek(dataset.file_name, is_multi_byte=is_multi_byte) + dataset.peek = get_file_peek(dataset.file_name) else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disk' @@ -666,12 +664,11 @@ class SMILES(Tabular): 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) if dataset.metadata.number_of_molecules == 1: dataset.blurb = "1 molecule" else: dataset.blurb = "%s molecules" % dataset.metadata.number_of_molecules - dataset.peek = data.get_file_peek(dataset.file_name, is_multi_byte=is_multi_byte) + dataset.peek = get_file_peek(dataset.file_name) else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disk' @@ -727,12 +724,11 @@ class CML(GenericXml): 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) if (dataset.metadata.number_of_molecules == 1): dataset.blurb = "1 molecule" else: dataset.blurb = "%s molecules" % dataset.metadata.number_of_molecules - dataset.peek = data.get_file_peek(dataset.file_name, is_multi_byte=is_multi_byte) + dataset.peek = get_file_peek(dataset.file_name) else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disk' diff --git a/lib/galaxy/datatypes/msa.py b/lib/galaxy/datatypes/msa.py index cc26438ceca..3985ef95488 100644 --- a/lib/galaxy/datatypes/msa.py +++ b/lib/galaxy/datatypes/msa.py @@ -17,7 +17,7 @@ class Hmmer(Text): 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) dataset.blurb = "HMMER Database" else: dataset.peek = 'file does not exist' @@ -104,12 +104,11 @@ class Stockholm_1_0(Text): 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) 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) else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disc' @@ -187,12 +186,11 @@ class MauveXmfa(Text): 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) 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) else: dataset.peek = 'file does not exist' dataset.blurb = 'file purged from disc' diff --git a/lib/galaxy/datatypes/plant_tribes.py b/lib/galaxy/datatypes/plant_tribes.py index d275e1aedf1..983600a86b9 100644 --- a/lib/galaxy/datatypes/plant_tribes.py +++ b/lib/galaxy/datatypes/plant_tribes.py @@ -23,7 +23,7 @@ class Smat(Text): 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) dataset.blurb = "ESTScan scores matrices" else: dataset.peek = 'file does not exist' @@ -125,7 +125,7 @@ class PlantTribesKsComponents(Tabular): 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) if (dataset.metadata.number_comp == 1): dataset.blurb = "1 significant component" else: @@ -159,7 +159,7 @@ class PlantTribesOrtho(PlantTribes): file_ext = "ptortho" def set_peek(self, dataset, is_multi_byte=False): - super(PlantTribesOrtho, self).set_peek(dataset, is_multi_byte=is_multi_byte) + super(PlantTribesOrtho, self).set_peek(dataset) dataset.blurb = "Proteins orthogroup fasta files: %d items" % dataset.metadata.num_files @@ -171,7 +171,7 @@ class PlantTribesOrthoCodingSequence(PlantTribes): file_ext = "ptorthocs" def set_peek(self, dataset, is_multi_byte=False): - super(PlantTribesOrthoCodingSequence, self).set_peek(dataset, is_multi_byte=is_multi_byte) + super(PlantTribesOrthoCodingSequence, self).set_peek(dataset) dataset.blurb = "Protein and coding sequences orthogroup fasta files: %d items" % dataset.metadata.num_files @@ -182,7 +182,7 @@ class PlantTribesTargetedGeneFamilies(PlantTribes): file_ext = "pttgf" def set_peek(self, dataset, is_multi_byte=False): - super(PlantTribesTargetedGeneFamilies, self).set_peek(dataset, is_multi_byte=is_multi_byte) + super(PlantTribesTargetedGeneFamilies, self).set_peek(dataset) dataset.blurb = "Targeted gene families" @@ -194,7 +194,7 @@ class PlantTribesPhylogeneticTree(PlantTribes): file_ext = "pttree" def set_peek(self, dataset, is_multi_byte=False): - super(PlantTribesPhylogeneticTree, self).set_peek(dataset, is_multi_byte=is_multi_byte) + super(PlantTribesPhylogeneticTree, self).set_peek(dataset) dataset.blurb = "Phylogenetic trees: %d items" % dataset.metadata.num_files @@ -205,7 +205,7 @@ class PlantTribesPhylip(PlantTribes): file_ext = "ptphylip" def set_peek(self, dataset, is_multi_byte=False): - super(PlantTribesPhylip, self).set_peek(dataset, is_multi_byte=is_multi_byte) + super(PlantTribesPhylip, self).set_peek(dataset) dataset.blurb = "Orthogroup phylip multiple sequence alignments: %d items" % dataset.metadata.num_files @@ -216,7 +216,7 @@ class PlantTribesMultipleSequenceAlignment(PlantTribes): file_ext = "ptalign" def set_peek(self, dataset, is_multi_byte=False): - super(PlantTribesMultipleSequenceAlignment, self).set_peek(dataset, is_multi_byte=is_multi_byte) + super(PlantTribesMultipleSequenceAlignment, self).set_peek(dataset) dataset.blurb = "Proteins orthogroup alignments: %d items" % dataset.metadata.num_files @@ -227,7 +227,7 @@ class PlantTribesMultipleSequenceAlignmentCodonAlignment(PlantTribes): file_ext = "ptalignca" def set_peek(self, dataset, is_multi_byte=False): - super(PlantTribesMultipleSequenceAlignmentCodonAlignment, self).set_peek(dataset, is_multi_byte=is_multi_byte) + super(PlantTribesMultipleSequenceAlignmentCodonAlignment, self).set_peek(dataset) dataset.blurb = "Protein and coding sequences orthogroup alignments: %d items" % dataset.metadata.num_files @@ -238,7 +238,7 @@ class PlantTribesMultipleSequenceAlignmentTrimmed(PlantTribes): file_ext = "ptaligntrimmed" def set_peek(self, dataset, is_multi_byte=False): - super(PlantTribesMultipleSequenceAlignmentTrimmed, self).set_peek(dataset, is_multi_byte=is_multi_byte) + super(PlantTribesMultipleSequenceAlignmentTrimmed, self).set_peek(dataset) dataset.blurb = "Trimmed proteins orthogroup alignments: %d items" % dataset.metadata.num_files @@ -249,7 +249,7 @@ class PlantTribesMultipleSequenceAlignmentTrimmedCodonAlignment(PlantTribes): file_ext = "ptaligntrimmedca" def set_peek(self, dataset, is_multi_byte=False): - super(PlantTribesMultipleSequenceAlignmentTrimmedCodonAlignment, self).set_peek(dataset, is_multi_byte=is_multi_byte) + super(PlantTribesMultipleSequenceAlignmentTrimmedCodonAlignment, self).set_peek(dataset) dataset.blurb = "Trimmed protein and coding sequences orthogroup alignments: %d items" % dataset.metadata.num_files @@ -260,7 +260,7 @@ class PlantTribesMultipleSequenceAlignmentFiltered(PlantTribes): file_ext = "ptalignfiltered" def set_peek(self, dataset, is_multi_byte=False): - super(PlantTribesMultipleSequenceAlignmentFiltered, self).set_peek(dataset, is_multi_byte=is_multi_byte) + super(PlantTribesMultipleSequenceAlignmentFiltered, self).set_peek(dataset) dataset.blurb = "Filtered proteins orthogroup alignments: %d items" % dataset.metadata.num_files @@ -271,5 +271,5 @@ class PlantTribesMultipleSequenceAlignmentFilteredCodonAlignment(PlantTribes): file_ext = "ptalignfilteredca" def set_peek(self, dataset, is_multi_byte=False): - super(PlantTribesMultipleSequenceAlignmentFilteredCodonAlignment, self).set_peek(dataset, is_multi_byte=is_multi_byte) + super(PlantTribesMultipleSequenceAlignmentFilteredCodonAlignment, self).set_peek(dataset) dataset.blurb = "Filtered protein and coding sequences orthogroup alignments: %d items" % dataset.metadata.num_files diff --git a/lib/galaxy/datatypes/proteomics.py b/lib/galaxy/datatypes/proteomics.py index 27e923c2b3b..932e0fe90fa 100644 --- a/lib/galaxy/datatypes/proteomics.py +++ b/lib/galaxy/datatypes/proteomics.py @@ -114,7 +114,7 @@ class ProteomicsXml(GenericXml): 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) dataset.blurb = self.blurb else: dataset.peek = 'file does not exist' @@ -221,7 +221,7 @@ class Mgf(Text): 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) dataset.blurb = 'mgf Mascot Generic Format' else: dataset.peek = 'file does not exist' @@ -249,7 +249,7 @@ class MascotDat(Text): 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) dataset.blurb = 'mascotdat Mascot Search Results' else: dataset.peek = 'file does not exist' @@ -334,7 +334,7 @@ class SPLibNoIndex(Text): 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) dataset.blurb = 'Spectral Library without index files' else: dataset.peek = 'file does not exist' @@ -374,7 +374,7 @@ class SPLib(Msp): 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) dataset.blurb = 'splib Spectral Library Format' else: dataset.peek = 'file does not exist' diff --git a/lib/galaxy/datatypes/registry.py b/lib/galaxy/datatypes/registry.py index 4fd441e5863..08ea3d4f544 100644 --- a/lib/galaxy/datatypes/registry.py +++ b/lib/galaxy/datatypes/registry.py @@ -314,13 +314,14 @@ class Registry(object): def append_to_sniff_order(): # Just in case any supported data types are not included in the config's sniff_order section. for ext, datatype in self.datatypes_by_extension.items(): - included = False - for atype in self.sniff_order: - if isinstance(atype, datatype.__class__): - included = True - break - if not included: - self.sniff_order.append(datatype) + if hasattr(datatype, 'sniff'): + included = False + for atype in self.sniff_order: + if isinstance(atype, datatype.__class__): + included = True + break + if not included: + self.sniff_order.append(datatype) append_to_sniff_order() def _load_build_sites(self, root): diff --git a/lib/galaxy/datatypes/sequence.py b/lib/galaxy/datatypes/sequence.py index ec47b34235d..fe521fb49b5 100644 --- a/lib/galaxy/datatypes/sequence.py +++ b/lib/galaxy/datatypes/sequence.py @@ -61,7 +61,7 @@ class SequenceSplitLocations(data.Text): 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) dataset.blurb = '%d sections' % len(parsed_data['sections']) except Exception: dataset.peek = 'Not FQTOC file' @@ -112,7 +112,7 @@ class Sequence(data.Text): 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) if dataset.metadata.sequences: dataset.blurb = "%s sequences" % util.commaify(str(dataset.metadata.sequences)) else: @@ -861,7 +861,7 @@ class Maf(Alignment): 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) if dataset.metadata.blocks: dataset.blurb = "%s blocks" % util.commaify(str(dataset.metadata.blocks)) else: diff --git a/lib/galaxy/datatypes/sniff.py b/lib/galaxy/datatypes/sniff.py index 970beefa63d..e8de38168a3 100644 --- a/lib/galaxy/datatypes/sniff.py +++ b/lib/galaxy/datatypes/sniff.py @@ -20,7 +20,6 @@ from galaxy.datatypes.binary import Binary from galaxy.util import ( compression_utils, multi_byte, - unicodify ) from galaxy.util.checkers import ( check_binary, @@ -204,17 +203,11 @@ def convert_newlines_sep2tabs(fname, in_place=True, patt="\\s+", tmp_dir=None, t return (i + 1, temp_name) -def iter_headers(fname, sep, count=60, is_multi_byte=False, comment_designator=None): +def iter_headers(fname, sep, count=60, 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') - 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): continue yield line.split(sep) @@ -223,22 +216,22 @@ def iter_headers(fname, sep, count=60, is_multi_byte=False, comment_designator=N break -def get_headers(fname, sep, count=60, is_multi_byte=False, comment_designator=None): +def get_headers(fname, sep, count=60, 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']] + >>> 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']] + True >>> 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']] + >>> 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']] + True """ - return list(iter_headers(fname=fname, sep=sep, count=count, is_multi_byte=is_multi_byte, comment_designator=comment_designator)) + return list(iter_headers(fname=fname, sep=sep, count=count, comment_designator=comment_designator)) -def is_column_based(fname, sep='\t', skip=0, is_multi_byte=False): +def is_column_based(fname, sep='\t', skip=0): """ Checks whether the file is column based with respect to a separator (defaults to tab separator). @@ -266,7 +259,10 @@ 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) + try: + headers = get_headers(fname, sep) + except UnicodeDecodeError: + return False count = 0 if not headers: return False @@ -284,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): """ Returns an extension that can be used in the datatype factory to generate a data for the 'fname' file @@ -393,6 +389,9 @@ def guess_ext(fname, sniff_order, is_multi_byte=False): >>> fname = get_test_fname('biom2_sparse_otu_table_hdf5.biom') >>> guess_ext(fname, sniff_order) 'biom2' + >>> fname = get_test_fname('454Score.pdf') + >>> guess_ext(fname, sniff_order) + 'pdf' """ file_ext = None for datatype in sniff_order: @@ -414,28 +413,16 @@ 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): file_ext = 'tabular' if file_ext is not None: return file_ext - headers = get_headers(fname, None) - is_binary = False - if is_multi_byte: - is_binary = False - else: - for hdr in headers: - 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) - if is_binary: - break - if is_binary: - break - if is_binary: + try: + get_headers(fname, None) + except UnicodeDecodeError: 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): return 'tabular' # default tabular data type file extension return 'txt' # default text data type file extension @@ -489,14 +476,14 @@ def handle_compressed_file(filename, datatypes_registry, ext='auto'): return is_valid, ext -def handle_uploaded_dataset_file(filename, datatypes_registry, ext='auto', is_multi_byte=False): +def handle_uploaded_dataset_file(filename, datatypes_registry, ext='auto'): is_valid, ext = handle_compressed_file(filename, datatypes_registry, ext=ext) if not is_valid: 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) if check_binary(filename): if not Binary.is_ext_unsniffable(ext) and not datatypes_registry.get_datatype_by_extension(ext).sniff(filename): diff --git a/lib/galaxy/datatypes/tabular.py b/lib/galaxy/datatypes/tabular.py index 731ec358464..7f94565fb89 100644 --- a/lib/galaxy/datatypes/tabular.py +++ b/lib/galaxy/datatypes/tabular.py @@ -51,7 +51,7 @@ class TabularData(data.Text): 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) + super(TabularData, self).set_peek(dataset, line_count=line_count, 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))) @@ -825,7 +825,7 @@ class Eland(Tabular): - LANE, TILEm X, Y, INDEX, READ_NO, SEQ, QUAL, POSITION, *STRAND, FILT must be correct - We will only check that up to the first 5 alignments are correctly formatted. """ - with compression_utils.get_fileobj(filename, gzip_only=True) as fh: + with compression_utils.get_fileobj(filename, compressed_formats=['gzip']) as fh: count = 0 while True: line = fh.readline() diff --git a/lib/galaxy/datatypes/test/454Score.pdf b/lib/galaxy/datatypes/test/454Score.pdf new file mode 100644 index 00000000000..1199be2305a --- /dev/null +++ b/lib/galaxy/datatypes/test/454Score.pdf @@ -0,0 +1,545 @@ +%PDF-1.1 +%�â�ã�ρÓ\r +1 0 obj +<< +/CreationDate (D:20080403110358) +/ModDate (D:20080403110358) +/Title (R Graphics Output) +/Producer (R 2.6.2) +/Creator (R) +>> +endobj +2 0 obj +<< +/Type /Catalog +/Pages 3 0 R +>> +endobj +5 0 obj +<< +/Type /Font +/Subtype /Type1 +/Name /F1 +/BaseFont /ZapfDingbats +>> +endobj +6 0 obj +<< +/Type /Page +/Parent 3 0 R +/Contents 7 0 R +/Resources 4 0 R +>> +endobj +7 0 obj +<< +/Length 8 0 R +>> +stream +q +Q q 59.04 73.44 342.72 299.52 re W n +0.000 0.000 0.000 RG +2.25 w +[] 0 d +1 J +1 j +10.00 M +73.40 149.79 m 86.76 149.79 l S +0.75 w +[ 3.00 5.00] 0 d +80.08 100.85 m 80.08 149.79 l S +80.08 296.61 m 80.08 263.98 l S +0.75 w +[] 0 d +76.74 100.85 m 83.42 100.85 l S +76.74 296.61 m 83.42 296.61 l S +73.40 149.79 m +86.76 149.79 l +86.76 263.98 l +73.40 263.98 l +73.40 149.79 l +S +2.25 w +[] 0 d +90.11 280.30 m 103.47 280.30 l S +0.75 w +[ 3.00 5.00] 0 d +96.79 263.98 m 96.79 280.30 l S +96.79 296.61 m 96.79 296.61 l S +0.75 w +[] 0 d +93.45 263.98 m 100.13 263.98 l S +93.45 296.61 m 100.13 296.61 l S +90.11 280.30 m +103.47 280.30 l +103.47 296.61 l +90.11 296.61 l +90.11 280.30 l +S +BT +/F1 1 Tf 1 Tr 7.48 0 0 7.48 93.82 342.96 Tm (l) Tj 0 Tr +2.25 w +[] 0 d +ET +106.81 280.30 m 120.17 280.30 l S +0.75 w +[ 3.00 5.00] 0 d +113.49 263.98 m 113.49 263.98 l S +113.49 280.30 m 113.49 280.30 l S +0.75 w +[] 0 d +110.15 263.98 m 116.83 263.98 l S +110.15 280.30 m 116.83 280.30 l S +106.81 263.98 m +120.17 263.98 l +120.17 280.30 l +106.81 280.30 l +106.81 263.98 l +S +BT +/F1 1 Tf 1 Tr 7.48 0 0 7.48 110.53 179.82 Tm (l) Tj 0 Tr +2.25 w +[] 0 d +ET +123.51 247.67 m 136.87 247.67 l S +0.75 w +[ 3.00 5.00] 0 d +130.19 247.67 m 130.19 247.67 l S +130.19 247.67 m 130.19 247.67 l S +0.75 w +[] 0 d +126.85 247.67 m 133.53 247.67 l S +126.85 247.67 m 133.53 247.67 l S +123.51 247.67 m +136.87 247.67 l +136.87 247.67 l +123.51 247.67 l +123.51 247.67 l +S +BT +/F1 1 Tf 1 Tr 7.48 0 0 7.48 127.23 261.39 Tm (l) Tj 0 Tr +2.25 w +[] 0 d +ET +140.21 280.30 m 153.57 280.30 l S +0.75 w +[ 3.00 5.00] 0 d +146.89 231.36 m 146.89 247.67 l S +146.89 361.87 m 146.89 296.61 l S +0.75 w +[] 0 d +143.55 231.36 m 150.23 231.36 l S +143.55 361.87 m 150.23 361.87 l S +140.21 247.67 m +153.57 247.67 l +153.57 296.61 l +140.21 296.61 l +140.21 247.67 l +S +2.25 w +[] 0 d +156.91 280.30 m 170.27 280.30 l S +0.75 w +[ 3.00 5.00] 0 d +163.59 198.73 m 163.59 231.36 l S +163.59 280.30 m 163.59 280.30 l S +0.75 w +[] 0 d +160.25 198.73 m 166.93 198.73 l S +160.25 280.30 m 166.93 280.30 l S +156.91 231.36 m +170.27 231.36 l +170.27 280.30 l +156.91 280.30 l +156.91 231.36 l +S +2.25 w +[] 0 d +173.61 280.30 m 186.98 280.30 l S +0.75 w +[ 3.00 5.00] 0 d +180.29 247.67 m 180.29 263.98 l S +180.29 280.30 m 180.29 280.30 l S +0.75 w +[] 0 d +176.95 247.67 m 183.64 247.67 l S +176.95 280.30 m 183.64 280.30 l S +173.61 263.98 m +186.98 263.98 l +186.98 280.30 l +173.61 280.30 l +173.61 263.98 l +S +2.25 w +[] 0 d +190.32 247.67 m 203.68 247.67 l S +0.75 w +[ 3.00 5.00] 0 d +197.00 247.67 m 197.00 247.67 l S +197.00 263.98 m 197.00 263.98 l S +0.75 w +[] 0 d +193.66 247.67 m 200.34 247.67 l S +193.66 263.98 m 200.34 263.98 l S +190.32 247.67 m +203.68 247.67 l +203.68 263.98 l +190.32 263.98 l +190.32 247.67 l +S +BT +/F1 1 Tf 1 Tr 7.48 0 0 7.48 194.03 294.02 Tm (l) Tj 0 Tr +2.25 w +[] 0 d +ET +207.02 263.98 m 220.38 263.98 l S +0.75 w +[ 3.00 5.00] 0 d +213.70 247.67 m 213.70 247.67 l S +213.70 263.98 m 213.70 263.98 l S +0.75 w +[] 0 d +210.36 247.67 m 217.04 247.67 l S +210.36 263.98 m 217.04 263.98 l S +207.02 247.67 m +220.38 247.67 l +220.38 263.98 l +207.02 263.98 l +207.02 247.67 l +S +BT +/F1 1 Tf 1 Tr 7.48 0 0 7.48 210.74 342.96 Tm (l) Tj 0 Tr +2.25 w +[] 0 d +ET +223.72 263.98 m 237.08 263.98 l S +0.75 w +[ 3.00 5.00] 0 d +230.40 263.98 m 230.40 263.98 l S +230.40 280.30 m 230.40 280.30 l S +0.75 w +[] 0 d +227.06 263.98 m 233.74 263.98 l S +227.06 280.30 m 233.74 280.30 l S +223.72 263.98 m +237.08 263.98 l +237.08 280.30 l +223.72 280.30 l +223.72 263.98 l +S +BT +/F1 1 Tf 1 Tr 7.48 0 0 7.48 227.44 163.51 Tm (l) Tj 0 Tr +2.25 w +[] 0 d +ET +240.42 280.30 m 253.78 280.30 l S +0.75 w +[ 3.00 5.00] 0 d +247.10 263.98 m 247.10 263.98 l S +247.10 280.30 m 247.10 280.30 l S +0.75 w +[] 0 d +243.76 263.98 m 250.44 263.98 l S +243.76 280.30 m 250.44 280.30 l S +240.42 263.98 m +253.78 263.98 l +253.78 280.30 l +240.42 280.30 l +240.42 263.98 l +S +BT +/F1 1 Tf 1 Tr 7.48 0 0 7.48 244.14 179.82 Tm (l) Tj 0 Tr +2.25 w +[] 0 d +ET +257.12 263.98 m 270.48 263.98 l S +0.75 w +[ 3.00 5.00] 0 d +263.80 247.67 m 263.80 247.67 l S +263.80 280.30 m 263.80 280.30 l S +0.75 w +[] 0 d +260.46 247.67 m 267.14 247.67 l S +260.46 280.30 m 267.14 280.30 l S +257.12 247.67 m +270.48 247.67 l +270.48 280.30 l +257.12 280.30 l +257.12 247.67 l +S +2.25 w +[] 0 d +273.82 247.67 m 287.19 247.67 l S +0.75 w +[ 3.00 5.00] 0 d +280.51 84.53 m 280.51 182.42 l S +280.51 296.61 m 280.51 296.61 l S +0.75 w +[] 0 d +277.16 84.53 m 283.85 84.53 l S +277.16 296.61 m 283.85 296.61 l S +273.82 182.42 m +287.19 182.42 l +287.19 296.61 l +273.82 296.61 l +273.82 182.42 l +S +2.25 w +[] 0 d +290.53 280.30 m 303.89 280.30 l S +0.75 w +[ 3.00 5.00] 0 d +297.21 280.30 m 297.21 280.30 l S +297.21 280.30 m 297.21 280.30 l S +0.75 w +[] 0 d +293.87 280.30 m 300.55 280.30 l S +293.87 280.30 m 300.55 280.30 l S +290.53 280.30 m +303.89 280.30 l +303.89 280.30 l +290.53 280.30 l +290.53 280.30 l +S +BT +/F1 1 Tf 1 Tr 7.48 0 0 7.48 294.25 294.02 Tm (l) Tj 0 Tr +/F1 1 Tf 1 Tr 7.48 0 0 7.48 294.25 228.76 Tm (l) Tj 0 Tr +2.25 w +[] 0 d +ET +307.23 263.98 m 320.59 263.98 l S +0.75 w +[ 3.00 5.00] 0 d +313.91 247.67 m 313.91 247.67 l S +313.91 280.30 m 313.91 280.30 l S +0.75 w +[] 0 d +310.57 247.67 m 317.25 247.67 l S +310.57 280.30 m 317.25 280.30 l S +307.23 247.67 m +320.59 247.67 l +320.59 280.30 l +307.23 280.30 l +307.23 247.67 l +S +2.25 w +[] 0 d +323.93 231.36 m 337.29 231.36 l S +0.75 w +[ 3.00 5.00] 0 d +330.61 198.73 m 330.61 215.04 l S +330.61 231.36 m 330.61 231.36 l S +0.75 w +[] 0 d +327.27 198.73 m 333.95 198.73 l S +327.27 231.36 m 333.95 231.36 l S +323.93 215.04 m +337.29 215.04 l +337.29 231.36 l +323.93 231.36 l +323.93 215.04 l +S +BT +/F1 1 Tf 1 Tr 7.48 0 0 7.48 327.65 261.39 Tm (l) Tj 0 Tr +2.25 w +[] 0 d +ET +340.63 263.98 m 353.99 263.98 l S +0.75 w +[ 3.00 5.00] 0 d +347.31 263.98 m 347.31 263.98 l S +347.31 263.98 m 347.31 263.98 l S +0.75 w +[] 0 d +343.97 263.98 m 350.65 263.98 l S +343.97 263.98 m 350.65 263.98 l S +340.63 263.98 m +353.99 263.98 l +353.99 263.98 l +340.63 263.98 l +340.63 263.98 l +S +BT +/F1 1 Tf 1 Tr 7.48 0 0 7.48 344.35 179.82 Tm (l) Tj 0 Tr +/F1 1 Tf 1 Tr 7.48 0 0 7.48 344.35 277.70 Tm (l) Tj 0 Tr +2.25 w +[] 0 d +ET +357.33 263.98 m 370.69 263.98 l S +0.75 w +[ 3.00 5.00] 0 d +364.01 247.67 m 364.01 247.67 l S +364.01 280.30 m 364.01 280.30 l S +0.75 w +[] 0 d +360.67 247.67 m 367.35 247.67 l S +360.67 280.30 m 367.35 280.30 l S +357.33 247.67 m +370.69 247.67 l +370.69 280.30 l +357.33 280.30 l +357.33 247.67 l +S +BT +/F1 1 Tf 1 Tr 7.48 0 0 7.48 361.05 81.94 Tm (l) Tj 0 Tr +2.25 w +[] 0 d +ET +374.04 263.98 m 387.40 263.98 l S +0.75 w +[ 3.00 5.00] 0 d +380.72 263.98 m 380.72 263.98 l S +380.72 296.61 m 380.72 296.61 l S +0.75 w +[] 0 d +377.38 263.98 m 384.06 263.98 l S +377.38 296.61 m 384.06 296.61 l S +374.04 263.98 m +387.40 263.98 l +387.40 296.61 l +374.04 296.61 l +374.04 263.98 l +S +BT +/F1 1 Tf 1 Tr 7.48 0 0 7.48 377.75 163.51 Tm (l) Tj 0 Tr +ET +Q q +0.000 0.000 0.000 RG +0.75 w +[] 0 d +1 J +1 j +10.00 M +59.04 133.47 m 59.04 296.61 l S +59.04 133.47 m 51.84 133.47 l S +59.04 215.04 m 51.84 215.04 l S +59.04 296.61 m 51.84 296.61 l S +BT +0.000 0.000 0.000 rg +/F2 1 Tf 0.00 12.00 -12.00 0.00 41.76 126.80 Tm (20) Tj +/F2 1 Tf 0.00 12.00 -12.00 0.00 41.76 208.37 Tm (25) Tj +/F2 1 Tf 0.00 12.00 -12.00 0.00 41.76 289.94 Tm (30) Tj +ET +Q q +BT +0.000 0.000 0.000 rg +/F3 1 Tf 14.00 0.00 -0.00 14.00 147.76 397.45 Tm (boxplot of quality scores) Tj +/F2 1 Tf 12.00 0.00 -0.00 12.00 130.36 18.72 Tm (position within read \(% of total length\)) Tj +ET +Q q +0.000 0.000 0.000 RG +0.75 w +[] 0 d +1 J +1 j +10.00 M +59.04 73.44 m +401.76 73.44 l +401.76 372.96 l +59.04 372.96 l +59.04 73.44 l +S +63.38 73.44 m 380.72 73.44 l S +63.38 73.44 m 63.38 66.24 l S +80.08 73.44 m 80.08 66.24 l S +96.79 73.44 m 96.79 66.24 l S +113.49 73.44 m 113.49 66.24 l S +130.19 73.44 m 130.19 66.24 l S +146.89 73.44 m 146.89 66.24 l S +163.59 73.44 m 163.59 66.24 l S +180.29 73.44 m 180.29 66.24 l S +197.00 73.44 m 197.00 66.24 l S +213.70 73.44 m 213.70 66.24 l S +230.40 73.44 m 230.40 66.24 l S +247.10 73.44 m 247.10 66.24 l S +263.80 73.44 m 263.80 66.24 l S +280.51 73.44 m 280.51 66.24 l S +297.21 73.44 m 297.21 66.24 l S +313.91 73.44 m 313.91 66.24 l S +330.61 73.44 m 330.61 66.24 l S +347.31 73.44 m 347.31 66.24 l S +364.01 73.44 m 364.01 66.24 l S +380.72 73.44 m 380.72 66.24 l S +BT +0.000 0.000 0.000 rg +/F2 1 Tf 12.00 0.00 -0.00 12.00 60.05 47.52 Tm (0) Tj +/F2 1 Tf 12.00 0.00 -0.00 12.00 76.75 47.52 Tm (5) Tj +/F2 1 Tf 12.00 0.00 -0.00 12.00 106.82 47.52 Tm (15) Tj +/F2 1 Tf 12.00 0.00 -0.00 12.00 140.22 47.52 Tm (25) Tj +/F2 1 Tf 12.00 0.00 -0.00 12.00 173.62 47.52 Tm (35) Tj +/F2 1 Tf 12.00 0.00 -0.00 12.00 207.03 47.52 Tm (45) Tj +/F2 1 Tf 12.00 0.00 -0.00 12.00 240.43 47.52 Tm (55) Tj +/F2 1 Tf 12.00 0.00 -0.00 12.00 273.83 47.52 Tm (65) Tj +/F2 1 Tf 12.00 0.00 -0.00 12.00 307.24 47.52 Tm (75) Tj +/F2 1 Tf 12.00 0.00 -0.00 12.00 340.64 47.52 Tm (85) Tj +/F2 1 Tf 12.00 0.00 -0.00 12.00 374.04 47.52 Tm (95) Tj +ET +Q +endstream +endobj +8 0 obj +8714 +endobj +3 0 obj +<< +/Type /Pages +/Kids [ +6 0 R +] +/Count 1 +/MediaBox [0 0 432 432] +>> +endobj +4 0 obj +<< +/ProcSet [/PDF /Text] +/Font << /F1 5 0 R /F2 10 0 R /F3 11 0 R >> +/ExtGState << >> +>> +endobj +9 0 obj +<< +/Type /Encoding +/BaseEncoding /WinAnsiEncoding +/Differences [ 45/minus 96/quoteleft +144/dotlessi /grave /acute /circumflex /tilde /macron /breve /dotaccent +/dieresis /.notdef /ring /cedilla /.notdef /hungarumlaut /ogonek /caron /space] +>> +endobj +10 0 obj << +/Type /Font +/Subtype /Type1 +/Name /F2 +/BaseFont /Helvetica +/Encoding 9 0 R +>> endobj +11 0 obj << +/Type /Font +/Subtype /Type1 +/Name /F3 +/BaseFont /Helvetica-Bold +/Encoding 9 0 R +>> endobj +xref +0 12 +0000000000 65535 f +0000000021 00000 n +0000000163 00000 n +0000009162 00000 n +0000009245 00000 n +0000000212 00000 n +0000000295 00000 n +0000000375 00000 n +0000009142 00000 n +0000009349 00000 n +0000009606 00000 n +0000009703 00000 n +trailer +<< +/Size 12 +/Info 1 0 R +/Root 2 0 R +>> +startxref +9805 +%%EOF diff --git a/lib/galaxy/datatypes/test/linkstudies.allegro_fparam b/lib/galaxy/datatypes/test/linkstudies.allegro_fparam new file mode 100644 index 00000000000..14739725075 --- /dev/null +++ b/lib/galaxy/datatypes/test/linkstudies.allegro_fparam @@ -0,0 +1,100 @@ +family location LOD marker +1 0.000 -inf rs9617264 +1 0.180 -5.9958 rs5746647 +1 0.233 -inf rs5747988 +1 2.123 -9.4599 rs4819979 +1 2.198 -5.2942 rs4266110 +1 2.253 -5.0474 rs5748981 +1 2.358 -4.9631 rs12483926 +1 2.539 -inf rs17807605 +1 2.685 -inf rs5749001 +1 2.741 -5.4371 rs5749010 +1 2.851 -5.1217 rs4819994 +1 2.906 -5.1123 rs9617982 +1 2.996 -5.2452 rs9619055 +1 3.101 -inf rs361626 +1 3.232 -5.3301 rs5994287 +1 3.293 -inf rs9604737 +1 3.451 -8.1586 rs5747087 +1 3.507 -24.9936 rs7286310 +1 3.557 -inf rs2189077 +1 3.609 -8.2376 rs5747183 +1 3.861 -11.0094 rs174293 +1 3.927 -inf rs1296805 +1 4.024 -inf rs5992746 +1 4.086 -9.0399 rs3747023 +1 4.138 -inf rs5747339 +1 4.232 -inf rs5747351 +1 4.375 -inf rs443627 +1 4.524 -inf rs389496 +1 4.586 -inf rs5992854 +1 4.649 -inf rs2587115 +1 4.702 -inf rs5992126 +1 4.753 -inf rs443983 +1 4.972 -inf rs390495 +1 5.041 -inf rs462904 +1 5.187 -22.4923 rs5992985 +1 5.279 -inf rs462055 +1 5.358 -7.3545 rs362043 +1 5.441 -6.9638 rs2540620 +1 6.072 -inf rs2543958 +1 6.158 -11.2824 rs418623 +1 6.219 -6.9834 rs759406 +1 6.293 -6.9610 rs5747950 +1 6.360 -7.0468 rs5993463 +1 6.471 -11.2945 rs737923 +1 21.656 -17.9844 rs2187966 +1 22.016 -8.9528 rs9624813 +1 22.162 -inf rs742030 +1 22.221 -inf rs1467387 +1 22.334 -8.9315 rs542162 +1 22.457 -inf rs12627968 +1 22.551 -inf rs133885 +1 22.633 -25.6794 rs713816 +1 22.688 -inf rs6004774 +1 22.756 -inf rs2301492 +1 22.911 -inf rs2157538 +1 23.108 -8.8047 rs8137311 +1 23.211 -inf rs1013815 +1 23.322 -inf rs4820663 +1 23.395 -inf rs9620577 +1 23.624 -7.6981 rs8136102 +1 23.760 -inf rs12628683 +1 23.908 -inf rs5752264 +1 24.053 -inf rs134760 +1 24.111 -10.3274 rs2331215 +1 24.164 -inf rs667270 +1 24.240 -9.4447 rs8138440 +1 24.318 -inf rs633132 +1 24.393 -7.3251 rs4822702 +1 24.515 -7.1150 rs5761487 +1 24.608 -inf rs1941126 +1 24.856 -7.2865 rs600878 +1 24.946 -22.4631 rs134157 +1 25.003 -inf rs9613212 +1 25.054 -11.5789 rs2071862 +1 25.106 -inf rs4822768 +1 25.215 -14.0176 rs2001121 +1 25.420 -8.4477 rs6005159 +1 25.492 -8.6606 rs4820706 +1 25.622 -inf rs4822791 +1 25.865 -inf rs1476033 +1 25.997 -inf rs6005189 +1 26.105 -8.1743 rs5761777 +1 26.193 -8.0074 rs5761805 +1 26.372 -8.2580 rs6005232 +1 26.447 -inf rs7290832 +1 26.674 -20.6108 rs6005254 +1 26.803 -18.5282 rs2516082 +1 26.906 -inf rs6005266 +1 27.050 -inf rs4418 +1 27.198 -inf rs79037 +1 27.268 -inf rs5761937 +1 27.358 -inf rs5761957 +1 27.483 -inf rs134949 +1 37.021 -inf rs5749711 +1 37.135 -10.6102 rs242990 +1 37.204 -10.3303 rs12485116 +1 37.277 -inf rs5999265 +1 37.363 -inf rs5755038 +1 37.474 -inf rs9621951 diff --git a/lib/galaxy/datatypes/test/linkstudies.alohomora_gts b/lib/galaxy/datatypes/test/linkstudies.alohomora_gts new file mode 100644 index 00000000000..f561c401a98 --- /dev/null +++ b/lib/galaxy/datatypes/test/linkstudies.alohomora_gts @@ -0,0 +1,80 @@ +Name 206007 2060011 206004 2060010 2060016 2060012 2060017 2060013 206008 206001 206002 206 +rs3094315 AA AB AB AA AA AB AA AB AA AB AA +rs2073813 BB AB AB BB BB AB BB AB BB AB BB +rs2905040 BB BB BB BB BB BB BB BB BB BB BB +rs12124819 AA AA AA AB AB AA AB AB BB AB AB +rs2980314 BB BB BB BB BB BB BB BB BB BB BB +rs6684487 BB BB BB BB BB BB BB BB BB BB BB +rs4245756 BB BB BB BB BB BB BB BB BB BB BB +rs12086311 BB BB BB BB BB BB BB BB BB BB BB +rs13303077 BB BB BB BB BB BB BB BB BB BB BB +rs28625089 BB BB BB BB BB BB BB BB BB BB BB +rs4475691 BB BB BB BB BB BB BB BB BB BB BB +rs28587382 BB BB BB BB BB BB BB BB BB BB BB +rs13303029 BB BB BB BB BB BB BB BB BB BB BB +rs2340589 BB BB BB BB BB BB BB BB BB BB BB +rs28576697 AA AB BB AA AA AB AA AB AA AB AB +rs1110052 AA AB BB AA AA AB AA AB AA AB AB +rs7523549 BB BB BB AB BB BB BB BB AB AB BB +rs2272756 BB AB AA BB BB AB BB AB BB AB AB +rs28711536 BB BB BB BB BB BB BB BB BB BB BB +rs13303010 AA AA AA AB AA AA AA AA AB AB AA +rs3935066 AA AA AA AB AA AA AA AA AB AB AA +rs13302925 BB BB BB BB BB BB BB BB BB BB BB +rs13303355 BB BB BB BB BB BB BB BB BB BB BB +rs6693747 AB AB AA BB AB AB AB AB AB AB AA +rs9777703 AA AA AA AA AA AA AA AA AA AA AA +rs9697457 BB BB BB BB BB BB BB BB BB BB BB +rs28360890 BB BB BB BB BB BB BB BB BB BB BB +rs3128117 AB AB BB AB AB AB AB AB BB BB BB +rs28446115 BB BB BB BB BB BB BB BB BB BB BB +rs28555183 BB BB BB BB BB BB BB BB BB BB BB +rs4970393 AB AB BB AB AB AB AB AB BB BB BB +rs4970349 AB AB BB AB AB AB AB AB BB BB BB +rs17160776 BB BB BB BB BB BB BB BB BB BB BB +rs2245754 AA AA AB AA AB AA AB AB AB AB AB +rs7526076 AB AB AA AB AB AB AB AB AA AA AA +rs3934834 BB BB BB BB AB BB AB BB BB BB BB +rs3766191 BB BB BB BB AB BB AB BB BB BB BB +rs9442372 AB AB AA AB AA AB AA AB AA AA AA +rs3737728 AB AB AA AB AB AB AB AB AA AA AA +rs10907178 AA AA AA AA AB AA AB AA AA AA AA +rs6687776 BB AB AB BB AB AB AB BB BB BB AB +rs11579015 AA AA AA AA AA AA AA AA AA AA AA +rs6671356 AA AA AA AA AB AA AB AA AA AA AA +rs11811175 BB BB BB BB BB BB BB BB BB BB BB +rs4970405 AA AA AA AA AA AA AA AA AA AA AA +rs1105065 BB BB BB BB AB BB AB BB BB BB BB +rs7540009 BB BB BB BB AB BB AB BB BB BB BB +rs10907182 AB AB AB AB AA AB AA AB AA AB AA +rs12737205 BB BB BB BB AB BB AB BB BB BB BB +rs11579425 AB BB BB AB AB BB AB BB AA AB AB +rs4970416 BB BB BB BB BB BB BB BB BB BB BB +rs4970362 AB BB BB BB BB BB BB AB BB BB BB +rs9660710 BB BB BB BB BB BB BB AB BB BB BB +rs4442317 AA BB AB AA AA BB AA AB AA AA AB +rs12092254 BB BB AB BB AB BB AB AB BB AB BB +rs6668667 BB BB BB BB AB BB AB BB BB BB BB +rs3813204 BB AB AB BB BB AB BB AB BB AB BB +rs4314833 AA AB AB AA AA AB AA AB AA AB AA +rs12060422 AB BB BB AB AB BB AB BB BB BB BB +rs9729550 AB AB AB AB AB AB AB AB AA AB AA +rs11466681 BB BB BB BB BB BB BB BB BB BB BB +rs34945898 BB BB BB BB BB BB BB BB BB BB BB +rs12036216 AB BB BB BB AB BB AB BB BB BB BB +rs3813199 AB BB BB BB AB BB AB BB BB BB BB +rs11260562 BB BB BB BB BB BB BB BB BB BB BB +rs7528416 AB AA AA AA AB AA AB AA AA AA AA +rs715643 BB BB BB BB BB BB BB BB BB BB BB +rs12093154 AB BB BB BB AB BB AB BB BB BB BB +rs6692115 NC NC NC NC NC NC NC NC NC NC NC +rs7524470 AA AA AA AA AA AA AA AA AA AA AA +rs6704013 BB BB BB BB BB BB BB BB BB BB BB +rs4018608 AA AB AB AA AA AB AA AA AA AA AB +rs12073590 AA AA AA AA AA AA AA AA AA AA AA +rs6689813 AA AB AB AA AA AB AA AA AA AA AB +rs34778240 BB BB BB BB BB BB BB BB BB BB BB +rs12077700 BB AB AB BB BB AB BB BB BB BB AB +rs1749951 BB BB BB BB BB BB BB BB BB BB BB +rs11586188 BB BB BB BB BB BB BB BB BB BB BB +rs35573647 BB BB BB BB BB BB BB BB BB BB BB diff --git a/lib/galaxy/datatypes/test/linkstudies.linkage_datain b/lib/galaxy/datatypes/test/linkstudies.linkage_datain new file mode 100644 index 00000000000..a385efe2be8 --- /dev/null +++ b/lib/galaxy/datatypes/test/linkstudies.linkage_datain @@ -0,0 +1,1172 @@ +582 0 0 5 + 0 0.0 0.0 0 + 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 +1 2 # TRAIT + 0.9999 0.0001 + 1 + 0.000 0.000 1.000 +3 2 # rs1296971 + 0.4583 0.5417 +3 2 # rs468601 + 0.1356 0.8644 +3 2 # rs2821973 + 0.4083 0.5917 +3 2 # rs1929150 + 0.3559 0.6441 +3 2 # rs2822124 + 0.4407 0.5593 +3 2 # rs2775054 + 0.2583 0.7417 +3 2 # rs7276618 + 0.4167 0.5833 +3 2 # rs392812 + 0.1000 0.9000 +3 2 # rs2822368 + 0.2833 0.7167 +3 2 # rs6516610 + 0.3167 0.6833 +3 2 # rs447455 + 0.2000 0.8000 +3 2 # rs437521 + 0.1667 0.8333 +3 2 # rs2822554 + 0.1417 0.8583 +3 2 # rs2822572 + 0.1000 0.9000 +3 2 # rs13050350 + 0.3167 0.6833 +3 2 # rs2142236 + 0.0667 0.9333 +3 2 # rs2822677 + 0.0508 0.9492 +3 2 # rs2822696 + 0.0833 0.9167 +3 2 # rs376635 + 0.4500 0.5500 +3 2 # rs2822765 + 0.4333 0.5667 +3 2 # rs2822780 + 0.1667 0.8333 +3 2 # rs465340 + 0.4917 0.5083 +3 2 # rs1888398 + 0.2250 0.7750 +3 2 # rs458052 + 0.3333 0.6667 +3 2 # rs2822907 + 0.3250 0.6750 +3 2 # rs8129531 + 0.2333 0.7667 +3 2 # rs12053660 + 0.1500 0.8500 +3 2 # rs1883003 + 0.2750 0.7250 +3 2 # rs2822974 + 0.4833 0.5167 +3 2 # rs11088231 + 0.1667 0.8333 +3 2 # rs2205239 + 0.4250 0.5750 +3 2 # rs2823045 + 0.2417 0.7583 +3 2 # rs926164 + 0.2750 0.7250 +3 2 # rs2823139 + 0.2417 0.7583 +3 2 # rs2823161 + 0.3667 0.6333 +3 2 # rs2823194 + 0.0750 0.9250 +3 2 # rs2049882 + 0.0917 0.9083 +3 2 # rs1736148 + 0.4417 0.5583 +3 2 # rs2823301 + 0.2583 0.7417 +3 2 # rs6517467 + 0.2333 0.7667 +3 2 # rs2064051 + 0.2333 0.7667 +3 2 # rs9974915 + 0.1000 0.9000 +3 2 # rs2823400 + 0.3583 0.6417 +3 2 # rs726634 + 0.3167 0.6833 +3 2 # rs7283707 + 0.1083 0.8917 +3 2 # rs7283161 + 0.4333 0.5667 +3 2 # rs9982633 + 0.2500 0.7500 +3 2 # rs2823621 + 0.4333 0.5667 +3 2 # rs2051347 + 0.3000 0.7000 +3 2 # rs2823702 + 0.3417 0.6583 +3 2 # rs189457 + 0.3167 0.6833 +3 2 # rs2823748 + 0.3167 0.6833 +3 2 # rs2849869 + 0.2417 0.7583 +3 2 # rs1667568 + 0.0917 0.9083 +3 2 # rs171267 + 0.4417 0.5583 +3 2 # rs11911987 + 0.0333 0.9667 +3 2 # rs2823881 + 0.0417 0.9583 +3 2 # rs2823912 + 0.0917 0.9083 +3 2 # rs2032287 + 0.3333 0.6667 +3 2 # rs4818278 + 0.2667 0.7333 +3 2 # rs11700643 + 0.2833 0.7167 +3 2 # rs418989 + 0.1333 0.8667 +3 2 # rs4818307 + 0.0583 0.9417 +3 2 # rs11701390 + 0.3667 0.6333 +3 2 # rs2824192 + 0.4825 0.5175 +3 2 # rs2824231 + 0.4250 0.5750 +3 2 # rs1501813 + 0.1917 0.8083 +3 2 # rs2824273 + 0.3833 0.6167 +3 2 # rs2824282 + 0.3500 0.6500 +3 2 # rs4536738 + 0.3000 0.7000 +3 2 # rs2824292 + 0.4917 0.5083 +3 2 # rs208885 + 0.2250 0.7750 +3 2 # rs2849886 + 0.1667 0.8333 +3 2 # rs764657 + 0.3917 0.6083 +3 2 # rs9977638 + 0.3417 0.6583 +3 2 # rs2824435 + 0.2000 0.8000 +3 2 # rs243570 + 0.2750 0.7250 +3 2 # rs243587 + 0.4917 0.5083 +3 2 # rs243619 + 0.4667 0.5333 +3 2 # rs2824560 + 0.3333 0.6667 +3 2 # rs200801 + 0.5000 0.5000 +3 2 # rs157740 + 0.3750 0.6250 +3 2 # rs2260211 + 0.1750 0.8250 +3 2 # rs1491781 + 0.3750 0.6250 +3 2 # rs2824646 + 0.2667 0.7333 +3 2 # rs2824669 + 0.3333 0.6667 +3 2 # rs2824688 + 0.1417 0.8583 +3 2 # rs2824699 + 0.0583 0.9417 +3 2 # rs2824704 + 0.3750 0.6250 +3 2 # rs9983595 + 0.1750 0.8250 +3 2 # rs7283555 + 0.1333 0.8667 +3 2 # rs2824733 + 0.2250 0.7750 +3 2 # rs2824760 + 0.4167 0.5833 +3 2 # rs17781453 + 0.2203 0.7797 +3 2 # rs978421 + 0.4583 0.5417 +3 2 # rs2251831 + 0.1167 0.8833 +3 2 # rs1735766 + 0.3833 0.6167 +3 2 # rs2824909 + 0.3667 0.6333 +3 2 # rs8133676 + 0.3083 0.6917 +3 2 # rs8129581 + 0.0417 0.9583 +3 2 # rs1159082 + 0.2083 0.7917 +3 2 # rs432177 + 0.2917 0.7083 +3 2 # rs443581 + 0.1500 0.8500 +3 2 # rs2178993 + 0.4000 0.6000 +3 2 # rs2825304 + 0.3917 0.6083 +3 2 # rs764063 + 0.4667 0.5333 +3 2 # rs2825410 + 0.3917 0.6083 +3 2 # rs856168 + 0.2083 0.7917 +3 2 # rs2825547 + 0.1167 0.8833 +3 2 # rs1489734 + 0.4417 0.5583 +3 2 # rs434435 + 0.1583 0.8417 +3 2 # rs2244367 + 0.2333 0.7667 +3 2 # rs2825673 + 0.1356 0.8644 +3 2 # rs7277498 + 0.2917 0.7083 +3 2 # rs2825728 + 0.1917 0.8083 +3 2 # rs2825750 + 0.0917 0.9083 +3 2 # rs13433388 + 0.1167 0.8833 +3 2 # rs7275645 + 0.1583 0.8417 +3 2 # rs2825924 + 0.3167 0.6833 +3 2 # rs2825960 + 0.3250 0.6750 +3 2 # rs2825977 + 0.1417 0.8583 +3 2 # rs2826065 + 0.4583 0.5417 +3 2 # rs2826125 + 0.4083 0.5917 +3 2 # rs2246644 + 0.2250 0.7750 +3 2 # rs2091991 + 0.2583 0.7417 +3 2 # rs994455 + 0.3250 0.6750 +3 2 # rs2826262 + 0.1000 0.9000 +3 2 # rs2826339 + 0.2833 0.7167 +3 2 # rs1893329 + 0.1833 0.8167 +3 2 # rs2826388 + 0.4083 0.5917 +3 2 # rs1351599 + 0.3250 0.6750 +3 2 # rs229448 + 0.3917 0.6083 +3 2 # rs2826418 + 0.1417 0.8583 +3 2 # rs9974471 + 0.0750 0.9250 +3 2 # rs2826538 + 0.2583 0.7417 +3 2 # rs9981937 + 0.4500 0.5500 +3 2 # rs2826596 + 0.1917 0.8083 +3 2 # rs6518018 + 0.4500 0.5500 +3 2 # rs8126872 + 0.0583 0.9417 +3 2 # rs1980966 + 0.2083 0.7917 +3 2 # rs2826662 + 0.4250 0.5750 +3 2 # rs2826676 + 0.3000 0.7000 +3 2 # rs2826713 + 0.4333 0.5667 +3 2 # rs1373484 + 0.2250 0.7750 +3 2 # rs2826844 + 0.1500 0.8500 +3 2 # rs233803 + 0.0750 0.9250 +3 2 # rs8134226 + 0.2500 0.7500 +3 2 # rs1735990 + 0.4167 0.5833 +3 2 # rs2826982 + 0.3417 0.6583 +3 2 # rs2827045 + 0.1083 0.8917 +3 2 # rs2827102 + 0.1167 0.8833 +3 2 # rs2246589 + 0.2417 0.7583 +3 2 # rs2154723 + 0.4667 0.5333 +3 2 # rs2827203 + 0.3333 0.6667 +3 2 # rs2827251 + 0.0833 0.9167 +3 2 # rs2827308 + 0.4583 0.5417 +3 2 # rs2827322 + 0.0917 0.9083 +3 2 # rs2827338 + 0.3167 0.6833 +3 2 # rs2014609 + 0.3000 0.7000 +3 2 # rs2827606 + 0.2750 0.7250 +3 2 # rs2827641 + 0.2500 0.7500 +3 2 # rs8127947 + 0.1250 0.8750 +3 2 # rs117674 + 0.3083 0.6917 +3 2 # rs198114 + 0.4000 0.6000 +3 2 # rs405227 + 0.4083 0.5917 +3 2 # rs2827880 + 0.1083 0.8917 +3 2 # rs8134936 + 0.0667 0.9333 +3 2 # rs2828019 + 0.1667 0.8333 +3 2 # rs2828068 + 0.3583 0.6417 +3 2 # rs2828098 + 0.4000 0.6000 +3 2 # rs9980706 + 0.2333 0.7667 +3 2 # rs2828167 + 0.2333 0.7667 +3 2 # rs2212575 + 0.3417 0.6583 +3 2 # rs1514417 + 0.1917 0.8083 +3 2 # rs1514414 + 0.4917 0.5083 +3 2 # rs2828457 + 0.1583 0.8417 +3 2 # rs2828520 + 0.3417 0.6583 +3 2 # rs2828549 + 0.0667 0.9333 +3 2 # rs2828622 + 0.2083 0.7917 +3 2 # rs9977556 + 0.2000 0.8000 +3 2 # rs2154734 + 0.3500 0.6500 +3 2 # rs1910670 + 0.2667 0.7333 +3 2 # rs11087904 + 0.0917 0.9083 +3 2 # rs12627150 + 0.2917 0.7083 +3 2 # rs2828914 + 0.1417 0.8583 +3 2 # rs2829026 + 0.2667 0.7333 +3 2 # rs2829049 + 0.4333 0.5667 +3 2 # rs2829086 + 0.3000 0.7000 +3 2 # rs12053689 + 0.0167 0.9833 +3 2 # rs728067 + 0.3833 0.6167 +3 2 # rs4816976 + 0.4083 0.5917 +3 2 # rs2829227 + 0.1833 0.8167 +3 2 # rs17000204 + 0.0833 0.9167 +3 2 # rs2829285 + 0.4167 0.5833 +3 2 # rs17000332 + 0.0917 0.9083 +3 2 # rs17689314 + 0.0750 0.9250 +3 2 # rs4283495 + 0.2083 0.7917 +3 2 # rs2154526 + 0.4250 0.5750 +3 2 # rs2829558 + 0.3500 0.6500 +3 2 # rs12626495 + 0.1667 0.8333 +3 2 # rs2829726 + 0.1750 0.8250 +3 2 # rs2829775 + 0.0750 0.9250 +3 2 # rs2829856 + 0.2833 0.7167 +3 2 # rs2829914 + 0.0417 0.9583 +3 2 # rs2829964 + 0.4667 0.5333 +3 2 # rs1787438 + 0.1083 0.8917 +3 2 # rs9305268 + 0.1917 0.8083 +3 2 # rs3991 + 0.3000 0.7000 +3 2 # rs466609 + 0.1167 0.8833 +3 2 # rs457581 + 0.2542 0.7458 +3 2 # rs468476 + 0.3250 0.6750 +3 2 # rs2117454 + 0.1833 0.8167 +3 2 # rs219712 + 0.1083 0.8917 +3 2 # rs2830206 + 0.4417 0.5583 +3 2 # rs2830217 + 0.0583 0.9417 +3 2 # rs2830227 + 0.4417 0.5583 +3 2 # rs219672 + 0.4083 0.5917 +3 2 # rs222920 + 0.1917 0.8083 +3 2 # rs222956 + 0.2417 0.7583 +3 2 # rs458076 + 0.4000 0.6000 +3 2 # rs9967959 + 0.1667 0.8333 +3 2 # rs2830442 + 0.3000 0.7000 +3 2 # rs2830511 + 0.2333 0.7667 +3 2 # rs370850 + 0.2500 0.7500 +3 2 # rs229100 + 0.1333 0.8667 +3 2 # rs2830585 + 0.2500 0.7500 +3 2 # rs2830605 + 0.3333 0.6667 +3 2 # rs2830640 + 0.3833 0.6167 +3 2 # rs2830715 + 0.3421 0.6579 +3 2 # rs969988 + 0.3644 0.6356 +3 2 # rs2830768 + 0.1583 0.8417 +3 2 # rs235938 + 0.4333 0.5667 +3 2 # rs2830801 + 0.1167 0.8833 +3 2 # rs2830824 + 0.0417 0.9583 +3 2 # rs1452084 + 0.1250 0.8750 +3 2 # rs239595 + 0.1083 0.8917 +3 2 # rs1376928 + 0.3917 0.6083 +3 2 # rs2830933 + 0.1583 0.8417 +3 2 # rs456874 + 0.3750 0.6250 +3 2 # rs4817181 + 0.4500 0.5500 +3 2 # rs2831005 + 0.2750 0.7250 +3 2 # rs2409209 + 0.3136 0.6864 +3 2 # rs3844464 + 0.1833 0.8167 +3 2 # rs9789838 + 0.2000 0.8000 +3 2 # rs11088049 + 0.3250 0.6750 +3 2 # rs9978929 + 0.2000 0.8000 +3 2 # rs2831507 + 0.3250 0.6750 +3 2 # rs7278583 + 0.3333 0.6667 +3 2 # rs2143682 + 0.4750 0.5250 +3 2 # rs1738121 + 0.0917 0.9083 +3 2 # rs2831806 + 0.1667 0.8333 +3 2 # rs2254751 + 0.2000 0.8000 +3 2 # rs9983632 + 0.1333 0.8667 +3 2 # rs1028990 + 0.4083 0.5917 +3 2 # rs981931 + 0.4583 0.5417 +3 2 # rs2832093 + 0.2500 0.7500 +3 2 # rs2832194 + 0.4583 0.5417 +3 2 # rs2300301 + 0.1000 0.9000 +3 2 # rs2832373 + 0.1167 0.8833 +3 2 # rs363478 + 0.1917 0.8083 +3 2 # rs363582 + 0.1583 0.8417 +3 2 # rs17772700 + 0.0500 0.9500 +3 2 # rs146662 + 0.4000 0.6000 +3 2 # rs2832463 + 0.0667 0.9333 +3 2 # rs455477 + 0.1167 0.8833 +3 2 # rs9976786 + 0.0667 0.9333 +3 2 # rs2832547 + 0.0500 0.9500 +3 2 # rs16985999 + 0.0083 0.9917 +3 2 # rs614118 + 0.1833 0.8167 +3 2 # rs2832729 + 0.2333 0.7667 +3 2 # rs9636832 + 0.1833 0.8167 +3 2 # rs9305434 + 0.4167 0.5833 +3 2 # rs2833124 + 0.3000 0.7000 +3 2 # rs4817363 + 0.4000 0.6000 +3 2 # rs2833160 + 0.0667 0.9333 +3 2 # rs2833162 + 0.1250 0.8750 +3 2 # rs7280222 + 0.0500 0.9500 +3 2 # rs1571691 + 0.3833 0.6167 +3 2 # rs2833270 + 0.1000 0.9000 +3 2 # rs9305454 + 0.0833 0.9167 +3 2 # rs845972 + 0.3917 0.6083 +3 2 # rs1034314 + 0.2917 0.7083 +3 2 # rs9984499 + 0.1271 0.8729 +3 2 # rs2833390 + 0.1250 0.8750 +3 2 # rs2833406 + 0.2667 0.7333 +3 2 # rs475142 + 0.1917 0.8083 +3 2 # rs577738 + 0.1833 0.8167 +3 2 # rs2833444 + 0.3250 0.6750 +3 2 # rs2409416 + 0.4333 0.5667 +3 2 # rs204740 + 0.1500 0.8500 +3 2 # rs964603 + 0.1583 0.8417 +3 2 # rs2211781 + 0.3333 0.6667 +3 2 # rs11700457 + 0.1333 0.8667 +3 2 # rs11910494 + 0.3583 0.6417 +3 2 # rs2833584 + 0.0750 0.9250 +3 2 # rs12165267 + 0.1500 0.8500 +3 2 # rs9305486 + 0.3500 0.6500 +3 2 # rs1892577 + 0.4250 0.5750 +3 2 # rs2833637 + 0.1000 0.9000 +3 2 # rs2833654 + 0.4167 0.5833 +3 2 # rs876382 + 0.4583 0.5417 +3 2 # rs12151995 + 0.3167 0.6833 +3 2 # rs2833718 + 0.1167 0.8833 +3 2 # rs2833757 + 0.2167 0.7833 +3 2 # rs11088218 + 0.1667 0.8333 +3 2 # rs2833854 + 0.1750 0.8250 +3 2 # rs1892655 + 0.4333 0.5667 +3 2 # rs7284048 + 0.0750 0.9250 +3 2 # rs845015 + 0.4750 0.5250 +3 2 # rs6517109 + 0.1750 0.8250 +3 2 # rs2014283 + 0.3917 0.6083 +3 2 # rs7278366 + 0.2500 0.7500 +3 2 # rs4817514 + 0.4667 0.5333 +3 2 # rs762236 + 0.4167 0.5833 +3 2 # rs2834076 + 0.3362 0.6638 +3 2 # rs6517142 + 0.2750 0.7250 +3 2 # rs2834098 + 0.1917 0.8083 +3 2 # rs2834101 + 0.2333 0.7667 +3 2 # rs9305523 + 0.1583 0.8417 +3 2 # rs2834135 + 0.0833 0.9167 +3 2 # rs2236758 + 0.3583 0.6417 +3 2 # rs2834168 + 0.2500 0.7500 +3 2 # rs735299 + 0.4500 0.5500 +3 2 # rs9974246 + 0.3250 0.6750 +3 2 # rs3761347 + 0.1333 0.8667 +3 2 # rs2268247 + 0.0667 0.9333 +3 2 # rs9636869 + 0.1333 0.8667 +3 2 # rs2073370 + 0.4667 0.5333 +3 2 # rs734746 + 0.3000 0.7000 +3 2 # rs2186553 + 0.4417 0.5583 +3 2 # rs2834371 + 0.5000 0.5000 +3 2 # rs873811 + 0.0417 0.9583 +3 2 # rs7278204 + 0.1833 0.8167 +3 2 # rs2834461 + 0.4250 0.5750 +3 2 # rs4817646 + 0.0833 0.9167 +3 2 # rs2834473 + 0.4333 0.5667 +3 2 # rs1012944 + 0.3667 0.6333 +3 2 # rs11700954 + 0.3220 0.6780 +3 2 # rs2300387 + 0.3051 0.6949 +3 2 # rs9305556 + 0.4068 0.5932 +3 2 # rs2834609 + 0.3167 0.6833 +3 2 # rs7282726 + 0.3583 0.6417 +3 2 # rs2243988 + 0.3390 0.6610 +3 2 # rs2268288 + 0.2250 0.7750 +3 2 # rs2284617 + 0.2000 0.8000 +3 2 # rs8134179 + 0.1583 0.8417 +3 2 # rs9984470 + 0.0583 0.9417 +3 2 # rs2834706 + 0.3667 0.6333 +3 2 # rs2834714 + 0.3667 0.6333 +3 2 # rs2834729 + 0.1333 0.8667 +3 2 # rs881386 + 0.3667 0.6333 +3 2 # rs7277157 + 0.2167 0.7833 +3 2 # rs984659 + 0.4833 0.5167 +3 2 # rs12626430 + 0.3917 0.6083 +3 2 # rs2075902 + 0.3000 0.7000 +3 2 # rs2834843 + 0.2417 0.7583 +3 2 # rs11088316 + 0.2583 0.7417 +3 2 # rs2834909 + 0.4833 0.5167 +3 2 # rs1882766 + 0.2583 0.7417 +3 2 # rs2834939 + 0.3417 0.6583 +3 2 # rs2205112 + 0.4417 0.5583 +3 2 # rs1627997 + 0.2583 0.7417 +3 2 # rs2835076 + 0.1750 0.8250 +3 2 # rs2242793 + 0.1833 0.8167 +3 2 # rs2835165 + 0.4917 0.5083 +3 2 # rs735143 + 0.2667 0.7333 +3 2 # rs2252991 + 0.3250 0.6750 +3 2 # rs2070515 + 0.4917 0.5083 +3 2 # rs7354757 + 0.3167 0.6833 +3 2 # rs2835346 + 0.3250 0.6750 +3 2 # rs219757 + 0.3083 0.6917 +3 2 # rs2000546 + 0.3917 0.6083 +3 2 # rs9976609 + 0.1417 0.8583 +3 2 # rs7281079 + 0.1333 0.8667 +3 2 # rs2835424 + 0.4583 0.5417 +3 2 # rs2409840 + 0.4500 0.5500 +3 2 # rs2835449 + 0.3167 0.6833 +3 2 # rs2073416 + 0.1500 0.8500 +3 2 # rs2845804 + 0.3814 0.6186 +3 2 # rs2016223 + 0.2667 0.7333 +3 2 # rs8134293 + 0.2833 0.7167 +3 2 # rs2073425 + 0.3793 0.6207 +3 2 # rs2835561 + 0.2750 0.7250 +3 2 # rs2249947 + 0.4583 0.5417 +3 2 # rs2835695 + 0.3500 0.6500 +3 2 # rs8131184 + 0.4750 0.5250 +3 2 # rs2835808 + 0.3083 0.6917 +3 2 # rs928723 + 0.4333 0.5667 +3 2 # rs3392 + 0.3500 0.6500 +3 2 # rs857958 + 0.2083 0.7917 +3 2 # rs2835955 + 0.3898 0.6102 +3 2 # rs3787840 + 0.1750 0.8250 +3 2 # rs4816585 + 0.4083 0.5917 +3 2 # rs2836034 + 0.0500 0.9500 +3 2 # rs9976411 + 0.1417 0.8583 +3 2 # rs2836067 + 0.2917 0.7083 +3 2 # rs2836096 + 0.3167 0.6833 +3 2 # rs2836143 + 0.4492 0.5508 +3 2 # rs2836278 + 0.2583 0.7417 +3 2 # rs2836316 + 0.2333 0.7667 +3 2 # rs2836338 + 0.4833 0.5167 +3 2 # rs2836344 + 0.3417 0.6583 +3 2 # rs2836379 + 0.1000 0.9000 +3 2 # rs2836400 + 0.4583 0.5417 +3 2 # rs9978004 + 0.0917 0.9083 +3 2 # rs2836478 + 0.1750 0.8250 +3 2 # rs2836526 + 0.1441 0.8559 +3 2 # rs2836544 + 0.2917 0.7083 +3 2 # rs1475878 + 0.2667 0.7333 +3 2 # rs2836640 + 0.4250 0.5750 +3 2 # rs2836656 + 0.4667 0.5333 +3 2 # rs2836664 + 0.4583 0.5417 +3 2 # rs2070531 + 0.4250 0.5750 +3 2 # rs12626608 + 0.0917 0.9083 +3 2 # rs2836754 + 0.3667 0.6333 +3 2 # rs13052608 + 0.3500 0.6500 +3 2 # rs733623 + 0.1333 0.8667 +3 2 # rs8130738 + 0.3083 0.6917 +3 2 # rs4816610 + 0.4917 0.5083 +3 2 # rs404074 + 0.1417 0.8583 +3 2 # rs2836891 + 0.2500 0.7500 +3 2 # rs7281463 + 0.4407 0.5593 +3 2 # rs2837011 + 0.2167 0.7833 +3 2 # rs517871 + 0.2917 0.7083 +3 2 # rs512384 + 0.3917 0.6083 +3 2 # rs10854398 + 0.4083 0.5917 +3 2 # rs2239385 + 0.2167 0.7833 +3 2 # rs12483089 + 0.4250 0.5750 +3 2 # rs11088492 + 0.1500 0.8500 +3 2 # rs467109 + 0.2000 0.8000 +3 2 # rs2837132 + 0.2833 0.7167 +3 2 # rs4816642 + 0.2083 0.7917 +3 2 # rs388152 + 0.1250 0.8750 +3 2 # rs438591 + 0.3333 0.6667 +3 2 # rs12106362 + 0.0917 0.9083 +3 2 # rs3819595 + 0.1417 0.8583 +3 2 # rs2299787 + 0.0667 0.9333 +3 2 # rs4816665 + 0.1667 0.8333 +3 2 # rs2837343 + 0.4583 0.5417 +3 2 # rs760168 + 0.3750 0.6250 +3 2 # rs2996053 + 0.2500 0.7500 +3 2 # rs2837382 + 0.2583 0.7417 +3 2 # rs2837426 + 0.1583 0.8417 +3 2 # rs8132673 + 0.2167 0.7833 +3 2 # rs7278658 + 0.2750 0.7250 +3 2 # rs2837480 + 0.3500 0.6500 +3 2 # rs2837508 + 0.4500 0.5500 +3 2 # rs1882760 + 0.4500 0.5500 +3 2 # rs2837574 + 0.4333 0.5667 +3 2 # rs1882756 + 0.3559 0.6441 +3 2 # rs9985057 + 0.4746 0.5254 +3 2 # rs12481965 + 0.1167 0.8833 +3 2 # rs2837699 + 0.3833 0.6167 +3 2 # rs9977890 + 0.3667 0.6333 +3 2 # rs2837765 + 0.2167 0.7833 +3 2 # rs2837770 + 0.4083 0.5917 +3 2 # rs8132311 + 0.2833 0.7167 +3 2 # rs2837798 + 0.1583 0.8417 +3 2 # rs7283279 + 0.2417 0.7583 +3 2 # rs2837854 + 0.2333 0.7667 +3 2 # rs732994 + 0.4333 0.5667 +3 2 # rs2837864 + 0.4333 0.5667 +3 2 # rs9636977 + 0.4667 0.5333 +3 2 # rs2837887 + 0.4417 0.5583 +3 2 # rs9305716 + 0.3898 0.6102 +3 2 # rs9981541 + 0.4083 0.5917 +3 2 # rs2094877 + 0.2833 0.7167 +3 2 # rs2837959 + 0.2417 0.7583 +3 2 # rs2837960 + 0.1000 0.9000 +3 2 # rs4816713 + 0.2083 0.7917 +3 2 # rs2837970 + 0.3917 0.6083 +3 2 # rs1541103 + 0.4000 0.6000 +3 2 # rs2410416 + 0.3167 0.6833 +3 2 # rs441810 + 0.1750 0.8250 +3 2 # rs390789 + 0.3729 0.6271 +3 2 # rs2838032 + 0.0750 0.9250 +3 2 # rs9979006 + 0.0917 0.9083 +3 2 # rs468440 + 0.3583 0.6417 +3 2 # rs2298857 + 0.2000 0.8000 +3 2 # rs3761373 + 0.0917 0.9083 +3 2 # rs9975623 + 0.2917 0.7083 +3 2 # rs2838067 + 0.2500 0.7500 +3 2 # rs2838070 + 0.3250 0.6750 +3 2 # rs2838077 + 0.3917 0.6083 +3 2 # rs2247788 + 0.1583 0.8417 +3 2 # rs9984259 + 0.2373 0.7627 +3 2 # rs7279061 + 0.1833 0.8167 +3 2 # rs2838090 + 0.2583 0.7417 +3 2 # rs8126725 + 0.0583 0.9417 +3 2 # rs12483161 + 0.4417 0.5583 +3 2 # rs2838121 + 0.2000 0.8000 +3 2 # rs2277793 + 0.2833 0.7167 +3 2 # rs2236440 + 0.1500 0.8500 +3 2 # rs2277795 + 0.1083 0.8917 +3 2 # rs365163 + 0.3750 0.6250 +3 2 # rs7280126 + 0.3250 0.6750 +3 2 # rs8133606 + 0.2500 0.7500 +3 2 # rs11701751 + 0.1833 0.8167 +3 2 # rs7275759 + 0.0583 0.9417 +3 2 # rs220229 + 0.1667 0.8333 +3 2 # rs220245 + 0.1583 0.8417 +3 2 # rs150796 + 0.1500 0.8500 +3 2 # rs2839461 + 0.1333 0.8667 +3 2 # rs220109 + 0.4583 0.5417 +3 2 # rs13051533 + 0.4661 0.5339 +3 2 # rs1563357 + 0.1250 0.8750 +3 2 # rs4148105 + 0.4000 0.6000 +3 2 # rs7281345 + 0.1583 0.8417 +3 2 # rs7275482 + 0.1750 0.8250 +3 2 # rs2236705 + 0.1167 0.8833 +3 2 # rs11701981 + 0.2250 0.7750 +3 2 # rs755946 + 0.4500 0.5500 +3 2 # rs3814900 + 0.0750 0.9250 +3 2 # rs9784215 + 0.3500 0.6500 +3 2 # rs8130358 + 0.2417 0.7583 +3 2 # rs2839536 + 0.4333 0.5667 +3 2 # rs228082 + 0.4750 0.5250 +3 2 # rs11701162 + 0.3083 0.6917 +3 2 # rs380422 + 0.2288 0.7712 +3 2 # rs2138327 + 0.3000 0.7000 +3 2 # rs2839576 + 0.3333 0.6667 +3 2 # rs7279886 + 0.3000 0.7000 +3 2 # rs2839585 + 0.2417 0.7583 +3 2 # rs1672133 + 0.1417 0.8583 +3 2 # rs2027745 + 0.2712 0.7288 +3 2 # rs879648 + 0.2500 0.7500 +3 2 # rs2839641 + 0.4000 0.6000 +3 2 # rs762391 + 0.2333 0.7667 +3 2 # rs638268 + 0.1417 0.8583 +3 2 # rs566038 + 0.0667 0.9333 +3 2 # rs681210 + 0.2917 0.7083 +3 2 # rs672163 + 0.1167 0.8833 +3 2 # rs657918 + 0.2250 0.7750 +3 2 # rs162382 + 0.3390 0.6610 +3 2 # rs1107204 + 0.4083 0.5917 +3 2 # rs4819314 + 0.0750 0.9250 +3 2 # rs2838429 + 0.3500 0.6500 +3 2 # rs3737358 + 0.0750 0.9250 +3 2 # rs2277806 + 0.2333 0.7667 +3 2 # rs743479 + 0.4909 0.5091 +3 2 # rs2070561 + 0.2583 0.7417 +3 2 # rs2018054 + 0.3083 0.6917 +3 2 # rs2256817 + 0.4083 0.5917 +3 2 # rs2070572 + 0.4237 0.5763 +3 2 # rs2838551 + 0.3667 0.6333 +3 2 # rs2003775 + 0.2500 0.7500 +3 2 # rs2838567 + 0.1917 0.8083 +3 2 # rs8134902 + 0.3417 0.6583 +3 2 # rs11702525 + 0.2333 0.7667 +3 2 # rs13051885 + 0.4250 0.5750 +3 2 # rs1107121 + 0.3667 0.6333 +3 2 # rs235380 + 0.1333 0.8667 +3 2 # rs2838674 + 0.3250 0.6750 +3 2 # rs2838704 + 0.3833 0.6167 +3 2 # rs3788142 + 0.2250 0.7750 +3 2 # rs760462 + 0.2417 0.7583 +3 2 # rs3788151 + 0.1000 0.9000 +3 2 # rs2838738 + 0.4750 0.5250 +3 2 # rs7283236 + 0.3833 0.6167 +3 2 # rs2838753 + 0.2833 0.7167 +3 2 # rs2838760 + 0.3000 0.7000 +3 2 # rs2838763 + 0.3000 0.7000 +3 2 # rs7281416 + 0.3475 0.6525 +3 2 # rs10098 + 0.4417 0.5583 +3 2 # rs914237 + 0.0750 0.9250 +3 2 # rs2838880 + 0.4333 0.5667 +3 2 # rs9306125 + 0.2917 0.7083 +3 2 # rs2026886 + 0.3833 0.6167 +3 2 # rs2838922 + 0.1167 0.8833 +3 2 # rs2236461 + 0.1833 0.8167 +3 2 # rs2838950 + 0.2583 0.7417 +3 2 # rs2838961 + 0.3500 0.6500 +3 2 # rs2838965 + 0.4333 0.5667 +3 2 # rs1556335 + 0.3500 0.6500 +3 2 # rs2776402 + 0.2417 0.7583 +3 2 # rs4819179 + 0.2917 0.7083 +3 2 # rs2839094 + 0.3583 0.6417 +3 2 # rs2156430 + 0.3644 0.6356 +3 2 # rs8133866 + 0.2750 0.7250 +3 2 # rs2839260 + 0.2250 0.7750 +3 2 # rs6518304 + 0.3051 0.6949 + 0 0 + 0.1 0.000699 0.000751 0.001625 0.000626 0.000902 0.001785 0.000970 0.001647 0.001241 0.000538 0.001138 0.000606 0.000504 0.000859 0.000504 0.001582 0.000504 0.001393 0.000552 0.001420 0.000533 0.000638 0.000596 0.000705 0.000567 0.001541 0.003324 0.001431 0.000703 0.001346 0.001978 0.000846 0.000598 0.000547 0.000887 0.002148 0.000873 0.001850 0.000800 0.000771 0.000785 0.000544 0.001836 0.000556 0.001838 0.000854 0.001010 0.001044 0.000913 0.002090 0.000800 0.003138 0.001451 0.000676 0.001048 0.005681 0.001075 0.001309 0.002052 0.002013 0.001811 0.001415 0.000890 0.000620 0.001575 0.000961 0.000590 0.000592 0.001485 0.000536 0.000692 0.003783 0.000504 0.001024 0.001595 0.002290 0.002725 0.001189 0.000622 0.000829 0.000530 0.001256 0.002036 0.001575 0.001122 0.000963 0.000909 0.001877 0.001159 0.000839 0.002653 0.000589 0.001128 0.001040 0.000572 0.000756 0.000518 0.000842 0.000642 0.000931 0.000852 0.000540 0.001125 0.002038 0.000547 0.000527 0.002150 0.001016 0.000564 0.000620 0.000526 0.001174 0.001572 0.000655 0.000776 0.000504 0.000521 0.000512 0.000629 0.000570 0.000515 0.001805 0.001103 0.001429 0.001403 0.003479 0.001278 0.001141 0.000866 0.000947 0.000979 0.000564 0.003575 0.001911 0.001271 0.000622 0.001919 0.000572 0.000855 0.001534 0.000840 0.000885 0.000505 0.000773 0.002112 0.000568 0.000553 0.000694 0.001058 0.000627 0.002328 0.002346 0.001778 0.000921 0.000535 0.000610 0.000785 0.000673 0.000625 0.000566 0.002054 0.001007 0.000625 0.000545 0.000733 0.000851 0.000544 0.001300 0.000559 0.000871 0.000571 0.000574 0.000588 0.000690 0.002225 0.000511 0.000839 0.001752 0.000544 0.001807 0.000633 0.000547 0.001712 0.001575 0.000512 0.000567 0.000586 0.001449 0.000862 0.001286 0.000506 0.000518 0.000545 0.001830 0.000665 0.000949 0.001156 0.001236 0.000501 0.000613 0.000572 0.000545 0.000695 0.000524 0.000578 0.000534 0.000970 0.000525 0.000634 0.000567 0.000523 0.000509 0.002036 0.001219 0.001222 0.002258 0.001873 0.001112 0.000799 0.002594 0.000695 0.000502 0.002240 0.000776 0.000534 0.000528 0.002549 0.000504 0.000538 0.001636 0.000791 0.000812 0.001350 0.000502 0.004374 0.000534 0.000518 0.000588 0.000535 0.000754 0.000555 0.001752 0.004448 0.000885 0.001773 0.000566 0.001217 0.000518 0.000732 0.000922 0.000684 0.000503 0.000685 0.000567 0.000543 0.000506 0.000527 0.001806 0.001391 0.000630 0.000505 0.001327 0.000513 0.000664 0.000529 0.000621 0.001258 0.001421 0.000511 0.002351 0.000501 0.000971 0.001581 0.001360 0.000778 0.000590 0.000685 0.001437 0.001497 0.000590 0.000905 0.000541 0.000503 0.001822 0.000779 0.000877 0.000562 0.000526 0.000625 0.000576 0.000600 0.000633 0.001288 0.001829 0.000504 0.000512 0.001125 0.000553 0.000785 0.000776 0.001828 0.000502 0.001045 0.001246 0.000698 0.000868 0.000518 0.000547 0.000676 0.000598 0.000596 0.000690 0.001273 0.000516 0.000503 0.000997 0.001706 0.000721 0.000533 0.000588 0.001516 0.000518 0.001056 0.000507 0.000502 0.002850 0.000626 0.000507 0.000568 0.001302 0.000633 0.002292 0.000500 0.002014 0.000533 0.001410 0.000595 0.000706 0.001283 0.000699 0.001022 0.000681 0.001685 0.001932 0.000864 0.001435 0.001734 0.000653 0.000832 0.000743 0.001876 0.000751 0.000520 0.001096 0.000558 0.000740 0.001345 0.000549 0.001962 0.000521 0.001245 0.000516 0.000517 0.002282 0.000511 0.000811 0.000722 0.001754 0.000511 0.000550 0.001908 0.000534 0.001669 0.001935 0.001493 0.000521 0.000733 0.000816 0.001478 0.000626 0.000520 0.000578 0.001743 0.000501 0.001703 0.000526 0.000628 0.000540 0.001511 0.001159 0.000523 0.000984 0.002506 0.000619 0.002135 0.001293 0.000559 0.000509 0.000530 0.000621 0.001215 0.000594 0.000945 0.002575 0.000588 0.000588 0.001190 0.000544 0.001123 0.001036 0.000564 0.000514 0.000816 0.000939 0.001210 0.000698 0.000526 0.001199 0.001589 0.002322 0.000500 0.000600 0.000804 0.001471 0.000520 0.000679 0.000669 0.000976 0.000614 0.000676 0.001038 0.000881 0.000790 0.001515 0.002322 0.000535 0.001803 0.002088 0.000883 0.000826 0.000703 0.000743 0.000651 0.001431 0.000923 0.000535 0.001250 0.000957 0.000697 0.001615 0.000817 0.001998 0.000638 0.000531 0.000910 0.001797 0.000571 0.001722 0.003449 0.001449 0.000801 0.001141 0.000504 0.001250 0.001787 0.000573 0.000845 0.000968 0.005449 0.000563 0.000721 0.001198 0.000552 0.001350 0.001014 0.000675 0.000537 0.000533 0.001793 0.000817 0.000520 0.002322 0.000551 0.000773 0.001852 0.000526 0.001451 0.000606 0.001702 0.000714 0.000602 0.000608 0.001443 0.000767 0.001225 0.000925 0.000505 0.000753 0.001616 0.001311 0.002005 0.001083 0.000615 0.000579 0.000516 0.001166 0.000934 0.001147 0.001770 0.000501 0.000674 0.001566 0.000736 0.000546 0.001212 0.000502 0.001283 0.000777 0.000782 0.000579 0.000977 0.001867 0.000720 0.002157 0.001494 0.002188 0.001952 0.001566 0.000694 0.001341 0.001138 0.000879 0.000570 0.001607 0.000732 0.000613 0.000502 0.000776 0.000990 0.000737 0.000643 0.000579 0.000625 0.000514 0.000962 0.000690 0.002351 0.001055 0.001971 0.000545 0.000640 0.002772 0.000510 0.000516 0.000636 0.000763 0.001327 0.000871 0.001994 0.000702 0.000581 0.000505 0.000585 0.001833 0.001476 0.000813 0.001382 0.001200 0.000533 0.001116 0.001082 0.001198 0.000976 0.000507 0.000527 0.001056 0.003941 0.001371 0.000501 0.000501 + 1 2.0 1.0 diff --git a/lib/galaxy/datatypes/test/linkstudies.linkage_map b/lib/galaxy/datatypes/test/linkstudies.linkage_map new file mode 100644 index 00000000000..20ec1cdbe48 --- /dev/null +++ b/lib/galaxy/datatypes/test/linkstudies.linkage_map @@ -0,0 +1,50 @@ +#Chr Genpos Marker Physpos Nr + 21 0.70496629 rs1296971 13609442 1 + 21 0.77494263 rs468601 13769165 2 + 21 0.85010932 rs2821973 13899316 3 + 21 1.01291458 rs1929150 14051249 4 + 21 1.07552959 rs2822124 14088675 5 + 21 1.16577144 rs2775054 14121682 6 + 21 1.34457997 rs7276618 14197852 7 + 21 1.44169062 rs392812 14252347 8 + 21 1.60661592 rs2822368 14311592 9 + 21 1.73090413 rs6516610 14385827 10 + 21 1.78470487 rs447455 14502698 11 + 21 1.89865013 rs437521 14513560 12 + 21 1.95928889 rs2822554 14585162 13 + 21 2.00968909 rs2822572 14618073 14 + 21 2.09569547 rs13050350 14653413 15 + 21 2.14611500 rs2142236 14748155 16 + 21 2.30459845 rs2822677 14767432 17 + 21 2.35507107 rs2822696 14783326 18 + 21 2.49458320 rs376635 14788185 19 + 21 2.54981913 rs2822765 14843387 20 + 21 2.69205997 rs2822780 14875052 21 + 21 2.74542953 rs465340 14884525 22 + 21 2.80926447 rs1888398 14981052 23 + 21 2.86888387 rs458052 15013563 24 + 21 2.93942924 rs2822907 15057767 25 + 21 2.99612354 rs8129531 15061195 26 + 21 3.15050759 rs12053660 15067326 27 + 21 3.48398239 rs1883003 15147347 28 + 21 3.62725156 rs2822974 15185311 29 + 21 3.69755891 rs11088231 15247441 30 + 21 3.83233721 rs2205239 15365789 31 + 21 4.03050303 rs2823045 15382630 32 + 21 4.11519240 rs926164 15435118 33 + 21 4.17500340 rs2823139 15498654 34 + 21 4.22973041 rs2823161 15515055 35 + 21 4.31848934 rs2823194 15568649 36 + 21 4.53372425 rs2049882 15633372 37 + 21 4.62105207 rs1736148 15735083 38 + 21 4.80636119 rs2823301 15780469 39 + 21 4.88644465 rs6517467 15806025 40 + 21 4.96357163 rs2064051 15860950 41 + 21 5.04209154 rs9974915 15879012 42 + 21 5.09648993 rs2823400 15922925 43 + 21 5.28044978 rs726634 15952748 44 + 21 5.33607360 rs7283707 16048865 45 + 21 5.52020653 rs7283161 16247081 46 + 21 5.60568022 rs9982633 16341775 47 + 21 5.70676016 rs2823621 16453860 48 + 21 5.81125072 rs2051347 16485140 49 diff --git a/lib/galaxy/datatypes/text.py b/lib/galaxy/datatypes/text.py index 33bdec72306..761e6a6ea78 100644 --- a/lib/galaxy/datatypes/text.py +++ b/lib/galaxy/datatypes/text.py @@ -50,13 +50,10 @@ class Html(Text): True """ headers = iter_headers(filename, None) - try: - for i, hdr in enumerate(headers): - if hdr and hdr[0].lower().find('') >= 0: - return True - return False - except Exception: - return True + for i, hdr in enumerate(headers): + if hdr and hdr[0].lower().find('') >= 0: + return True + return False class Json(Text): @@ -65,7 +62,7 @@ class Json(Text): 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) dataset.blurb = "JavaScript Object Notation (JSON)" else: dataset.peek = 'file does not exist' @@ -113,7 +110,7 @@ class Ipynb(Json): 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) dataset.blurb = "Jupyter Notebook" else: dataset.peek = 'file does not exist' @@ -186,7 +183,7 @@ class Biom1(Json): 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) + super(Biom1, self).set_peek(dataset) if not dataset.dataset.purged: dataset.blurb = "Biological Observation Matrix v1" @@ -270,7 +267,7 @@ class Obo(Text): 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) dataset.blurb = "Open Biomedical Ontology (OBO)" else: dataset.peek = 'file does not exist' @@ -309,7 +306,7 @@ class Arff(Text): 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) dataset.blurb = "Attribute-Relation File Format (ARFF)" dataset.blurb += ", %s comments, %s attributes" % (dataset.metadata.comment_lines, dataset.metadata.columns) else: @@ -503,20 +500,19 @@ class SnpSiftDbNSFP(Text): 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) + return 'SnpSiftDbNSFP Composite Dataset' def regenerate_primary_file(self, dataset): """ cannot do this until we are setting metadata """ annotations = "dbNSFP Annotations: %s\n" % ','.join(dataset.metadata.annotation) - f = open(dataset.file_name, 'a') - if dataset.metadata.bgzip: - bn = dataset.metadata.bgzip - f.write(bn) - f.write('\n') - f.write(annotations) - f.close() + with open(dataset.file_name, 'a') as f: + if dataset.metadata.bgzip: + bn = dataset.metadata.bgzip + f.write(bn) + f.write('\n') + f.write(annotations) def set_meta(self, dataset, overwrite=True, **kwd): try: diff --git a/lib/galaxy/datatypes/triples.py b/lib/galaxy/datatypes/triples.py index 377a47efd3d..c8ce7631d60 100644 --- a/lib/galaxy/datatypes/triples.py +++ b/lib/galaxy/datatypes/triples.py @@ -31,7 +31,7 @@ class Triples(data.Data): 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) dataset.blurb = 'Triple data' else: dataset.peek = 'file does not exist' @@ -55,7 +55,7 @@ class NTriples(data.Text, Triples): 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) dataset.blurb = 'N-Triples triple data' else: dataset.peek = 'file does not exist' @@ -78,7 +78,7 @@ class N3(data.Text, Triples): 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) dataset.blurb = 'Notation-3 Triple data' else: dataset.peek = 'file does not exist' @@ -105,7 +105,7 @@ class Turtle(data.Text, Triples): 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) dataset.blurb = 'Turtle triple data' else: dataset.peek = 'file does not exist' @@ -132,7 +132,7 @@ class Rdf(xml.GenericXml, Triples): 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) dataset.blurb = 'RDF/XML triple data' else: dataset.peek = 'file does not exist' @@ -158,7 +158,7 @@ class Jsonld(text.Json, Triples): 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) dataset.blurb = 'JSON-LD triple data' else: dataset.peek = 'file does not exist' @@ -181,7 +181,7 @@ class HDT(binary.Binary, Triples): 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) dataset.blurb = 'HDT triple data' else: dataset.peek = 'file does not exist' diff --git a/lib/galaxy/datatypes/xml.py b/lib/galaxy/datatypes/xml.py index aba2272c2a7..18ab8009b8f 100644 --- a/lib/galaxy/datatypes/xml.py +++ b/lib/galaxy/datatypes/xml.py @@ -21,7 +21,7 @@ class GenericXml(data.Text): 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) dataset.blurb = 'XML data' else: dataset.peek = 'file does not exist' @@ -68,7 +68,7 @@ class MEMEXml(GenericXml): 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) dataset.blurb = 'MEME XML data' else: dataset.peek = 'file does not exist' @@ -85,7 +85,7 @@ class CisML(GenericXml): 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) dataset.blurb = 'CisML data' else: dataset.peek = 'file does not exist' @@ -104,7 +104,7 @@ class Phyloxml(GenericXml): 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) dataset.blurb = 'Phyloxml data' else: dataset.peek = 'file does not exist' @@ -139,7 +139,7 @@ class Owl(GenericXml): 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) dataset.blurb = "Web Ontology Language OWL" else: dataset.peek = 'file does not exist' diff --git a/lib/galaxy/jobs/__init__.py b/lib/galaxy/jobs/__init__.py index 34365d1ac37..8c82a17da06 100644 --- a/lib/galaxy/jobs/__init__.py +++ b/lib/galaxy/jobs/__init__.py @@ -46,6 +46,7 @@ TOOL_PROVIDED_JOB_METADATA_KEYS = ['name', 'info', 'dbkey'] # Override with config.default_job_shell. DEFAULT_JOB_SHELL = '/bin/bash' +DEFAULT_LOCAL_WORKERS = 4 DEFAULT_CLEANUP_JOB = "always" @@ -139,7 +140,15 @@ class JobConfiguration(object, ConfiguresHandlers): self.resource_groups = {} self.default_resource_group = None self.resource_parameters = {} - self.limits = Bunch() + self.limits = Bunch(registered_user_concurrent_jobs=None, + anonymous_user_concurrent_jobs=None, + walltime=None, + walltime_delta=None, + total_walltime={}, + output_size=None, + destination_user_concurrent_jobs={}, + destination_total_concurrent_jobs={}) + self._is_handler = None default_resubmits = [] default_resubmit_condition = self.app.config.default_job_resubmission_condition @@ -159,10 +168,10 @@ class JobConfiguration(object, ConfiguresHandlers): tree = load(job_config_file) self.__parse_job_conf_xml(tree) except IOError: - log.warning('Job configuration "%s" does not exist, using legacy' - ' job configuration from Galaxy config file "%s" instead' - % (self.app.config.job_config_file, self.app.config.config_file)) - self.__parse_job_conf_legacy() + log.warning('Job configuration "%s" does not exist, using default' + ' job configuration (this server will run jobs)', + self.app.config.job_config_file) + self.__set_default_job_conf() except Exception as e: raise config_exception(e, job_config_file) @@ -203,11 +212,21 @@ class JobConfiguration(object, ConfiguresHandlers): handlers_conf = root.find('handlers') self._init_handlers(handlers_conf) - # Must define at least one handler to have a default. - if not self.handlers: - raise ValueError("Job configuration file defines no valid handler elements.") # Determine the default handler(s) - self.default_handler_id = self._get_default(self.app.config, handlers_conf, list(self.handlers.keys())) + try: + self.default_handler_id = self._get_default(self.app.config, handlers_conf, list(self.handlers.keys())) + except Exception: + pass + # For tets, this may not exist + try: + base_server_name = self.app.config.base_server_name + except AttributeError: + base_server_name = self.app.config.get('base_server_name', None) + if (self.default_handler_id is None + or (len(self.handlers) == 1 and base_server_name == self.handlers.keys()[0])): + # Shortcut for compatibility with existing job confs that use the default handlers block, + # there are no defined handlers, or there's only one handler and it's this server + self.__set_default_job_handler() # Parse destinations destinations = root.find('destinations') @@ -277,15 +296,6 @@ class JobConfiguration(object, ConfiguresHandlers): total_walltime=str, output_size=util.size_to_bytes) - self.limits = Bunch(registered_user_concurrent_jobs=None, - anonymous_user_concurrent_jobs=None, - walltime=None, - walltime_delta=None, - total_walltime={}, - output_size=None, - destination_user_concurrent_jobs={}, - destination_total_concurrent_jobs={}) - # Parse job limits limits = root.find('limits') if limits is not None: @@ -327,73 +337,39 @@ class JobConfiguration(object, ConfiguresHandlers): self.handler_runner_plugins[handler_id] = [] self.handler_runner_plugins[handler_id].append(plugin.get('id')) - def __parse_job_conf_legacy(self): - """Loads the old-style job configuration from options in the galaxy config file (by default, config/galaxy.ini). - """ - log.debug('Loading job configuration from %s' % self.app.config.config_file) - - # Always load local - self.runner_plugins = [dict(id='local', load='local', workers=self.app.config.local_job_queue_workers)] + def __set_default_job_conf(self): + # Run jobs locally + self.runner_plugins = [dict(id='local', load='local', workers=DEFAULT_LOCAL_WORKERS)] # Load tasks if configured if self.app.config.use_tasked_jobs: - self.runner_plugins.append(dict(id='tasks', load='tasks', workers=self.app.config.local_task_queue_workers)) - for runner in self.app.config.start_job_runners: - self.runner_plugins.append(dict(id=runner, load=runner, workers=self.app.config.cluster_job_queue_workers)) - + self.runner_plugins.append(dict(id='tasks', load='tasks', workers=DEFAULT_LOCAL_WORKERS)) # Set the handlers - for id in self.app.config.job_handlers: - self.handlers[id] = (id,) - - self.handlers['default_job_handlers'] = self.app.config.default_job_handlers - self.default_handler_id = 'default_job_handlers' - - # Set tool handler configs - for id, tool_handlers in self.app.config.tool_handlers.items(): - self.tools[id] = list() - for handler_config in tool_handlers: - # rename the 'name' key to 'handler' - handler_config['handler'] = handler_config.pop('name') - self.tools[id].append(JobToolConfiguration(**handler_config)) - - # Set tool runner configs - for id, tool_runners in self.app.config.tool_runners.items(): - # Might have been created in the handler parsing above - if id not in self.tools: - self.tools[id] = list() - for runner_config in tool_runners: - url = runner_config['url'] - if url not in self.destinations: - # Create a new "legacy" JobDestination - it will have its URL converted to a destination params once the appropriate plugin has loaded - self.destinations[url] = (JobDestination(id=url, runner=url.split(':', 1)[0], url=url, legacy=True, converted=False),) - for tool_conf in self.tools[id]: - if tool_conf.params == runner_config.get('params', {}): - tool_conf['destination'] = url - break - else: - # There was not an existing config (from the handlers section) with the same params - # rename the 'url' key to 'destination' - runner_config['destination'] = runner_config.pop('url') - self.tools[id].append(JobToolConfiguration(**runner_config)) - - self.destinations[self.app.config.default_cluster_job_runner] = (JobDestination(id=self.app.config.default_cluster_job_runner, - runner=self.app.config.default_cluster_job_runner.split(':', 1)[0], - url=self.app.config.default_cluster_job_runner, - legacy=True, - converted=False),) - self.default_destination_id = self.app.config.default_cluster_job_runner - - # Set the job limits - self.limits = Bunch(registered_user_concurrent_jobs=self.app.config.registered_user_job_limit, - anonymous_user_concurrent_jobs=self.app.config.anonymous_user_job_limit, - walltime=self.app.config.job_walltime, - walltime_delta=self.app.config.job_walltime_delta, - total_walltime={}, - output_size=self.app.config.output_size_limit, - destination_user_concurrent_jobs={}, - destination_total_concurrent_jobs={}) - + self.__set_default_job_handler() + # Set the destination + self.default_destination_id = 'local' + self.destinations['local'] = [JobDestination(id='local', runner='local')] log.debug('Done loading job configuration') + def __set_default_job_handler(self): + # Called when self.default_handler_id is None + if self.app.application_stack.has_pool(self.app.application_stack.pools.JOB_HANDLERS): + if (self.app.application_stack.in_pool(self.app.application_stack.pools.JOB_HANDLERS)): + log.info("Found job handler pool managed by application stack, this server (%s) is a member of pool: " + "%s", self.app.config.server_name, self.app.application_stack.pools.JOB_HANDLERS) + self._is_handler = True + else: + log.info("Found job handler pool managed by application stack, this server (%s) will submit jobs to " + "pool: %s", self.app.config.server_name, self.app.application_stack.pools.JOB_HANDLERS) + else: + log.info('Did not find job handler pool managed by application stack, this server (%s) will handle jobs ' + 'submitted to it', self.app.config.server_name) + self._is_handler = True + self.app.application_stack.register_postfork_function(self.make_self_default_handler) + + def make_self_default_handler(self): + self.default_handler_id = self.app.config.server_name + self.handlers[self.app.config.server_name] = [self.app.config.server_name] + def get_tool_resource_xml(self, tool_id, tool_type): """ Given a tool id, return XML elements describing parameters to insert into job resources. @@ -1311,15 +1287,9 @@ class JobWrapper(object, HasResourceParameters): 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) - else: - dataset.set_peek(line_count=context['line_count']) + dataset.set_peek(line_count=context['line_count']) except Exception: - 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() + dataset.set_peek() else: # Handle an empty dataset. dataset.blurb = "empty" diff --git a/lib/galaxy/jobs/handler.py b/lib/galaxy/jobs/handler.py index e478b2d79f2..86cdd868870 100644 --- a/lib/galaxy/jobs/handler.py +++ b/lib/galaxy/jobs/handler.py @@ -27,6 +27,7 @@ from galaxy.jobs import ( ) from galaxy.jobs.mapper import JobNotReadyException from galaxy.util.monitors import Monitors +from galaxy.web.stack.message import JobHandlerMessage log = logging.getLogger(__name__) @@ -50,6 +51,7 @@ class JobHandler(object): def start(self): self.job_queue.start() + self.job_stop_queue.start() def shutdown(self): self.job_queue.shutdown() @@ -90,10 +92,14 @@ class JobHandlerQueue(Monitors, object): """ Starts the JobHandler's thread after checking for any unhandled jobs. """ + log.debug('Handler queue starting for jobs assigned to handler: %s', self.app.config.server_name) # Recover jobs at startup self.__check_jobs_at_startup() # Start the queue self.monitor_thread.start() + # The stack code is initialized in the application + JobHandlerMessage().bind_default_handler(self, '_handle_message') + self.app.application_stack.register_message_handler(self._handle_message, name=JobHandlerMessage.target) log.info("job handler queue started") def job_wrapper(self, job, use_persisted_destination=False): @@ -652,11 +658,30 @@ class JobHandlerQueue(Monitors, object): return JOB_WAIT return JOB_READY + def _handle_setup_msg(self, job_id=None): + job = self.sa_session.query(model.Job).get(job_id) + if job.handler is None: + job.handler = self.app.config.server_name + self.sa_session.add(job) + self.sa_session.flush() + # If not tracking jobs in the database + self.put(job.id, job.tool_id) + else: + log.warning("(%s) Handler '%s' received setup message but handler '%s' is already assigned, ignoring", job.id, self.app.config.server_name, job.handler) + 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.sleeper.wake() + else: + # Workflow invocations farmed out to workers will submit jobs through here. If a handler is unassigned, we + # will submit for one, or else claim it ourself. TODO: This should be moved to a higher level as it's now + # implemented here and in MessageJobQueue + job = self.sa_session.query(model.Job).get(job_id) + if job.handler is None and self.app.application_stack.has_pool(self.app.application_stack.pools.JOB_HANDLERS): + msg = JobHandlerMessage(task='setup', job_id=job_id) + self.app.application_stack.send_message(self.app.application_stack.pools.JOB_HANDLERS, msg) def shutdown(self): """Attempts to gracefully shut down the worker thread""" @@ -668,6 +693,9 @@ class JobHandlerQueue(Monitors, object): self.stop_monitoring() if not self.app.config.track_jobs_in_database: self.queue.put(self.STOP_SIGNAL) + # A message could still be received while shutting down, should be ok since they will be picked up on next startup. + self.app.application_stack.deregister_message_handler(name=JobHandlerMessage.target) + self.sleeper.wake() self.shutdown_monitor() log.info("job handler queue stopped") self.dispatcher.shutdown() @@ -695,7 +723,12 @@ class JobHandlerStopQueue(Monitors): self.waiting = [] name = "JobHandlerStopQueue.monitor_thread" - self._init_monitor_thread(name, start=True, config=app.config) + self._init_monitor_thread(name, config=app.config) + log.info("job handler stop queue started") + + def start(self): + # Start the queue + self.monitor_thread.start() log.info("job handler stop queue started") def monitor(self): diff --git a/lib/galaxy/jobs/manager.py b/lib/galaxy/jobs/manager.py index 811cb637f5e..2cb68405502 100644 --- a/lib/galaxy/jobs/manager.py +++ b/lib/galaxy/jobs/manager.py @@ -4,7 +4,11 @@ Top-level Galaxy job manager, moves jobs to handler(s) import logging +from sqlalchemy.sql.expression import null + from galaxy.jobs import handler, NoopQueue +from galaxy.model import Job +from galaxy.web.stack.message import JobHandlerMessage log = logging.getLogger(__name__) @@ -19,15 +23,19 @@ class JobManager(object): def __init__(self, app): self.app = app - if self.app.is_job_handler(): - log.debug("Starting job handler") + self.job_lock = False + if self.app.is_job_handler: + log.debug("Initializing job handler") self.job_handler = handler.JobHandler(app) - self.job_queue = self.job_handler.job_queue self.job_stop_queue = self.job_handler.job_stop_queue + elif app.application_stack.has_pool(app.application_stack.pools.JOB_HANDLERS): + log.debug("Initializing job handler messaging interface") + self.job_handler = MessageJobHandler(app) + self.job_stop_queue = NoopQueue() else: self.job_handler = NoopHandler() - self.job_queue = self.job_stop_queue = NoopQueue() - self.job_lock = False + self.job_stop_queue = NoopQueue() + self.job_queue = self.job_handler.job_queue def start(self): self.job_handler.start() @@ -46,3 +54,36 @@ class NoopHandler(object): def shutdown(self, *args): pass + + +class MessageJobHandler(NoopHandler): + """ + Implements the JobHandler interface but just to send setup messages on startup + + TODO: It should be documented that starting two Galaxy uWSGI master processes simultaneously would result in a race condition that *could* cause two handlers to pick up the same job. + + The recommended config for now will be webless handlers if running more than one uWSGI (master) process + """ + def __init__(self, app): + # This runs in the web (main) process pre-fork + self.app = app + self.job_queue = MessageJobQueue(app) + self.job_stop_queue = NoopQueue() + jobs_at_startup = self.app.model.context.query(Job).enable_eagerloads(False) \ + .filter((Job.state == Job.states.NEW) & (Job.handler == null())).all() + if jobs_at_startup: + log.info('No handler assigned at startup for the following jobs, will dispatch via message: %s', ', '.join([str(j.id) for j in jobs_at_startup])) + for job in jobs_at_startup: + self.job_queue.put(job.id, job.tool_id) + + +class MessageJobQueue(NoopQueue): + """ + Implements the JobQueue / JobStopQueue interface but only sends messages to the actual job queue + """ + def __init__(self, app): + self.app = app + + def put(self, job_id, tool_id): + msg = JobHandlerMessage(task='setup', job_id=job_id) + self.app.application_stack.send_message(self.app.application_stack.pools.JOB_HANDLERS, msg) diff --git a/lib/galaxy/jobs/runners/__init__.py b/lib/galaxy/jobs/runners/__init__.py index 7435e8e5c9a..ecf935bd0cb 100644 --- a/lib/galaxy/jobs/runners/__init__.py +++ b/lib/galaxy/jobs/runners/__init__.py @@ -83,7 +83,7 @@ class BaseJobRunner(object): 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.start() + self.app.application_stack.register_postfork_function(worker.start) self.work_threads.append(worker) def run_next(self): @@ -413,6 +413,7 @@ class JobState(object): runner_states = Bunch( WALLTIME_REACHED='walltime_reached', MEMORY_LIMIT_REACHED='memory_limit_reached', + JOB_OUTPUT_NOT_RETURNED_FROM_CLUSTER='Job output not returned from cluster', UNKNOWN_ERROR='unknown_error', GLOBAL_WALLTIME_REACHED='global_walltime_reached', OUTPUT_SIZE_LIMIT='output_size_limit' @@ -612,6 +613,7 @@ class AsynchronousJobRunner(Monitors, BaseJobRunner): # wait for the files to appear which_try = 0 + collect_output_success = True 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) @@ -620,12 +622,19 @@ class AsynchronousJobRunner(Monitors, BaseJobRunner): except Exception as e: if which_try == self.app.config.retry_job_output_collection: stdout = '' - stderr = 'Job output not returned from cluster' + stderr = job_state.runner_states.JOB_OUTPUT_NOT_RETURNED_FROM_CLUSTER log.error('(%s/%s) %s: %s' % (galaxy_id_tag, external_job_id, stderr, str(e))) + collect_output_success = False else: time.sleep(1) which_try += 1 + if not collect_output_success: + job_state.fail_message = stderr + job_state.runner_state = job_state.runner_states.JOB_OUTPUT_NOT_RETURNED_FROM_CLUSTER + self.mark_as_failed(job_state) + return + 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) diff --git a/lib/galaxy/jobs/runners/state_handlers/resubmit.py b/lib/galaxy/jobs/runners/state_handlers/resubmit.py index 392d8fbe27f..25ccd5bf8e5 100644 --- a/lib/galaxy/jobs/runners/state_handlers/resubmit.py +++ b/lib/galaxy/jobs/runners/state_handlers/resubmit.py @@ -65,6 +65,7 @@ def failure(app, job_runner, job_state): runner_state = getattr(job_state, 'runner_state', None) or JobState.runner_states.UNKNOWN_ERROR if (runner_state not in (JobState.runner_states.WALLTIME_REACHED, JobState.runner_states.MEMORY_LIMIT_REACHED, + JobState.runner_states.JOB_OUTPUT_NOT_RETURNED_FROM_CLUSTER, JobState.runner_states.UNKNOWN_ERROR)): # not set or not a handleable runner state return diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index 147090a6171..01689a1a555 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -2071,8 +2071,8 @@ class DatasetInstance(object): """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): + return self.datatype.set_peek(self) def init_meta(self, copy_from=None): return self.datatype.init_meta(self, copy_from=copy_from) diff --git a/lib/galaxy/model/metadata.py b/lib/galaxy/model/metadata.py index 1cb3e508e3d..ba937e61391 100644 --- a/lib/galaxy/model/metadata.py +++ b/lib/galaxy/model/metadata.py @@ -130,18 +130,11 @@ class MetadataCollection(object): def element_is_set(self, name): return bool(self.parent._metadata.get(name, False)) - 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) - if rval is None: - return self.spec[name].no_value - return rval - 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 + field = self.spec[name].param.get_field(getattr(self, name), self, None, **kwd) + field.value = getattr(self, name) + return field def make_dict_copy(self, to_copy): """Makes a deep copy of input iterable to_copy according to self.spec""" @@ -229,7 +222,7 @@ 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_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) @@ -253,9 +246,9 @@ class MetadataParameter(object): 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() + return checkbox.get_html() + self.get_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_field(value=value, context=context, other_values=other_values, **kwd).get_html() def to_string(self, value): return str(value) @@ -374,7 +367,7 @@ class SelectParameter(MetadataParameter): value = [value] return ",".join(map(str, value)) - def get_html_field(self, value=None, context=None, other_values=None, values=None, **kwd): + def get_field(self, value=None, context=None, other_values=None, values=None, **kwd): context = context or {} other_values = other_values or {} @@ -431,14 +424,14 @@ class SelectParameter(MetadataParameter): class DBKeyParameter(SelectParameter): - def get_html_field(self, value=None, context=None, other_values=None, values=None, **kwd): + def get_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']) except KeyError: pass - return super(DBKeyParameter, self).get_html_field(value, context, other_values, values, **kwd) + return super(DBKeyParameter, self).get_field(value, context, other_values, values, **kwd) def get_html(self, value=None, context=None, other_values=None, values=None, **kwd): context = context or {} @@ -459,13 +452,13 @@ class RangeParameter(SelectParameter): 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_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) + return SelectParameter.get_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): context = context or {} @@ -484,14 +477,14 @@ class RangeParameter(SelectParameter): class ColumnParameter(RangeParameter): - def get_html_field(self, value=None, context=None, other_values=None, values=None, **kwd): + def get_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) + return RangeParameter.get_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): context = context or {} @@ -532,7 +525,7 @@ class PythonObjectParameter(MetadataParameter): 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_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)) @@ -558,7 +551,7 @@ class FileParameter(MetadataParameter): # We do not sanitize file names return self.to_string(value) - def get_html_field(self, value=None, context=None, other_values=None, **kwd): + def get_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)) 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 4a86c22d983..c40ec37e151 100644 --- a/lib/galaxy/model/migrate/versions/0005_cleanup_datasets_fix.py +++ b/lib/galaxy/model/migrate/versions/0005_cleanup_datasets_fix.py @@ -241,8 +241,8 @@ class DatasetInstance(object): """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): + return self.datatype.set_peek(self) def init_meta(self, copy_from=None): return self.datatype.init_meta(self, copy_from=copy_from) diff --git a/lib/galaxy/model/orm/scripts.py b/lib/galaxy/model/orm/scripts.py index dbca8fbb6bf..fc7b4742c97 100644 --- a/lib/galaxy/model/orm/scripts.py +++ b/lib/galaxy/model/orm/scripts.py @@ -2,14 +2,15 @@ Code to support database helper scripts (create_db.py, manage_db.py, etc...). """ import logging +import os.path -from galaxy.util import listify +from galaxy.util.path import get_ext from galaxy.util.properties import find_config_file, load_app_properties log = logging.getLogger(__name__) -DEFAULT_CONFIG_FILE = 'config/galaxy.ini' +DEFAULT_CONFIG_NAMES = ['galaxy', 'universe_wsgi'] DEFAULT_CONFIG_PREFIX = '' DEFAULT_DATABASE = 'galaxy' @@ -17,15 +18,19 @@ DATABASE = { "galaxy": { 'repo': 'lib/galaxy/model/migrate', - 'old_config_files': ['universe_wsgi.ini'], + 'default_sqlite_file': './database/universe.sqlite', + 'config_override': 'GALAXY_CONFIG_', + }, + "tools": + { + 'repo': 'lib/tool_shed/galaxy_install/migrate', '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.yml', - 'old_config_files': ['config/tool_shed.ini', 'tool_shed_wsgi.ini'], + 'config_names': ['tool_shed', 'tool_shed_wsgi'], 'default_sqlite_file': './database/community.sqlite', 'config_override': 'TOOL_SHED_CONFIG_', 'config_section': 'tool_shed', @@ -33,7 +38,6 @@ DATABASE = { "install": { 'repo': 'lib/galaxy/model/tool_shed_install/migrate', - 'old_config_files': ['universe_wsgi.ini'], 'config_prefix': 'install_', 'default_sqlite_file': './database/install.sqlite', 'config_override': 'GALAXY_INSTALL_CONFIG_', @@ -41,14 +45,14 @@ DATABASE = { } -def read_config_file_arg(argv, default, old_defaults, cwd=None): - config_file = None +def read_config_file_arg(argv, config_names, cwd=None): if '-c' in argv: pos = argv.index('-c') 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 argv.pop(pos) + if cwd: + cwd = [cwd, os.path.join(cwd, 'config')] + return find_config_file(config_names, dirs=cwd) def get_config(argv, cwd=None): @@ -57,6 +61,7 @@ def get_config(argv, cwd=None): >>> import os >>> from ConfigParser import SafeConfigParser + >>> from shutil import rmtree >>> from tempfile import mkdtemp >>> config_dir = mkdtemp() >>> os.makedirs(os.path.join(config_dir, 'config')) @@ -77,6 +82,7 @@ def get_config(argv, cwd=None): 'sqlite:///moo.sqlite?isolation_level=IMMEDIATE' >>> config['repo'] 'lib/galaxy/model/migrate' + >>> rmtree(config_dir) """ if argv and (argv[-1] in DATABASE): database = argv.pop() # database name tool_shed, galaxy, or install. @@ -84,14 +90,13 @@ def get_config(argv, cwd=None): database = 'galaxy' database_defaults = DATABASE[database] - 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) + config_names = database_defaults.get('config_names', DEFAULT_CONFIG_NAMES) + config_file = read_config_file_arg(argv, config_names, 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"): + if not config_file or get_ext(config_file, ignore='sample') == 'yaml': config_section = database_defaults.get('config_section', None) else: # An .ini file - just let load_app_properties find app:main. diff --git a/lib/galaxy/tools/actions/__init__.py b/lib/galaxy/tools/actions/__init__.py index cc47db76e62..66bbac2c78a 100644 --- a/lib/galaxy/tools/actions/__init__.py +++ b/lib/galaxy/tools/actions/__init__.py @@ -510,7 +510,7 @@ class DefaultToolAction(object): 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) + app.job_manager.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 diff --git a/lib/galaxy/tools/actions/history_imp_exp.py b/lib/galaxy/tools/actions/history_imp_exp.py index a67b1b509ee..222f0b14e28 100644 --- a/lib/galaxy/tools/actions/history_imp_exp.py +++ b/lib/galaxy/tools/actions/history_imp_exp.py @@ -59,7 +59,7 @@ class ImportHistoryToolAction(ToolAction): trans.sa_session.flush() # Queue the job for execution - trans.app.job_queue.put(job.id, tool.id) + trans.app.job_manager.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() @@ -138,7 +138,7 @@ class ExportHistoryToolAction(ToolAction): trans.sa_session.flush() # Queue the job for execution - trans.app.job_queue.put(job.id, tool.id) + trans.app.job_manager.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 6e97706dc66..a41c24549ad 100644 --- a/lib/galaxy/tools/actions/metadata.py +++ b/lib/galaxy/tools/actions/metadata.py @@ -103,7 +103,7 @@ class SetMetadataToolAction(ToolAction): sa_session.flush() # Queue the job for execution - app.job_queue.put(job.id, tool.id) + app.job_manager.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 ) diff --git a/lib/galaxy/tools/actions/model_operations.py b/lib/galaxy/tools/actions/model_operations.py index d87c44e33a6..4edcd6ce141 100644 --- a/lib/galaxy/tools/actions/model_operations.py +++ b/lib/galaxy/tools/actions/model_operations.py @@ -61,7 +61,7 @@ class ModelOperationToolAction(DefaultToolAction): trans.sa_session.flush() # ensure job.id are available # Queue the job for execution - # trans.app.job_queue.put( job.id, tool.id ) + # trans.app.job_manager.job_queue.put( job.id, tool.id ) # trans.log_event( "Added database job action to the job queue, id: %s" % str(job.id), tool_id=job.tool_id ) log.info("Calling produce_outputs, tool is %s" % tool) return job, out_data diff --git a/lib/galaxy/tools/actions/upload_common.py b/lib/galaxy/tools/actions/upload_common.py index 6025d092fe7..fb3dbb87ba9 100644 --- a/lib/galaxy/tools/actions/upload_common.py +++ b/lib/galaxy/tools/actions/upload_common.py @@ -516,7 +516,7 @@ def create_job(trans, params, tool, json_file_path, data_list, folder=None, hist trans.sa_session.flush() # Queue the job for execution - trans.app.job_queue.put(job.id, job.tool_id) + trans.app.job_manager.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): diff --git a/lib/galaxy/tools/error_reports/plugins/sentry.py b/lib/galaxy/tools/error_reports/plugins/sentry.py index 5c8fcec5694..00dd96ca719 100644 --- a/lib/galaxy/tools/error_reports/plugins/sentry.py +++ b/lib/galaxy/tools/error_reports/plugins/sentry.py @@ -31,11 +31,21 @@ class SentryPlugin(ErrorPlugin): 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.custom_dsn = kwargs.get('custom_dsn', None) + self.sentry = None + # Use the built in one by default + if hasattr(self.app, 'sentry_client'): + self.sentry = self.app.sentry_client + + # if they've set a custom one, override. + if self.custom_dsn: + import raven + self.sentry = raven.Client(self.custom_dsn) def submit_report(self, dataset, job, tool, **kwargs): """Submit the error report to sentry """ - if self.app.sentry_client: + if self.sentry_client: user = job.get_user() extra = { 'info': job.info, @@ -64,7 +74,7 @@ class SentryPlugin(ErrorPlugin): error_message = ERROR_TEMPLATE.format(**extra) # Update context with user information in a sentry-specific manner - self.app.sentry_client.context.merge({ + self.sentry_client.context.merge({ # User information here also places email links + allows seeing # a list of affected users in the tags/filtering. 'user': { @@ -83,7 +93,7 @@ class SentryPlugin(ErrorPlugin): }) # Send the message, using message because - response = self.app.sentry_client.capture( + response = self.sentry_client.capture( 'raven.events.Message', tags={ 'tool_id': job.tool_id, diff --git a/lib/galaxy/tools/imp_exp/imp_history_from_archive.xml b/lib/galaxy/tools/imp_exp/imp_history_from_archive.xml index b73c428fedd..719eb5de65f 100644 --- a/lib/galaxy/tools/imp_exp/imp_history_from_archive.xml +++ b/lib/galaxy/tools/imp_exp/imp_history_from_archive.xml @@ -1,7 +1,7 @@ - unpack_tar_gz_archive.py "${ str( $__ARCHIVE_SOURCE__ ).encode( 'base64' ) }" "${ str( $__DEST_DIR__ ).encode( 'base64' ) }" --$__ARCHIVE_TYPE__ --encoded + python '$__tool_directory__/unpack_tar_gz_archive.py' '${ str( $__ARCHIVE_SOURCE__ ).encode( 'base64' ) }' '${ str( $__DEST_DIR__ ).encode( 'base64' ) }' --$__ARCHIVE_TYPE__ --encoded 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 c8562f959d4..8dcf8b9b4b9 100644 --- a/lib/galaxy/tools/imp_exp/unpack_tar_gz_archive.py +++ b/lib/galaxy/tools/imp_exp/unpack_tar_gz_archive.py @@ -49,7 +49,7 @@ 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') as archive_fp: for arc_path in archive_fp.getnames(): assert os.path.normpath( os.path.join( @@ -64,7 +64,7 @@ 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 = tarfile.open(archive_file, mode='r') archive_fp.extractall(path=dest_dir) archive_fp.close() diff --git a/lib/galaxy/tools/verify/__init__.py b/lib/galaxy/tools/verify/__init__.py index c2f873dfa45..82fbfce9eb4 100644 --- a/lib/galaxy/tools/verify/__init__.py +++ b/lib/galaxy/tools/verify/__init__.py @@ -168,77 +168,74 @@ def files_diff(file1, file2, attributes=None): if (line.startswith('+') and not line.startswith('+++')) or (line.startswith('-') and not line.startswith('---')): count += 1 return count + 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() + if decompress: + # None means all compressed formats are allowed + compressed_formats = None else: - local_file = get_fileobj(file1, 'U').readlines() - history_data = get_fileobj(file2, 'U').readlines() + compressed_formats = [] + is_pdf = False + try: + local_file = get_fileobj(file1, 'U', compressed_formats=compressed_formats).readlines() + history_data = get_fileobj(file2, 'U', compressed_formats=compressed_formats).readlines() + except UnicodeDecodeError: + if file1.endswith('.pdf') or file2.endswith('.pdf'): + is_pdf = True + local_file = open(file1, 'rb').readlines() + history_data = open(file2, 'rb').readlines() + else: + raise AssertionError("Binary data detected, not displaying diff") 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'): - 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) - if diff_lines > allowed_diff_count: - if 'GALAXY_TEST_RAW_DIFF' in os.environ: - diff_slice = 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 + else: + if len(diff) < 60: + diff_slice = diff[0:40] else: - if len(diff) < 60: - diff_slice = diff[0:40] - else: - diff_slice = diff[:25] + ["********\n", "*SNIP *\n", "********\n"] + diff[-25:] - # FIXME: This pdf stuff is rather special cased and has not been updated to consider lines_diff - # due to unknown desired behavior when used in conjunction with a non-zero lines_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'): - # 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 = 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: - for vdf in valid_diff_strs: - if line.find(vdf) < 0: - valid_diff = False - else: - valid_diff = True - # Stop checking as soon as we know we have a valid difference - break - if not valid_diff: - invalid_diff_lines += 1 - log.info('## files diff on %s and %s lines_diff=%d, found diff = %d, found pdf invalid diff = %d' % (file1, file2, allowed_diff_count, diff_lines, invalid_diff_lines)) - 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)) - 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") + diff_slice = diff[:25] + ["********\n", "*SNIP *\n", "********\n"] + diff[-25:] + # FIXME: This pdf stuff is rather special cased and has not been updated to consider lines_diff + # due to unknown desired behavior when used in conjunction with a non-zero lines_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 is_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 = 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: + for vdf in valid_diff_strs: + if line.find(vdf) < 0: + valid_diff = False + else: + valid_diff = True + # Stop checking as soon as we know we have a valid difference + break + if not valid_diff: + invalid_diff_lines += 1 + log.info('## files diff on %s and %s lines_diff=%d, found diff = %d, found pdf invalid diff = %d' % (file1, file2, allowed_diff_count, diff_lines, invalid_diff_lines)) + 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)) + else: + log.info('## files diff on %s and %s lines_diff=%d, found diff = %d' % (file1, file2, allowed_diff_count, diff_lines)) + raise AssertionError("".join(diff_slice)) def files_re_match(file1, file2, attributes=None): diff --git a/lib/galaxy/util/compression_utils.py b/lib/galaxy/util/compression_utils.py index 8d46e80f8b4..7f9ebd4356f 100644 --- a/lib/galaxy/util/compression_utils.py +++ b/lib/galaxy/util/compression_utils.py @@ -1,4 +1,5 @@ import gzip +import io import sys import zipfile @@ -9,32 +10,44 @@ from .checkers import ( if sys.version_info < (3, 3): import bz2file as bz2 + gzip.GzipFile.read1 = gzip.GzipFile.read # workaround for https://bugs.python.org/issue12591 else: import bz2 -def get_fileobj(filename, mode="r", gzip_only=False, bz2_only=False, zip_only=False): +def get_fileobj(filename, mode="r", compressed_formats=None): """ - Returns a fileobj. If the file is compressed, return appropriate file reader. + Returns a fileobj. If the file is compressed, return an appropriate file + reader. In text mode, always use 'utf-8' encoding. :param filename: path to file that should be opened :param mode: mode to pass to opener - :param gzip_only: only open file if file is gzip compressed or not compressed - :param bz2_only: only open file if file is bz2 compressed or not compressed - :param zip_only: only open file if file is zip compressed or not compressed + :param compressed_formats: list of allowed compressed file formats among + 'bz2', 'gzip' and 'zip'. If left to None, all 3 formats are allowed """ + if compressed_formats is None: + compressed_formats = ['bz2', 'gzip', 'zip'] + # Remove 't' from mode, which may cause an error for compressed files + mode = mode.replace('t', '') # the various compression readers don't support 'U' mode, # so we open in 'r'. if mode == 'U': cmode = 'r' else: cmode = mode - if not bz2_only and not zip_only and is_gzip(filename): - return gzip.GzipFile(filename, cmode) - if not gzip_only and not zip_only and is_bz2(filename): - return bz2.BZ2File(filename, cmode) - if not bz2_only and not gzip_only and zipfile.is_zipfile(filename): + if 'gzip' in compressed_formats and is_gzip(filename): + fh = gzip.GzipFile(filename, cmode) + elif 'bz2' in compressed_formats and is_bz2(filename): + fh = bz2.BZ2File(filename, cmode) + elif 'zip' in compressed_formats and zipfile.is_zipfile(filename): # Return fileobj for the first file in a zip file. with zipfile.ZipFile(filename, cmode) as zh: - return zh.open(zh.namelist()[0], cmode) - return open(filename, mode) + fh = zh.open(zh.namelist()[0], cmode) + elif 'b' in mode: + return open(filename, mode) + else: + return io.open(filename, mode, encoding='utf-8') + if 'b' not in mode: + return io.TextIOWrapper(fh, encoding='utf-8') + else: + return fh diff --git a/lib/galaxy/util/facts.py b/lib/galaxy/util/facts.py new file mode 100644 index 00000000000..bd6699f6460 --- /dev/null +++ b/lib/galaxy/util/facts.py @@ -0,0 +1,65 @@ +"""Return various facts for string formatting. +""" +import socket +from collections import MutableMapping + +from six import string_types + + +class Facts(MutableMapping): + """A dict-like object that evaluates values at access time.""" + + def __init__(self, config=None, **kwargs): + self.__dict__ = {} + self.__set_defaults(config) + self.__set_config(config) + self.__dict__.update(dict(**kwargs)) + + def __set_defaults(self, config): + # config here may be a Galaxy config object, or it may just be a dict + defaults = { + 'server_name': config.base_server_name, + 'server_id': None, + 'instance_id': None, + 'pool_name': None, + 'fqdn': lambda: socket.getfqdn(), + 'hostname': lambda: socket.gethostname().split('.', 1)[0], + } + self.__dict__.update(defaults) + + def __set_config(self, config): + if config is not None: + for name in dir(config): + if not name.startswith('_') and isinstance(getattr(config, name), string_types): + self.__dict__['config_' + name] = lambda name=name: getattr(config, name) + + def __getitem__(self, key): + item = self.__dict__.__getitem__(key) + if callable(item): + return item() + else: + return item + + # Other methods pass through to the corresponding dict methods + + def __setitem__(self, key, value): + return self.__dict__.__setitem__(key, value) + + def __delitem__(self, key): + return self.__dict__.__delitem__(key) + + def __iter__(self): + return self.__dict__.__iter__() + + def __len__(self): + return self.__dict__.__len__() + + def __str__(self): + return self.__dict__.__str__() + + def __repr__(self): + return self.__dict__.__repr__() + + +def get_facts(config=None, **kwargs): + return Facts(config=config, **kwargs) diff --git a/lib/galaxy/util/handlers.py b/lib/galaxy/util/handlers.py index dd05f00762b..bd8c2571f16 100644 --- a/lib/galaxy/util/handlers.py +++ b/lib/galaxy/util/handlers.py @@ -9,6 +9,7 @@ import logging import os import random + log = logging.getLogger(__name__) @@ -93,16 +94,19 @@ class ConfiguresHandlers: rval.append(elem) return rval - def is_handler(self, server_name): - """Given a server name, indicate whether the server is a handler. + @property + def is_handler(self): + """Indicate whether the current server is a handler. :param server_name: The name to check :type server_name: str :return: bool """ + if self._is_handler is not None: + return self._is_handler for collection in self.handlers.values(): - if server_name in collection: + if self.app.config.server_name in collection: return True return False @@ -128,6 +132,8 @@ class ConfiguresHandlers: :returns: str -- A valid job handler ID. """ + if id_or_tag is None and self.default_handler_id is None: + return None if id_or_tag is None: id_or_tag = self.default_handler_id return self._get_single_item(self.handlers[id_or_tag], index=index) diff --git a/lib/galaxy/util/path/__init__.py b/lib/galaxy/util/path/__init__.py index 3cde6fb0934..aef913f3bf2 100644 --- a/lib/galaxy/util/path/__init__.py +++ b/lib/galaxy/util/path/__init__.py @@ -8,6 +8,7 @@ from functools import partial from itertools import starmap from operator import getitem from os import ( + extsep, makedirs, walk, ) @@ -22,7 +23,7 @@ from os.path import ( relpath, ) -from six import string_types +from six import iteritems, string_types from six.moves import filterfalse, map, zip @@ -97,6 +98,92 @@ def unsafe_walk(path, whitelist=None): return filterfalse(partial(safe_contains, path, whitelist=whitelist), __walk(abspath(path))) +def joinext(root, ext): + """ + Roughly the reverse of os.path.splitext. + + :type root: string + :param root: part of the filename before the extension + :type root: string + :param ext: the extension + :rtype: string + :returns: ``root`` joined with ``ext`` separated by a single ``os.extsep`` + """ + return extsep.join([root.rstrip(extsep), ext.lstrip(extsep)]) + + +def has_ext(path, ext, aliases=False, ignore=None): + """ + Determine whether ``path`` has extension ``ext`` + + :type path: string + :param path: Path to check + :type ext: string + :param ext: Extension to check + :type aliases: bool + :param aliases: Check any known aliases for the given extension + :type ignore: string + :param ignore: Ignore this extension at the end of the path (e.g. ``sample``) + :rtype: bool + :returns: ``True`` if path is a YAML file, ``False`` otherwise. + """ + ext = __ext_strip_sep(ext) + root, _ext = __splitext_ignore(path, ignore=ignore) + if aliases: + return _ext in extensions[ext] + else: + return _ext == ext + + +def get_ext(path, ignore=None, canonicalize=True): + """ + Return the extension of ``path`` + + :type path: string + :param path: Path to check + :type ignore: string + :param ignore: Ignore this extension at the end of the path (e.g. ``sample``) + :type canonicalize: bool + :param canonicalize: If the extension is known to this module, return the canonicalized extension instead of the + file's actual extension + :rtype: string + """ + root, ext = __splitext_ignore(path, ignore=ignore) + if canonicalize: + try: + ext = extensions.canonicalize(ext) + except KeyError: + pass # should do something else here? + return ext + + +class Extensions(dict): + """Mappings for extension aliases. + + A dict-like object that returns values for keys that are not mapped if the key can be found in any of the dict's + values (which should be sequence types). + + The first item in the sequence should match the key and is the "canonicalization". + """ + def __missing__(self, key): + for k, v in iteritems(self): + if key in v: + self[key] = v + return v + raise KeyError(key) + + def canonicalize(self, ext): + # shouldn't raise an IndexError because it should raise a KeyError first + return self[ext][0] + + +extensions = Extensions({ + 'ini': ['ini'], + 'json': ['json'], + 'yaml': ['yaml', 'yml'], +}) + + def __listify(item): """A non-splitting version of :func:`galaxy.util.listify`. """ @@ -124,6 +211,23 @@ def __contains(prefix, path, whitelist=None): yield not relpath(real, wldir).startswith(pardir) +def __ext_strip_sep(ext): + return ext.lstrip(extsep) + + +def __splitext_no_sep(path): + return (path.rsplit(extsep, 1) + [''])[0:2] + + +def __splitext_ignore(path, ignore=None): + # note: unlike os.path.splitext this strips extsep from ext + ignore = map(__ext_strip_sep, __listify(ignore)) + root, ext = __splitext_no_sep(path) + if ext in ignore: + root, ext = __splitext_no_sep(path) + return (root, ext) + + # cross-platform support @@ -178,6 +282,10 @@ __pathfxns__ = ( ) __all__ = ( + 'extensions', + 'get_ext', + 'has_ext', + 'joinext', 'safe_contains', 'safe_makedirs', 'safe_relpath', diff --git a/lib/galaxy/util/properties.py b/lib/galaxy/util/properties.py index 23890219c0f..5fd57f3158c 100644 --- a/lib/galaxy/util/properties.py +++ b/lib/galaxy/util/properties.py @@ -5,41 +5,62 @@ this should be reusable by tool shed and pulsar as well. import os import os.path import sys +from functools import partial +from itertools import product, starmap import yaml -from six import iteritems + +from six import iteritems, string_types from six.moves.configparser import ConfigParser -from galaxy.util import listify +from galaxy.util.path import extensions, has_ext, joinext -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) - 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) +def find_config_file(names, exts=None, dirs=None, include_samples=False): + """Locate a config file in multiple directories, with multiple extensions. - if explicit: - if os.path.exists(explicit): - config_file = explicit - else: - raise Exception("Problem determining Galaxy's configuration - the specified configuration file cannot be found.") - else: - config_file = None - if os.path.exists(default): - config_file = default - 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 + >>> from shutil import rmtree + >>> from tempfile import mkdtemp + >>> def touch(d, f): + ... open(os.path.join(d, f), 'w').close() + >>> def _find_config_file(*args, **kwargs): + ... return find_config_file(*args, **kwargs).replace(d, '') + >>> d = mkdtemp() + >>> d1 = os.path.join(d, 'd1') + >>> d2 = os.path.join(d, 'd2') + >>> os.makedirs(d1) + >>> os.makedirs(d2) + >>> touch(d1, 'foo.ini') + >>> touch(d1, 'foo.bar') + >>> touch(d1, 'baz.ini.sample') + >>> touch(d2, 'foo.yaml') + >>> touch(d2, 'baz.yml') + >>> _find_config_file('foo', dirs=(d1, d2)) + '/d1/foo.ini' + >>> _find_config_file('baz', dirs=(d1, d2)) + '/d2/baz.yml' + >>> _find_config_file('baz', dirs=(d1, d2), include_samples=True) + '/d2/baz.yml' + >>> _find_config_file('baz', dirs=(d1,), include_samples=True) + '/d1/baz.ini.sample' + >>> _find_config_file('foo', dirs=(d2, d1)) + '/d2/foo.yaml' + >>> find_config_file('quux', dirs=(d,)) + >>> _find_config_file('foo', exts=('bar', 'ini'), dirs=(d1,)) + '/d1/foo.bar' + >>> rmtree(d) + """ + found = __find_config_files( + names, + exts=exts or extensions['yaml'] + extensions['ini'], + dirs=dirs or [os.getcwd(), os.path.join(os.getcwd(), 'config')], + include_samples=include_samples, + ) + if not found: + return None + # doesn't really make sense to log here but we should probably generate a warning of some kind if more than one + # config is found. + return found[0] def load_app_properties( @@ -56,18 +77,22 @@ def load_app_properties( config_section = ini_section if config_file: - if not config_file.endswith(".yml") and not config_file.endswith(".yml.sample"): + if not has_ext(config_file, 'yaml', aliases=True, ignore='sample'): if config_section is None: config_section = "app:main" parser = nice_config_parser(config_file) - properties.update(dict(parser.items(config_section))) + if parser.has_section(config_section): + properties.update(dict(parser.items(config_section))) + else: + properties.update(parser.defaults()) else: if config_section is None: config_section = "galaxy" with open(config_file, "r") as f: raw_properties = yaml.safe_load(f) - properties = raw_properties[config_section] or {} + properties = __default_properties(config_file) + properties.update(raw_properties.get(config_section) or {}) override_prefix = "%sOVERRIDE_" % config_prefix for key in os.environ: @@ -83,11 +108,7 @@ def load_app_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 = NicerConfigParser(path, defaults=__default_properties(path)) parser.optionxform = str # Don't lower-case keys with open(path) as f: parser.read_file(f) @@ -149,4 +170,31 @@ class NicerConfigParser(ConfigParser): raise +def __get_all_configs(dirs, names): + return list(filter(os.path.exists, starmap(os.path.join, product(dirs, names)))) + + +def __find_config_files(names, exts=None, dirs=None, include_samples=False): + sample_names = [] + if isinstance(names, string_types): + names = [names] + if not dirs: + dirs = [os.getcwd()] + if exts: + # add exts to names, converts back into a list because it's going to be small and we might consume names twice + names = list(starmap(joinext, product(names, exts))) + if include_samples: + sample_names = map(partial(joinext, ext='sample'), names) + # check for all names in each dir before moving to the next dir. could do it the other way around but that makes + # less sense to me. + return __get_all_configs(dirs, names) or __get_all_configs(dirs, sample_names) + + +def __default_properties(path): + return { + 'here': os.path.dirname(os.path.abspath(path)), + '__file__': os.path.abspath(path) + } + + __all__ = ('find_config_file', 'load_app_properties', 'NicerConfigParser') diff --git a/lib/galaxy/web/form_builder.py b/lib/galaxy/web/form_builder.py index b4a04140d24..c0c49f9af0b 100644 --- a/lib/galaxy/web/form_builder.py +++ b/lib/galaxy/web/form_builder.py @@ -8,7 +8,11 @@ from cgi import escape from six import string_types -from galaxy.util import restore_text, unicodify +from galaxy.util import ( + asbool, + restore_text, + unicodify +) log = logging.getLogger(__name__) @@ -19,7 +23,10 @@ class BaseField(object): self.label = label self.value = value self.disabled = kwds.get('disabled', False) - self.optional = kwds.get('optional', True) and kwds.get('required', 'optional') == 'optional' + if 'optional' in kwds: + self.optional = asbool(kwds.get('optional')) + else: + self.optional = kwds.get('required', 'optional') == 'optional' self.help = kwds.get('helptext') def get_html(self, prefix=""): diff --git a/lib/galaxy/web/framework/webapp.py b/lib/galaxy/web/framework/webapp.py index a240f73aee9..831474246a1 100644 --- a/lib/galaxy/web/framework/webapp.py +++ b/lib/galaxy/web/framework/webapp.py @@ -37,6 +37,7 @@ from galaxy.web.framework import ( helpers, url_for ) +from galaxy.web.stack import get_app_kwds log = logging.getLogger(__name__) @@ -961,17 +962,9 @@ 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, - }) + # TODO: just move this to a classmethod on stack? + app_kwds = get_app_kwds(config_section) + uwsgi_app = paste_factory({}, load_app_kwds=app_kwds) return uwsgi_app diff --git a/lib/galaxy/web/stack/__init__.py b/lib/galaxy/web/stack/__init__.py index e23899db6f5..44c15601861 100644 --- a/lib/galaxy/web/stack/__init__.py +++ b/lib/galaxy/web/stack/__init__.py @@ -1,6 +1,6 @@ """Web application stack operations """ -from __future__ import print_function +from __future__ import absolute_import import inspect import logging @@ -14,59 +14,297 @@ try: except ImportError: uwsgi = None -try: - from uwsgidecorators import postfork as uwsgi_postfork -except (AttributeError, ImportError): - uwsgi_postfork = lambda x: x # noqa: E731 - if uwsgi is not None and hasattr(uwsgi, 'numproc'): - print("WARNING: This is a uwsgi process but the uwsgidecorators library" - " is unavailable. This is likely due to using an external (not" - " in Galaxy's virtualenv) uwsgi and you may experience errors. " - "HINT:\n {venv}/bin/pip install uwsgidecorators".format( - venv=os.environ.get('VIRTUAL_ENV', '/path/to/venv'))) +from six import string_types + +from galaxy.util.bunch import Bunch +from galaxy.util.facts import get_facts +from galaxy.util.properties import nice_config_parser + +from .message import ApplicationStackMessage, ApplicationStackMessageDispatcher +from .transport import ApplicationStackTransport, UWSGIFarmMessageTransport log = logging.getLogger(__name__) +class ApplicationStackLogFilter(logging.Filter): + def filter(self, record): + record.worker_id = None + record.mule_id = None + return True + + +class UWSGILogFilter(logging.Filter): + def filter(self, record): + record.worker_id = uwsgi.worker_id() + record.mule_id = uwsgi.mule_id() + return True + + class ApplicationStack(object): name = None prohibited_middleware = frozenset() + transport_class = ApplicationStackTransport + log_filter_class = ApplicationStackLogFilter + log_format = '%(name)s %(levelname)s %(asctime)s %(message)s' + # TODO: this belongs in the pool configuration + server_name_template = '{server_name}' + default_app_name = 'main' + + # used both to route jobs to a pool with this name and indicate whether or + # not a stack is using messaging for handler assignment + pools = Bunch( + JOB_HANDLERS='job-handlers', + ) + + @classmethod + def log_filter(cls): + return cls.log_filter_class() + + @classmethod + def get_app_kwds(cls, config_section, app_name=None, for_paste_app=False): + return {} @classmethod def register_postfork_function(cls, f, *args, **kwargs): f(*args, **kwargs) - def workers(self): - return [] + def __init__(self, app=None, config=None): + self.app = app + self.config = config or (app and app.config) + self.running = False + + def start(self): + # TODO: with a stack config the pools could be parsed here + pass def allowed_middleware(self, middleware): if hasattr(middleware, '__name__'): middleware = middleware.__name__ return middleware not in self.prohibited_middleware + def workers(self): + return [] + + @property + def pool_name(self): + # TODO: ideally jobs would be mappable to handlers by pool name + return None + + def has_pool(self, pool_name): + return False + + def in_pool(self, pool_name): + return False + + @property + def facts(self): + facts = get_facts(config=self.config) + facts.update({'pool_name': self.pool_name}) + return facts + def set_postfork_server_name(self, app): + app.config.server_name = self.server_name_template.format(**self.facts) + log.debug('server_name set to: %s', app.config.server_name) + + def register_message_handler(self, func, name=None): + pass + + def deregister_message_handler(self, func=None, name=None): + pass + + def send_message(self, dest, msg=None, target=None, params=None, **kwargs): + pass + + def shutdown(self): pass -class UWSGIApplicationStack(ApplicationStack): +class MessageApplicationStack(ApplicationStack): + def __init__(self, app=None, config=None): + super(MessageApplicationStack, self).__init__(app=app, config=config) + self.dispatcher = ApplicationStackMessageDispatcher() + self.transport = self.transport_class(app, stack=self, dispatcher=self.dispatcher) + + def start(self): + super(MessageApplicationStack, self).start() + if not self.running: + self.transport.start() + self.running = True + + def register_message_handler(self, func, name=None): + self.dispatcher.register_func(func, name) + self.transport.start_if_needed() + + def deregister_message_handler(self, func=None, name=None): + self.dispatcher.deregister_func(func, name) + self.transport.stop_if_unneeded() + + def send_message(self, dest, msg=None, target=None, params=None, **kwargs): + assert msg is not None or target is not None, "Either 'msg' or 'target' parameters must be set" + if not msg: + msg = ApplicationStackMessage( + target=target, + params=params, + **kwargs + ) + self.transport.send_message(msg.encode(), dest) + + def shutdown(self): + if self.running: + log.info('Application stack interface shutting down') + self.transport.shutdown() + self.running = False + + +class UWSGIApplicationStack(MessageApplicationStack): + """Interface to the uWSGI application stack. Supports running additional webless Galaxy workers as mules. Mules + must be farmed to be communicable via uWSGI mule messaging, unfarmed mules are not supported. + + Note that mules will use this as their stack class even though they start with the "webless" loading point. + """ name = 'uWSGI' prohibited_middleware = frozenset([ 'wrap_in_static', 'EvalException', ]) + transport_class = UWSGIFarmMessageTransport + log_filter_class = UWSGILogFilter + log_format = '%(name)s %(levelname)s %(asctime)s [p:%(process)s,w:%(worker_id)s,m:%(mule_id)s] [%(threadName)s] %(message)s' + server_name_template = '{server_name}.{pool_name}.{instance_id}' postfork_functions = [] + @classmethod + def get_app_kwds(cls, config_section, app_name=None): + kwds = { + 'config_file': None, + 'config_section': config_section, + } + uwsgi_opt = uwsgi.opt + # check for --yaml or --json uWSGI config options first + config_file = uwsgi_opt.get("yaml") or uwsgi_opt.get("json") + # --ini and --ini-paste don't behave the same way, but this method will only be called by mules if the main + # application was loaded with --ini-paste, so we can make some assumptions, most notably, uWSGI does not have + # any way to set the app name when loading with paste.deploy:loadapp(), so hardcoding the alternate section + # name to `app:main` is fine. + if config_file is None and uwsgi_opt.get("ini") or uwsgi_opt.get("ini-paste"): + config_file = uwsgi_opt.get("ini") or uwsgi_opt.get("ini-paste") + parser = nice_config_parser(config_file) + if not parser.has_section(config_section) and parser.has_section('app:main'): + kwds['config_section'] = 'app:main' + # check for --set galaxy_config_file=, this overrides whatever config file uWSGI was loaded with (which + # may not actually include a Galaxy config) + if uwsgi_opt.get("galaxy_config_file"): + config_file = uwsgi_opt.get("galaxy_config_file") + kwds['config_file'] = config_file + return kwds + @classmethod def register_postfork_function(cls, f, *args, **kwargs): - cls.postfork_functions.append((f, args, kwargs)) + if uwsgi.mule_id() == 0: + cls.postfork_functions.append((f, args, kwargs)) + else: + # mules are forked from the master and run the master's postfork functions immediately before the forked + # process is replaced. that is prevented in the _do_uwsgi_postfork function, and because programmed mules + # are standalone non-forking processes, they should run postfork functions immediately + f(*args, **kwargs) + + def __init__(self, app=None, config=None): + self._farms_dict = None + self._mules_list = None + super(UWSGIApplicationStack, self).__init__(app=app, config=config) + + @property + def _configured_mules(self): + if self._mules_list is None: + self._mules_list = _uwsgi_configured_mules() + return self._mules_list + + @property + def _is_mule(self): + return uwsgi.mule_id() > 0 + + @property + def _configured_farms(self): + if self._farms_dict is None: + self._farms_dict = {} + farms = uwsgi.opt.get('farm', []) + farms = [farms] if isinstance(farms, string_types) else farms + for farm in farms: + name, mules = farm.split(':', 1) + self._farms_dict[name] = [int(m) for m in mules.split(',')] + return self._farms_dict + + @property + def _farms(self): + farms = [] + for farm, mules in self._configured_farms.items(): + if uwsgi.mule_id() in mules: + farms.append(farm) + return farms + + def _mule_index_in_farm(self, farm_name): + try: + mules = self._configured_farms[farm_name] + return mules.index(uwsgi.mule_id()) + except (KeyError, ValueError): + return -1 + + @property + def _farm_name(self): + try: + return self._farms[0] + except IndexError: + return None + + @property + def instance_id(self): + if not self._is_mule: + instance_id = uwsgi.worker_id() + elif self._farm_name: + return self._mule_index_in_farm(self._farm_name) + 1 + else: + instance_id = uwsgi.mule_id() + return instance_id + + def start(self): + # Does a generalized `is_worker` attribute make sense? Hard to say w/o other stack paradigms. + if self._is_mule and self._farm_name: + # used by main.py to send a shutdown message on termination + os.environ['_GALAXY_UWSGI_FARM_NAMES'] = ','.join(self._farms) + super(UWSGIApplicationStack, self).start() + + def has_pool(self, pool_name): + return pool_name in self._configured_farms + + def in_pool(self, pool_name): + if not self._is_mule: + return False + else: + return pool_name in self._farms def workers(self): return uwsgi.workers() - def set_postfork_server_name(self, app): - app.config.server_name += ".%d" % uwsgi.worker_id() + @property + def facts(self): + facts = super(UWSGIApplicationStack, self).facts + if not self._is_mule: + facts.update({ + 'pool_name': 'web', + 'server_id': uwsgi.worker_id(), + }) + else: + facts.update({ + 'pool_name': self._farm_name, + 'server_id': uwsgi.mule_id(), + }) + facts['instance_id'] = self.instance_id + return facts + + def shutdown(self): + super(UWSGIApplicationStack, self).shutdown() class PasteApplicationStack(ApplicationStack): @@ -91,16 +329,51 @@ def application_stack_class(): return WeblessApplicationStack -def application_stack_instance(): +def application_stack_instance(app=None, config=None): stack_class = application_stack_class() - return stack_class() + return stack_class(app=app, config=config) + + +def application_stack_log_filter(): + return application_stack_class().log_filter_class() + + +def application_stack_log_formatter(): + return logging.Formatter(fmt=application_stack_class().log_format) def register_postfork_function(f, *args, **kwargs): application_stack_class().register_postfork_function(f, *args, **kwargs) -@uwsgi_postfork -def _do_postfork(): +def get_app_kwds(config_section, app_name=None): + return application_stack_class().get_app_kwds(config_section, app_name=app_name) + + +def get_stack_facts(config=None): + return application_stack_instance(config=config).facts + + +def _uwsgi_configured_mules(): + mules = uwsgi.opt.get('mule', []) + return [mules] if isinstance(mules, string_types) or mules is True else mules + + +def _do_uwsgi_postfork(): + for i, mule in enumerate(_uwsgi_configured_mules()): + if mule is not True and i + 1 == uwsgi.mule_id(): + # mules will inherit the postfork function list and call them immediately upon fork, but programmed mules + # should not do that (they will call the postfork functions in-place as they start up after exec()) + UWSGIApplicationStack.postfork_functions = [(_mule_fixup, (), {})] for f, args, kwargs in [t for t in UWSGIApplicationStack.postfork_functions]: + log.debug('Calling postfork function: %s', f) f(*args, **kwargs) + + +def _mule_fixup(): + import urllib2 + urllib2._opener = None + + +if uwsgi: + uwsgi.post_fork_hook = _do_uwsgi_postfork diff --git a/lib/galaxy/web/stack/message.py b/lib/galaxy/web/stack/message.py new file mode 100644 index 00000000000..83fac262791 --- /dev/null +++ b/lib/galaxy/web/stack/message.py @@ -0,0 +1,168 @@ +"""Web Application Stack worker messaging +""" +from __future__ import absolute_import + +import json +import logging +from types import MethodType + + +log = logging.getLogger(__name__) + + +class ApplicationStackMessageDispatcher(object): + def __init__(self): + self.__funcs = {} + + def __func_name(self, func, name): + if not name: + name = func.__name__ + return name + + def register_func(self, func, name=None): + name = self.__func_name(func, name) + self.__funcs[name] = func + + def deregister_func(self, func=None, name=None): + name = self.__func_name(func, name) + try: + del self.__funcs[name] + except KeyError: + pass + + @property + def handler_count(self): + return len(self.__funcs) + + def dispatch(self, msg_str): + msg = decode(msg_str) + try: + msg.validate() + except AssertionError as exc: + log.error('Invalid message received: %s, error: %s', msg_str, exc) + return + if msg.target not in self.__funcs: + log.error("Received message with target '%s' but no functions were registered with that name. Params were: %s", msg.target, msg.params) + else: + self.__funcs[msg.target](msg) + + +class ApplicationStackMessage(dict): + target = None + default_handler = None + _validate_kwargs = ('target',) + + def __init__(self, target=None, **kwargs): + self['target'] = target or self.__class__.target + self._merge_class_tuples() + + def _merge_class_tuples(self): + """Locates any class-level tuples beginning with a single (but not double) underscore in the MRO and creates a + property on the instance with the same name (without the leading underscore) that will return the union of + those tuples. + """ + names = set() + for cls in reversed(self.__class__.mro()): + names.update([x for x in dir(cls) if x.startswith('_') and not x.startswith('__') and type(getattr(cls, x)) == tuple]) + for name in names: + setattr(self.__class__, name.lstrip('_'), property(lambda self, name=name: self._get_list_from_mro(name))) + + def _get_list_from_mro(self, name): + """Locate all class-level tuples with the given `name` in the MRO and return their union. + """ + r = set() + for cls in reversed(self.__class__.mro()): + r.update(getattr(cls, name, [])) + return r + + def _validate_items(self, obj, items, name): + for item in items: + assert item in obj, "Missing '%s' message %" % (item, name) + + def validate(self): + self._validate_items(self, self.validate_kwargs, 'argument') + + def encode(self): + self['__classname__'] = self.__class__.__name__ + return json.dumps(self) + + def bind_default_handler(self, obj, name): + """Bind the default handler method to `obj` as attribute `name`. + + This could also be implemented as a mixin class. + """ + assert self.default_handler is not None, '%s has no default handler method, cannot bind' % self.__class__.__name__ + setattr(obj, name, MethodType(self.default_handler, obj, obj.__class__)) + log.debug("Bound default message handler '%s.%s' to %s", self.__class__.__name__, self.default_handler.__name__, + getattr(obj, name)) + + @property + def target(self): + return self['target'] + + @target.setter + def set_target(self, target): + self['target'] = target + + +class ParamMessage(ApplicationStackMessage): + _validate_kwargs = ('params',) + _validate_params = () + _exclude_params = () + + def __init__(self, target=None, params=None, **kwargs): + super(ParamMessage, self).__init__(target=target) + self['params'] = params or {} + for k, v in kwargs.items(): + self['params'][k] = v + + def validate(self): + super(ParamMessage, self).validate() + self._validate_items(self['params'], self.validate_params, 'parameters') + + @property + def params(self): + d = self['params'].copy() + for key in self.exclude_params: + d.pop(key, None) + return d + + @params.setter + def set_params(self, params): + self['params'] = params + + +class TaskMessage(ParamMessage): + _validate_params = ('task',) + _exclude_params = ('task',) + + @staticmethod + def default_handler(self, msg): + """Can be bound to an instance of any class that has message handling methods named like `_handle_{task}_method` + """ + name = '_handle_{task}_msg'.format(task=msg.task) + assert name in dir(self), "{cls} has no method _handle_{task}_msg, cannot handle message: {msg}".format( + cls=self.__class__.__name__, + task=msg.task, + msg=msg) + getattr(self, '_handle_%s_msg' % msg.task)(**msg.params) + + @property + def task(self): + return self['params']['task'] + + +class JobHandlerMessage(TaskMessage): + target = 'job_handler' + _validate_params = ('job_id',) + + +class WorkflowSchedulingMessage(TaskMessage): + target = 'workflow_scheduling' + _validate_params = ('workflow_invocation_id',) + + +def decode(msg_str): + d = json.loads(msg_str) + cls = d.pop('__classname__') + return globals()[cls](**d) diff --git a/lib/galaxy/web/stack/transport.py b/lib/galaxy/web/stack/transport.py new file mode 100644 index 00000000000..14226eb728b --- /dev/null +++ b/lib/galaxy/web/stack/transport.py @@ -0,0 +1,152 @@ +"""Web application stack operations +""" +from __future__ import absolute_import + +import logging +import sys +import threading + +try: + import uwsgi +except ImportError: + uwsgi = None + + +log = logging.getLogger(__name__) + + +class ApplicationStackTransport(object): + SHUTDOWN_MSG = '__SHUTDOWN__' + + def __init__(self, app, stack, dispatcher=None): + """ Pre-fork initialization. + """ + self.app = app + self.stack = stack + self.can_run = False + self.running = False + self.dispatcher = dispatcher + self.dispatcher_thread = None + + def _dispatch_messages(self): + pass + + def start_if_needed(self): + # Don't unnecessarily start a thread that we don't need. + if self.can_run and not self.running and not self.dispatcher_thread and self.dispatcher and self.dispatcher.handler_count: + self.running = True + self.dispatcher_thread = threading.Thread(name=self.__class__.__name__ + ".dispatcher_thread", target=self._dispatch_messages) + self.dispatcher_thread.start() + log.info('%s dispatcher started', self.__class__.__name__) + + def stop_if_unneeded(self): + if self.can_run and self.running and self.dispatcher_thread and self.dispatcher and not self.dispatcher.handler_count: + self.running = False + self.dispatcher_thread.join() + self.dispatcher_thread = None + log.info('%s dispatcher stopped', self.__class__.__name__) + + def start(self): + """ Post-fork initialization. + """ + self.can_run = True + self.start_if_needed() + + def send_message(self, msg, dest): + pass + + def shutdown(self): + self.running = False + if self.dispatcher_thread: + log.info('Joining application stack transport dispatcher thread') + self.dispatcher_thread.join() + self.dispatcher_thread = None + + +class UWSGIFarmMessageTransport(ApplicationStackTransport): + """ Communication via uWSGI Mule Farm messages. Communication is unidirectional (workers -> mules). + """ + # Define any static lock names here, additional locks will be appended for each configured farm's message handler + _locks = [] + + def __initialize_locks(self): + num = int(uwsgi.opt.get('locks', 0)) + 1 + farms = self.stack._configured_farms.keys() + need = len(farms) + if num < need: + raise RuntimeError('Need %i uWSGI locks but only %i exist(s): Set `locks = %i` in uWSGI configuration' % (need, num, need - 1)) + sys.exit(1) + self._locks.extend(map(lambda x: 'RECV_MSG_FARM_' + x, farms)) + # this would be nice, but in my 2.0.15 uWSGI, the uwsgi module has no set_option function, and I don't know if it'd work even if the function existed as documented + # if len(self.lock_map) > 1: + # uwsgi.set_option('locks', len(self.lock_map)) + # log.debug('Created %s uWSGI locks' % len(self.lock_map)) + + def __init__(self, app, stack, dispatcher=None): + super(UWSGIFarmMessageTransport, self).__init__(app, stack, dispatcher=dispatcher) + self.__initialize_locks() + + def __lock(self, name_or_id): + try: + uwsgi.lock(name_or_id) + except TypeError: + uwsgi.lock(self._locks.index(name_or_id)) + + def __unlock(self, name_or_id): + try: + uwsgi.unlock(name_or_id) + except TypeError: + uwsgi.unlock(self._locks.index(name_or_id)) + + def _farm_recv_msg_lock_num(self): + return self._locks.index('RECV_MSG_FARM_' + self.stack._farm_name) + + def _dispatch_messages(self): + # this could be moved to the base class if locking was abstracted and a get_message method was added + log.info('Application stack message dispatcher thread starting up') + # we are going to do this a lot, so cache the lock number + lock = self._farm_recv_msg_lock_num() + while self.running: + msg = None + self.__lock(lock) + try: + log.debug('Acquired message lock, waiting for new message') + msg = uwsgi.farm_get_msg() + log.debug('Received message: %s', msg) + if msg == self.SHUTDOWN_MSG: + self.running = False + else: + self.dispatcher.dispatch(msg) + except Exception: + log.exception('Exception in mule message handling') + finally: + self.__unlock(lock) + log.debug('Released lock') + log.info('Application stack message dispatcher thread exiting') + + # TODO: start_if_needed would be called on a web worker by the stack's register_message_handler function if a + # function were registered in a web handler, that should probably be prevented. + + def start(self): + """ Post-fork initialization. + + This is mainly done here for the future possibility that we'll be able to run mules post-fork without exec()ing. In a programmed mule it could be done at __init__ time. + """ + if self.stack._is_mule: + if not uwsgi.in_farm(): + raise RuntimeError('Mule %s is not in a farm! Set `farm = :%s` in uWSGI configuration' + % (uwsgi.mule_id(), + ','.join(map(str, range(1, len(filter(lambda x: x.endswith('galaxy/main.py'), self.stack._configured_mules)) + 1))))) + elif len(self.stack._farms) > 1: + raise RuntimeError('Mule %s is in multiple farms! This configuration is not supported due to locking issues' % uwsgi.mule_id()) + # only mules receive messages so don't bother starting the dispatcher if we're not a mule (although + # currently it doesn't have any registered handlers and so wouldn't start anyway) + super(UWSGIFarmMessageTransport, self).start() + + def shutdown(self): + if self.stack._is_mule: + super(UWSGIFarmMessageTransport, self).shutdown() + + def send_message(self, msg, dest): + log.debug('Sending message to farm %s: %s', dest, msg) + uwsgi.farm_msg(dest, msg) diff --git a/lib/galaxy/webapps/galaxy/api/histories.py b/lib/galaxy/webapps/galaxy/api/histories.py index 832fb97ea90..69c8940e0ed 100644 --- a/lib/galaxy/webapps/galaxy/api/histories.py +++ b/lib/galaxy/webapps/galaxy/api/histories.py @@ -317,9 +317,16 @@ class HistoriesController(BaseAPIController, ExportsHistoryMixin, ImportsHistory if "archive_source" in payload: archive_source = payload["archive_source"] - archive_type = payload.get("archive_type", "url") + archive_file = payload.get("archive_file") + if archive_source: + archive_type = payload.get("archive_type", "url") + elif hasattr(archive_file, "file"): + archive_source = payload["archive_file"].file.name + archive_type = "file" + else: + raise exceptions.MessageException("Please provide a url or file.") self.queue_history_import(trans, archive_type=archive_type, archive_source=archive_source) - return {} + return {"message": "Importing history from source '%s'. This history will be visible when the import is complete." % archive_source} new_history = None # if a history id was passed, copy that history diff --git a/lib/galaxy/webapps/galaxy/buildapp.py b/lib/galaxy/webapps/galaxy/buildapp.py index b50e489038f..5b8aadb97c0 100644 --- a/lib/galaxy/webapps/galaxy/buildapp.py +++ b/lib/galaxy/webapps/galaxy/buildapp.py @@ -3,13 +3,11 @@ Provides factory methods to assemble the Galaxy web application """ import atexit import logging -import os import sys import threading import traceback from paste import httpexceptions -from six.moves import configparser import galaxy.app import galaxy.datatypes.registry @@ -34,16 +32,13 @@ class GalaxyWebApplication(galaxy.web.framework.webapp.WebApplication): pass -def app_factory(global_conf, **kwargs): - return paste_app_factory(global_conf, **kwargs) - - -def paste_app_factory(global_conf, **kwargs): +def app_factory(global_conf, load_app_kwds={}, **kwargs): """ Return a wsgi application serving the root object """ kwargs = load_app_properties( - kwds=kwargs + kwds=kwargs, + **load_app_kwds ) # Create the Galaxy application unless passed in if 'app' in kwargs: @@ -58,7 +53,7 @@ def paste_app_factory(global_conf, **kwargs): 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) + app.application_stack.register_postfork_function(atexit.register, app.shutdown) # Create the universe WSGI application webapp = GalaxyWebApplication(app, session_cookie='galaxysession', name='galaxy') @@ -105,14 +100,12 @@ def paste_app_factory(global_conf, **kwargs): webapp.add_client_route('/admin/repositories', '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('/admin/form/{form_id}', 'admin') webapp.add_client_route('/admin/api_keys', '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('/visualizations/edit') @@ -122,6 +115,7 @@ def paste_app_factory(global_conf, **kwargs): webapp.add_client_route('/pages/edit') webapp.add_client_route('/histories/citations') webapp.add_client_route('/histories/list') + webapp.add_client_route('/histories/import') webapp.add_client_route('/histories/list_published') webapp.add_client_route('/histories/list_shared') webapp.add_client_route('/histories/rename') @@ -129,8 +123,11 @@ def paste_app_factory(global_conf, **kwargs): 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('/workflows/list') + webapp.add_client_route('/workflows/list_published') + webapp.add_client_route('/workflows/create') + webapp.add_client_route('/workflows/run') + webapp.add_client_route('/workflows/import_workflow') webapp.add_client_route('/custom_builds') # ==== Done @@ -165,27 +162,16 @@ def paste_app_factory(global_conf, **kwargs): return webapp -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} - parser = configparser.ConfigParser() - parser.read(config_file) - try: - kwargs = dict(parser.items('app:main')) - except configparser.NoSectionError: - kwargs = {} - return app_factory(global_conf, **kwargs) +def uwsgi_app(): + return galaxy.web.framework.webapp.build_native_uwsgi_app(app_factory, "galaxy") + + +# For backwards compatibility +uwsgi_app_factory = uwsgi_app def postfork_setup(): from galaxy.app import app - app.application_stack.set_postfork_server_name(app) app.control_worker.bind_and_start() diff --git a/lib/galaxy/webapps/galaxy/controllers/dataset.py b/lib/galaxy/webapps/galaxy/controllers/dataset.py index cf7ffc324ff..e87487a9871 100644 --- a/lib/galaxy/webapps/galaxy/controllers/dataset.py +++ b/lib/galaxy/webapps/galaxy/controllers/dataset.py @@ -309,7 +309,7 @@ class DatasetInterface(BaseUIController, UsesAnnotations, UsesItemRatings, UsesE attribute_inputs.append({ 'type' : 'select', 'multiple' : attributes.multiple, - 'optional' : attributes.optional, + 'optional' : spec.get('optional'), 'name' : name, 'label' : spec.desc, 'options' : attributes.options, diff --git a/lib/galaxy/webapps/galaxy/controllers/history.py b/lib/galaxy/webapps/galaxy/controllers/history.py index 6b242fc378e..38a28e85359 100644 --- a/lib/galaxy/webapps/galaxy/controllers/history.py +++ b/lib/galaxy/webapps/galaxy/controllers/history.py @@ -1185,33 +1185,6 @@ class HistoryController(BaseUIController, SharableMixin, UsesAnnotations, UsesIt return self.get_ave_item_rating_data(trans.sa_session, history) # TODO: used in display_base.mako - @web.expose - # TODO: Remove require_login when users are warned that, if they are not - # logged in, this will remove their current history. - @web.require_login("use Galaxy histories") - def import_archive(self, trans, **kwargs): - """ Import a history from a file archive. """ - # Set archive source and type. - archive_file = kwargs.get('archive_file', None) - archive_url = kwargs.get('archive_url', None) - archive_source = None - if hasattr(archive_file, 'file'): - archive_source = archive_file.file.name - archive_type = 'file' - elif archive_url: - archive_source = archive_url - archive_type = 'url' - # If no source to create archive from, show form to upload archive or specify URL. - if not archive_source: - form = web.FormBuilder(web.url_for(controller='history', action='import_archive'), "Import a History from an Archive", submit_text="Submit") - form.add_input("text", "Archived History URL", "archive_url", value="", error=None) - form.add_input("file", "Archived History File", "archive_file", value="", error=None) - return trans.show_form(form) - self.queue_history_import(trans, archive_type=archive_type, archive_source=archive_source) - return trans.show_message("Importing history from '%s'. \ - This history will be visible when the import is complete" % archive_source) - # TODO: used in this file and index.mako - @web.expose def export_archive(self, trans, id=None, gzip=True, include_hidden=False, include_deleted=False, preview=False): """ Export a history to an archive. """ diff --git a/lib/galaxy/webapps/galaxy/controllers/workflow.py b/lib/galaxy/webapps/galaxy/controllers/workflow.py index 78abd6af8f9..0609afd5222 100644 --- a/lib/galaxy/webapps/galaxy/controllers/workflow.py +++ b/lib/galaxy/webapps/galaxy/controllers/workflow.py @@ -33,11 +33,9 @@ from galaxy.web.base.controller import ( SharableMixin, UsesStoredWorkflowMixin ) -from galaxy.web.framework.formbuilder import form from galaxy.web.framework.helpers import ( grids, time_ago, - to_unicode ) from galaxy.workflow.extract import ( extract_workflow, @@ -385,28 +383,7 @@ class WorkflowController(BaseUIController, SharableMixin, UsesStoredWorkflowMixi # Redirect to load galaxy frames. return trans.show_ok_message( message="""Workflow "%s" has been imported.
You can start using this workflow or %s.""" - % (stored.name, web.url_for(controller='workflow'), referer_message), use_panels=True) - - @web.expose - @web.require_login("use Galaxy workflows") - def rename(self, trans, id, new_name=None, **kwargs): - stored = self.get_stored_workflow(trans, id) - if new_name is not None: - san_new_name = sanitize_html(new_name) - stored.name = san_new_name - stored.latest_workflow.name = san_new_name - trans.sa_session.flush() - message = 'Workflow renamed to: %s' % escape(san_new_name) - trans.set_message(message) - # Take care of proxy prefix in url as well - redirect_url = url_for('/') + 'workflow?status=done&message=%s' % escape(message) - return trans.response.send_redirect(redirect_url) - else: - return form(url_for(controller='workflow', action='rename', id=trans.security.encode_id(stored.id)), - "Rename workflow", - submit_text="Rename", - use_panels=True) \ - .add_text("new_name", "Workflow Name", value=to_unicode(stored.name)) + % (stored.name, web.url_for('/workflows/list'), referer_message)) @web.expose @web.require_login("use Galaxy workflows") @@ -537,14 +514,26 @@ class WorkflowController(BaseUIController, SharableMixin, UsesStoredWorkflowMixi return_url = url_for('/') + 'workflow?status=done&message=%s' % escape(message) trans.response.send_redirect(return_url) - @web.expose - @web.require_login("create workflows") - def create(self, trans, workflow_name=None, workflow_annotation=""): - """ - Create a new stored workflow with name `workflow_name`. - """ - user = trans.get_user() - if workflow_name is not None: + @web.expose_api + def create(self, trans, payload=None, **kwd): + if trans.request.method == 'GET': + return { + 'title' : 'Create Workflow', + 'inputs' : [{ + 'name' : 'workflow_name', + 'label' : 'Name', + 'value' : 'Unnamed workflow' + }, { + 'name' : 'workflow_annotation', + 'label' : 'Annotation', + 'help' : 'A description of the workflow; annotation is shown alongside shared or published workflows.' + }]} + else: + user = trans.get_user() + workflow_name = payload.get('workflow_name') + workflow_annotation = payload.get('workflow_annotation') + if not workflow_name: + return self.message_exception(trans, 'Please provide a workflow name.') # Create the new stored workflow stored_workflow = model.StoredWorkflow() stored_workflow.name = workflow_name @@ -562,14 +551,7 @@ class WorkflowController(BaseUIController, SharableMixin, UsesStoredWorkflowMixi session = trans.sa_session session.add(stored_workflow) session.flush() - return self.editor(trans, id=trans.security.encode_id(stored_workflow.id)) - else: - return form(url_for(controller="workflow", action="create"), "Create New Workflow", submit_text="Create", use_panels=True) \ - .add_text("workflow_name", "Workflow Name", value="Unnamed workflow") \ - .add_text("workflow_annotation", - "Workflow Annotation", - value="", - help="A description of the workflow; annotation is shown alongside shared or published workflows.") + return {'id': trans.security.encode_id(stored_workflow.id), 'message': 'Workflow %s has been created.' % workflow_name} @web.json def save_workflow_as(self, trans, workflow_name, workflow_data, workflow_annotation=""): @@ -922,12 +904,12 @@ class WorkflowController(BaseUIController, SharableMixin, UsesStoredWorkflowMixi id=repository_id, message=message, status=status)) - redirect_url = url_for('/') + 'workflow?status=' + status + '&message=%s' % escape(message) + redirect_url = url_for('/') + 'workflows/list?status=' + status + '&message=%s' % escape(message) return trans.response.send_redirect(redirect_url) if cntrller == 'api': return status, message if status == 'error': - redirect_url = url_for('/') + 'workflow?status=' + status + '&message=%s' % escape(message) + redirect_url = url_for('/') + 'workflows/list?status=' + status + '&message=%s' % escape(message) return trans.response.send_redirect(redirect_url) else: return { diff --git a/lib/galaxy/webapps/tool_shed/config.py b/lib/galaxy/webapps/tool_shed/config.py index 0ff3e2f3061..6a46410542f 100644 --- a/lib/galaxy/webapps/tool_shed/config.py +++ b/lib/galaxy/webapps/tool_shed/config.py @@ -105,7 +105,6 @@ class Configuration(object): self.smtp_username = kwargs.get('smtp_username', None) self.smtp_password = kwargs.get('smtp_password', None) self.smtp_ssl = kwargs.get('smtp_ssl', None) - self.start_job_runners = kwargs.get('start_job_runners', None) self.email_from = kwargs.get('email_from', None) self.nginx_upload_path = kwargs.get('nginx_upload_path', False) self.log_actions = string_as_bool(kwargs.get('log_actions', 'False')) @@ -123,12 +122,6 @@ class Configuration(object): self.log_events = False self.cloud_controller_instance = False self.server_name = '' - self.job_manager = '' - self.default_job_handlers = [] - self.default_cluster_job_runner = 'local:///' - self.job_handlers = [] - self.tool_handlers = [] - self.tool_runners = [] # Error logging with sentry self.sentry_dsn = kwargs.get('sentry_dsn', None) # Where the tool shed hgweb.config file is stored - the default is the Galaxy installation directory. diff --git a/lib/galaxy/workflow/scheduling_manager.py b/lib/galaxy/workflow/scheduling_manager.py index 8ce11db5601..013485d56c1 100644 --- a/lib/galaxy/workflow/scheduling_manager.py +++ b/lib/galaxy/workflow/scheduling_manager.py @@ -7,6 +7,7 @@ from galaxy import model from galaxy.util import plugin_config from galaxy.util.handlers import ConfiguresHandlers from galaxy.util.monitors import Monitors +from galaxy.web.stack.message import WorkflowSchedulingMessage log = logging.getLogger(__name__) @@ -30,11 +31,16 @@ class WorkflowSchedulingManager(object, ConfiguresHandlers): self.__handlers_configured = False self.workflow_schedulers = {} self.active_workflow_schedulers = {} + # TODO: this should not hardcode the job handlers pool + self.__handler_pool = self.app.application_stack.pools.JOB_HANDLERS + # TODO: and we need a better way to indicate messaging should be used + self.__use_stack_messages = app.application_stack.has_pool(self.__handler_pool) # Passive workflow schedulers won't need to be monitored I guess. self.request_monitor = None self.handlers = {} + self._is_handler = None self.__plugin_classes = self.__plugins_dict() self.__init_schedulers() @@ -44,9 +50,14 @@ class WorkflowSchedulingManager(object, ConfiguresHandlers): self.__start_schedulers() if self.active_workflow_schedulers: self.__start_request_monitor() + if self.__use_stack_messages: + WorkflowSchedulingMessage().bind_default_handler(self, '_handle_message') + self.app.application_stack.register_message_handler( + self._handle_message, + name=WorkflowSchedulingMessage.target) else: - # Process should not schedule workflows - do nothing. - pass + # Process should not schedule workflows but should check for any unassigned to handlers + self.__startup_recovery() # When assinging handlers to workflows being queued - use job_conf # if not explicit workflow scheduling handlers have be specified or @@ -56,13 +67,36 @@ class WorkflowSchedulingManager(object, ConfiguresHandlers): else: self.__has_handlers = app.job_config + def __startup_recovery(self): + sa_session = self.app.model.context + if self.__use_stack_messages: + for workflow_invocation in model.WorkflowInvocation.poll_active_workflow_ids( + sa_session, + handler=None): + log.info("(%s) Handler unassigned at startup, queueing workflow invocation via stack messaging for pool" + " [%s]", workflow_invocation.id, self.__handler_pool) + msg = WorkflowSchedulingMessage(task='setup', workflow_invocation_id=workflow_invocation.id) + self.app.application_stack.send_message(self.app.application_stack.pools.JOB_HANDLERS, msg) + + def _handle_setup_msg(self, workflow_invocation_id=None): + sa_session = self.app.model.context + workflow_invocation = sa_session.query(model.WorkflowInvocation).get(workflow_invocation_id) + if workflow_invocation.handler is None: + workflow_invocation.handler = self.app.config.server_name + sa_session.add(workflow_invocation) + sa_session.flush() + else: + log.warning("(%s) Handler '%s' received setup message for workflow invocation but handler '%s' is" + " already assigned, ignoring", workflow_invocation.id, self.app.config.server_name, + workflow_invocation.handler) + def _is_workflow_handler(self): # If we have explicitly configured handlers, check them. # Else just make sure we are a job handler. if self.__handlers_configured: - is_handler = self.is_handler(self.app.config.server_name) + is_handler = self.is_handler else: - is_handler = self.app.is_job_handler() + is_handler = self.app.is_job_handler return is_handler def _get_handler(self, history_id): @@ -95,14 +129,24 @@ class WorkflowSchedulingManager(object, ConfiguresHandlers): workflow_invocation.state = model.WorkflowInvocation.states.NEW scheduler = request_params.get("scheduler", None) or self.default_scheduler_id handler = self._get_handler(workflow_invocation.history.id) - log.info("Queueing workflow invocation for handler [%s]" % handler) + if handler is None and not self.__use_stack_messages: + raise RuntimeError("Unable to set a handler for workflow invocation '%s'" % workflow_invocation.id) + + log.info("Queueing workflow invocation for handler [%s]", handler) workflow_invocation.scheduler = scheduler workflow_invocation.handler = handler sa_session = self.app.model.context sa_session.add(workflow_invocation) sa_session.flush() + + if handler is None and self.__use_stack_messages: + log.info("(%s) Queueing workflow invocation via stack messaging for pool [%s]", + workflow_invocation.id, self.__handler_pool) + msg = WorkflowSchedulingMessage(task='setup', workflow_invocation_id=workflow_invocation.id) + self.app.application_stack.send_message(self.__handler_pool, msg) + return workflow_invocation def __start_schedulers(self): @@ -173,6 +217,7 @@ class WorkflowSchedulingManager(object, ConfiguresHandlers): def __start_request_monitor(self): self.request_monitor = WorkflowRequestMonitor(self.app, self) + self.app.application_stack.register_postfork_function(self.request_monitor.start) class WorkflowRequestMonitor(Monitors, object): @@ -180,7 +225,7 @@ class WorkflowRequestMonitor(Monitors, object): def __init__(self, app, workflow_scheduling_manager): self.app = app self.workflow_scheduling_manager = workflow_scheduling_manager - self._init_monitor_thread(name="WorkflowRequestMonitor.monitor_thread", target=self.__monitor, start=True, config=app.config) + self._init_monitor_thread(name="WorkflowRequestMonitor.monitor_thread", target=self.__monitor, config=app.config) def __monitor(self): to_monitor = self.workflow_scheduling_manager.active_workflow_schedulers @@ -235,5 +280,8 @@ class WorkflowRequestMonitor(Monitors, object): handler=handler, ) + def start(self): + self.monitor_thread.start() + def shutdown(self): self.shutdown_monitor() diff --git a/lib/tool_shed/galaxy_install/migrate/check.py b/lib/tool_shed/galaxy_install/migrate/check.py index c95a0d5e3f7..19ea3705d18 100644 --- a/lib/tool_shed/galaxy_install/migrate/check.py +++ b/lib/tool_shed/galaxy_install/migrate/check.py @@ -55,11 +55,10 @@ def verify_tools(app, url, galaxy_config_file=None, engine_options={}): if not app.config.running_functional_tests: if tool_shed_accessible: # Automatically update the value of the migrate_tools.version database table column. - config_arg = '' + cmd = ['sh', 'manage_db.sh', 'upgrade', 'tools'] if galaxy_config_file: - config_arg = " -c %s" % galaxy_config_file - cmd = 'sh manage_tools.sh%s upgrade' % config_arg - proc = subprocess.Popen(args=cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + cmd[2:2] = ['-c', galaxy_config_file] + proc = subprocess.Popen(args=cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) return_code = proc.wait() output = proc.stdout.read(32768) if return_code != 0: diff --git a/lib/tool_shed/util/commit_util.py b/lib/tool_shed/util/commit_util.py index 86b66009500..b73fdf01eae 100644 --- a/lib/tool_shed/util/commit_util.py +++ b/lib/tool_shed/util/commit_util.py @@ -10,7 +10,8 @@ from collections import namedtuple from sqlalchemy.sql.expression import null import tool_shed.repository_types.util as rt_util -from galaxy.util import checkers, safe_relpath +from galaxy.util import checkers +from galaxy.util.path import safe_relpath from tool_shed.tools import data_table_manager from tool_shed.util import basic_util, hg_util, shed_util_common as suc diff --git a/lib/tool_shed/util/tool_util.py b/lib/tool_shed/util/tool_util.py index b9aef505bdf..da0518b1659 100644 --- a/lib/tool_shed/util/tool_util.py +++ b/lib/tool_shed/util/tool_util.py @@ -4,8 +4,8 @@ import shutil import galaxy.tools from galaxy import util +from galaxy.datatypes.sniff import is_column_based from galaxy.util import checkers -from galaxy.util import unicodify from galaxy.util.expressions import ExpressionContext from galaxy.web.form_builder import SelectField from tool_shed.util import basic_util @@ -125,20 +125,6 @@ def generate_message_for_invalid_tools(app, invalid_file_tups, repository, metad return message -def get_headers(fname, sep, count=60, is_multi_byte=False): - """Returns a list with the first 'count' lines split by 'sep'.""" - headers = [] - for idx, line in enumerate(open(fname)): - line = line.rstrip('\n\r') - if is_multi_byte: - line = unicodify(line, 'utf-8') - sep = sep.encode('utf-8') - headers.append(line.split(sep)) - if idx == count: - break - return headers - - def get_tool_path_install_dir(partial_install_dir, shed_tool_conf_dict, tool_dict, config_elems): for elem in config_elems: if elem.tag == 'tool': @@ -184,26 +170,6 @@ def handle_missing_index_file(app, tool_path, sample_files, repository_tools_tup return repository_tools_tups, sample_files_copied -def is_column_based(fname, sep='\t', skip=0, is_multi_byte=False): - """See if the file is column based with respect to a separator.""" - headers = get_headers(fname, sep, is_multi_byte=is_multi_byte) - count = 0 - if not headers: - return False - for hdr in headers[skip:]: - if hdr and hdr[0] and not hdr[0].startswith('#'): - if len(hdr) > 1: - count = len(hdr) - break - if count < 2: - return False - for hdr in headers[skip:]: - if hdr and hdr[0] and not hdr[0].startswith('#'): - if len(hdr) != count: - return False - return True - - def is_data_index_sample_file(file_path): """ Attempt to determine if a .sample file is appropriate for copying to ~/tool-data when diff --git a/manage_tools.sh b/manage_tools.sh deleted file mode 100644 index 234d1d85fd5..00000000000 --- a/manage_tools.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh - -cd `dirname $0` -: ${GALAXY_VIRTUAL_ENV:=.venv} - -if [ -d "$GALAXY_VIRTUAL_ENV" ]; -then - printf "Activating virtualenv at $GALAXY_VIRTUAL_ENV\n" - . "$GALAXY_VIRTUAL_ENV/bin/activate" -fi - -python ./scripts/manage_tools.py $@ diff --git a/run.sh b/run.sh index ccb4621e715..ea1a8e98052 100755 --- a/run.sh +++ b/run.sh @@ -1,5 +1,12 @@ #!/bin/sh + +# Usage: ./run.sh +# +# +# Description: This script can be used to start or stop the galaxy +# web application. + cd "$(dirname "$0")" . ./scripts/common_startup_functions.sh @@ -16,6 +23,11 @@ then . $GALAXY_LOCAL_ENV_FILE fi +GALAXY_PID=${GALAXY_PID:-galaxy.pid} +GALAXY_LOG=${GALAXY_LOG:-galaxy.log} +PID_FILE=$GALAXY_PID +LOG_FILE=$GALAXY_LOG + parse_common_args $@ run_common_start_up @@ -39,18 +51,27 @@ if [ -z "$GALAXY_CONFIG_FILE" ]; then GALAXY_CONFIG_FILE=universe_wsgi.ini elif [ -f config/galaxy.ini ]; then GALAXY_CONFIG_FILE=config/galaxy.ini - else + elif [ -f config/galaxy.yml ]; then + GALAXY_CONFIG_FILE=config/galaxy.yml + elif [ -f config/galaxy.ini.sample -a -z "$GALAXY_UWSGI" ]; then GALAXY_CONFIG_FILE=config/galaxy.ini.sample fi export GALAXY_CONFIG_FILE fi -if [ $INITIALIZE_TOOL_DEPENDENCIES -eq 1 ]; then - # Install Conda environment if needed. - python ./scripts/manage_tool_dependencies.py -c "$GALAXY_CONFIG_FILE" init_if_needed +if [ -n "$GALAXY_CONFIG_FILE" ]; then + config_file_arg="-c $GALAXY_CONFIG_FILE" fi -if [ -n "$GALAXY_RUN_ALL" ]; then +if [ $INITIALIZE_TOOL_DEPENDENCIES -eq 1 ]; then + # Install Conda environment if needed. + python ./scripts/manage_tool_dependencies.py $config_file_arg init_if_needed +fi + +[ -n "$GALAXY_UWSGI" ] && APP_WEBSERVER='uwsgi' +find_server ${GALAXY_CONFIG_FILE:-none} galaxy + +if [ "$run_server" = "python" -a -n "$GALAXY_RUN_ALL" ]; then servers=$(sed -n 's/^\[server:\(.*\)\]/\1/ p' "$GALAXY_CONFIG_FILE" | xargs echo) if [ -z "$stop_daemon_arg_set" -a -z "$daemon_or_restart_arg_set" ]; then echo "ERROR: \$GALAXY_RUN_ALL cannot be used without the '--daemon', '--stop-daemon' or 'restart' arguments to run.sh" @@ -80,6 +101,7 @@ if [ -n "$GALAXY_RUN_ALL" ]; then fi done else - # Handle only 1 server, whose name can be specified with --server-name parameter (defaults to "main") - python ./scripts/paster.py serve "$GALAXY_CONFIG_FILE" $paster_args + echo "executing: $run_server $server_args" + # args are properly quoted so use eval + eval $run_server $server_args fi diff --git a/run_reports.sh b/run_reports.sh index 93da90847c8..9692904d41b 100755 --- a/run_reports.sh +++ b/run_reports.sh @@ -39,8 +39,6 @@ if [ -z "$GALAXY_REPORTS_CONFIG" ]; then GALAXY_REPORTS_CONFIG=config/reports.ini elif [ -f config/reports.yml ]; then GALAXY_REPORTS_CONFIG=config/reports.yml - else - GALAXY_REPORTS_CONFIG=config/reports.yml.sample fi export GALAXY_REPORTS_CONFIG fi @@ -49,5 +47,6 @@ if [ -n "$GALAXY_REPORTS_CONFIG_DIR" ]; then python ./scripts/build_universe_config.py "$GALAXY_REPORTS_CONFIG_DIR" "$GALAXY_REPORTS_CONFIG" fi -find_server $GALAXY_REPORTS_CONFIG -$run_server $server_args +find_server ${GALAXY_REPORTS_CONFIG:-none} reports +echo "executing: $run_server $server_args" +eval $run_server $server_args diff --git a/run_tool_shed.sh b/run_tool_shed.sh index a05226f173a..e5bd35749e8 100755 --- a/run_tool_shed.sh +++ b/run_tool_shed.sh @@ -32,11 +32,10 @@ if [ -z "$TOOL_SHED_CONFIG_FILE" ]; then TOOL_SHED_CONFIG_FILE=config/tool_shed.ini elif [ -f config/tool_shed.yml ]; then TOOL_SHED_CONFIG_FILE=config/tool_shed.yml - else - TOOL_SHED_CONFIG_FILE=config/tool_shed.yml.sample fi export TOOL_SHED_CONFIG_FILE fi -find_server $TOOL_SHED_CONFIG_FILE -$run_server $server_args +find_server ${TOOL_SHED_CONFIG_FILE:-none} tool_shed +echo "executing: $run_server $server_args" +eval $run_server $server_args diff --git a/scripts/common_startup_functions.sh b/scripts/common_startup_functions.sh index 6b2e0cc3d6e..6acdf5bead4 100644 --- a/scripts/common_startup_functions.sh +++ b/scripts/common_startup_functions.sh @@ -1,9 +1,5 @@ #!/bin/sh -: ${GALAXY_STATIC_DIRECTORY:=static} -: ${GALAXY_STYLE_DIRECTORY:=static/style/blue} -uwsgi_args="--master --pythonpath=lib --static-map /static=$GALAXY_STATIC_DIRECTORY --static-map /static/style=$GALAXY_STYLE_DIRECTORY" - parse_common_args() { INITIALIZE_TOOL_DEPENDENCIES=1 # Pop args meant for common_startup.sh @@ -23,9 +19,9 @@ parse_common_args() { common_startup_args="$common_startup_args $1" shift ;; - --stop-daemon) - common_startup_args="$common_startup_args $1" - paster_args="$paster_args $1" + --stop-daemon|stop) + common_startup_args="$common_startup_args --stop-daemon" + paster_args="$paster_args --pid-file $PID_FILE --stop-daemon" uwsgi_args="$uwsgi_args --stop $PID_FILE" stop_daemon_arg_set=1 shift @@ -41,8 +37,8 @@ parse_common_args() { daemon_or_restart_arg_set=1 shift ;; - --daemon) - paster_args="$paster_args $1" + --daemon|start) + paster_args="$paster_args --pid-file $PID_FILE --log-file $LOG_FILE --daemon" # --daemonize2 waits until after the application has loaded # to daemonize, thus it stops if any errors are found uwsgi_args="$uwsgi_args --daemonize2 $LOG_FILE --safe-pidfile $PID_FILE" @@ -87,25 +83,13 @@ setup_python() { python ./scripts/check_python.py || exit 1 } -find_uwsgi() { - # Look for uwsgi - if [ -z "$skip_venv" -a -x $GALAXY_VIRTUAL_ENV/bin/uwsgi ]; then - UWSGI=$GALAXY_VIRTUAL_ENV/bin/uwsgi - elif command -v uwsgi >/dev/null 2>&1; then - UWSGI=uwsgi - else - echo 'ERROR: Could not find uwsgi executable' - exit 1 - fi -} - find_server() { server_config="$1" - server_config_style="ini-paste" + server_app="$2" + arg_getter_args= default_webserver="paste" case "$server_config" in - *.y*ml*) - server_config_style="yaml" + *.y*ml|''|none) default_webserver="uwsgi" # paste incapable of this ;; esac @@ -122,10 +106,12 @@ find_server() { echo 'ERROR: Could not find uwsgi executable' exit 1 fi + [ "$server_config" != "none" ] && arg_getter_args="-c $server_config" + [ -n "$server_app" ] && arg_getter_args="--app $server_app" run_server="$UWSGI" - server_args="--$server_config_style $server_config $uwsgi_args" + server_args="$(python ./scripts/get_uwsgi_args.py $arg_getter_args) $uwsgi_args" else run_server="python" - server_args="./scripts/paster.py serve $server_config $paster_args --pid-file $PID_FILE --log-file $LOG_FILE $paster_args" + server_args="./scripts/paster.py serve $server_config $paster_args" fi } diff --git a/scripts/galaxy-main b/scripts/galaxy-main index 7523c549fcd..9cb5b7da345 100755 --- a/scripts/galaxy-main +++ b/scripts/galaxy-main @@ -15,12 +15,20 @@ a loggers section in Galaxy's ini file - this can be overridden with sensible defaults logging to a single file with the following: galaxy-main -d --server-name handler0 --daemon-log-file=handler0-daemon.log --pid-file handler0.pid --log-file handler0.log + +This can also be used to start Galaxy as a uWSGI mule, e.g. for job handling: + +uwsgi ... --py-call-osafterfork --mule=lib/galaxy/main.py --mule=lib/galaxy/main.py --farm=job-handlers:1,2 + +The --py-call-osafterfork allows for proper shutdown on SIGTERM/SIGINT. """ import functools import logging import os +import signal import sys -import time +import threading +from argparse import ArgumentParser from logging.config import fileConfig try: @@ -33,25 +41,10 @@ try: except ImportError: Daemonize = None -# Vaguely Python 2.6 compatibile ArgumentParser import try: - from argparse import ArgumentParser + import uwsgi except ImportError: - from optparse import OptionParser - - class ArgumentParser(OptionParser): - - def __init__(self, **kwargs): - self.delegate = OptionParser(**kwargs) - - def add_argument(self, *args, **kwargs): - if "required" in kwargs: - del kwargs["required"] - return self.delegate.add_option(*args, **kwargs) - - def parse_args(self, args=None): - (options, args) = self.delegate.parse_args(args) - return options + uwsgi = None REQUIRES_DAEMONIZE_MESSAGE = "Attempted to use Galaxy in daemon mode, but daemonize is unavailable." @@ -61,12 +54,19 @@ real_file = os.path.realpath(__file__) GALAXY_ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(real_file), os.pardir)) GALAXY_LIB_DIR = os.path.join(GALAXY_ROOT_DIR, "lib") DEFAULT_INI_APP = "main" +DEFAULT_CONFIG_SECTION = "galaxy" DEFAULT_INIS = ["config/galaxy.ini", "universe_wsgi.ini", "config/galaxy.ini.sample"] DEFAULT_PID = "galaxy.pid" DEFAULT_VERBOSE = True DESCRIPTION = "Daemonized entry point for Galaxy." +SHUTDOWN_MSG = '__SHUTDOWN__' +UWSGI_FARMS_VAR = '_GALAXY_UWSGI_FARM_NAMES' + + +exit = threading.Event() + def load_galaxy_app( config_builder, @@ -86,11 +86,6 @@ def load_galaxy_app( except Exception: log.exception("Failed to chdir") raise - try: - sys.path.insert(1, GALAXY_LIB_DIR) - except Exception: - log.exception("Failed to add Galaxy to sys.path") - raise config_builder.setup_logging() from galaxy.util.properties import load_app_properties @@ -98,13 +93,31 @@ def load_galaxy_app( kwds = load_app_properties(**kwds) from galaxy.app import UniverseApplication app = UniverseApplication( - global_conf={"__file__": config_builder.ini_path}, + global_conf=config_builder.global_conf(), **kwds ) app.control_worker.bind_and_start() return app +def handle_signal(signum, frame): + log.info('Received signal %d, exiting', signum) + if uwsgi and 'mule_id' in dir(uwsgi) and uwsgi.mule_id() > 0: + farms = os.environ.get(UWSGI_FARMS_VAR, None) + if farms: + for farm in farms.split(','): + uwsgi.farm_msg(farm, SHUTDOWN_MSG) + else: + uwsgi.mule_msg(SHUTDOWN_MSG, uwsgi.mule_id()) + exit.set() + + +def register_signals(): + for name in ('TERM', 'INT', 'HUP'): + sig = getattr(signal, 'SIG%s' % name) + signal.signal(sig, handle_signal) + + def app_loop(args, log): try: config_builder = GalaxyConfigBuilder(args) @@ -116,16 +129,12 @@ def app_loop(args, log): except BaseException: log.exception("Failed to initialize Galaxy application") raise - sleep = True - while sleep: - try: - time.sleep(5) - except KeyboardInterrupt: - sleep = False - except SystemExit: - sleep = False - except Exception: + try: + # A timeout is required or the signals won't be handled + while not exit.wait(20): pass + except (KeyboardInterrupt, SystemExit): + pass try: galaxy_app.shutdown() except Exception: @@ -139,16 +148,16 @@ def absolute_config_path(path, galaxy_root): return path -def find_ini(supplied_ini, galaxy_root): - if supplied_ini: - return supplied_ini +def find_config(supplied_config, galaxy_root): + if supplied_config: + return supplied_config - # If not explicitly supplied an ini, check server.ini and then - # just restort to sample if that has not been configured. + # If not explicitly supplied an config, check galaxy.ini and then + # just resort to sample if that has not been configured. for guess in DEFAULT_INIS: - ini_path = os.path.join(galaxy_root, guess) - if os.path.exists(ini_path): - return ini_path + config_path = os.path.join(galaxy_root, guess) + if os.path.exists(config_path): + return config_path return guess @@ -158,54 +167,79 @@ class GalaxyConfigBuilder(object): """ def __init__(self, args=None, **kwds): - ini_path = kwds.get("ini_path", None) or (args and args.ini_path) - # If given app_conf_path - use that - else we need to ensure we have an - # ini path. - if not ini_path: + self.app_name = kwds.get("app") or (args and args.app) or DEFAULT_CONFIG_SECTION + config_file = kwds.get("config_file", None) or (args and args.config_file) + # If given app_conf_path - use that - else we need to ensure we have a + # config file path. + if not config_file and 'config_file' in self.app_kwds(): + config_file = self.app_kwds()['config_file'] + if not config_file: galaxy_root = kwds.get("galaxy_root", GALAXY_ROOT_DIR) - ini_path = find_ini(ini_path, galaxy_root) - ini_path = absolute_config_path(ini_path, galaxy_root=galaxy_root) - self.ini_path = ini_path - self.app_name = kwds.get("app") or (args and args.app) or DEFAULT_INI_APP + config_file = find_config(config_file, galaxy_root) + config_file = absolute_config_path(config_file, galaxy_root=galaxy_root) + self.config_file = config_file + # FIXME: this won't work for non-Paste ini configs + self.config_section = self.app_name if not self.config_is_ini else 'app:%s' % (kwds.get("app") or (args and args.app) or DEFAULT_INI_APP) self.log_file = (args and args.log_file) @classmethod def populate_options(cls, arg_parser): - arg_parser.add_argument("-c", "--ini-path", default=None, help="Galaxy ini config file (defaults to config/galaxy.ini)") - arg_parser.add_argument("--app", default=DEFAULT_INI_APP, help="app section in ini file (defaults to main)") + arg_parser.add_argument("-c", "--config-file", default=None, help="Galaxy config file (defaults to config/galaxy.ini)") + arg_parser.add_argument("--ini-path", default=None, help="DEPRECATED: use -c/--config-file") + arg_parser.add_argument("--app", default=None, help="app section in ini file (defaults to 'galaxy' for YAML/JSON, 'main' (w/ 'app:' prepended) for INI") arg_parser.add_argument("-d", "--daemonize", default=False, help="Daemonzie process", action="store_true") arg_parser.add_argument("--daemon-log-file", default=None, help="log file for daemon script ") - arg_parser.add_argument("--log-file", default=None, help="Galaxy log file (overrides log configuration in ini_path if set)") + arg_parser.add_argument("--log-file", default=None, help="Galaxy log file (overrides log configuration in config_file if set)") arg_parser.add_argument("--pid-file", default=DEFAULT_PID, help="pid file (default is %s)" % DEFAULT_PID) arg_parser.add_argument("--server-name", default=None, help="set a galaxy server name") + @property + def config_is_ini(self): + return self.config_file.endswith('.ini') or self.config_file.endswith('.ini.sample') + def app_kwds(self): - config = dict( - ini_file=self.ini_path, - ini_section="app:%s" % self.app_name, - ) - return config + from galaxy.web.stack import get_app_kwds + kwds = get_app_kwds(self.app_name, app_name=self.app_name) + if 'config_file' not in kwds: + kwds['config_file'] = self.config_file + if 'config_section' not in kwds: + kwds['config_section'] = self.config_section + return kwds + + def global_conf(self): + conf = {} + if self.config_is_ini: + conf["__file__"] = self.config_file + return conf def setup_logging(self): # Galaxy will attempt to setup logging if loggers is not present in # ini config file - this handles that loggers block however if present # (the way paste normally would) - if not self.ini_path: + if not self.config_file: return - raw_config = configparser.ConfigParser() - raw_config.read([self.ini_path]) - if raw_config.has_section('loggers'): - config_file = os.path.abspath(self.ini_path) - fileConfig( - config_file, - dict(__file__=config_file, here=os.path.dirname(config_file)) - ) + if self.config_is_ini: + raw_config = configparser.ConfigParser() + raw_config.read([self.config_file]) + if raw_config.has_section('loggers'): + config_file = os.path.abspath(self.config_file) + fileConfig( + config_file, + dict(__file__=config_file, here=os.path.dirname(config_file)) + ) def main(): arg_parser = ArgumentParser(description=DESCRIPTION) + try: + sys.path.insert(1, GALAXY_LIB_DIR) + except Exception: + log.exception("Failed to add Galaxy to sys.path") + raise GalaxyConfigBuilder.populate_options(arg_parser) args = arg_parser.parse_args() + if args.ini_path and not args.config_file: + args.config_file = args.ini_path if args.log_file: os.environ["GALAXY_CONFIG_LOG_DESTINATION"] = os.path.abspath(args.log_file) if args.server_name: @@ -214,6 +248,7 @@ def main(): log.setLevel(logging.DEBUG) log.propagate = False + register_signals() if args.daemonize: if Daemonize is None: raise ImportError(REQUIRES_DAEMONIZE_MESSAGE) diff --git a/scripts/get_uwsgi_args.py b/scripts/get_uwsgi_args.py new file mode 100644 index 00000000000..bd73a5501e6 --- /dev/null +++ b/scripts/get_uwsgi_args.py @@ -0,0 +1,116 @@ +from __future__ import print_function + +import os +import sys + +from six import string_types +from six.moves import shlex_quote + +sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir, 'lib'))) + +from galaxy.util.path import get_ext +from galaxy.util.properties import load_app_properties, nice_config_parser +from galaxy.util.script import main_factory + + +DESCRIPTION = "Script to determine uWSGI command line arguments" +# socket is not an alias for http, but it is assumed that if you configure a socket in your uwsgi config you do not +# want to run the default http server (or you can configure it yourself) +ALIASES = { + 'virtualenv': ('home', 'venv', 'pyhome'), + 'pythonpath': ('python-path', 'pp'), + 'http': ('httprouter', 'socket', 'uwsgi-socket', 'suwsgi-socket', 'ssl-socket'), +} +DEFAULT_ARGS = { + '_all_': ('virtualenv', 'pythonpath', 'master', 'threads', 'http', 'static-map', 'die-on-term', 'hook-master-start', 'enable-threads'), + 'galaxy': ('py-call-osafterfork',), + 'reports': (), + 'tool_shed': (), +} +DEFAULT_PORTS = { + 'galaxy': 8080, + 'reports': 9001, + 'tool_shed': 9009, +} + + +def __arg_set(arg, kwargs): + if arg in kwargs: + return True + for alias in ALIASES.get(arg, ()): + if alias in kwargs: + return True + return False + + +def __add_arg(args, arg, value): + optarg = '--%s' % arg + if isinstance(value, bool): + if value is True: + args.append(optarg) + elif isinstance(value, string_types): + # the = in --optarg=value is usually, but not always, optional + if value.startswith('='): + args.append(shlex_quote(optarg + value)) + else: + args.append(optarg) + args.append(shlex_quote(value)) + else: + [__add_arg(args, arg, v) for v in value] + + +def __add_config_file_arg(args, config_file, app): + ext = None + if config_file: + ext = get_ext(config_file) + if ext in ('yaml', 'json'): + __add_arg(args, ext, config_file) + elif ext == 'ini': + config = nice_config_parser(config_file) + has_logging = config.has_section('loggers') + if config.has_section('app:main'): + # uWSGI does not have any way to set the app name when loading with paste.deploy:loadapp(), so hardcoding + # the name to `main` is fine + __add_arg(args, 'ini-paste' if not has_logging else 'ini-paste-logged', config_file) + return # do not add --module + else: + __add_arg(args, ext, config_file) + if has_logging: + __add_arg(args, 'paste-logger', True) + __add_arg(args, 'module', 'galaxy.webapps.{app}.buildapp:uwsgi_app()'.format(app=app)) + + +def _get_uwsgi_args(cliargs, kwargs): + # it'd be nice if we didn't have to reparse here but we need things out of more than one section + config_file = cliargs.config_file or kwargs.get('__file__') + uwsgi_kwargs = load_app_properties(config_file=config_file, config_section='uwsgi') + args = [] + __add_config_file_arg(args, config_file, cliargs.app) + defaults = { + 'virtualenv': os.environ.get('VIRTUAL_ENV', './.venv'), + 'pythonpath': 'lib', + 'master': True, + 'threads': '4', + 'http': 'localhost:{port}'.format(port=DEFAULT_PORTS[cliargs.app]), + 'static-map': ('/static/style={here}/static/style/blue'.format(here=os.getcwd()), + '/static={here}/static'.format(here=os.getcwd())), + 'die-on-term': True, + 'enable-threads': True, + 'hook-master-start': ('unix_signal:2 gracefully_kill_them_all', + 'unix_signal:15 gracefully_kill_them_all'), + 'py-call-osafterfork': True, + } + for arg in DEFAULT_ARGS['_all_'] + DEFAULT_ARGS[cliargs.app]: + if not __arg_set(arg, uwsgi_kwargs): + __add_arg(args, arg, defaults[arg]) + print(' '.join(args)) + + +ACTIONS = { + "get_uwsgi_args": _get_uwsgi_args, +} + + +if __name__ == '__main__': + main = main_factory(description=DESCRIPTION, actions=ACTIONS, default_action="get_uwsgi_args") + main() diff --git a/scripts/manage_tools.py b/scripts/manage_tools.py deleted file mode 100644 index fafc9c6e2be..00000000000 --- a/scripts/manage_tools.py +++ /dev/null @@ -1,39 +0,0 @@ -from __future__ import print_function - -import logging -import os.path -import sys -from ConfigParser import SafeConfigParser - -from migrate.versioning.shell import main - -sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir, 'lib'))) - -from galaxy.model.orm.scripts import read_config_file_arg -from galaxy.util.properties import load_app_properties - -log = logging.getLogger(__name__) - -config_file = read_config_file_arg(sys.argv, 'config/galaxy.ini', 'universe_wsgi.ini') -if not os.path.exists(config_file): - print("Galaxy config file does not exist (hint: use '-c config.ini' for non-standard locations): %s" % config_file) - sys.exit(1) -repo = 'lib/tool_shed/galaxy_install/migrate' - -properties = load_app_properties(config_file=config_file) -cp = SafeConfigParser() -cp.read(config_file) - -if config_file == 'config/galaxy.ini.sample' and 'GALAXY_TEST_DBURI' in os.environ: - # Running functional tests. - db_url = os.environ['GALAXY_TEST_DBURI'] -elif "install_database_connection" in properties: - db_url = properties["install_database_connection"] -elif "database_connection" in properties: - db_url = properties["database_connection"] -elif "database_file" in properties: - db_url = "sqlite:///%s?isolation_level=IMMEDIATE" % properties["database_file"] -else: - db_url = "sqlite:///./database/universe.sqlite?isolation_level=IMMEDIATE" - -main(repository=repo, url=db_url) diff --git a/static/scripts/bundled/admin.bundled.js b/static/scripts/bundled/admin.bundled.js index e74b7147033..0e165c5aa1b 100644 --- a/static/scripts/bundled/admin.bundled.js +++ b/static/scripts/bundled/admin.bundled.js @@ -1,7 +1,7 @@ -webpackJsonp([1],[,,,,function(t,e,i){"use strict";(function(t,n,a){function r(t){return t&&t.__esModule?t:{default:t}}Object.defineProperty(e,"__esModule",{value:!0});var o=i(3),s=(r(o),i(22)),l=r(s),d=i(46),c=r(d),u=i(23),h=r(u),f=i(47),p=r(f),_=i(12),m=r(_),g=i(9),v=r(g),b=t.View.extend({tagName:"label",initialize:function(e){this.model=e&&e.model||new t.Model(e),this.tagName=e.tagName||this.tagName,this.setElement(n("<"+this.tagName+"/>")),this.listenTo(this.model,"change",this.render,this),this.render()},title:function(t){this.model.set("title",t)},value:function(){return this.model.get("title")},render:function(){return this.$el.removeClass().addClass("ui-label").addClass(this.model.get("cls")).html(this.model.get("title")),this}}),w=t.View.extend({initialize:function(e){this.model=e&&e.model||new t.Model({message:null,status:"info",cls:"",persistent:!1,fade:!0}).set(e),this.listenTo(this.model,"change",this.render,this),this.render()},update:function(t){this.model.set(t)},render:function(){this.$el.removeClass().addClass("ui-message").addClass(this.model.get("cls"));var t=this.model.get("status");if(this.model.get("large")?this.$el.addClass(("success"==t&&"done"||"danger"==t&&"error"||t)+"messagelarge"):this.$el.addClass("alert").addClass("alert-"+t),this.model.get("message")){if(this.$el.html(this.messageForDisplay()),this.$el[this.model.get("fade")?"fadeIn":"show"](),this.timeout&&window.clearTimeout(this.timeout),!this.model.get("persistent")){var e=this;this.timeout=window.setTimeout(function(){e.model.set("message","")},3e3)}}else this.$el.fadeOut();return this},messageForDisplay:function(){return a.escape(this.model.get("message"))}}),y=w.extend({messageForDisplay:function(){return this.model.get("message")}}),x=t.View.extend({initialize:function(e){this.model=e&&e.model||new t.Model({type:"text",placeholder:"",disabled:!1,readonly:!1,visible:!0,cls:"",area:!1,color:null,style:null}).set(e),this.tagName=this.model.get("area")?"textarea":"input",this.setElement(n("<"+this.tagName+"/>")),this.listenTo(this.model,"change",this.render,this),this.render()},events:{input:"_onchange"},value:function(t){return void 0!==t&&this.model.set("value","string"==typeof t?t:""),this.model.get("value")},render:function(){var t=this;this.$el.removeClass().addClass("ui-"+this.tagName).addClass(this.model.get("cls")).addClass(this.model.get("style")).attr("id",this.model.id).attr("type",this.model.get("type")).attr("placeholder",this.model.get("placeholder")).css("color",this.model.get("color")||"").css("border-color",this.model.get("color")||"");var e=this.model.get("datalist");return n.isArray(e)&&e.length>0&&this.$el.autocomplete({source:function(e,i){i(t.model.get("datalist"))},change:function(){t._onchange()}}),this.model.get("value")!==this.$el.val()&&this.$el.val(this.model.get("value")),a.each(["readonly","disabled"],function(e){t.model.get(e)?t.$el.attr(e,!0):t.$el.removeAttr(e)}),this.$el[this.model.get("visible")?"show":"hide"](),this},_onchange:function(){this.value(this.$el.val()),this.model.get("onchange")&&this.model.get("onchange")(this.model.get("value"))}}),k=t.View.extend({initialize:function(e){this.model=e&&e.model||new t.Model(e),this.setElement(n("
").append(this.$info=n("
")).append(this.$hidden=n("
"))),this.listenTo(this.model,"change",this.render,this),this.render()},value:function(t){return void 0!==t&&this.model.set("value",t),this.model.get("value")},render:function(){return this.$el.attr("id",this.model.id),this.$hidden.val(this.model.get("value")),this.model.get("info")?this.$info.show().text(this.model.get("info")):this.$info.hide(),this}}),C=t.View.extend({initialize:function(e){var i=this;this.model=e&&e.model||new t.Model(e),this.setElement(n("
").append(this.$info=n("
")).append(this.$file=n("").attr("type","file").addClass("ui-margin-bottom")).append(this.$text=n("','',"<% } %>","folder","","",'<%= _.escape(content_item.get("update_time")) %>',"","","","<% if(edit_mode) { %>",'",'","<% } else if (!edit_mode){%>",'",'','","","<% } %>","",""].join(""))},templateRowFile:function(){return s.template(['',"",'',"",'','',"","",'/datasets/<%- content_item.id %>" class="library-dataset">','<%- content_item.get("name") %>',"","",'<%- content_item.get("message") %>','<%= _.escape(content_item.get("file_ext")) %>','<%= _.escape(content_item.get("file_size")) %>','<%= _.escape(content_item.get("update_time")) %>',"",'<% if ( content_item.get("state") !== "ok" ) { %>','<%= _.escape(content_item.get("state")) %>',"<% } %>","","",'<% if (content_item.get("is_unrestricted")) { %>','',"<% } %>",'<% if (content_item.get("is_private")) { %>','',"<% } %>",'<% if ((content_item.get("is_unrestricted") === false) && (content_item.get("is_private") === false)) { %>','',"<% } %>",'<% if (content_item.get("can_manage")) { %>','/datasets/<%- content_item.id %>/permissions">','","","<% } %>","",""].join(""))},templateRowDeletedFile:function(){return s.template(['',"",'',"","",'','<%- content_item.get("name") %>',"","",'<%- content_item.get("message") %>',"","",'<%= _.escape(content_item.get("file_ext")) %>',"","",'<%= _.escape(content_item.get("file_size")) %>',"","",'<%= _.escape(content_item.get("update_time")) %>',"","",'<% if ( content_item.get("state") !== "ok" ) { %>','<%= _.escape(content_item.get("state")) %>',"<% } %>","","",'','","",""].join(""))},templateRowDeletedFolder:function(){return s.template(['',"",'',"","",'','<%- content_item.get("name") %>',"","",'<%- content_item.get("description") %>',"","","folder","","","",'<%= _.escape(content_item.get("update_time")) %>',"","","",'','","",""].join(""))}});t.default={FolderRowView:_}}).call(t,i(1),i(2),i(0))},297:function(e,t,i){"use strict";(function(e,s){function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=i(42),o=a(r),n=i(156),l=a(n),d=i(298),c=a(d),h=i(2),_=a(h),p=e.View.extend({el:"#libraries_element",events:{"click .sort-libraries-link":"sort_clicked"},defaults:{page_count:null,show_page:null,all_fetched:!1},initialize:function(e){this.options=_.default.defaults(this.options||{},e,this.defaults);var t=this;this.modal=null,this.collection=new l.default.Libraries,this.collection.url=this.collection.urlRoot+"?deleted=false",this.collection.fetch({success:function(){t.render()},error:function(e,t){void 0!==t.responseJSON?o.default.error(t.responseJSON.err_msg):o.default.error("An error occurred.")}})},render:function(e){this.options=_.default.extend(this.options,e),this.setElement("#libraries_element");var t=this.templateLibraryList(),i=null,a=null,r=function(e){return!0===e.get("public")};if(s(".tooltip").hide(),void 0!==e&&(a=void 0!==e.models?e.models:null),null!==this.collection&&null===a)this.sortLibraries(),i=Galaxy.libraries.preferences.get("with_deleted")?this.collection.models:this.collection.where({deleted:!1}),Galaxy.libraries.preferences.get("without_restricted")&&(i=_.default.filter(i,r));else if(null!==a){if(Galaxy.libraries.preferences.get("with_deleted"))i=a;else{var o=function(e){return!1===e.get("deleted")};i=_.default.filter(a,o)}Galaxy.libraries.preferences.get("without_restricted")&&(i=_.default.filter(i,r))}else i=[];(null===this.options.show_page||this.options.show_page<1)&&(this.options.show_page=1),this.options.total_libraries_count=i.length;var n=Galaxy.libraries.preferences.get("library_page_size")*(this.options.show_page-1);this.options.page_count=Math.ceil(this.options.total_libraries_count/Galaxy.libraries.preferences.get("library_page_size")),this.options.total_libraries_count>0&&nthis.options.total_libraries_count+Galaxy.libraries.preferences.get("library_page_size")&&(i=[]),this.$el.html(t({length:1,order:Galaxy.libraries.preferences.get("sort_order"),search_term:Galaxy.libraries.libraryToolbarView.options.search_term})),Galaxy.libraries.libraryToolbarView.renderPaginator(this.options),this.renderRows(i)):(this.$el.html(t({length:0,order:Galaxy.libraries.preferences.get("sort_order"),search_term:Galaxy.libraries.libraryToolbarView.options.search_term})),Galaxy.libraries.libraryToolbarView.renderPaginator(this.options)),s("#center [data-toggle]").tooltip(),s("#center").css("overflow","auto")},fetchDeleted:function(){if(this.options.all_fetched)this.render();else{var e=this;this.collection.url=this.collection.urlRoot+"?deleted=true",this.collection.fetch({remove:!1,success:function(){e.options.all_fetched=!0,e.render()},error:function(e,t){void 0!==t.responseJSON?o.default.error(t.responseJSON.err_msg):o.default.error("An error occurred.")}})}},renderRows:function(e){for(var t=0;t',"<% if(length === 0) { %>","<% if(search_term.length > 0) { %>","
","There are no libraries matching your search. Try different keyword.","
","<% } else{ %>","
","There are no libraries visible to you here. If you expected some to show up please consult the",' library security wikipage',' or visit the Galaxy support site.',"
","<% }%>","<% } else{ %>",'',"",'",'',' ','',"",'',"","
','',"name","",'',"descriptionsynopsis
","<% }%>","
"].join(""))}});t.default={LibraryListView:p}}).call(t,i(1),i(0))},298:function(e,t,i){"use strict";(function(e,s,a){Object.defineProperty(t,"__esModule",{value:!0});var r=i(42),o=function(e){return e&&e.__esModule?e:{default:e}}(r),n=e.View.extend({events:{"click .edit_library_btn":"edit_button_clicked","click .cancel_library_btn":"cancel_library_modification","click .save_library_btn":"save_library_modification","click .delete_library_btn":"delete_library","click .undelete_library_btn":"undelete_library"},edit_mode:!1,element_visibility_config:{upload_library_btn:!1,edit_library_btn:!1,permission_library_btn:!1,save_library_btn:!1,cancel_library_btn:!1,delete_library_btn:!1,undelete_library_btn:!1},initialize:function(e){this.render(e)},render:function(e){void 0===e&&(e=Galaxy.libraries.libraryListView.collection.get(this.$el.data("id"))),this.prepareButtons(e);var t=this.templateRow();return this.setElement(t({library:e,button_config:this.element_visibility_config,edit_mode:this.edit_mode})),this.$el.show(),this},repaint:function(e){s(".tooltip").hide();var t=this.$el;this.render(),t.replaceWith(this.$el),this.$el.find("[data-toggle]").tooltip()},prepareButtons:function(e){var t=this.element_visibility_config;!1===this.edit_mode?(t.save_library_btn=!1,t.cancel_library_btn=!1,t.delete_library_btn=!1,!0===e.get("deleted")?(t.undelete_library_btn=!0,t.upload_library_btn=!1,t.edit_library_btn=!1,t.permission_library_btn=!1):!1===e.get("deleted")&&(t.save_library_btn=!1,t.cancel_library_btn=!1,t.undelete_library_btn=!1,!0===e.get("can_user_add")&&(t.upload_library_btn=!0),!0===e.get("can_user_modify")&&(t.edit_library_btn=!0),!0===e.get("can_user_manage")&&(t.permission_library_btn=!0))):!0===this.edit_mode&&(t.upload_library_btn=!1,t.edit_library_btn=!1,t.permission_library_btn=!1,t.save_library_btn=!0,t.cancel_library_btn=!0,t.delete_library_btn=!0,t.undelete_library_btn=!1),this.element_visibility_config=t},edit_button_clicked:function(){this.edit_mode=!0,this.repaint()},cancel_library_modification:function(){this.edit_mode=!1,this.repaint()},save_library_modification:function(){var e=Galaxy.libraries.libraryListView.collection.get(this.$el.data("id")),t=!1,i=this.$el.find(".input_library_name").val();if(void 0!==i&&i!==e.get("name")){if(!(i.length>2))return void o.default.warning("Library name has to be at least 3 characters long.");e.set("name",i),t=!0}var s=this.$el.find(".input_library_description").val();void 0!==s&&s!==e.get("description")&&(e.set("description",s),t=!0);var a=this.$el.find(".input_library_synopsis").val();if(void 0!==a&&a!==e.get("synopsis")&&(e.set("synopsis",a),t=!0),t){var r=this;e.save(null,{patch:!0,success:function(e){r.edit_mode=!1,r.repaint(e),o.default.success("Changes to library saved.")},error:function(e,t){void 0!==t.responseJSON?o.default.error(t.responseJSON.err_msg):o.default.error("An error occured while attempting to update the library.")}})}else this.edit_mode=!1,this.repaint(e),o.default.info("Nothing has changed.")},delete_library:function(){var e=Galaxy.libraries.libraryListView.collection.get(this.$el.data("id")),t=this;e.destroy({success:function(e){e.set("deleted",!0),Galaxy.libraries.libraryListView.collection.add(e),t.edit_mode=!1,!1===Galaxy.libraries.preferences.get("with_deleted")?(s(".tooltip").hide(),t.repaint(e),t.$el.remove()):!0===Galaxy.libraries.preferences.get("with_deleted")&&t.repaint(e),o.default.success("Library has been marked deleted.")},error:function(e,t){void 0!==t.responseJSON?o.default.error(t.responseJSON.err_msg):o.default.error("An error occured during deleting the library.")}})},undelete_library:function(){var e=Galaxy.libraries.libraryListView.collection.get(this.$el.data("id")),t=this;e.url=e.urlRoot+e.id+"?undelete=true",e.destroy({success:function(e){e.set("deleted",!1),Galaxy.libraries.libraryListView.collection.add(e),t.edit_mode=!1,t.repaint(e),o.default.success("Library has been undeleted.")},error:function(e,t){void 0!==t.responseJSON?o.default.error(t.responseJSON.err_msg):o.default.error("An error occured while undeleting the library.")}})},templateRow:function(){return a.template(['" style="display:none;" data-id="<%- library.get("id") %>">',"<% if(!edit_mode) { %>",'<% if(library.get("deleted")) { %>','<%- library.get("name") %>',"<% } else { %>",'"><%- library.get("name") %>',"<% } %>",'<% if(library.get("description")) { %>','<% if( (library.get("description")).length> 80 ) { %>','"><%= _.escape(library.get("description")).substring(0, 80) + "..." %>',"<% } else { %>",'<%= _.escape(library.get("description"))%>',"<% } %>","<% } else { %>","","<% } %>",'<% if(library.get("synopsis")) { %>','<% if( (library.get("synopsis")).length> 120 ) { %>','"><%= _.escape(library.get("synopsis")).substring(0, 120) + "..." %>',"<% } else { %>",'<%= _.escape(library.get("synopsis"))%>',"<% } %>","<% } else { %>","","<% } %>","<% } else if(edit_mode){ %>",'','','',"<% } %>",'','<% if( (library.get("public")) && (library.get("deleted") === false) ) { %>',' ',"<% }%>",'','/permissions">','','','','" class="fa fa-ban fa-lg">','',"",""].join(""))}});t.default={LibraryRowView:n}}).call(t,i(1),i(0),i(2))},299:function(e,t,i){"use strict";(function(e,s,a){function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var o=i(42),n=r(o),l=i(156),d=r(l),c=e.View.extend({el:"#center",defaults:{search_term:""},events:{"click #create_new_library_btn":"createLibraryFromModal","click #include_deleted_chk":"includeDeletedChecked","click #exclude_restricted_chk":"excludeRestrictedChecked","click .page_size_prompt":"showPageSizePrompt","keyup .library-search-input":"searchLibraries"},initialize:function(e){this.options=s.defaults(this.options||{},e,this.defaults),this.render()},render:function(){var e=this.templateToolBar(),t=!1,i=!0;Galaxy.user&&(t=Galaxy.user.isAdmin(),i=Galaxy.user.isAnonymous()),this.$el.html(e({admin_user:t,anon_user:i})),t&&(this.$el.find("#include_deleted_chk")[0].checked=Galaxy.libraries.preferences.get("with_deleted"),this.$el.find("#exclude_restricted_chk")[0].checked=Galaxy.libraries.preferences.get("without_restricted"))},renderPaginator:function(e){this.options=s.extend(this.options,e);var t=this.templatePaginator();this.$el.find("#library_paginator").html(t({show_page:parseInt(this.options.show_page),page_count:parseInt(this.options.page_count),total_libraries_count:this.options.total_libraries_count,libraries_shown:this.options.libraries_shown,library_page_size:Galaxy.libraries.preferences.get("library_page_size")}))},createLibraryFromModal:function(e){e.preventDefault(),e.stopPropagation();var t=this;this.modal=Galaxy.modal,this.modal.show({closing_events:!0,title:"Create New Library",body:this.templateNewLibraryInModal(),buttons:{Create:function(){t.createNewLibrary()},Close:function(){t.modal.hide()}}})},createNewLibrary:function(){var e=this.serializeNewLibrary();if(this.validateNewLibrary(e)){var t=new d.default.Library,i=this;t.save(e,{success:function(e){Galaxy.libraries.libraryListView.collection.add(e),i.modal.hide(),i.clearLibraryModal(),Galaxy.libraries.libraryListView.render(),n.default.success("Library created.")},error:function(e,t){void 0!==t.responseJSON?n.default.error(t.responseJSON.err_msg):n.default.error("An error occured.")}})}else n.default.error("Library's name is missing.");return!1},showPageSizePrompt:function(e){e.preventDefault();var t=prompt("How many libraries per page do you want to see?",Galaxy.libraries.preferences.get("library_page_size"));null!=t&&t==parseInt(t)&&(Galaxy.libraries.preferences.set({library_page_size:parseInt(t)}),Galaxy.libraries.libraryListView.render({show_page:1}))},clearLibraryModal:function(){a("input[name='Name']").val(""),a("input[name='Description']").val(""),a("input[name='Synopsis']").val("")},serializeNewLibrary:function(){return{name:a("input[name='Name']").val(),description:a("input[name='Description']").val(),synopsis:a("input[name='Synopsis']").val()}},validateNewLibrary:function(e){return""!==e.name},includeDeletedChecked:function(e){e.target.checked?(Galaxy.libraries.preferences.set({with_deleted:!0}),Galaxy.libraries.libraryListView.fetchDeleted()):(Galaxy.libraries.preferences.set({with_deleted:!1}),Galaxy.libraries.libraryListView.render())},excludeRestrictedChecked:function(e){e.target.checked?Galaxy.libraries.preferences.set({without_restricted:!0}):Galaxy.libraries.preferences.set({without_restricted:!1}),Galaxy.libraries.libraryListView.render()},searchLibraries:function(e){var t=a(".library-search-input").val();this.options.search_term=t,Galaxy.libraries.libraryListView.searchLibraries(t)},templateToolBar:function(){return s.template(['
','
','
','
','DATA LIBRARIES','',"",'
','',"
","<% if(admin_user === true) { %>",'
',"","","
",'','',"","<% } %>",'','','',"","","
","
","
",'
',"
","
"].join(""))},templatePaginator:function(){return s.template(['","",' <%- libraries_shown %> libraries shown (change)',"",""," <%- total_libraries_count %> total",""].join(""))},templateNewLibraryInModal:function(){return s.template(['
',"
",'','','',"
","
"].join(""))}});t.default={LibraryToolbarView:c}}).call(t,i(1),i(2),i(0))},300:function(e,t,i){"use strict";(function(e,s,a,r){function o(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var n=i(3),l=o(n),d=i(42),c=o(d),h=i(156),_=o(h),p=i(139),u=o(p);i(198);var f=e.View.extend({el:"#center",events:{"click #toolbtn_create_folder":"createFolderFromModal","click #toolbtn_bulk_import":"modalBulkImport","click #include_deleted_datasets_chk":"checkIncludeDeleted","click #toolbtn_bulk_delete":"deleteSelectedItems","click .toolbtn-show-locinfo":"showLocInfo","click .page_size_prompt":"showPageSizePrompt"},defaults:{can_add_library_item:!1,contains_file_or_folder:!1,chain_call_control:{total_number:0,failed_number:0},disabled_jstree_element:"folders"},modal:null,jstree:null,histories:null,select_genome:null,select_extension:null,list_extensions:[],auto:{id:"auto",text:"Auto-detect",description:"This system will try to detect the file type automatically. If your file is not detected properly as one of the known formats, it most likely means that it has some format problems (e.g., different number of columns on different rows). You can still coerce the system to set your data to the format you think it should be. You can also upload compressed files, which will automatically be decompressed."},list_genomes:[],initialize:function(e){this.options=s.defaults(e||{},this.defaults),this.fetchExtAndGenomes(),this.render()},render:function(e){this.options=s.extend(this.options,e);var t=this.templateToolBar(),i={id:this.options.id,is_admin:!1,is_anonym:!0,mutiple_add_dataset_options:!1};Galaxy.user&&(i.is_admin=Galaxy.user.isAdmin(),i.is_anonym=Galaxy.user.isAnonymous(),null===Galaxy.config.user_library_import_dir&&!1===Galaxy.config.allow_library_path_paste&&null===Galaxy.config.library_import_dir||(i.mutiple_add_dataset_options=!0)),this.$el.html(t(i))},renderPaginator:function(e){this.options=s.extend(this.options,e);var t=this.templatePaginator();a("body").find(".folder-paginator").html(t({id:this.options.id,show_page:parseInt(this.options.show_page),page_count:parseInt(this.options.page_count),total_items_count:this.options.total_items_count,items_shown:this.options.items_shown,folder_page_size:Galaxy.libraries.preferences.get("folder_page_size")}))},configureElements:function(e){this.options=s.extend(this.options,e),!0===this.options.can_add_library_item?a(".add-library-items").show():a(".add-library-items").hide(),!0===this.options.contains_file_or_folder&&Galaxy.user?Galaxy.user.isAnonymous()?(a(".dataset-manipulation").show(),a(".logged-dataset-manipulation").hide()):(a(".logged-dataset-manipulation").show(),a(".dataset-manipulation").show()):(a(".logged-dataset-manipulation").hide(),a(".dataset-manipulation").hide()),this.$el.find("[data-toggle]").tooltip()},createFolderFromModal:function(e){e.preventDefault(),e.stopPropagation();var t=this,i=this.templateNewFolderInModal();this.modal=Galaxy.modal,this.modal.show({closing_events:!0,title:"Create New Folder",body:i(),buttons:{Create:function(){t.create_new_folder_event()},Close:function(){Galaxy.modal.hide()}}})},create_new_folder_event:function(){var t=this.serialize_new_folder();if(this.validate_new_folder(t)){var i,s=new _.default.FolderAsModel,a=e.history.fragment.split("/");i=a.indexOf("page")>-1?a[a.length-3]:a[a.length-1],s.url=s.urlRoot+i,s.save(t,{success:function(e){Galaxy.modal.hide(),c.default.success("Folder created."),e.set({type:"folder"}),Galaxy.libraries.folderListView.collection.add(e)},error:function(e,t){Galaxy.modal.hide(),void 0!==t.responseJSON?c.default.error(t.responseJSON.err_msg):c.default.error("An error occurred.")}})}else c.default.error("Folder's name is missing.");return!1},serialize_new_folder:function(){return{name:a("input[name='Name']").val(),description:a("input[name='Description']").val()}},validate_new_folder:function(e){return""!==e.name},modalBulkImport:function(){if(0===this.findCheckedRows().length)c.default.info("You must select some datasets first.");else{var e=this;this.histories=new _.default.GalaxyHistories,this.histories.fetch().done(function(){var t=e.templateBulkImportInModal();e.modal=Galaxy.modal,e.modal.show({closing_events:!0,title:"Import into History",body:t({histories:e.histories.models}),buttons:{Import:function(){e.importAllIntoHistory()},Close:function(){Galaxy.modal.hide()}}})}).fail(function(e,t){void 0!==t.responseJSON?c.default.error(t.responseJSON.err_msg):c.default.error("An error occurred.")})}},importAllIntoHistory:function(){this.modal.disableButton("Import");var e=this.modal.$("input[name=history_name]").val(),t=this;if(""!==e)a.post(Galaxy.root+"api/histories",{name:e}).done(function(e){t.options.last_used_history_id=e.id,t.processImportToHistory(e.id,e.name)}).fail(function(e,t,i){c.default.error("An error occurred.")}).always(function(){t.modal.enableButton("Import")});else{var i=a("select[name=dataset_import_bulk] option:selected").val();this.options.last_used_history_id=i;var s=a("select[name=dataset_import_bulk] option:selected").text();this.processImportToHistory(i,s),this.modal.enableButton("Import")}},processImportToHistory:function(e,t){var i=[],s=[];this.findCheckedRows().each(function(){var e=a(this).closest("tr").data("id");"F"==e.substring(0,1)?s.push(e):i.push(e)});for(var o=[],n=i.length-1;n>=0;n--){var l=i[n],d=new _.default.HistoryItem;d.url=d.urlRoot+e+"/contents",d.content=l,d.source="library",o.push(d)}for(var n=s.length-1;n>=0;n--){var c=s[n],d=new _.default.HistoryItem;d.url=d.urlRoot+e+"/contents",d.content=c,d.source="library_folder",o.push(d)}this.initChainCallControl({length:o.length,action:"to_history",history_name:t}),r.getJSON(Galaxy.root+"history/set_as_current?id="+e),this.chainCallImportingIntoHistory(o,t)},updateProgress:function(){this.progress+=this.progressStep,a(".progress-bar-import").width(Math.round(this.progress)+"%");var e=Math.round(this.progress)+"% Complete";a(".completion_span").text(e)},download:function(e,t){var i=[],s=[];this.findCheckedRows().each(function(){var e=a(this).closest("tr").data("id");"F"==e.substring(0,1)?s.push(e):i.push(e)});var r=Galaxy.root+"api/libraries/datasets/download/"+t,o={ld_ids:i,folder_ids:s};this.processDownload(r,o,"get")},processDownload:function(e,t,i){if(e&&t){t="string"==typeof t?t:a.param(t);var s="";a.each(t.split("&"),function(){var e=this.split("=");s+=''}),a('
'+s+"
").appendTo("body").submit().remove(),c.default.info("Your download will begin soon.")}else c.default.error("An error occurred.")},addFilesFromHistoryModal:function(){this.histories=new _.default.GalaxyHistories;var e=this;this.histories.fetch().done(function(){e.modal=Galaxy.modal;var t=e.templateAddFilesFromHistory();e.modal.show({closing_events:!0,title:"Adding datasets from your history",body:t({histories:e.histories.models}),buttons:{Add:function(){e.addAllDatasetsFromHistory()},Close:function(){Galaxy.modal.hide()}},closing_callback:function(){Galaxy.libraries.library_router.navigate("folders/"+e.id,{trigger:!0})}}),e.fetchAndDisplayHistoryContents(e.histories.models[0].id),a("#dataset_add_bulk").change(function(t){e.fetchAndDisplayHistoryContents(t.target.value)})}).fail(function(e,t){void 0!==t.responseJSON?c.default.error(t.responseJSON.err_msg):c.default.error("An error occurred.")})},importFilesFromPathModal:function(){var e=this;this.modal=Galaxy.modal;var t=this.templateImportPathModal();this.modal.show({closing_events:!0,title:"Please enter paths to import",body:t({}),buttons:{Import:function(){e.importFromPathsClicked(e)},Close:function(){Galaxy.modal.hide()}},closing_callback:function(){Galaxy.libraries.library_router.navigate("folders/"+e.id,{trigger:!0})}}),this.renderSelectBoxes()},fetchExtAndGenomes:function(){var e=this;l.default.get({url:Galaxy.root+"api/datatypes?extension_only=False",success:function(t){e.list_extensions=[];for(var i in t)e.list_extensions.push({id:t[i].extension,text:t[i].extension,description:t[i].description,description_url:t[i].description_url});e.list_extensions.sort(function(e,t){return e.id>t.id?1:e.idt.id?1:e.id=0;h--){var _=l[h].trim();0!==_.length&&d.push(_)}this.initChainCallControl({length:d.length,action:"adding_datasets"}),this.chainCallImportingFolders({paths:d,preserve_dirs:e,link_data:t,space_to_tab:i,to_posix_lines:s,source:"admin_path",file_type:o,tag_using_filenames:r,dbkey:n})}else c.default.info("Please enter a path relative to Galaxy root.")},initChainCallControl:function(e){var t;switch(e.action){case"adding_datasets":t=this.templateAddingDatasetsProgressBar(),this.modal.$el.find(".modal-body").html(t({folder_name:this.options.folder_name}));break;case"deleting_datasets":t=this.templateDeletingItemsProgressBar(),this.modal.$el.find(".modal-body").html(t());break;case"to_history":t=this.templateImportIntoHistoryProgressBar(),this.modal.$el.find(".modal-body").html(t({history_name:e.history_name}));break;default:Galaxy.emit.error("Wrong action specified.","datalibs")}this.progress=0,this.progressStep=100/e.length,this.options.chain_call_control.total_number=e.length,this.options.chain_call_control.failed_number=0},importFromJstreePath:function(e,t){var i=a("#jstree_browser").jstree().get_selected(!0),r=s.filter(i,function(e){return 0==e.state.disabled}),o=this.modal.$el.find(".preserve-checkbox").is(":checked"),n=this.modal.$el.find(".link-checkbox").is(":checked"),l=this.modal.$el.find(".spacetab-checkbox").is(":checked"),d=this.modal.$el.find(".posix-checkbox").is(":checked"),h=this.select_extension.value(),_=this.select_genome.value(),p=this.modal.$el.find(".tag-files").is(":checked"),u=r[0].type,f=[];if(r.length<1)c.default.info("Please select some items first.");else{this.modal.disableButton("Import");for(var g=r.length-1;g>=0;g--)void 0!==r[g].li_attr.full_path&&f.push(r[g].li_attr.full_path);if(this.initChainCallControl({length:f.length,action:"adding_datasets"}),"folder"===u){var m=t.source+"_folder";this.chainCallImportingFolders({paths:f,preserve_dirs:o,link_data:n,space_to_tab:l,to_posix_lines:d,source:m,file_type:h,dbkey:_,tag_using_filenames:p})}else if("file"===u){var m=t.source+"_file";this.chainCallImportingUserdirFiles({paths:f,file_type:h,dbkey:_,link_data:n,space_to_tab:l,to_posix_lines:d,source:m,tag_using_filenames:p})}}},fetchAndDisplayHistoryContents:function(e){var t=new _.default.HistoryContents({id:e}),i=this;t.fetch({success:function(t){var s=i.templateHistoryContents();i.histories.get(e).set({contents:t}),i.modal.$el.find("#selected_history_content").html(s({history_contents:t.models.reverse()})),i.modal.$el.find(".history-import-select-all").bind("click",function(){a("#selected_history_content [type=checkbox]").prop("checked",!0)}),i.modal.$el.find(".history-import-unselect-all").bind("click",function(){a("#selected_history_content [type=checkbox]").prop("checked",!1)})},error:function(e,t){void 0!==t.responseJSON?c.default.error(t.responseJSON.err_msg):c.default.error("An error occurred.")}})},addAllDatasetsFromHistory:function(){var e=this.modal.$el.find("#selected_history_content").find(":checked"),t=[],i=[],s=[];if(e.length<1)c.default.info("You must select some datasets first.");else{this.modal.disableButton("Add"),e.each(function(){var e=a(this).closest("li").data("id");if(e){var s=a(this).closest("li").data("name");t.push(e),i.push(s)}});for(var r=t.length-1;r>=0;r--){var o=t[r],n=new _.default.Item;n.url=Galaxy.root+"api/folders/"+this.options.id+"/contents","collection"===i[r]?n.set({from_hdca_id:o}):n.set({from_hda_id:o}),s.push(n)}this.initChainCallControl({length:s.length,action:"adding_datasets"}),this.chainCallAddingHdas(s)}},chainCallImportingIntoHistory:function(e,t){var i=this,s=e.pop();if(void 0===s)return 0===this.options.chain_call_control.failed_number?c.default.success("Selected datasets imported into history. Click this to start analyzing it.","",{onclick:function(){window.location=Galaxy.root}}):this.options.chain_call_control.failed_number===this.options.chain_call_control.total_number?c.default.error("There was an error and no datasets were imported into history."):this.options.chain_call_control.failed_number=0;n--){var l=new _.default.Item({id:e[n]});o.push(l)}for(var n=t.length-1;n>=0;n--){var d=new _.default.FolderAsModel({id:t[n]});o.push(d)}this.options.chain_call_control.total_number=r,this.chainCallDeletingItems(o)}},showLocInfo:function(){var e=null,t=this;null!==Galaxy.libraries.libraryListView?(e=Galaxy.libraries.libraryListView.collection.get(this.options.parent_library_id),this.showLocInfoModal(e)):(e=new _.default.Library({id:this.options.parent_library_id}),e.fetch({success:function(){t.showLocInfoModal(e)},error:function(e,t){void 0!==t.responseJSON?c.default.error(t.responseJSON.err_msg):c.default.error("An error occurred.")}}))},showLocInfoModal:function(e){var t=this,i=this.templateLocInfoInModal();this.modal=Galaxy.modal,this.modal.show({closing_events:!0,title:"Location Details",body:i({library:e,options:t.options}),buttons:{Close:function(){Galaxy.modal.hide()}}})},showImportModal:function(e){switch(e.source){case"history":this.addFilesFromHistoryModal();break;case"importdir":this.importFilesFromGalaxyFolderModal({source:"importdir"});break;case"path":this.importFilesFromPathModal();break;case"userdir":this.importFilesFromGalaxyFolderModal({source:"userdir"});break;default:Galaxy.libraries.library_router.back(),c.default.error("Invalid import source.")}},showPageSizePrompt:function(e){e.preventDefault();var t=prompt("How many items per page do you want to see?",Galaxy.libraries.preferences.get("folder_page_size"));null!=t&&t==parseInt(t)&&(Galaxy.libraries.preferences.set({folder_page_size:parseInt(t)}),Galaxy.libraries.folderListView.render({id:this.options.id,show_page:1}))},findCheckedRows:function(){return a("#folder_list_body").find(":checked")},templateToolBar:function(){return s.template(['
','
','
',"DATA LIBRARIES",'','",'","<% if(mutiple_add_dataset_options) { %>",'","<% } else { %>",'","<% } %>",'",'",'','",'','','","","","
","",'
',"
",'
',"
"].join(""))},templateLocInfoInModal:function(){return s.template(["
",'',"",'',"","","","","",'',"",'<% if(library.get("description") !== "") { %>',"","",'',"","<% } %>",'<% if(library.get("synopsis") !== "") { %>',"","",'',"","<% } %>",'<% if(library.get("create_time_pretty") !== "") { %>',"","",'',"","<% } %>","","",'',"","","
library
name<%- library.get("name") %>
description<%- library.get("description") %>
synopsis<%- library.get("synopsis") %>
created"><%- library.get("create_time_pretty") %>
id<%- library.get("id") %>
",'',"",'',"","","","","","","",'<% if(options.folder_description !== "") { %>',"","","","","<% } %>","","","","","","
folder
name<%- options.folder_name %>
description<%- options.folder_description %>
id<%- options.id %>
","
"].join(""))},templateNewFolderInModal:function(){return s.template(['
',"
",'','',"
","
"].join(""))},templateBulkImportInModal:function(){return s.template(["
",'
',"Select history: ",'","
",'
',"or create new: ",'',"","
","
"].join(""))},templateImportIntoHistoryProgressBar:function(){return s.template(['
',"Importing selected items to history <%= _.escape(history_name) %>","
",'
','
','0% Complete',"
","
"].join(""))},templateAddingDatasetsProgressBar:function(){return s.template(['
',"Adding selected datasets to library folder <%= _.escape(folder_name) %>","
",'
','
','0% Complete',"
","
"].join(""))},templateDeletingItemsProgressBar:function(){return s.template(['
',"
",'
','
','0% Complete',"
","
"].join(""))},templateBrowserModal:function(){return s.template(['
','
All files you select will be imported into the current folder ignoring their folder structure.
','','
','",'","
",'
','",'",'",'","
",'",'","
",'
',"
","
","

You can set extension type and genome for all imported datasets at once:

","
",'Type: ','Genome: ',"
","
","
",'","
","
"].join(""))},templateImportPathModal:function(){return s.template(['
','
All files within the given folders and their subfolders will be imported into the current folder.
','
','",'","
",'",'","
",'',"
","

You can set extension type and genome for all imported datasets at once:

","
",'Type: ','Genome: ',"
","
",'","
","
"].join(""))},templateAddFilesFromHistory:function(){return s.template(['
',"
","1. Select history: ",'","
","
",'
',"
","
"].join(""))},templateHistoryContents:function(){return s.template(["

2. Choose the datasets to import:

","
",'",'","
","
","
    ","<% _.each(history_contents, function(history_item) { %>",'<% if (history_item.get("deleted") != true ) { %>','<% var item_name = history_item.get("name") %>','<% if (history_item.get("type") === "collection") { %>','<% var collection_type = history_item.get("collection_type") %>','<% if (collection_type === "list") { %>','
  • " data-name="<%= _.escape(history_item.get("type")) %>">',"
  • ","<% } else { %>",'
  • ','','<%= _.escape(history_item.get("hid")) %>: ','<%= item_name.length > 75 ? _.escape("...".concat(item_name.substr(-75))) : _.escape(item_name) %> (Dataset Collection of type <%= _.escape(collection_type) %> not supported.)',"","
  • ","<% } %>",'<% } else if (history_item.get("visible") === true && history_item.get("state") === "ok") { %>','
  • " data-name="<%= _.escape(history_item.get("type")) %>">','","
  • ","<% } %>","<% } %>","<% }); %>","
"].join(""))},templatePaginator:function(){return s.template(['","",' <%- items_shown %> items shown (change)',"",""," <%- total_items_count %> total",""].join(""))}});t.default={FolderToolbarView:f}}).call(t,i(1),i(2),i(0),i(0))},301:function(e,t,i){"use strict";(function(e,s,a){function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var o=i(42),n=r(o),l=i(156),d=r(l),c=i(139),h=r(c),_=e.View.extend({el:"#center",model:null,options:{},events:{"click .toolbtn_save_permissions":"savePermissions"},initialize:function(e){this.options=s.extend(this.options,e),this.options.id&&this.fetchLibrary()},fetchLibrary:function(e){this.options=s.extend(this.options,e),this.model=new d.default.Library({id:this.options.id});var t=this;this.model.fetch({success:function(){t.options.show_permissions&&t.showPermissions()},error:function(e,t){void 0!==t.responseJSON?n.default.error(t.responseJSON.err_msg+" Click this to go back.","",{onclick:function(){Galaxy.libraries.library_router.back()}}):n.default.error("An error occurred. Click this to go back.","",{onclick:function(){Galaxy.libraries.library_router.back()}})}})},showPermissions:function(e){this.options=s.extend(this.options,e),a(".tooltip").remove(),void 0!==this.options.fetched_permissions&&(0===this.options.fetched_permissions.access_library_role_list.length?this.model.set({is_unrestricted:!0}):this.model.set({is_unrestricted:!1}));var t=!1;Galaxy.user&&(t=Galaxy.user.isAdmin());var i=this.templateLibraryPermissions();this.$el.html(i({library:this.model,is_admin:t}));var r=this;a.get(Galaxy.root+"api/libraries/"+r.id+"/permissions?scope=current").done(function(e){r.prepareSelectBoxes({fetched_permissions:e})}).fail(function(){n.default.error("An error occurred while attempting to fetch library permissions.")}),a("#center [data-toggle]").tooltip(),a("#center").css("overflow","auto")},_serializeRoles:function(e){for(var t=[],i=0;i=0;i--)t.push(e[i].id);return t},savePermissions:function(e){var t=this,i=this._extractIds(this.accessSelectObject.$el.select2("data")),s=this._extractIds(this.addSelectObject.$el.select2("data")),r=this._extractIds(this.manageSelectObject.$el.select2("data")),o=this._extractIds(this.modifySelectObject.$el.select2("data"));a.post(Galaxy.root+"api/libraries/"+t.id+"/permissions?action=set_permissions",{"access_ids[]":i,"add_ids[]":s,"manage_ids[]":r,"modify_ids[]":o}).done(function(e){t.showPermissions({fetched_permissions:e}),n.default.success("Permissions saved.")}).fail(function(){n.default.error("An error occurred while attempting to set library permissions.")})},templateLibraryPermissions:function(){return s.template(['
','","

",'Library: <%= _.escape(library.get("name")) %>',"

",'
',"<% if (is_admin) { %>","You are logged in as an administrator therefore you can manage any library on this Galaxy instance. Please make sure you understand the consequences.","<% } else { %>","You can assign any number of roles to any of the following permission types. However please read carefully the implications of such actions.","<% }%>","
",'
',"

Library permissions

","

Roles that can access the library

",'
','
',"User with any of these roles can access this library. If there are no access roles set on the library it is considered unrestricted.","
","

Roles that can manage permissions on this library

",'
','
',"User with any of these roles can manage permissions on this library (includes giving access).","
","

Roles that can add items to this library

",'
','
',"User with any of these roles can add items to this library (folders and datasets).","
","

Roles that can modify this library

",'
','
',"User with any of these roles can modify this library (name, synopsis, etc.).","
",'","
","
"].join(""))}});t.default={LibraryView:_}}).call(t,i(1),i(2),i(0))},302:function(e,t,i){"use strict";(function(e,s,a){function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var o=i(42),n=r(o),l=i(156),d=r(l),c=i(139),h=r(c),_=e.View.extend({el:"#center",model:null,options:{},events:{"click .toolbtn_save_permissions":"savePermissions"},initialize:function(e){this.options=s.extend(this.options,e),this.options.id&&this.fetchFolder()},fetchFolder:function(e){this.options=s.extend(this.options,e),this.model=new d.default.FolderAsModel({id:this.options.id});var t=this;this.model.fetch({success:function(){t.options.show_permissions&&t.showPermissions()},error:function(e,t){void 0!==t.responseJSON?n.default.error(t.responseJSON.err_msg+" Click this to go back.","",{onclick:function(){Galaxy.libraries.library_router.back()}}):n.default.error("An error occurred. Click this to go back.","",{onclick:function(){Galaxy.libraries.library_router.back()}})}})},showPermissions:function(e){this.options=s.extend(this.options,e),a(".tooltip").remove();var t=!1;Galaxy.user&&(t=Galaxy.user.isAdmin());var i=this.templateFolderPermissions();this.$el.html(i({folder:this.model,is_admin:t}));var r=this;a.get(Galaxy.root+"api/folders/"+r.id+"/permissions?scope=current").done(function(e){r.prepareSelectBoxes({fetched_permissions:e})}).fail(function(){n.default.error("An error occurred while attempting to fetch folder permissions.")}),a("#center [data-toggle]").tooltip(),a("#center").css("overflow","auto")},_serializeRoles:function(e){for(var t=[],i=0;i=0;i--)t.push(e[i].id);return t},savePermissions:function(e){var t=this,i=this._extractIds(this.addSelectObject.$el.select2("data")),s=this._extractIds(this.manageSelectObject.$el.select2("data")),r=this._extractIds(this.modifySelectObject.$el.select2("data"));a.post(Galaxy.root+"api/folders/"+t.id+"/permissions?action=set_permissions",{"add_ids[]":i,"manage_ids[]":s,"modify_ids[]":r}).done(function(e){t.showPermissions({fetched_permissions:e}),n.default.success("Permissions saved.")}).fail(function(){n.default.error("An error occurred while attempting to set folder permissions.")})},templateFolderPermissions:function(){return s.template(['
','","

",'Folder: <%= _.escape(folder.get("name")) %>',"

",'
',"<% if (is_admin) { %>","You are logged in as an administrator therefore you can manage any folder on this Galaxy instance. Please make sure you understand the consequences.","<% } else { %>","You can assign any number of roles to any of the following permission types. However please read carefully the implications of such actions.","<% }%>","
",'
',"

Folder permissions

","

","Roles that can manage permissions on this folder","

",'
','
',"User with any of these roles can manage permissions on this folder.","
","

","Roles that can add items to this folder","

",'
','
',"User with any of these roles can add items to this folder (folders and datasets).","
","

","Roles that can modify this folder","

",'
','
',"User with any of these roles can modify this folder (name, etc.).","
",'","
","
"].join(""))}});t.default={FolderView:_}}).call(t,i(1),i(2),i(0))},59:function(e,t,i){"use strict";function s(e,t){var i=void 0!==e.prototype?e.prototype:e;return void 0!==t&&(i._logNamespace=t),a.forEach(function(e){i[e]=function(){if(this.logger)return this.logger.emit?this.logger.emit(e,this._logNamespace,arguments):this.logger[e]?this.logger[e].apply(this.logger,arguments):void 0}}),e}Object.defineProperty(t,"__esModule",{value:!0});var a=["log","debug","info","warn","error","metric"];t.default=s},8:function(e,t,i){"use strict";(function(e){function s(e){return e&&e.__esModule?e:{default:e}}function a(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&(t[i]=e[i]);return t.default=e,t}function r(e,t){var i=Array.prototype.slice.call(arguments,0),s=i.pop();return i.unshift(s),d.defaults.apply(d,i)}function o(e,t){t=t||"model";var i=d.template(e.join(""));return function(e,s){var a={view:s||{},_l:f.default};return a[t]=e||{},i(a)}}function n(e,t){t=t||{};var i=t.ascending?1:-1;return function(t,s){return t=t.get(e),s=s.get(e),(ts?1:0)*i}}Object.defineProperty(t,"__esModule",{value:!0});var l=i(2),d=a(l),c=i(1),h=a(c),_=i(59),p=s(_),u=i(7),f=s(u),g={logger:null,_logNamespace:"."};(0,p.default)(g);var m=h.Model.extend({initialize:function(e){if(this._checkEnabledSessionStorage(),!e.id)throw new Error("SessionStorageModel requires an id in the initial attributes");this.id=e.id;var t=this.isNew()?{}:this._read(this);this.clear({silent:!0}),this.save(d.extend({},this.defaults,t,e),{silent:!0}),this.on("change",function(){this.save()})},_checkEnabledSessionStorage:function(){try{return window.sessionStorage.length>=0}catch(e){return alert("Please enable cookies in your browser for this Galaxy site"),!1}},sync:function(e,t,i){i.silent||t.trigger("request",t,{},i);var s={};switch(e){case"create":s=this._create(t);break;case"read":s=this._read(t);break;case"update":s=this._update(t);break;case"delete":s=this._delete(t)}return void 0!==s||null!==s?i.success&&i.success():i.error&&i.error(),s},_create:function(e){try{var t=e.toJSON(),i=sessionStorage.setItem(e.id,JSON.stringify(t));return null===i?i:t}catch(e){if(!(e instanceof DOMException&&navigator.userAgent.indexOf("Safari")>-1))throw e}return null},_read:function(e){return JSON.parse(sessionStorage.getItem(e.id))},_update:function(e){return e._create(e)},_delete:function(e){return sessionStorage.removeItem(e.id)},isNew:function(){return!sessionStorage.hasOwnProperty(this.id)},_log:function(){return JSON.stringify(this.toJSON(),null," ")},toString:function(){return"SessionStorageModel("+this.id+")"}});!function(){m.prototype=d.omit(m.prototype,"url","urlRoot")}();var b={searchAttributes:[],searchAliases:{},searchAttribute:function(e,t){var i=this.get(e);return!(!t||void 0===i||null===i)&&(d.isArray(i)?this._searchArrayAttribute(i,t):-1!==i.toString().toLowerCase().indexOf(t.toLowerCase()))},_searchArrayAttribute:function(e,t){return t=t.toLowerCase(),d.any(e,function(e){return-1!==e.toString().toLowerCase().indexOf(t.toLowerCase())})},search:function(e){var t=this;return d.filter(this.searchAttributes,function(i){return t.searchAttribute(i,e)})},matches:function(e){var t=e.split("=");if(t.length>=2){var i=t[0];return i=this.searchAliases[i]||i,this.searchAttribute(i,t[1])}return!!this.search(e).length},matchesAll:function(e){var t=this;return e=e.match(/(".*"|\w*=".*"|\S*)/g).filter(function(e){return!!e}),d.all(e,function(e){return e=e.replace(/"/g,""),t.matches(e)})}},y={hiddenUntilActivated:function(t,i){if(i=i||{},this.HUAVOptions={$elementShown:this.$el,showFn:e.prototype.toggle,showSpeed:"fast"},d.extend(this.HUAVOptions,i||{}),this.HUAVOptions.hasBeenShown=this.HUAVOptions.$elementShown.is(":visible"),this.hidden=this.isHidden(),t){var s=this;t.on("click",function(e){s.toggle(s.HUAVOptions.showSpeed)})}},isHidden:function(){return this.HUAVOptions.$elementShown.is(":hidden")},toggle:function(){return this.hidden?(this.HUAVOptions.hasBeenShown||d.isFunction(this.HUAVOptions.onshowFirstTime)&&(this.HUAVOptions.hasBeenShown=!0,this.HUAVOptions.onshowFirstTime.call(this)),d.isFunction(this.HUAVOptions.onshow)&&(this.HUAVOptions.onshow.call(this),this.trigger("hiddenUntilActivated:shown",this)),this.hidden=!1):(d.isFunction(this.HUAVOptions.onhide)&&(this.HUAVOptions.onhide.call(this),this.trigger("hiddenUntilActivated:hidden",this)),this.hidden=!0),this.HUAVOptions.showFn.apply(this.HUAVOptions.$elementShown,arguments)}},v={initialize:function(e){this.draggable=e.draggable||!1},$dragHandle:function(){return this.$(".title-bar")},toggleDraggable:function(){this.draggable?this.draggableOff():this.draggableOn()},draggableOn:function(){this.draggable=!0,this.dragStartHandler=d.bind(this._dragStartHandler,this),this.dragEndHandler=d.bind(this._dragEndHandler,this);var e=this.$dragHandle().attr("draggable",!0).get(0);e.addEventListener("dragstart",this.dragStartHandler,!1),e.addEventListener("dragend",this.dragEndHandler,!1)},draggableOff:function(){this.draggable=!1;var e=this.$dragHandle().attr("draggable",!1).get(0);e.removeEventListener("dragstart",this.dragStartHandler,!1),e.removeEventListener("dragend",this.dragEndHandler,!1)},_dragStartHandler:function(e){return e.dataTransfer.effectAllowed="move",e.dataTransfer.setData("text",JSON.stringify(this.model.toJSON())),this.trigger("draggable:dragstart",e,this),!1},_dragEndHandler:function(e){return this.trigger("draggable:dragend",e,this),!1}},w={initialize:function(e){this.selectable=e.selectable||!1,this.selected=e.selected||!1},$selector:function(){return this.$(".selector")},_renderSelected:function(){this.$selector().find("span").toggleClass("fa-check-square-o",this.selected).toggleClass("fa-square-o",!this.selected)},toggleSelector:function(){this.$selector().is(":visible")?this.hideSelector():this.showSelector()},showSelector:function(e){e=void 0!==e?e:this.fxSpeed,this.selectable=!0,this.trigger("selectable",!0,this),this._renderSelected(),e?this.$selector().show(e):this.$selector().show()},hideSelector:function(e){e=void 0!==e?e:this.fxSpeed,this.selectable=!1,this.trigger("selectable",!1,this),e?this.$selector().hide(e):this.$selector().hide()},toggleSelect:function(e){this.selected?this.deselect(e):this.select(e)},select:function(e){return this.selected||(this.trigger("selected",this,e),this.selected=!0,this._renderSelected()),!1},deselect:function(e){return this.selected&&(this.trigger("de-selected",this,e),this.selected=!1,this._renderSelected()),!1}};t.default={LoggableMixin:g,SessionStorageModel:m,mixin:r,SearchableModelMixin:b,HiddenUntilActivatedViewMixin:y,DraggableViewMixin:v,SelectableViewMixin:w,wrapTemplate:o,buildComparator:n}}).call(t,i(0))}},[292]); \ No newline at end of file +p=!0,u=0,f=o.data.nodes.length;ue.inArray(o.data.nodes[u],b.children)&&(m-=1)),!(p=p&&(w&&w.settings&&w.settings.dnd&&!1===w.settings.dnd.check_while_dragging||w.check(g,o.data.origin&&o.data.origin!==w?o.data.origin.get_node(o.data.nodes[u]):o.data.nodes[u],c,m,{dnd:!0,ref:w.get_node(x.parent()),pos:j,is_multi:o.data.origin&&o.data.origin!==w,is_foreign:!o.data.origin})))){w&&w.last_error&&(i=w.last_error());break}if(p)return"i"===j&&x.parent().is(".jstree-closed")&&w.settings.dnd.open_timeout&&(s=setTimeout(function(e,t){return function(){e.open_node(t)}}(w,x),w.settings.dnd.open_timeout)),t={ins:w,par:c,pos:"i"!==j||"last"!==y||0!==h||w.is_loaded(v)?h:"last"},a.css({left:n+"px",top:l+"px"}).show(),o.helper.find(".jstree-icon:eq(0)").removeClass("jstree-er").addClass("jstree-ok"),i={},_=!0,!1}),!0===_))return}else{for(p=!0,u=0,f=o.data.nodes.length;u
").html(a).css({display:"block",margin:"0",padding:"0",position:"absolute",top:"-2000px",lineHeight:"16px",zIndex:"10000"})),e(document).bind("mousemove touchmove",e.vakata.dnd.drag),e(document).bind("mouseup touchend",e.vakata.dnd.stop),!1},drag:function(i){if("touchmove"===i.type&&i.originalEvent&&i.originalEvent.changedTouches&&i.originalEvent.changedTouches[0]&&(i.pageX=i.originalEvent.changedTouches[0].pageX,i.pageY=i.originalEvent.changedTouches[0].pageY,i.target=document.elementFromPoint(i.originalEvent.changedTouches[0].pageX-window.pageXOffset,i.originalEvent.changedTouches[0].pageY-window.pageYOffset)),t.is_down){if(!t.is_drag){if(!(Math.abs(i.pageX-t.init_x)>(t.is_touch?e.vakata.dnd.settings.threshold_touch:e.vakata.dnd.settings.threshold)||Math.abs(i.pageY-t.init_y)>(t.is_touch?e.vakata.dnd.settings.threshold_touch:e.vakata.dnd.settings.threshold)))return;t.helper&&(t.helper.appendTo("body"),t.helper_w=t.helper.outerWidth()),t.is_drag=!0,e.vakata.dnd._trigger("start",i)}var s=!1,a=!1,r=!1,o=!1,n=!1,l=!1,d=!1,c=!1,h=!1,_=!1;return t.scroll_t=0,t.scroll_l=0,t.scroll_e=!1,e(e(i.target).parentsUntil("body").addBack().get().reverse()).filter(function(){return/^auto|scroll$/.test(e(this).css("overflow"))&&(this.scrollHeight>this.offsetHeight||this.scrollWidth>this.offsetWidth)}).each(function(){var s=e(this),a=s.offset();if(this.scrollHeight>this.offsetHeight&&(a.top+s.height()-i.pageYthis.offsetWidth&&(a.left+s.width()-i.pageXo&&i.pageY-do&&o-(i.pageY-d)l&&i.pageX-cl&&l-(i.pageX-c)r&&(h=r-50),n&&_+t.helper_w>n&&(_=n-(t.helper_w+2)),t.helper.css({left:_+"px",top:h+"px"})),e.vakata.dnd._trigger("move",i),!1}},stop:function(i){if("touchend"===i.type&&i.originalEvent&&i.originalEvent.changedTouches&&i.originalEvent.changedTouches[0]&&(i.pageX=i.originalEvent.changedTouches[0].pageX,i.pageY=i.originalEvent.changedTouches[0].pageY,i.target=document.elementFromPoint(i.originalEvent.changedTouches[0].pageX-window.pageXOffset,i.originalEvent.changedTouches[0].pageY-window.pageYOffset)),t.is_drag)e.vakata.dnd._trigger("stop",i);else if("touchend"===i.type&&i.target===t.target){var s=setTimeout(function(){e(i.target).click()},100);e(i.target).one("click",function(){s&&clearTimeout(s)})}return e.vakata.dnd._clean(),!1}}}(e),e.jstree.defaults.search={ajax:!1,fuzzy:!1,case_sensitive:!1,show_only_matches:!1,close_opened_onclear:!0,search_leaves_only:!1,search_callback:!1},e.jstree.plugins.search=function(t,i){this.bind=function(){i.bind.call(this),this._data.search.str="",this._data.search.dom=e(),this._data.search.res=[],this._data.search.opn=[],this.element.on("before_open.jstree",e.proxy(function(t,i){var s,a,r=this._data.search.res,o=[],n=e();if(r&&r.length&&(this._data.search.dom=e(this.element[0].querySelectorAll("#"+e.map(r,function(t){return-1!=="0123456789".indexOf(t[0])?"\\3"+t[0]+" "+t.substr(1).replace(e.jstree.idregex,"\\$&"):t.replace(e.jstree.idregex,"\\$&")}).join(", #"))),this._data.search.dom.children(".jstree-anchor").addClass("jstree-search"),this.settings.search.show_only_matches&&this._data.search.res.length)){for(s=0,a=r.length;s32&&(i.fuzzy=!1),i.fuzzy&&(s=1<=u;l--)if(m=a[t.charAt(l-1)],g[l]=0===o?(g[l+1]<<1|1)&m:(g[l+1]<<1|1)&m|(p[l+1]|p[l])<<1|1|p[l+1],g[l]&s&&(x=r(o,l-1))<=y){if(y=x,v=l-1,k.push(v),!(v>n))break;u=Math.max(1,2*n-v)}if(r(o+1,n)>y)break;p=g}return{isMatch:v>=0,score:x}},!0===t?{search:o}:o(t)}}(e),e.jstree.defaults.sort=function(e,t){return this.get_text(e)>this.get_text(t)?1:-1},e.jstree.plugins.sort=function(t,i){this.bind=function(){i.bind.call(this),this.element.on("model.jstree",e.proxy(function(e,t){this.sort(t.parent,!0)},this)).on("rename_node.jstree create_node.jstree",e.proxy(function(e,t){this.sort(t.parent||t.node.parent,!1),this.redraw_node(t.parent||t.node.parent,!0)},this)).on("move_node.jstree copy_node.jstree",e.proxy(function(e,t){this.sort(t.parent,!1),this.redraw_node(t.parent,!0)},this))},this.sort=function(t,i){var s,a;if((t=this.get_node(t))&&t.children&&t.children.length&&(t.children.sort(e.proxy(this.settings.sort,this)),i))for(s=0,a=t.children_d.length;st.ttl)&&(t&&t.state&&(t=t.state),t&&e.isFunction(this.settings.state.filter)&&(t=this.settings.state.filter.call(this,t)),!!t&&(this.element.one("set_state.jstree",function(i,s){s.instance.trigger("restore_state",{state:e.extend(!0,{},t)})}),this.set_state(t),!0))},this.clear_state=function(){return e.vakata.storage.del(this.settings.state.key)}},function(e,t){e.vakata.storage={set:function(e,t){return window.localStorage.setItem(e,t)},get:function(e){return window.localStorage.getItem(e)},del:function(e){return window.localStorage.removeItem(e)}}}(e),e.jstree.defaults.types={"#":{},default:{}},e.jstree.plugins.types=function(i,s){this.init=function(e,i){var a,r;if(i&&i.types&&i.types.default)for(a in i.types)if("default"!==a&&"#"!==a&&i.types.hasOwnProperty(a))for(r in i.types.default)i.types.default.hasOwnProperty(r)&&i.types[a][r]===t&&(i.types[a][r]=i.types.default[r]);s.init.call(this,e,i),this._model.data["#"].type="#"},this.refresh=function(e,t){s.refresh.call(this,e,t),this._model.data["#"].type="#"},this.bind=function(){this.element.on("model.jstree",e.proxy(function(e,i){var s,a,r=this._model.data,o=i.nodes,n=this.settings.types,l="default";for(s=0,a=o.length;s .jstree-ocl",e.proxy(function(t){t.stopImmediatePropagation();var i=e.Event("click",{metaKey:t.metaKey,ctrlKey:t.ctrlKey,altKey:t.altKey,shiftKey:t.shiftKey});e(t.currentTarget).closest(".jstree-node").children(".jstree-anchor:eq(0)").trigger(i).focus()},this)).on("mouseover.jstree",".jstree-wholerow, .jstree-icon",e.proxy(function(e){return e.stopImmediatePropagation(),this.hover_node(e.currentTarget),!1},this)).on("mouseleave.jstree",".jstree-node",e.proxy(function(e){this.dehover_node(e.currentTarget)},this))},this.teardown=function(){this.settings.wholerow&&this.element.find(".jstree-wholerow").remove(),i.teardown.call(this)},this.redraw_node=function(t,s,a){if(t=i.redraw_node.call(this,t,s,a)){var r=u.cloneNode(!0);-1!==e.inArray(t.id,this._data.core.selected)&&(r.className+=" jstree-wholerow-clicked"),t.insertBefore(r,t.childNodes[0])}return t}}}})},224:function(e,t,i){"use strict";(function(e,s,a,r){function o(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var n=i(42),l=o(n),d=i(156),c=o(d),h=i(3),_=o(h),p=i(139),u=o(p),f=e.View.extend({el:"#center",model:null,options:{},defaults:{edit_mode:!1},events:{"click .toolbtn_modify_dataset":"enableModification","click .toolbtn_cancel_modifications":"render","click .toolbtn-download-dataset":"downloadDataset","click .toolbtn-import-dataset":"importIntoHistory","click .copy-link-to-clipboard":"copyToClipboard","click .make-private":"makeDatasetPrivate","click .remove-restrictions":"removeDatasetRestrictions","click .toolbtn_save_permissions":"savePermissions","click .toolbtn_save_modifications":"saveModifications"},select_genome:null,select_extension:null,list_extensions:[],auto:{id:"auto",text:"Auto-detect",description:"This system will try to detect the file type automatically. If your file is not detected properly as one of the known formats, it most likely means that it has some format problems (e.g., different number of columns on different rows). You can still coerce the system to set your data to the format you think it should be. You can also upload compressed files, which will automatically be decompressed."},list_genomes:[],initialize:function(e){this.options=s.extend(this.options,e),this.fetchExtAndGenomes(),this.options.id&&this.fetchDataset()},fetchDataset:function(e){this.options=s.extend(this.options,e),this.model=new c.default.Item({id:this.options.id});var t=this;this.model.fetch({success:function(){t.options.show_permissions?t.showPermissions():t.options.show_version?t.fetchVersion():t.render()},error:function(e,t){void 0!==t.responseJSON?l.default.error(t.responseJSON.err_msg+" Click this to go back.","",{onclick:function(){Galaxy.libraries.library_router.back()}}):l.default.error("An error occurred. Click this to go back.","",{onclick:function(){Galaxy.libraries.library_router.back()}})}})},render:function(e){this.options=s.extend(this.options,e),a(".tooltip").remove();var t=this.templateDataset();this.$el.html(t({item:this.model})),a(".peek").html(this.model.get("peek")),a("#center [data-toggle]").tooltip()},fetchVersion:function(e){this.options=s.extend(this.options,e);var t=this;this.options.ldda_id?(this.ldda=new c.default.Ldda({id:this.options.ldda_id}),this.ldda.url=this.ldda.urlRoot+this.model.id+"/versions/"+this.ldda.id,this.ldda.fetch({success:function(){t.renderVersion()},error:function(e,t){void 0!==t.responseJSON?l.default.error(t.responseJSON.err_msg):l.default.error("An error occurred.")}})):(this.render(),l.default.error("Library dataset version requested but no id provided."))},renderVersion:function(){a(".tooltip").remove();var e=this.templateVersion();this.$el.html(e({item:this.model,ldda:this.ldda})),a(".peek").html(this.ldda.get("peek"))},enableModification:function(){a(".tooltip").remove();var e=this.templateModifyDataset();this.$el.html(e({item:this.model})),this.renderSelectBoxes({genome_build:this.model.get("genome_build"),file_ext:this.model.get("file_ext")}),a(".peek").html(this.model.get("peek")),a("#center [data-toggle]").tooltip()},downloadDataset:function(){var e=Galaxy.root+"api/libraries/datasets/download/uncompressed",t={ld_ids:this.id};this.processDownload(e,t)},processDownload:function(e,t,i){if(e&&t){t="string"==typeof t?t:a.param(t);var s="";a.each(t.split("&"),function(){var e=this.split("=");s+=''}),a('
'+s+"
").appendTo("body").submit().remove(),l.default.info("Your download will begin soon.")}},importIntoHistory:function(){this.refreshUserHistoriesList(function(e){var t=e.templateBulkImportInModal();e.modal=Galaxy.modal,e.modal.show({closing_events:!0,title:"Import into History",body:t({histories:e.histories.models}),buttons:{Import:function(){e.importCurrentIntoHistory()},Close:function(){Galaxy.modal.hide()}}})})},refreshUserHistoriesList:function(e){var t=this;this.histories=new c.default.GalaxyHistories,this.histories.fetch({success:function(i){0===i.length?l.default.warning("You have to create history first. Click this to do so.","",{onclick:function(){window.location=Galaxy.root}}):e(t)},error:function(e,t){void 0!==t.responseJSON?l.default.error(t.responseJSON.err_msg):l.default.error("An error occurred.")}})},importCurrentIntoHistory:function(){this.modal.disableButton("Import");var e=this.modal.$("input[name=history_name]").val(),t=this;if(""!==e)a.post(Galaxy.root+"api/histories",{name:e}).done(function(e){t.processImportToHistory(e.id)}).fail(function(e,t,i){l.default.error("An error occurred.")}).always(function(){t.modal.enableButton("Import")});else{var i=a(this.modal.$el).find("select[name=dataset_import_single] option:selected").val();this.processImportToHistory(i),this.modal.enableButton("Import")}},processImportToHistory:function(e){var t=new c.default.HistoryItem;t.url=t.urlRoot+e+"/contents",r.getJSON(Galaxy.root+"history/set_as_current?id="+e),t.save({content:this.id,source:"library"},{success:function(){Galaxy.modal.hide(),l.default.success("Dataset imported. Click this to start analyzing it.","",{onclick:function(){window.location=Galaxy.root}})},error:function(e,t){void 0!==t.responseJSON?l.default.error("Dataset not imported. "+t.responseJSON.err_msg):l.default.error("An error occured. Dataset not imported. Please try again.")}})},showPermissions:function(e){var t=this.templateDatasetPermissions(),i=this;this.options=s.extend(this.options,e),a(".tooltip").remove(),void 0!==this.options.fetched_permissions&&(0===this.options.fetched_permissions.access_dataset_roles.length?this.model.set({is_unrestricted:!0}):this.model.set({is_unrestricted:!1})),this.$el.html(t({item:this.model,is_admin:Galaxy.config.is_admin_user})),a.get(Galaxy.root+"api/libraries/datasets/"+i.id+"/permissions?scope=current").done(function(e){i.prepareSelectBoxes({fetched_permissions:e,is_admin:Galaxy.config.is_admin_user})}).fail(function(){l.default.error("An error occurred while attempting to fetch dataset permissions.")}),a("#center [data-toggle]").tooltip(),a("#center").css("overflow","auto")},_serializeRoles:function(e){for(var t=[],i=0;i0))return void l.default.warning("Library dataset name has to be at least 1 character long.");i.set("name",s),t=!0}var a=this.$el.find(".input_dataset_misc_info").val();void 0!==a&&a!==i.get("misc_info")&&(i.set("misc_info",a),t=!0);var r=this.select_genome.$el.select2("data").id;void 0!==r&&r!==i.get("genome_build")&&(i.set("genome_build",r),t=!0);var o=this.select_extension.$el.select2("data").id;void 0!==o&&o!==i.get("file_ext")&&(i.set("file_ext",o),t=!0);var n=this;t?i.save(null,{patch:!0,success:function(e){n.render(),l.default.success("Changes to library dataset saved.")},error:function(e,t){void 0!==t.responseJSON?l.default.error(t.responseJSON.err_msg):l.default.error("An error occured while attempting to update the library dataset.")}}):(n.render(),l.default.info("Nothing has changed."))},copyToClipboard:function(t){t.preventDefault();var i=e.history.location.href;-1!==i.lastIndexOf("/permissions")&&(i=i.substr(0,i.lastIndexOf("/permissions"))),window.prompt("Copy to clipboard: Ctrl+C, Enter",i)},makeDatasetPrivate:function(){var e=this;a.post(Galaxy.root+"api/libraries/datasets/"+e.id+"/permissions?action=make_private").done(function(t){e.model.set({is_unrestricted:!1}),e.showPermissions({fetched_permissions:t}),l.default.success("The dataset is now private to you.")}).fail(function(){l.default.error("An error occurred while attempting to make dataset private.")})},removeDatasetRestrictions:function(){var e=this;a.post(Galaxy.root+"api/libraries/datasets/"+e.id+"/permissions?action=remove_restrictions").done(function(t){e.model.set({is_unrestricted:!0}),e.showPermissions({fetched_permissions:t}),l.default.success("Access to this dataset is now unrestricted.")}).fail(function(){l.default.error("An error occurred while attempting to make dataset unrestricted.")})},_extractIds:function(e){for(var t=[],i=e.length-1;i>=0;i--)t.push(e[i].id);return t},savePermissions:function(e){var t=this,i=this._extractIds(this.accessSelectObject.$el.select2("data")),s=this._extractIds(this.manageSelectObject.$el.select2("data")),r=this._extractIds(this.modifySelectObject.$el.select2("data"));a.post(Galaxy.root+"api/libraries/datasets/"+t.id+"/permissions?action=set_permissions",{"access_ids[]":i,"manage_ids[]":s,"modify_ids[]":r}).done(function(e){t.showPermissions({fetched_permissions:e}),l.default.success("Permissions saved.")}).fail(function(){l.default.error("An error occurred while attempting to set dataset permissions.")})},fetchExtAndGenomes:function(){var e=this;0==this.list_genomes.length&&_.default.get({url:Galaxy.root+"api/datatypes?extension_only=False",success:function(t){for(var i in t)e.list_extensions.push({id:t[i].extension,text:t[i].extension,description:t[i].description,description_url:t[i].description_url});e.list_extensions.sort(function(e,t){return e.id>t.id?1:e.idt.id?1:e.id','
','",'",'<% if (item.get("can_user_modify")) { %>','","<% } %>",'<% if (item.get("can_user_manage")) { %>','/datasets/<%- item.id %>/permissions">','","","<% } %>","
",'",'<% if (item.get("is_unrestricted")) { %>',"
",'This dataset is unrestricted so everybody with the link can access it. Just share this page.',"
","<% } %>",'
','',"",'','',"",'<% if (item.get("file_ext")) { %>',"",'','',"","<% } %>",'<% if (item.get("genome_build")) { %>',"",'','',"","<% } %>",'<% if (item.get("file_size")) { %>',"",'','',"","<% } %>",'<% if (item.get("date_uploaded")) { %>',"",'','',"","<% } %>",'<% if (item.get("uploaded_by")) { %>',"",'','',"","<% } %>",'<% if (item.get("metadata_data_lines")) { %>',"",'','',"","<% } %>",'<% if (item.get("metadata_comment_lines")) { %>',"",'','',"","<% } %>",'<% if (item.get("metadata_columns")) { %>',"",'','',"","<% } %>",'<% if (item.get("metadata_column_types")) { %>',"",'','',"","<% } %>",'<% if (item.get("message")) { %>',"",'','',"","<% } %>",'<% if (item.get("misc_blurb")) { %>',"",'','',"","<% } %>",'<% if (item.get("misc_info")) { %>',"",'','',"","<% } %>",'<% if (item.get("tags")) { %>',"",'','',"","<% } %>",'<% if ( item.get("uuid") !== "ok" ) { %>',"",'','',"","<% } %>",'<% if ( item.get("state") !== "ok" ) { %>',"",'','',"","<% } %>","
">Name<%= _.escape(item.get("name")) %>
Data type<%= _.escape(item.get("file_ext")) %>
Genome build<%= _.escape(item.get("genome_build")) %>
Size<%= _.escape(item.get("file_size")) %>
Date uploaded (UTC)<%= _.escape(item.get("date_uploaded")) %>
Uploaded by<%= _.escape(item.get("uploaded_by")) %>
Data Lines<%= _.escape(item.get("metadata_data_lines")) %>
Comment Lines<%= _.escape(item.get("metadata_comment_lines")) %>
Number of Columns<%= _.escape(item.get("metadata_columns")) %>
Column Types<%= _.escape(item.get("metadata_column_types")) %>
Message<%= _.escape(item.get("message")) %>
Misc. blurb<%= _.escape(item.get("misc_blurb")) %>
Misc. info<%= _.escape(item.get("misc_info")) %>
Tags<%= _.escape(item.get("tags")) %>
UUID<%= _.escape(item.get("uuid")) %>
State<%= _.escape(item.get("state")) %>
",'<% if (item.get("job_stderr")) { %>',"

Job Standard Error

",'
','<%= _.escape(item.get("job_stderr")) %>',"
","<% } %>",'<% if (item.get("job_stdout")) { %>',"

Job Standard Output

",'
','<%= _.escape(item.get("job_stdout")) %>',"
","<% } %>","
",'
',"
","
",'<% if (item.get("has_versions")) { %>',"
","

Expired versions:

","
","<% } %>","
","
"].join(""))},templateVersion:function(){return s.template(['
','",'",'
This is an expired version of the library dataset: <%= _.escape(item.get("name")) %>
','
','',"",'','',"",'<% if (ldda.get("file_ext")) { %>',"",'','',"","<% } %>",'<% if (ldda.get("genome_build")) { %>',"",'','',"","<% } %>",'<% if (ldda.get("file_size")) { %>',"",'','',"","<% } %>",'<% if (ldda.get("date_uploaded")) { %>',"",'','',"","<% } %>",'<% if (ldda.get("uploaded_by")) { %>',"",'','',"","<% } %>",'<% if (ldda.get("metadata_data_lines")) { %>',"",'','',"","<% } %>",'<% if (ldda.get("metadata_comment_lines")) { %>',"",'','',"","<% } %>",'<% if (ldda.get("metadata_columns")) { %>',"",'','',"","<% } %>",'<% if (ldda.get("metadata_column_types")) { %>',"",'','',"","<% } %>",'<% if (ldda.get("message")) { %>',"",'','',"","<% } %>",'<% if (ldda.get("misc_blurb")) { %>',"",'','',"","<% } %>",'<% if (ldda.get("misc_info")) { %>',"",'','',"","<% } %>",'<% if (item.get("tags")) { %>',"",'','',"","<% } %>","
Name<%= _.escape(ldda.get("name")) %>
Data type<%= _.escape(ldda.get("file_ext")) %>
Genome build<%= _.escape(ldda.get("genome_build")) %>
Size<%= _.escape(ldda.get("file_size")) %>
Date uploaded (UTC)<%= _.escape(ldda.get("date_uploaded")) %>
Uploaded by<%= _.escape(ldda.get("uploaded_by")) %>
Data Lines<%= _.escape(ldda.get("metadata_data_lines")) %>
Comment Lines<%= _.escape(ldda.get("metadata_comment_lines")) %>
Number of Columns<%= _.escape(ldda.get("metadata_columns")) %>
Column Types<%= _.escape(ldda.get("metadata_column_types")) %>
Message<%= _.escape(ldda.get("message")) %>
Miscellaneous blurb<%= _.escape(ldda.get("misc_blurb")) %>
Miscellaneous information<%= _.escape(ldda.get("misc_info")) %>
Tags<%= _.escape(item.get("tags")) %>
","
",'
',"
","
","
","
"].join(""))},templateModifyDataset:function(){return s.template(['
','
','",'","
",'",'
','',"",'','',"","",'',"","","",'',"","","",'','',"","",'','',"","",'','',"",'','','',"",'','<% if (item.get("metadata_comment_lines") === "") { %>','',"<% } else { %>",'',"<% } %>","","",'','',"","",'','',"","",'','',"","",'','',"","",'','',"",'<% if (item.get("tags")) { %>',"",'','',"","<% } %>","
">Name">
Data type",'',"
Genome build",'',"
Size<%= _.escape(item.get("file_size")) %>
Date uploaded (UTC)<%= _.escape(item.get("date_uploaded")) %>
Uploaded by<%= _.escape(item.get("uploaded_by")) %>
Data Lines<%= _.escape(item.get("metadata_data_lines")) %>
Comment Lines<%= _.escape(item.get("metadata_comment_lines")) %>unknown
Number of Columns<%= _.escape(item.get("metadata_columns")) %>
Column Types<%= _.escape(item.get("metadata_column_types")) %>
Message<%= _.escape(item.get("message")) %>
Misc. blurb<%= _.escape(item.get("misc_blurb")) %>
Misc. information">
Tags<%= _.escape(item.get("tags")) %>
","
",'
',"
","
","
","
"].join(""))},templateDatasetPermissions:function(){return s.template(['
','",'",'

Dataset: <%= _.escape(item.get("name")) %>

','
',"<% if (is_admin) { %>","You are logged in as an administrator therefore you can manage any dataset on this Galaxy instance. Please make sure you understand the consequences.","<% } else { %>","You can assign any number of roles to any of the following permission types. However please read carefully the implications of such actions.","<% } %>","
",'
',"

Library-related permissions

","

Roles that can modify the library item

",'
','
User with any of these roles can modify name, metadata, and other information about this library item.
',"
","

Dataset-related permissions

",'
Changes made below will affect every library item that was created from this dataset and also every history this dataset is part of.
','<% if (!item.get("is_unrestricted")) { %>','

You can remove all access restrictions on this dataset.

',"<% } else { %>",'

You can make this dataset private to you.

',"<% } %>","

Roles that can access the dataset

",'
','
',"User has to have all these roles in order to access this dataset."," Users without access permission cannot have other permissions on this dataset."," If there are no access roles set on the dataset it is considered unrestricted.","
","

Roles that can manage permissions on the dataset

",'
','
',"User with any of these roles can manage permissions of this dataset. If you remove yourself you will lose the ability manage this dataset unless you are an admin.","
",'","
","
"].join(""))},templateBulkImportInModal:function(){return s.template(["
",'
',"Select history: ",'","
",'
',"or create new: ",'',"","
","
"].join(""))}});t.default={LibraryDatasetView:f}}).call(t,i(1),i(2),i(0),i(0))},297:function(e,t,i){"use strict";function s(e){new r.default.GalaxyApp(e)}Object.defineProperty(t,"__esModule",{value:!0}),t.App=s;var a=i(298),r=function(e){return e&&e.__esModule?e:{default:e}}(a);window.libraryApp=s},298:function(e,t,i){"use strict";(function(e,s){function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=i(3),o=(a(r),i(42)),n=a(o),l=i(8),d=a(l),c=i(156),h=(a(c),i(300)),_=a(h),p=i(302),u=a(p),f=i(304),g=a(f),m=i(305),b=a(m),y=i(224),v=a(y),w=i(306),x=a(w),k=i(307),j=a(k),C=e.Router.extend({initialize:function(){this.routesHit=0,e.history.on("route",function(){this.routesHit++},this),this.bind("route",this.trackPageview)},routes:{"":"libraries","page/:show_page":"libraries_page","library/:library_id/permissions":"library_permissions","folders/:folder_id/permissions":"folder_permissions","folders/:id":"folder_content","folders/:id/page/:show_page":"folder_page","folders/:folder_id/datasets/:dataset_id":"dataset_detail","folders/:folder_id/datasets/:dataset_id/permissions":"dataset_permissions","folders/:folder_id/datasets/:dataset_id/versions/:ldda_id":"dataset_version","folders/:folder_id/download/:format":"download","folders/:folder_id/import/:source":"import_datasets"},back:function(){this.routesHit>1?window.history.back():this.navigate("#",{trigger:!0,replace:!0})},trackPageview:function(){var t=e.history.getFragment();/^\//.test(t)||""==t||(t="/"+t),"undefined"!=typeof ga&&ga("send","pageview",Galaxy.root+"library/list"+t)}}),S=d.default.SessionStorageModel.extend({defaults:{with_deleted:!1,without_restricted:!1,sort_order:"asc",sort_by:"name",library_page_size:20,folder_page_size:15}}),G=e.View.extend({libraryToolbarView:null,libraryListView:null,library_router:null,libraryView:null,folderToolbarView:null,folderListView:null,datasetView:null,initialize:function(){window.Galaxy.config.ga_code&&(!function(e,t,i,s,a,r,o){e.GoogleAnalyticsObject=a,e[a]=e[a]||function(){(e[a].q=e[a].q||[]).push(arguments)},e[a].l=1*new Date,r=t.createElement(i),o=t.getElementsByTagName(i)[0],r.async=1,r.src="//www.google-analytics.com/analytics.js",o.parentNode.insertBefore(r,o)}(window,document,"script",0,"ga"),ga("create",window.Galaxy.config.ga_code,"auto"),ga("send","pageview")),Galaxy.libraries=this,this.preferences=new S({id:"global-lib-prefs"}),this.library_router=new C,this.library_router.on("route:libraries",function(){Galaxy.libraries.libraryToolbarView&&Galaxy.libraries.libraryToolbarView.$el.unbind("click"),Galaxy.libraries.libraryToolbarView=new g.default.LibraryToolbarView,Galaxy.libraries.libraryListView=new u.default.LibraryListView}),this.library_router.on("route:libraries_page",function(e){null===Galaxy.libraries.libraryToolbarView?(Galaxy.libraries.libraryToolbarView=new g.default.LibraryToolbarView,Galaxy.libraries.libraryListView=new u.default.LibraryListView({show_page:e})):Galaxy.libraries.libraryListView.render({show_page:e})}),this.library_router.on("route:folder_content",function(e){Galaxy.libraries.folderToolbarView&&Galaxy.libraries.folderToolbarView.$el.unbind("click"),Galaxy.libraries.folderToolbarView=new b.default.FolderToolbarView({id:e}),Galaxy.libraries.folderListView=new _.default.FolderListView({id:e})}),this.library_router.on("route:folder_page",function(e,t){null===Galaxy.libraries.folderToolbarView?(Galaxy.libraries.folderToolbarView=new b.default.FolderToolbarView({id:e}),Galaxy.libraries.folderListView=new _.default.FolderListView({id:e,show_page:t})):Galaxy.libraries.folderListView.render({id:e,show_page:parseInt(t)})}),this.library_router.on("route:download",function(e,t){0===s("#folder_list_body").find(":checked").length?(n.default.info("You must select at least one dataset to download"),Galaxy.libraries.library_router.navigate("folders/"+e,{trigger:!0,replace:!0})):(Galaxy.libraries.folderToolbarView.download(e,t),Galaxy.libraries.library_router.navigate("folders/"+e,{trigger:!1,replace:!0}))}),this.library_router.on("route:dataset_detail",function(e,t){Galaxy.libraries.datasetView&&Galaxy.libraries.datasetView.$el.unbind("click"),Galaxy.libraries.datasetView=new v.default.LibraryDatasetView({id:t,show_version:!1,show_permissions:!1})}),this.library_router.on("route:dataset_version",function(e,t,i){Galaxy.libraries.datasetView&&Galaxy.libraries.datasetView.$el.unbind("click"),Galaxy.libraries.datasetView=new v.default.LibraryDatasetView({id:t,ldda_id:i,show_version:!0})}),this.library_router.on("route:dataset_permissions",function(e,t){Galaxy.libraries.datasetView&&Galaxy.libraries.datasetView.$el.unbind("click"),Galaxy.libraries.datasetView=new v.default.LibraryDatasetView({id:t,show_permissions:!0})}),this.library_router.on("route:library_permissions",function(e){Galaxy.libraries.libraryView&&Galaxy.libraries.libraryView.$el.unbind("click"),Galaxy.libraries.libraryView=new x.default.LibraryView({id:e,show_permissions:!0})}),this.library_router.on("route:folder_permissions",function(e){Galaxy.libraries.folderView&&Galaxy.libraries.folderView.$el.unbind("click"),Galaxy.libraries.folderView=new j.default.FolderView({id:e,show_permissions:!0})}),this.library_router.on("route:import_datasets",function(e,t){Galaxy.libraries.folderToolbarView&&Galaxy.libraries.folderListView?Galaxy.libraries.folderToolbarView.showImportModal({source:t}):(Galaxy.libraries.folderToolbarView=new b.default.FolderToolbarView({id:e}),Galaxy.libraries.folderListView=new _.default.FolderListView({id:e}),Galaxy.libraries.folderToolbarView.showImportModal({source:t}))}),e.history.start({pushState:!1})}});t.default={GalaxyApp:G}}).call(t,i(1),i(0))},299:function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var s=function(e,t){return function(i,s){return i.get("type")===s.get("type")?i.has(e)||s.has(e)?i.has(e)?s.has(e)?i.get(e).toLowerCase()>s.get(e).toLowerCase()?"asc"===t?1:-1:s.get(e).toLowerCase()>i.get(e).toLowerCase()?"asc"===t?-1:1:0:-1:1:0:"folder"===i.get("type")?-1:1}},a=function(e,t){return function(i,s){return i.get(e).toLowerCase()>s.get(e).toLowerCase()?"asc"===t?1:-1:s.get(e).toLowerCase()>i.get(e).toLowerCase()?"asc"===t?-1:1:0}};t.default={generateFolderComparator:s,generateLibraryComparator:a}},300:function(e,t,i){"use strict";(function(e,s,a){function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var o=i(42),n=r(o),l=i(156),d=r(l),c=i(301),h=r(c),_=e.View.extend({el:"#folder_items_element",progress:0,progressStep:1,folderContainer:null,current_sort_order:"asc",current_sort_key:"name",events:{"click #select-all-checkboxes":"selectAll","click .dataset_row":"selectClickedRow","click .folder_row":"selectClickedRow","click .sort-folder-name":"sortColumnClicked","click .sort-folder-file_ext":"sortColumnClicked","click .sort-folder-description":"sortColumnClicked","click .sort-folder-state":"sortColumnClicked"},collection:null,defaults:{include_deleted:!1,page_count:null,show_page:null},initialize:function(e){this.options=s.defaults(this.options||{},this.defaults,e),this.modal=null,this.rowViews={},this.collection=new d.default.Folder,this.listenTo(this.collection,"add",this.renderOne),this.listenTo(this.collection,"remove",this.removeOne),this.listenTo(this.collection,"sort",this.rePaint),this.listenTo(this.collection,"reset",this.rePaint),this.fetchFolder()},fetchFolder:function(e){var e=e||{};this.options.include_deleted=e.include_deleted;var t=this;this.folderContainer=new d.default.FolderContainer({id:this.options.id}),this.folderContainer.url=this.folderContainer.attributes.urlRoot+this.options.id+"/contents",this.options.include_deleted&&(this.folderContainer.url=this.folderContainer.url+"?include_deleted=true"),this.folderContainer.fetch({success:function(e){t.folder_container=e,t.render()},error:function(e,t){void 0!==t.responseJSON?n.default.error(t.responseJSON.err_msg+" Click this to go back.","",{onclick:function(){Galaxy.libraries.library_router.back()}}):n.default.error("An error occurred. Click this to go back.","",{onclick:function(){Galaxy.libraries.library_router.back()}})}})},render:function(e){this.options=s.extend(this.options,e);var t=this.templateFolder();a(".tooltip").hide();var i,r=this.folderContainer.attributes.metadata.full_path;if(i=1===r.length?0:r[r.length-2][0],this.$el.html(t({path:this.folderContainer.attributes.metadata.full_path,parent_library_id:this.folderContainer.attributes.metadata.parent_library_id,id:this.options.id,upper_folder_id:i,order:this.current_sort_order})),this.options.dataset_id){var o=s.findWhere(self.rowViews,{id:this.options.dataset_id});o?o.showDatasetDetails():n.default.error("Requested dataset not found. Showing folder instead.")}else(null===this.options.show_page||this.options.show_page<1)&&(this.options.show_page=1),this.paginate();a("#center [data-toggle]").tooltip(),a("#center").css("overflow","auto")},paginate:function(e){this.options=s.extend(this.options,e),(null===this.options.show_page||this.options.show_page<1)&&(this.options.show_page=1),this.options.total_items_count=this.folder_container.get("folder").models.length,this.options.page_count=Math.ceil(this.options.total_items_count/Galaxy.libraries.preferences.get("folder_page_size"));var t=Galaxy.libraries.preferences.get("folder_page_size")*(this.options.show_page-1),i=null;i=this.folder_container.get("folder").models.slice(t,t+Galaxy.libraries.preferences.get("folder_page_size")),this.options.items_shown=i.length,Galaxy.libraries.preferences.get("folder_page_size")*this.options.show_page>this.options.total_items_count+Galaxy.libraries.preferences.get("folder_page_size")&&(i=[]),Galaxy.libraries.folderToolbarView.renderPaginator(this.options),this.collection.reset(i)},rePaint:function(e){this.options=s.extend(this.options,e),this.removeAllRows(),this.renderAll(),this.checkEmptiness()},addAll:function(e){s.each(e,function(e){Galaxy.libraries.folderListView.collection.add(e,{current_sort_order:!1})}),a("#center [data-toggle]").tooltip(),this.checkEmptiness(),this.postRender()},postRender:function(){var e=this.folderContainer.attributes.metadata;e.contains_file_or_folder=void 0!==this.collection.findWhere({type:"file"})||void 0!==this.collection.findWhere({type:"folder"}),Galaxy.libraries.folderToolbarView.configureElements(e)},renderAll:function(){var e=this;s.each(this.collection.models.reverse(),function(t){e.renderOne(t)}),this.postRender()},renderOne:function(e){this.options.contains_file_or_folder=!0,e.set("folder_id",this.id);var t=new h.default.FolderRowView({model:e});this.rowViews[e.get("id")]=t,this.$el.find("#first_folder_item").after(t.el)},removeOne:function(e){this.$el.find("tr").filter(function(){return a(this).data("id")&&a(this).data("id")===e.id}).remove()},removeAllRows:function(){a(".library-row").remove()},checkEmptiness:function(){0===this.$el.find(".dataset_row").length&&0===this.$el.find(".folder_row").length?this.$el.find(".empty-folder-message").show():this.$el.find(".empty-folder-message").hide()},sortColumnClicked:function(e){e.preventDefault(),this.current_sort_order="asc"===this.current_sort_order?"desc":"asc",this.current_sort_key=e.currentTarget.className.replace("sort-folder-",""),this.collection.sortFolder(this.current_sort_key,this.current_sort_order),this.renderSortIcon()},selectAll:function(e){var t=e.target.checked,i=this;a(":checkbox","#folder_list_body").each(function(){this.checked=t;var e=a(this).closest("tr");t?i.makeDarkRow(e):i.makeWhiteRow(e)})},selectClickedRow:function(e){var t,i,s="";t=a(e.target).closest("tr"),"input"===e.target.localName?(s=e.target,i="input"):"td"===e.target.localName&&(s=t.find(":checkbox")[0],i="td"),s.checked?"td"===i?(s.checked="",this.makeWhiteRow(t)):"input"===i&&this.makeDarkRow(t):"td"===i?(s.checked="selected",this.makeDarkRow(t)):"input"===i&&this.makeWhiteRow(t)},makeDarkRow:function(e){e.removeClass("light").addClass("dark"),e.find("a").removeClass("light").addClass("dark"),e.find(".fa-file-o").removeClass("fa-file-o").addClass("fa-file"),e.find(".fa-folder-o").removeClass("fa-folder-o").addClass("fa-folder")},makeWhiteRow:function(e){e.removeClass("dark").addClass("light"),e.find("a").removeClass("dark").addClass("light"),e.find(".fa-file").removeClass("fa-file").addClass("fa-file-o"),e.find(".fa-folder").removeClass("fa-folder").addClass("fa-folder-o")},renderSortIcon:function(){"asc"===this.current_sort_order?(a('[class*="sort-icon"]').removeClass("fa-sort-alpha-desc").removeClass("fa-sort-alpha-asc"),a(".sort-icon-"+this.current_sort_key).addClass("fa-sort-alpha-asc")):a(".sort-icon-"+this.current_sort_key).addClass("fa-sort-alpha-desc")},templateFolder:function(){return s.template(['",'',"",'','','','','','','','',' ',"",'','',"","","","","","","","","","","","
name description data type sizetime updated (UTC)state
",'" title="Go to parent folder" class="btn_open_folder btn btn-default btn-xs">..',"
",'"].join(""))}});t.default={FolderListView:_}}).call(t,i(1),i(2),i(0))},301:function(e,t,i){"use strict";(function(e,s,a){function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var o=i(42),n=r(o),l=i(156),d=r(l),c=i(224),h=r(c),_=e.View.extend({events:{"click .undelete_dataset_btn":"undeleteDataset","click .undelete_folder_btn":"undeleteFolder","click .edit_folder_btn":"startModifications","click .cancel_folder_btn":"cancelModifications","click .save_folder_btn":"saveModifications"},defaults:{type:null,visibility_config:{edit_folder_btn:!1,save_folder_btn:!1,cancel_folder_btn:!1,permission_folder_btn:!1},edit_mode:!1},initialize:function(e){this.options=s.defaults(e||{},this.defaults),this.render(this.options)},render:function(e){this.options=s.extend(this.options,e);var t=this.options.model,i=null;return"folder"===t.get("type")||"LibraryFolder"===t.get("model_class")?(this.options.type="folder",this.prepareButtons(t),i=t.get("deleted")?this.templateRowDeletedFolder():this.templateRowFolder()):"file"===t.get("type")||"LibraryDatasetDatasetAssociation"===t.get("model_class")||"LibraryDataset"===t.get("model_class")?(this.options.type="file",i=t.get("deleted")?this.templateRowDeletedFile():this.templateRowFile()):(Galaxy.emit.error("Unknown library item type found."),Galaxy.emit.error(t.get("type")||t.get("model_class"))),this.setElement(i({content_item:t,edit_mode:this.options.edit_mode,button_config:this.options.visibility_config})),this.$el.show(),this},prepareButtons:function(e){var t=this.options.visibility_config;!1===this.options.edit_mode?(t.save_folder_btn=!1,t.cancel_folder_btn=!1,!0===e.get("deleted")?(t.edit_folder_btn=!1,t.permission_folder_btn=!1):!1===e.get("deleted")&&(t.save_folder_btn=!1,t.cancel_folder_btn=!1,!0===e.get("can_modify")&&(t.edit_folder_btn=!0),!0===e.get("can_manage")&&(t.permission_folder_btn=!0))):!0===this.options.edit_mode&&(t.edit_folder_btn=!1,t.permission_folder_btn=!1,t.save_folder_btn=!0,t.cancel_folder_btn=!0),this.options.visibility_config=t},showDatasetDetails:function(){Galaxy.libraries.datasetView=new h.default.LibraryDatasetView({id:this.id})},undeleteDataset:function(e){a(".tooltip").hide();var t=this,i=a(e.target).closest("tr").data("id"),s=Galaxy.libraries.folderListView.collection.get(i);s.url=s.urlRoot+s.id+"?undelete=true",s.destroy({success:function(e,s){Galaxy.libraries.folderListView.collection.remove(i);var a=new d.default.Item(s);Galaxy.libraries.folderListView.collection.add(a),Galaxy.libraries.folderListView.collection.sortFolder("name","asc"),n.default.success("Dataset undeleted. Click this to see it.","",{onclick:function(){var e=t.model.get("folder_id");window.location=Galaxy.root+"library/list#folders/"+e+"/datasets/"+t.id}})},error:function(e,t){void 0!==t.responseJSON?n.default.error("Dataset was not undeleted. "+t.responseJSON.err_msg):n.default.error("An error occured! Dataset was not undeleted. Please try again.")}})},undeleteFolder:function(e){a(".tooltip").hide();var t=a(e.target).closest("tr").data("id"),i=Galaxy.libraries.folderListView.collection.get(t);i.url=i.urlRoot+i.id+"?undelete=true",i.destroy({success:function(e,i){Galaxy.libraries.folderListView.collection.remove(t);var s=new d.default.FolderAsModel(i);Galaxy.libraries.folderListView.collection.add(s),Galaxy.libraries.folderListView.collection.sortFolder("name","asc"),n.default.success("Folder undeleted.")},error:function(e,t){void 0!==t.responseJSON?n.default.error("Folder was not undeleted. "+t.responseJSON.err_msg):n.default.error("An error occured! Folder was not undeleted. Please try again.")}})},startModifications:function(){this.options.edit_mode=!0,this.repaint()},cancelModifications:function(){this.options.edit_mode=!1,this.repaint()},saveModifications:function(){var e=Galaxy.libraries.folderListView.collection.get(this.$el.data("id")),t=!1,i=this.$el.find(".input_folder_name").val();if(void 0!==i&&i!==e.get("name")){if(!(i.length>2))return void n.default.warning("Folder name has to be at least 3 characters long.");e.set("name",i),t=!0}var s=this.$el.find(".input_folder_description").val();if(void 0!==s&&s!==e.get("description")&&(e.set("description",s),t=!0),t){var a=this;e.save(null,{patch:!0,success:function(e){a.options.edit_mode=!1,a.repaint(e),n.default.success("Changes to folder saved.")},error:function(e,t){void 0!==t.responseJSON?n.default.error(t.responseJSON.err_msg):n.default.error("An error occured while attempting to update the folder.")}})}else this.options.edit_mode=!1,this.repaint(e),n.default.info("Nothing has changed.")},repaint:function(){a(".tooltip").hide();var e=this.$el;this.render(),e.replaceWith(this.$el),this.$el.find("[data-toggle]").tooltip()},templateRowFolder:function(){return s.template(['',"",'',"",'',"<% if(!edit_mode) { %>","",'<%- content_item.get("name") %>',"","",'<%- content_item.get("description") %>',"","<% } else if(edit_mode){ %>",'','',"<% } %>","folder","","",'<%= _.escape(content_item.get("update_time")) %>',"","","","<% if(edit_mode) { %>",'",'","<% } else if (!edit_mode){%>",'",'','","","<% } %>","",""].join(""))},templateRowFile:function(){return s.template(['',"",'',"",'','',"","",'/datasets/<%- content_item.id %>" class="library-dataset">','<%- content_item.get("name") %>',"","",'<%- content_item.get("message") %>','<%= _.escape(content_item.get("file_ext")) %>','<%= _.escape(content_item.get("file_size")) %>','<%= _.escape(content_item.get("update_time")) %>',"",'<% if ( content_item.get("state") !== "ok" ) { %>','<%= _.escape(content_item.get("state")) %>',"<% } %>","","",'<% if (content_item.get("is_unrestricted")) { %>','',"<% } %>",'<% if (content_item.get("is_private")) { %>','',"<% } %>",'<% if ((content_item.get("is_unrestricted") === false) && (content_item.get("is_private") === false)) { %>','',"<% } %>",'<% if (content_item.get("can_manage")) { %>','/datasets/<%- content_item.id %>/permissions">','","","<% } %>","",""].join(""))},templateRowDeletedFile:function(){return s.template(['',"",'',"","",'','<%- content_item.get("name") %>',"","",'<%- content_item.get("message") %>',"","",'<%= _.escape(content_item.get("file_ext")) %>',"","",'<%= _.escape(content_item.get("file_size")) %>',"","",'<%= _.escape(content_item.get("update_time")) %>',"","",'<% if ( content_item.get("state") !== "ok" ) { %>','<%= _.escape(content_item.get("state")) %>',"<% } %>","","",'','","",""].join(""))},templateRowDeletedFolder:function(){return s.template(['',"",'',"","",'','<%- content_item.get("name") %>',"","",'<%- content_item.get("description") %>',"","","folder","","","",'<%= _.escape(content_item.get("update_time")) %>',"","","",'','","",""].join(""))}});t.default={FolderRowView:_}}).call(t,i(1),i(2),i(0))},302:function(e,t,i){"use strict";(function(e,s){function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=i(42),o=a(r),n=i(156),l=a(n),d=i(303),c=a(d),h=i(2),_=a(h),p=e.View.extend({el:"#libraries_element",events:{"click .sort-libraries-link":"sort_clicked"},defaults:{page_count:null,show_page:null,all_fetched:!1},initialize:function(e){this.options=_.default.defaults(this.options||{},e,this.defaults);var t=this;this.modal=null,this.collection=new l.default.Libraries,this.collection.url=this.collection.urlRoot+"?deleted=false",this.collection.fetch({success:function(){t.render()},error:function(e,t){void 0!==t.responseJSON?o.default.error(t.responseJSON.err_msg):o.default.error("An error occurred.")}})},render:function(e){this.options=_.default.extend(this.options,e),this.setElement("#libraries_element");var t=this.templateLibraryList(),i=null,a=null,r=function(e){return!0===e.get("public")};if(s(".tooltip").hide(),void 0!==e&&(a=void 0!==e.models?e.models:null),null!==this.collection&&null===a)this.sortLibraries(),i=Galaxy.libraries.preferences.get("with_deleted")?this.collection.models:this.collection.where({deleted:!1}),Galaxy.libraries.preferences.get("without_restricted")&&(i=_.default.filter(i,r));else if(null!==a){if(Galaxy.libraries.preferences.get("with_deleted"))i=a;else{var o=function(e){return!1===e.get("deleted")};i=_.default.filter(a,o)}Galaxy.libraries.preferences.get("without_restricted")&&(i=_.default.filter(i,r))}else i=[];(null===this.options.show_page||this.options.show_page<1)&&(this.options.show_page=1),this.options.total_libraries_count=i.length;var n=Galaxy.libraries.preferences.get("library_page_size")*(this.options.show_page-1);this.options.page_count=Math.ceil(this.options.total_libraries_count/Galaxy.libraries.preferences.get("library_page_size")),this.options.total_libraries_count>0&&nthis.options.total_libraries_count+Galaxy.libraries.preferences.get("library_page_size")&&(i=[]),this.$el.html(t({length:1,order:Galaxy.libraries.preferences.get("sort_order"),search_term:Galaxy.libraries.libraryToolbarView.options.search_term})),Galaxy.libraries.libraryToolbarView.renderPaginator(this.options),this.renderRows(i)):(this.$el.html(t({length:0,order:Galaxy.libraries.preferences.get("sort_order"),search_term:Galaxy.libraries.libraryToolbarView.options.search_term})),Galaxy.libraries.libraryToolbarView.renderPaginator(this.options)),s("#center [data-toggle]").tooltip(),s("#center").css("overflow","auto")},fetchDeleted:function(){if(this.options.all_fetched)this.render();else{var e=this;this.collection.url=this.collection.urlRoot+"?deleted=true",this.collection.fetch({remove:!1,success:function(){e.options.all_fetched=!0,e.render()},error:function(e,t){void 0!==t.responseJSON?o.default.error(t.responseJSON.err_msg):o.default.error("An error occurred.")}})}},renderRows:function(e){for(var t=0;t',"<% if(length === 0) { %>","<% if(search_term.length > 0) { %>","
","There are no libraries matching your search. Try different keyword.","
","<% } else{ %>","
","There are no libraries visible to you here. If you expected some to show up please consult the",' library security wikipage',' or visit the Galaxy support site.',"
","<% }%>","<% } else{ %>",'',"",'",'',' ','',"",'',"","
','',"name","",'',"descriptionsynopsis
","<% }%>","
"].join(""))}});t.default={LibraryListView:p}}).call(t,i(1),i(0))},303:function(e,t,i){"use strict";(function(e,s,a){Object.defineProperty(t,"__esModule",{value:!0});var r=i(42),o=function(e){return e&&e.__esModule?e:{default:e}}(r),n=e.View.extend({events:{"click .edit_library_btn":"edit_button_clicked","click .cancel_library_btn":"cancel_library_modification","click .save_library_btn":"save_library_modification","click .delete_library_btn":"delete_library","click .undelete_library_btn":"undelete_library"},edit_mode:!1,element_visibility_config:{upload_library_btn:!1,edit_library_btn:!1,permission_library_btn:!1,save_library_btn:!1,cancel_library_btn:!1,delete_library_btn:!1,undelete_library_btn:!1},initialize:function(e){this.render(e)},render:function(e){void 0===e&&(e=Galaxy.libraries.libraryListView.collection.get(this.$el.data("id"))),this.prepareButtons(e);var t=this.templateRow();return this.setElement(t({library:e,button_config:this.element_visibility_config,edit_mode:this.edit_mode})),this.$el.show(),this},repaint:function(e){s(".tooltip").hide();var t=this.$el;this.render(),t.replaceWith(this.$el),this.$el.find("[data-toggle]").tooltip()},prepareButtons:function(e){var t=this.element_visibility_config;!1===this.edit_mode?(t.save_library_btn=!1,t.cancel_library_btn=!1,t.delete_library_btn=!1,!0===e.get("deleted")?(t.undelete_library_btn=!0,t.upload_library_btn=!1,t.edit_library_btn=!1,t.permission_library_btn=!1):!1===e.get("deleted")&&(t.save_library_btn=!1,t.cancel_library_btn=!1,t.undelete_library_btn=!1,!0===e.get("can_user_add")&&(t.upload_library_btn=!0),!0===e.get("can_user_modify")&&(t.edit_library_btn=!0),!0===e.get("can_user_manage")&&(t.permission_library_btn=!0))):!0===this.edit_mode&&(t.upload_library_btn=!1,t.edit_library_btn=!1,t.permission_library_btn=!1,t.save_library_btn=!0,t.cancel_library_btn=!0,t.delete_library_btn=!0,t.undelete_library_btn=!1),this.element_visibility_config=t},edit_button_clicked:function(){this.edit_mode=!0,this.repaint()},cancel_library_modification:function(){this.edit_mode=!1,this.repaint()},save_library_modification:function(){var e=Galaxy.libraries.libraryListView.collection.get(this.$el.data("id")),t=!1,i=this.$el.find(".input_library_name").val();if(void 0!==i&&i!==e.get("name")){if(!(i.length>2))return void o.default.warning("Library name has to be at least 3 characters long.");e.set("name",i),t=!0}var s=this.$el.find(".input_library_description").val();void 0!==s&&s!==e.get("description")&&(e.set("description",s),t=!0);var a=this.$el.find(".input_library_synopsis").val();if(void 0!==a&&a!==e.get("synopsis")&&(e.set("synopsis",a),t=!0),t){var r=this;e.save(null,{patch:!0,success:function(e){r.edit_mode=!1,r.repaint(e),o.default.success("Changes to library saved.")},error:function(e,t){void 0!==t.responseJSON?o.default.error(t.responseJSON.err_msg):o.default.error("An error occured while attempting to update the library.")}})}else this.edit_mode=!1,this.repaint(e),o.default.info("Nothing has changed.")},delete_library:function(){var e=Galaxy.libraries.libraryListView.collection.get(this.$el.data("id")),t=this;e.destroy({success:function(e){e.set("deleted",!0),Galaxy.libraries.libraryListView.collection.add(e),t.edit_mode=!1,!1===Galaxy.libraries.preferences.get("with_deleted")?(s(".tooltip").hide(),t.repaint(e),t.$el.remove()):!0===Galaxy.libraries.preferences.get("with_deleted")&&t.repaint(e),o.default.success("Library has been marked deleted.")},error:function(e,t){void 0!==t.responseJSON?o.default.error(t.responseJSON.err_msg):o.default.error("An error occured during deleting the library.")}})},undelete_library:function(){var e=Galaxy.libraries.libraryListView.collection.get(this.$el.data("id")),t=this;e.url=e.urlRoot+e.id+"?undelete=true",e.destroy({success:function(e){e.set("deleted",!1),Galaxy.libraries.libraryListView.collection.add(e),t.edit_mode=!1,t.repaint(e),o.default.success("Library has been undeleted.")},error:function(e,t){void 0!==t.responseJSON?o.default.error(t.responseJSON.err_msg):o.default.error("An error occured while undeleting the library.")}})},templateRow:function(){return a.template(['" style="display:none;" data-id="<%- library.get("id") %>">',"<% if(!edit_mode) { %>",'<% if(library.get("deleted")) { %>','<%- library.get("name") %>',"<% } else { %>",'"><%- library.get("name") %>',"<% } %>",'<% if(library.get("description")) { %>','<% if( (library.get("description")).length> 80 ) { %>','"><%= _.escape(library.get("description")).substring(0, 80) + "..." %>',"<% } else { %>",'<%= _.escape(library.get("description"))%>',"<% } %>","<% } else { %>","","<% } %>",'<% if(library.get("synopsis")) { %>','<% if( (library.get("synopsis")).length> 120 ) { %>','"><%= _.escape(library.get("synopsis")).substring(0, 120) + "..." %>',"<% } else { %>",'<%= _.escape(library.get("synopsis"))%>',"<% } %>","<% } else { %>","","<% } %>","<% } else if(edit_mode){ %>",'','','',"<% } %>",'','<% if( (library.get("public")) && (library.get("deleted") === false) ) { %>',' ',"<% }%>",'','/permissions">','','','','" class="fa fa-ban fa-lg">','',"",""].join(""))}});t.default={LibraryRowView:n}}).call(t,i(1),i(0),i(2))},304:function(e,t,i){"use strict";(function(e,s,a){function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var o=i(42),n=r(o),l=i(156),d=r(l),c=e.View.extend({el:"#center",defaults:{search_term:""},events:{"click #create_new_library_btn":"createLibraryFromModal","click #include_deleted_chk":"includeDeletedChecked","click #exclude_restricted_chk":"excludeRestrictedChecked","click .page_size_prompt":"showPageSizePrompt","keyup .library-search-input":"searchLibraries"},initialize:function(e){this.options=s.defaults(this.options||{},e,this.defaults),this.render()},render:function(){var e=this.templateToolBar(),t=!1,i=!0;Galaxy.user&&(t=Galaxy.user.isAdmin(),i=Galaxy.user.isAnonymous()),this.$el.html(e({admin_user:t,anon_user:i})),t&&(this.$el.find("#include_deleted_chk")[0].checked=Galaxy.libraries.preferences.get("with_deleted"),this.$el.find("#exclude_restricted_chk")[0].checked=Galaxy.libraries.preferences.get("without_restricted"))},renderPaginator:function(e){this.options=s.extend(this.options,e);var t=this.templatePaginator();this.$el.find("#library_paginator").html(t({show_page:parseInt(this.options.show_page),page_count:parseInt(this.options.page_count),total_libraries_count:this.options.total_libraries_count,libraries_shown:this.options.libraries_shown,library_page_size:Galaxy.libraries.preferences.get("library_page_size")}))},createLibraryFromModal:function(e){e.preventDefault(),e.stopPropagation();var t=this;this.modal=Galaxy.modal,this.modal.show({closing_events:!0,title:"Create New Library",body:this.templateNewLibraryInModal(),buttons:{Create:function(){t.createNewLibrary()},Close:function(){t.modal.hide()}}})},createNewLibrary:function(){var e=this.serializeNewLibrary();if(this.validateNewLibrary(e)){var t=new d.default.Library,i=this;t.save(e,{success:function(e){Galaxy.libraries.libraryListView.collection.add(e),i.modal.hide(),i.clearLibraryModal(),Galaxy.libraries.libraryListView.render(),n.default.success("Library created.")},error:function(e,t){void 0!==t.responseJSON?n.default.error(t.responseJSON.err_msg):n.default.error("An error occured.")}})}else n.default.error("Library's name is missing.");return!1},showPageSizePrompt:function(e){e.preventDefault();var t=prompt("How many libraries per page do you want to see?",Galaxy.libraries.preferences.get("library_page_size"));null!=t&&t==parseInt(t)&&(Galaxy.libraries.preferences.set({library_page_size:parseInt(t)}),Galaxy.libraries.libraryListView.render({show_page:1}))},clearLibraryModal:function(){a("input[name='Name']").val(""),a("input[name='Description']").val(""),a("input[name='Synopsis']").val("")},serializeNewLibrary:function(){return{name:a("input[name='Name']").val(),description:a("input[name='Description']").val(),synopsis:a("input[name='Synopsis']").val()}},validateNewLibrary:function(e){return""!==e.name},includeDeletedChecked:function(e){e.target.checked?(Galaxy.libraries.preferences.set({with_deleted:!0}),Galaxy.libraries.libraryListView.fetchDeleted()):(Galaxy.libraries.preferences.set({with_deleted:!1}),Galaxy.libraries.libraryListView.render())},excludeRestrictedChecked:function(e){e.target.checked?Galaxy.libraries.preferences.set({without_restricted:!0}):Galaxy.libraries.preferences.set({without_restricted:!1}),Galaxy.libraries.libraryListView.render()},searchLibraries:function(e){var t=a(".library-search-input").val();this.options.search_term=t,Galaxy.libraries.libraryListView.searchLibraries(t)},templateToolBar:function(){return s.template(['
','
','
','
','DATA LIBRARIES','',"",'
','',"
","<% if(admin_user === true) { %>",'
',"","","
",'','',"","<% } %>",'','','',"","","
","
","
",'
',"
","
"].join(""))},templatePaginator:function(){return s.template(['","",' <%- libraries_shown %> libraries shown (change)',"",""," <%- total_libraries_count %> total",""].join(""))},templateNewLibraryInModal:function(){return s.template(['
',"
",'','','',"
","
"].join(""))}});t.default={LibraryToolbarView:c}}).call(t,i(1),i(2),i(0))},305:function(e,t,i){"use strict";(function(e,s,a,r){function o(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var n=i(3),l=o(n),d=i(42),c=o(d),h=i(156),_=o(h),p=i(139),u=o(p);i(198);var f=e.View.extend({el:"#center",events:{"click #toolbtn_create_folder":"createFolderFromModal","click #toolbtn_bulk_import":"modalBulkImport","click #include_deleted_datasets_chk":"checkIncludeDeleted","click #toolbtn_bulk_delete":"deleteSelectedItems","click .toolbtn-show-locinfo":"showLocInfo","click .page_size_prompt":"showPageSizePrompt"},defaults:{can_add_library_item:!1,contains_file_or_folder:!1,chain_call_control:{total_number:0,failed_number:0},disabled_jstree_element:"folders"},modal:null,jstree:null,histories:null,select_genome:null,select_extension:null,list_extensions:[],auto:{id:"auto",text:"Auto-detect",description:"This system will try to detect the file type automatically. If your file is not detected properly as one of the known formats, it most likely means that it has some format problems (e.g., different number of columns on different rows). You can still coerce the system to set your data to the format you think it should be. You can also upload compressed files, which will automatically be decompressed."},list_genomes:[],initialize:function(e){this.options=s.defaults(e||{},this.defaults),this.fetchExtAndGenomes(),this.render()},render:function(e){this.options=s.extend(this.options,e);var t=this.templateToolBar(),i={id:this.options.id,is_admin:!1,is_anonym:!0,mutiple_add_dataset_options:!1};Galaxy.user&&(i.is_admin=Galaxy.user.isAdmin(),i.is_anonym=Galaxy.user.isAnonymous(),null===Galaxy.config.user_library_import_dir&&!1===Galaxy.config.allow_library_path_paste&&null===Galaxy.config.library_import_dir||(i.mutiple_add_dataset_options=!0)),this.$el.html(t(i))},renderPaginator:function(e){this.options=s.extend(this.options,e);var t=this.templatePaginator();a("body").find(".folder-paginator").html(t({id:this.options.id,show_page:parseInt(this.options.show_page),page_count:parseInt(this.options.page_count),total_items_count:this.options.total_items_count,items_shown:this.options.items_shown,folder_page_size:Galaxy.libraries.preferences.get("folder_page_size")}))},configureElements:function(e){this.options=s.extend(this.options,e),!0===this.options.can_add_library_item?a(".add-library-items").show():a(".add-library-items").hide(),!0===this.options.contains_file_or_folder&&Galaxy.user?Galaxy.user.isAnonymous()?(a(".dataset-manipulation").show(),a(".logged-dataset-manipulation").hide()):(a(".logged-dataset-manipulation").show(),a(".dataset-manipulation").show()):(a(".logged-dataset-manipulation").hide(),a(".dataset-manipulation").hide()),this.$el.find("[data-toggle]").tooltip()},createFolderFromModal:function(e){e.preventDefault(),e.stopPropagation();var t=this,i=this.templateNewFolderInModal();this.modal=Galaxy.modal,this.modal.show({closing_events:!0,title:"Create New Folder",body:i(),buttons:{Create:function(){t.create_new_folder_event()},Close:function(){Galaxy.modal.hide()}}})},create_new_folder_event:function(){var t=this.serialize_new_folder();if(this.validate_new_folder(t)){var i,s=new _.default.FolderAsModel,a=e.history.fragment.split("/");i=a.indexOf("page")>-1?a[a.length-3]:a[a.length-1],s.url=s.urlRoot+i,s.save(t,{success:function(e){Galaxy.modal.hide(),c.default.success("Folder created."),e.set({type:"folder"}),Galaxy.libraries.folderListView.collection.add(e)},error:function(e,t){Galaxy.modal.hide(),void 0!==t.responseJSON?c.default.error(t.responseJSON.err_msg):c.default.error("An error occurred.")}})}else c.default.error("Folder's name is missing.");return!1},serialize_new_folder:function(){return{name:a("input[name='Name']").val(),description:a("input[name='Description']").val()}},validate_new_folder:function(e){return""!==e.name},modalBulkImport:function(){if(0===this.findCheckedRows().length)c.default.info("You must select some datasets first.");else{var e=this;this.histories=new _.default.GalaxyHistories,this.histories.fetch().done(function(){var t=e.templateBulkImportInModal();e.modal=Galaxy.modal,e.modal.show({closing_events:!0,title:"Import into History",body:t({histories:e.histories.models}),buttons:{Import:function(){e.importAllIntoHistory()},Close:function(){Galaxy.modal.hide()}}})}).fail(function(e,t){void 0!==t.responseJSON?c.default.error(t.responseJSON.err_msg):c.default.error("An error occurred.")})}},importAllIntoHistory:function(){this.modal.disableButton("Import");var e=this.modal.$("input[name=history_name]").val(),t=this;if(""!==e)a.post(Galaxy.root+"api/histories",{name:e}).done(function(e){t.options.last_used_history_id=e.id,t.processImportToHistory(e.id,e.name)}).fail(function(e,t,i){c.default.error("An error occurred.")}).always(function(){t.modal.enableButton("Import")});else{var i=a("select[name=dataset_import_bulk] option:selected").val();this.options.last_used_history_id=i;var s=a("select[name=dataset_import_bulk] option:selected").text();this.processImportToHistory(i,s),this.modal.enableButton("Import")}},processImportToHistory:function(e,t){var i=[],s=[];this.findCheckedRows().each(function(){var e=a(this).closest("tr").data("id");"F"==e.substring(0,1)?s.push(e):i.push(e)});for(var o=[],n=i.length-1;n>=0;n--){var l=i[n],d=new _.default.HistoryItem;d.url=d.urlRoot+e+"/contents",d.content=l,d.source="library",o.push(d)}for(var n=s.length-1;n>=0;n--){var c=s[n],d=new _.default.HistoryItem;d.url=d.urlRoot+e+"/contents",d.content=c,d.source="library_folder",o.push(d)}this.initChainCallControl({length:o.length,action:"to_history",history_name:t}),r.getJSON(Galaxy.root+"history/set_as_current?id="+e),this.chainCallImportingIntoHistory(o,t)},updateProgress:function(){this.progress+=this.progressStep,a(".progress-bar-import").width(Math.round(this.progress)+"%");var e=Math.round(this.progress)+"% Complete";a(".completion_span").text(e)},download:function(e,t){var i=[],s=[];this.findCheckedRows().each(function(){var e=a(this).closest("tr").data("id");"F"==e.substring(0,1)?s.push(e):i.push(e)});var r=Galaxy.root+"api/libraries/datasets/download/"+t,o={ld_ids:i,folder_ids:s};this.processDownload(r,o,"get")},processDownload:function(e,t,i){if(e&&t){t="string"==typeof t?t:a.param(t);var s="";a.each(t.split("&"),function(){var e=this.split("=");s+=''}),a('
'+s+"
").appendTo("body").submit().remove(),c.default.info("Your download will begin soon.")}else c.default.error("An error occurred.")},addFilesFromHistoryModal:function(){this.histories=new _.default.GalaxyHistories;var e=this;this.histories.fetch().done(function(){e.modal=Galaxy.modal;var t=e.templateAddFilesFromHistory();e.modal.show({closing_events:!0,title:"Adding datasets from your history",body:t({histories:e.histories.models}),buttons:{Add:function(){e.addAllDatasetsFromHistory()},Close:function(){Galaxy.modal.hide()}},closing_callback:function(){Galaxy.libraries.library_router.navigate("folders/"+e.id,{trigger:!0})}}),e.fetchAndDisplayHistoryContents(e.histories.models[0].id),a("#dataset_add_bulk").change(function(t){e.fetchAndDisplayHistoryContents(t.target.value)})}).fail(function(e,t){void 0!==t.responseJSON?c.default.error(t.responseJSON.err_msg):c.default.error("An error occurred.")})},importFilesFromPathModal:function(){var e=this;this.modal=Galaxy.modal;var t=this.templateImportPathModal();this.modal.show({closing_events:!0,title:"Please enter paths to import",body:t({}),buttons:{Import:function(){e.importFromPathsClicked(e)},Close:function(){Galaxy.modal.hide()}},closing_callback:function(){Galaxy.libraries.library_router.navigate("folders/"+e.id,{trigger:!0})}}),this.renderSelectBoxes()},fetchExtAndGenomes:function(){var e=this;l.default.get({url:Galaxy.root+"api/datatypes?extension_only=False",success:function(t){e.list_extensions=[];for(var i in t)e.list_extensions.push({id:t[i].extension,text:t[i].extension,description:t[i].description,description_url:t[i].description_url});e.list_extensions.sort(function(e,t){return e.id>t.id?1:e.idt.id?1:e.id=0;h--){var _=l[h].trim();0!==_.length&&d.push(_)}this.initChainCallControl({length:d.length,action:"adding_datasets"}),this.chainCallImportingFolders({paths:d,preserve_dirs:e,link_data:t,space_to_tab:i,to_posix_lines:s,source:"admin_path",file_type:o,tag_using_filenames:r,dbkey:n})}else c.default.info("Please enter a path relative to Galaxy root.")},initChainCallControl:function(e){var t;switch(e.action){case"adding_datasets":t=this.templateAddingDatasetsProgressBar(),this.modal.$el.find(".modal-body").html(t({folder_name:this.options.folder_name}));break;case"deleting_datasets":t=this.templateDeletingItemsProgressBar(),this.modal.$el.find(".modal-body").html(t());break;case"to_history":t=this.templateImportIntoHistoryProgressBar(),this.modal.$el.find(".modal-body").html(t({history_name:e.history_name}));break;default:Galaxy.emit.error("Wrong action specified.","datalibs")}this.progress=0,this.progressStep=100/e.length,this.options.chain_call_control.total_number=e.length,this.options.chain_call_control.failed_number=0},importFromJstreePath:function(e,t){var i=a("#jstree_browser").jstree().get_selected(!0),r=s.filter(i,function(e){return 0==e.state.disabled}),o=this.modal.$el.find(".preserve-checkbox").is(":checked"),n=this.modal.$el.find(".link-checkbox").is(":checked"),l=this.modal.$el.find(".spacetab-checkbox").is(":checked"),d=this.modal.$el.find(".posix-checkbox").is(":checked"),h=this.select_extension.value(),_=this.select_genome.value(),p=this.modal.$el.find(".tag-files").is(":checked"),u=r[0].type,f=[];if(r.length<1)c.default.info("Please select some items first.");else{this.modal.disableButton("Import");for(var g=r.length-1;g>=0;g--)void 0!==r[g].li_attr.full_path&&f.push(r[g].li_attr.full_path);if(this.initChainCallControl({length:f.length,action:"adding_datasets"}),"folder"===u){var m=t.source+"_folder";this.chainCallImportingFolders({paths:f,preserve_dirs:o,link_data:n,space_to_tab:l,to_posix_lines:d,source:m,file_type:h,dbkey:_,tag_using_filenames:p})}else if("file"===u){var m=t.source+"_file";this.chainCallImportingUserdirFiles({paths:f,file_type:h,dbkey:_,link_data:n,space_to_tab:l,to_posix_lines:d,source:m,tag_using_filenames:p})}}},fetchAndDisplayHistoryContents:function(e){var t=new _.default.HistoryContents({id:e}),i=this;t.fetch({success:function(t){var s=i.templateHistoryContents();i.histories.get(e).set({contents:t}),i.modal.$el.find("#selected_history_content").html(s({history_contents:t.models.reverse()})),i.modal.$el.find(".history-import-select-all").bind("click",function(){a("#selected_history_content [type=checkbox]").prop("checked",!0)}),i.modal.$el.find(".history-import-unselect-all").bind("click",function(){a("#selected_history_content [type=checkbox]").prop("checked",!1)})},error:function(e,t){void 0!==t.responseJSON?c.default.error(t.responseJSON.err_msg):c.default.error("An error occurred.")}})},addAllDatasetsFromHistory:function(){var e=this.modal.$el.find("#selected_history_content").find(":checked"),t=[],i=[],s=[];if(e.length<1)c.default.info("You must select some datasets first.");else{this.modal.disableButton("Add"),e.each(function(){var e=a(this).closest("li").data("id");if(e){var s=a(this).closest("li").data("name");t.push(e),i.push(s)}});for(var r=t.length-1;r>=0;r--){var o=t[r],n=new _.default.Item;n.url=Galaxy.root+"api/folders/"+this.options.id+"/contents","collection"===i[r]?n.set({from_hdca_id:o}):n.set({from_hda_id:o}),s.push(n)}this.initChainCallControl({length:s.length,action:"adding_datasets"}),this.chainCallAddingHdas(s)}},chainCallImportingIntoHistory:function(e,t){var i=this,s=e.pop();if(void 0===s)return 0===this.options.chain_call_control.failed_number?c.default.success("Selected datasets imported into history. Click this to start analyzing it.","",{onclick:function(){window.location=Galaxy.root}}):this.options.chain_call_control.failed_number===this.options.chain_call_control.total_number?c.default.error("There was an error and no datasets were imported into history."):this.options.chain_call_control.failed_number=0;n--){var l=new _.default.Item({id:e[n]});o.push(l)}for(var n=t.length-1;n>=0;n--){var d=new _.default.FolderAsModel({id:t[n]});o.push(d)}this.options.chain_call_control.total_number=r,this.chainCallDeletingItems(o)}},showLocInfo:function(){var e=null,t=this;null!==Galaxy.libraries.libraryListView?(e=Galaxy.libraries.libraryListView.collection.get(this.options.parent_library_id),this.showLocInfoModal(e)):(e=new _.default.Library({id:this.options.parent_library_id}),e.fetch({success:function(){t.showLocInfoModal(e)},error:function(e,t){void 0!==t.responseJSON?c.default.error(t.responseJSON.err_msg):c.default.error("An error occurred.")}}))},showLocInfoModal:function(e){var t=this,i=this.templateLocInfoInModal();this.modal=Galaxy.modal,this.modal.show({closing_events:!0,title:"Location Details",body:i({library:e,options:t.options}),buttons:{Close:function(){Galaxy.modal.hide()}}})},showImportModal:function(e){switch(e.source){case"history":this.addFilesFromHistoryModal();break;case"importdir":this.importFilesFromGalaxyFolderModal({source:"importdir"});break;case"path":this.importFilesFromPathModal();break;case"userdir":this.importFilesFromGalaxyFolderModal({source:"userdir"});break;default:Galaxy.libraries.library_router.back(),c.default.error("Invalid import source.")}},showPageSizePrompt:function(e){e.preventDefault();var t=prompt("How many items per page do you want to see?",Galaxy.libraries.preferences.get("folder_page_size"));null!=t&&t==parseInt(t)&&(Galaxy.libraries.preferences.set({folder_page_size:parseInt(t)}),Galaxy.libraries.folderListView.render({id:this.options.id,show_page:1}))},findCheckedRows:function(){return a("#folder_list_body").find(":checked")},templateToolBar:function(){return s.template(['
','
','
',"DATA LIBRARIES",'','",'","<% if(mutiple_add_dataset_options) { %>",'","<% } else { %>",'","<% } %>",'",'",'','",'','','","","","
","",'
',"
",'
',"
"].join(""))},templateLocInfoInModal:function(){return s.template(["
",'',"",'',"","","","","",'',"",'<% if(library.get("description") !== "") { %>',"","",'',"","<% } %>",'<% if(library.get("synopsis") !== "") { %>',"","",'',"","<% } %>",'<% if(library.get("create_time_pretty") !== "") { %>',"","",'',"","<% } %>","","",'',"","","
library
name<%- library.get("name") %>
description<%- library.get("description") %>
synopsis<%- library.get("synopsis") %>
created"><%- library.get("create_time_pretty") %>
id<%- library.get("id") %>
",'',"",'',"","","","","","","",'<% if(options.folder_description !== "") { %>',"","","","","<% } %>","","","","","","
folder
name<%- options.folder_name %>
description<%- options.folder_description %>
id<%- options.id %>
","
"].join(""))},templateNewFolderInModal:function(){return s.template(['
',"
",'','',"
","
"].join(""))},templateBulkImportInModal:function(){return s.template(["
",'
',"Select history: ",'","
",'
',"or create new: ",'',"","
","
"].join(""))},templateImportIntoHistoryProgressBar:function(){return s.template(['
',"Importing selected items to history <%= _.escape(history_name) %>","
",'
','
','0% Complete',"
","
"].join(""))},templateAddingDatasetsProgressBar:function(){return s.template(['
',"Adding selected datasets to library folder <%= _.escape(folder_name) %>","
",'
','
','0% Complete',"
","
"].join(""))},templateDeletingItemsProgressBar:function(){return s.template(['
',"
",'
','
','0% Complete',"
","
"].join(""))},templateBrowserModal:function(){return s.template(['
','
All files you select will be imported into the current folder ignoring their folder structure.
','','
','",'","
",'
','",'",'",'","
",'",'","
",'
',"
","
","

You can set extension type and genome for all imported datasets at once:

","
",'Type: ','Genome: ',"
","
","
",'","
","
"].join(""))},templateImportPathModal:function(){return s.template(['
','
All files within the given folders and their subfolders will be imported into the current folder.
','
','",'","
",'",'","
",'',"
","

You can set extension type and genome for all imported datasets at once:

","
",'Type: ','Genome: ',"
","
",'","
","
"].join(""))},templateAddFilesFromHistory:function(){return s.template(['
',"
","1. Select history: ",'","
","
",'
',"
","
"].join(""))},templateHistoryContents:function(){return s.template(["

2. Choose the datasets to import:

","
",'",'","
","
","
    ","<% _.each(history_contents, function(history_item) { %>",'<% if (history_item.get("deleted") != true ) { %>','<% var item_name = history_item.get("name") %>','<% if (history_item.get("type") === "collection") { %>','<% var collection_type = history_item.get("collection_type") %>','<% if (collection_type === "list") { %>','
  • " data-name="<%= _.escape(history_item.get("type")) %>">',"
  • ","<% } else { %>",'
  • ','','<%= _.escape(history_item.get("hid")) %>: ','<%= item_name.length > 75 ? _.escape("...".concat(item_name.substr(-75))) : _.escape(item_name) %> (Dataset Collection of type <%= _.escape(collection_type) %> not supported.)',"","
  • ","<% } %>",'<% } else if (history_item.get("visible") === true && history_item.get("state") === "ok") { %>','
  • " data-name="<%= _.escape(history_item.get("type")) %>">','","
  • ","<% } %>","<% } %>","<% }); %>","
"].join(""))},templatePaginator:function(){return s.template(['","",' <%- items_shown %> items shown (change)',"",""," <%- total_items_count %> total",""].join(""))}});t.default={FolderToolbarView:f}}).call(t,i(1),i(2),i(0),i(0))},306:function(e,t,i){"use strict";(function(e,s,a){function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var o=i(42),n=r(o),l=i(156),d=r(l),c=i(139),h=r(c),_=e.View.extend({el:"#center",model:null,options:{},events:{"click .toolbtn_save_permissions":"savePermissions"},initialize:function(e){this.options=s.extend(this.options,e),this.options.id&&this.fetchLibrary()},fetchLibrary:function(e){this.options=s.extend(this.options,e),this.model=new d.default.Library({id:this.options.id});var t=this;this.model.fetch({success:function(){t.options.show_permissions&&t.showPermissions()},error:function(e,t){void 0!==t.responseJSON?n.default.error(t.responseJSON.err_msg+" Click this to go back.","",{onclick:function(){Galaxy.libraries.library_router.back()}}):n.default.error("An error occurred. Click this to go back.","",{onclick:function(){Galaxy.libraries.library_router.back()}})}})},showPermissions:function(e){this.options=s.extend(this.options,e),a(".tooltip").remove(),void 0!==this.options.fetched_permissions&&(0===this.options.fetched_permissions.access_library_role_list.length?this.model.set({is_unrestricted:!0}):this.model.set({is_unrestricted:!1}));var t=!1;Galaxy.user&&(t=Galaxy.user.isAdmin());var i=this.templateLibraryPermissions();this.$el.html(i({library:this.model,is_admin:t}));var r=this;a.get(Galaxy.root+"api/libraries/"+r.id+"/permissions?scope=current").done(function(e){r.prepareSelectBoxes({fetched_permissions:e})}).fail(function(){n.default.error("An error occurred while attempting to fetch library permissions.")}),a("#center [data-toggle]").tooltip(),a("#center").css("overflow","auto")},_serializeRoles:function(e){for(var t=[],i=0;i=0;i--)t.push(e[i].id);return t},savePermissions:function(e){var t=this,i=this._extractIds(this.accessSelectObject.$el.select2("data")),s=this._extractIds(this.addSelectObject.$el.select2("data")),r=this._extractIds(this.manageSelectObject.$el.select2("data")),o=this._extractIds(this.modifySelectObject.$el.select2("data"));a.post(Galaxy.root+"api/libraries/"+t.id+"/permissions?action=set_permissions",{"access_ids[]":i,"add_ids[]":s,"manage_ids[]":r,"modify_ids[]":o}).done(function(e){t.showPermissions({fetched_permissions:e}),n.default.success("Permissions saved.")}).fail(function(){n.default.error("An error occurred while attempting to set library permissions.")})},templateLibraryPermissions:function(){return s.template(['
','","

",'Library: <%= _.escape(library.get("name")) %>',"

",'
',"<% if (is_admin) { %>","You are logged in as an administrator therefore you can manage any library on this Galaxy instance. Please make sure you understand the consequences.","<% } else { %>","You can assign any number of roles to any of the following permission types. However please read carefully the implications of such actions.","<% }%>","
",'
',"

Library permissions

","

Roles that can access the library

",'
','
',"User with any of these roles can access this library. If there are no access roles set on the library it is considered unrestricted.","
","

Roles that can manage permissions on this library

",'
','
',"User with any of these roles can manage permissions on this library (includes giving access).","
","

Roles that can add items to this library

",'
','
',"User with any of these roles can add items to this library (folders and datasets).","
","

Roles that can modify this library

",'
','
',"User with any of these roles can modify this library (name, synopsis, etc.).","
",'","
","
"].join(""))}});t.default={LibraryView:_}}).call(t,i(1),i(2),i(0))},307:function(e,t,i){"use strict";(function(e,s,a){function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var o=i(42),n=r(o),l=i(156),d=r(l),c=i(139),h=r(c),_=e.View.extend({el:"#center",model:null,options:{},events:{"click .toolbtn_save_permissions":"savePermissions"},initialize:function(e){this.options=s.extend(this.options,e),this.options.id&&this.fetchFolder()},fetchFolder:function(e){this.options=s.extend(this.options,e),this.model=new d.default.FolderAsModel({id:this.options.id});var t=this;this.model.fetch({success:function(){t.options.show_permissions&&t.showPermissions()},error:function(e,t){void 0!==t.responseJSON?n.default.error(t.responseJSON.err_msg+" Click this to go back.","",{onclick:function(){Galaxy.libraries.library_router.back()}}):n.default.error("An error occurred. Click this to go back.","",{onclick:function(){Galaxy.libraries.library_router.back()}})}})},showPermissions:function(e){this.options=s.extend(this.options,e),a(".tooltip").remove();var t=!1;Galaxy.user&&(t=Galaxy.user.isAdmin());var i=this.templateFolderPermissions();this.$el.html(i({folder:this.model,is_admin:t}));var r=this;a.get(Galaxy.root+"api/folders/"+r.id+"/permissions?scope=current").done(function(e){r.prepareSelectBoxes({fetched_permissions:e})}).fail(function(){n.default.error("An error occurred while attempting to fetch folder permissions.")}),a("#center [data-toggle]").tooltip(),a("#center").css("overflow","auto")},_serializeRoles:function(e){for(var t=[],i=0;i=0;i--)t.push(e[i].id);return t},savePermissions:function(e){var t=this,i=this._extractIds(this.addSelectObject.$el.select2("data")),s=this._extractIds(this.manageSelectObject.$el.select2("data")),r=this._extractIds(this.modifySelectObject.$el.select2("data"));a.post(Galaxy.root+"api/folders/"+t.id+"/permissions?action=set_permissions",{"add_ids[]":i,"manage_ids[]":s,"modify_ids[]":r}).done(function(e){t.showPermissions({fetched_permissions:e}),n.default.success("Permissions saved.")}).fail(function(){n.default.error("An error occurred while attempting to set folder permissions.")})},templateFolderPermissions:function(){return s.template(['
','","

",'Folder: <%= _.escape(folder.get("name")) %>',"

",'
',"<% if (is_admin) { %>","You are logged in as an administrator therefore you can manage any folder on this Galaxy instance. Please make sure you understand the consequences.","<% } else { %>","You can assign any number of roles to any of the following permission types. However please read carefully the implications of such actions.","<% }%>","
",'
',"

Folder permissions

","

","Roles that can manage permissions on this folder","

",'
','
',"User with any of these roles can manage permissions on this folder.","
","

","Roles that can add items to this folder","

",'
','
',"User with any of these roles can add items to this folder (folders and datasets).","
","

","Roles that can modify this folder","

",'
','
',"User with any of these roles can modify this folder (name, etc.).","
",'","
","
"].join(""))}});t.default={FolderView:_}}).call(t,i(1),i(2),i(0))},62:function(e,t,i){"use strict";function s(e,t){var i=void 0!==e.prototype?e.prototype:e;return void 0!==t&&(i._logNamespace=t),a.forEach(function(e){i[e]=function(){if(this.logger)return this.logger.emit?this.logger.emit(e,this._logNamespace,arguments):this.logger[e]?this.logger[e].apply(this.logger,arguments):void 0}}),e}Object.defineProperty(t,"__esModule",{value:!0});var a=["log","debug","info","warn","error","metric"];t.default=s},8:function(e,t,i){"use strict";(function(e){function s(e){return e&&e.__esModule?e:{default:e}}function a(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&(t[i]=e[i]);return t.default=e,t}function r(e,t){var i=Array.prototype.slice.call(arguments,0),s=i.pop();return i.unshift(s),d.defaults.apply(d,i)}function o(e,t){t=t||"model";var i=d.template(e.join(""));return function(e,s){var a={view:s||{},_l:f.default};return a[t]=e||{},i(a)}}function n(e,t){t=t||{};var i=t.ascending?1:-1;return function(t,s){return t=t.get(e),s=s.get(e),(ts?1:0)*i}}Object.defineProperty(t,"__esModule",{value:!0});var l=i(2),d=a(l),c=i(1),h=a(c),_=i(62),p=s(_),u=i(7),f=s(u),g={logger:null,_logNamespace:"."};(0,p.default)(g);var m=h.Model.extend({initialize:function(e){if(this._checkEnabledSessionStorage(),!e.id)throw new Error("SessionStorageModel requires an id in the initial attributes");this.id=e.id;var t=this.isNew()?{}:this._read(this);this.clear({silent:!0}),this.save(d.extend({},this.defaults,t,e),{silent:!0}),this.on("change",function(){this.save()})},_checkEnabledSessionStorage:function(){try{return window.sessionStorage.length>=0}catch(e){return alert("Please enable cookies in your browser for this Galaxy site"),!1}},sync:function(e,t,i){i.silent||t.trigger("request",t,{},i);var s={};switch(e){case"create":s=this._create(t);break;case"read":s=this._read(t);break;case"update":s=this._update(t);break;case"delete":s=this._delete(t)}return void 0!==s||null!==s?i.success&&i.success():i.error&&i.error(),s},_create:function(e){try{var t=e.toJSON(),i=sessionStorage.setItem(e.id,JSON.stringify(t));return null===i?i:t}catch(e){if(!(e instanceof DOMException&&navigator.userAgent.indexOf("Safari")>-1))throw e}return null},_read:function(e){return JSON.parse(sessionStorage.getItem(e.id))},_update:function(e){return e._create(e)},_delete:function(e){return sessionStorage.removeItem(e.id)},isNew:function(){return!sessionStorage.hasOwnProperty(this.id)},_log:function(){return JSON.stringify(this.toJSON(),null," ")},toString:function(){return"SessionStorageModel("+this.id+")"}});!function(){m.prototype=d.omit(m.prototype,"url","urlRoot")}();var b={searchAttributes:[],searchAliases:{},searchAttribute:function(e,t){var i=this.get(e);return!(!t||void 0===i||null===i)&&(d.isArray(i)?this._searchArrayAttribute(i,t):-1!==i.toString().toLowerCase().indexOf(t.toLowerCase()))},_searchArrayAttribute:function(e,t){return t=t.toLowerCase(),d.any(e,function(e){return-1!==e.toString().toLowerCase().indexOf(t.toLowerCase())})},search:function(e){var t=this;return d.filter(this.searchAttributes,function(i){return t.searchAttribute(i,e)})},matches:function(e){var t=e.split("=");if(t.length>=2){var i=t[0];return i=this.searchAliases[i]||i,this.searchAttribute(i,t[1])}return!!this.search(e).length},matchesAll:function(e){var t=this;return e=e.match(/(".*"|\w*=".*"|\S*)/g).filter(function(e){return!!e}),d.all(e,function(e){return e=e.replace(/"/g,""),t.matches(e)})}},y={hiddenUntilActivated:function(t,i){if(i=i||{},this.HUAVOptions={$elementShown:this.$el,showFn:e.prototype.toggle,showSpeed:"fast"},d.extend(this.HUAVOptions,i||{}),this.HUAVOptions.hasBeenShown=this.HUAVOptions.$elementShown.is(":visible"),this.hidden=this.isHidden(),t){var s=this;t.on("click",function(e){s.toggle(s.HUAVOptions.showSpeed)})}},isHidden:function(){return this.HUAVOptions.$elementShown.is(":hidden")},toggle:function(){return this.hidden?(this.HUAVOptions.hasBeenShown||d.isFunction(this.HUAVOptions.onshowFirstTime)&&(this.HUAVOptions.hasBeenShown=!0,this.HUAVOptions.onshowFirstTime.call(this)),d.isFunction(this.HUAVOptions.onshow)&&(this.HUAVOptions.onshow.call(this),this.trigger("hiddenUntilActivated:shown",this)),this.hidden=!1):(d.isFunction(this.HUAVOptions.onhide)&&(this.HUAVOptions.onhide.call(this),this.trigger("hiddenUntilActivated:hidden",this)),this.hidden=!0),this.HUAVOptions.showFn.apply(this.HUAVOptions.$elementShown,arguments)}},v={initialize:function(e){this.draggable=e.draggable||!1},$dragHandle:function(){return this.$(".title-bar")},toggleDraggable:function(){this.draggable?this.draggableOff():this.draggableOn()},draggableOn:function(){this.draggable=!0,this.dragStartHandler=d.bind(this._dragStartHandler,this),this.dragEndHandler=d.bind(this._dragEndHandler,this);var e=this.$dragHandle().attr("draggable",!0).get(0);e.addEventListener("dragstart",this.dragStartHandler,!1),e.addEventListener("dragend",this.dragEndHandler,!1)},draggableOff:function(){this.draggable=!1;var e=this.$dragHandle().attr("draggable",!1).get(0);e.removeEventListener("dragstart",this.dragStartHandler,!1),e.removeEventListener("dragend",this.dragEndHandler,!1)},_dragStartHandler:function(e){return e.dataTransfer.effectAllowed="move",e.dataTransfer.setData("text",JSON.stringify(this.model.toJSON())),this.trigger("draggable:dragstart",e,this),!1},_dragEndHandler:function(e){return this.trigger("draggable:dragend",e,this),!1}},w={initialize:function(e){this.selectable=e.selectable||!1,this.selected=e.selected||!1},$selector:function(){return this.$(".selector")},_renderSelected:function(){this.$selector().find("span").toggleClass("fa-check-square-o",this.selected).toggleClass("fa-square-o",!this.selected)},toggleSelector:function(){this.$selector().is(":visible")?this.hideSelector():this.showSelector()},showSelector:function(e){e=void 0!==e?e:this.fxSpeed,this.selectable=!0,this.trigger("selectable",!0,this),this._renderSelected(),e?this.$selector().show(e):this.$selector().show()},hideSelector:function(e){e=void 0!==e?e:this.fxSpeed,this.selectable=!1,this.trigger("selectable",!1,this),e?this.$selector().hide(e):this.$selector().hide()},toggleSelect:function(e){this.selected?this.deselect(e):this.select(e)},select:function(e){return this.selected||(this.trigger("selected",this,e),this.selected=!0,this._renderSelected()),!1},deselect:function(e){return this.selected&&(this.trigger("de-selected",this,e),this.selected=!1,this._renderSelected()),!1}};t.default={LoggableMixin:g,SessionStorageModel:m,mixin:r,SearchableModelMixin:b,HiddenUntilActivatedViewMixin:y,DraggableViewMixin:v,SelectableViewMixin:w,wrapTemplate:o,buildComparator:n}}).call(t,i(0))}},[297]); \ No newline at end of file diff --git a/static/scripts/bundled/libs.bundled.js b/static/scripts/bundled/libs.bundled.js index fdc06a0a310..579fd1325d6 100644 --- a/static/scripts/bundled/libs.bundled.js +++ b/static/scripts/bundled/libs.bundled.js @@ -53,4 +53,4 @@ return o&&(a.length<4&&i("jQuery.fn.attr( props, pass ) is deprecated"),e&&!h.te * @license MIT | GPL | Apache 2.0, see LICENSE.txt * @see https://github.com/dyve/jquery-autocomplete */ -!function(t){"use strict";t.fn.autocomplete_verheul=function(e){var n;arguments.length>1?(n=e,e=arguments[1],e.url=n):"string"==typeof e&&(n=e,e={url:n});var i=t.extend({},t.fn.autocomplete_verheul.defaults,e);return this.each(function(){var e=t(this);e.data("autocompleter",new t.Autocompleter(e,t.meta?t.extend({},i,e.data()):i))})},t.fn.autocomplete_verheul.defaults={inputClass:"acInput",loadingClass:"acLoading",resultsClass:"acResults",selectClass:"acSelect",queryParamName:"q",extraParams:{},remoteDataType:!1,lineSeparator:"\n",cellSeparator:"|",minChars:2,maxItemsToShow:10,delay:400,useCache:!0,maxCacheLength:10,matchSubset:!0,matchCase:!1,matchInside:!0,mustMatch:!1,selectFirst:!1,selectOnly:!1,showResult:null,preventDefaultReturn:1,preventDefaultTab:0,autoFill:!1,filterResults:!0,filter:!0,sortResults:!0,sortFunction:null,onItemSelect:null,onNoMatch:null,onFinish:null,matchStringConverter:null,beforeUseConverter:null,autoWidth:"min-width",useDelimiter:!1,delimiterChar:",",delimiterKeyCode:188,processData:null,onError:null,enabled:!0};var e=function(e){var n,i,r=typeof e;return"string"===r?(n=e,i={}):t.isArray(e)?(n=e[0],i=e.slice(1)):"object"===r&&(n=e.value,i=e.data),n=String(n),"object"!=typeof i&&(i={}),{value:n,data:i}},n=function(t,e,n){var i=parseInt(t,10);return n=n||{},(isNaN(i)||n.min&&ie?1:t
").hide().addClass(this.options.resultsClass).css({position:"absolute"}),t("body").append(this.dom.$results),e.keydown(function(t){switch(r.lastKeyPressed_=t.keyCode,r.lastKeyPressed_){case r.options.delimiterKeyCode:r.options.useDelimiter&&r.active_&&r.selectCurrent();break;case 35:case 36:case 16:case 17:case 18:case 37:case 39:break;case 38:return t.preventDefault(),r.active_?r.focusPrev():r.activate(),!1;case 40:return t.preventDefault(),r.active_?r.focusNext():r.activate(),!1;case 9:if(r.active_&&(r.selectCurrent(),r.options.preventDefaultTab))return t.preventDefault(),!1;if(2===r.options.preventDefaultTab)return t.preventDefault(),!1;break;case 13:if(r.active_&&(r.selectCurrent(),r.options.preventDefaultReturn))return t.preventDefault(),!1;if(2===r.options.preventDefaultReturn)return t.preventDefault(),!1;break;case 27:if(r.active_)return t.preventDefault(),r.deactivate(!0),!1;break;default:r.activate()}}),e.on("paste",function(){r.activate()});var o=function(){r.deactivate(!0)};e.blur(function(){r.finishOnBlur_&&(r.finishTimeout_=setTimeout(o,200))}),e.parents("form").on("submit",o)},t.Autocompleter.prototype.position=function(){var e=this.dom.$elem.offset(),n=this.dom.$results.outerHeight(),i=t(window).outerHeight(),r=e.top+this.dom.$elem.outerHeight(),o=r+n,s={top:r,left:e.left};if(o>i){var a=e.top-n;a>=0&&(s.top=a)}this.dom.$results.css(s)},t.Autocompleter.prototype.cacheRead=function(t){var e,n,i,r,o;if(this.options.useCache)for(t=String(t),e=t.length,n=this.options.matchSubset?1:e;n<=e;){for(r=this.options.matchInside?e-n:0,o=0;o<=r;){if(i=t.substr(0,n),void 0!==this.cacheData_[i])return this.cacheData_[i];o++}n++}return!1},t.Autocompleter.prototype.cacheWrite=function(t,e){return!!this.options.useCache&&(this.cacheLength_>=this.options.maxCacheLength&&this.cacheFlush(),t=String(t),void 0!==this.cacheData_[t]&&this.cacheLength_++,this.cacheData_[t]=e,this.cacheData_[t])},t.Autocompleter.prototype.cacheFlush=function(){this.cacheData_={},this.cacheLength_=0},t.Autocompleter.prototype.callHook=function(e,n){var i=this.options[e];return!(!i||!t.isFunction(i))&&i(n,this)},t.Autocompleter.prototype.activate=function(){if(this.options.enabled){var t=this;this.keyTimeout_&&clearTimeout(this.keyTimeout_),this.keyTimeout_=setTimeout(function(){t.activateNow()},this.options.delay)}},t.Autocompleter.prototype.activateNow=function(){var t=this.beforeUseConverter(this.dom.$elem.val());t!==this.lastProcessedValue_&&t!==this.lastSelectedValue_&&this.fetchData(t)},t.Autocompleter.prototype.fetchData=function(t){var e=this,n=function(t,n){e.options.processData&&(t=e.options.processData(t)),e.showResults(e.filterResults(t,n),n)};this.lastProcessedValue_=t,t.length-1:0===r}return!0},t.Autocompleter.prototype.filterResult=function(e,n){return!1===this.options.filter||(t.isFunction(this.options.filter)?this.options.filter(e,n):this.defaultFilter(e,n))},t.Autocompleter.prototype.filterResults=function(t,n){var i,r,o=[];for(i=0;i0&&this.options.maxItemsToShow");return i.html(this.showResult(e.value,e.data)),i.data({value:e.value,data:e.data}).click(function(){n.selectItem(i)}).mousedown(n.disableFinishOnBlur).mouseup(n.enableFinishOnBlur),i},t.Autocompleter.prototype.getItems=function(){return t(">ul>li",this.dom.$results)},t.Autocompleter.prototype.showResults=function(e,n){var i,r,o,s,a=e.length,l=this,u=t("
    "),c=!1,h=!1;if(a){for(i=0;i

    ").text(e).html()},t.Autocompleter.prototype.autoFill=function(t,e){var n,i,r,o;if(this.options.autoFill&&8!==this.lastKeyPressed_&&(n=String(t).toLowerCase(),i=String(e).toLowerCase(),r=t.length,o=e.length,n.substr(0,o)===i)){var s=this.getDelimiterOffsets(),a=s.start?" ":"";this.setValue(a+t);var l=o+s.start+a.length,u=r+s.start+a.length;return this.selectRange(l,u),!0}return!1},t.Autocompleter.prototype.focusNext=function(){this.focusMove(1)},t.Autocompleter.prototype.focusPrev=function(){this.focusMove(-1)},t.Autocompleter.prototype.focusMove=function(e){var i=this.getItems();if(e=n(e,0))for(var r=0;r=i.length&&(e=i.length-1),n=t(i[e])):n=t(e),n&&n.addClass(this.selectClass_).addClass(this.options.selectClass))},t.Autocompleter.prototype.selectCurrent=function(){var e=t("li."+this.selectClass_,this.dom.$results);1===e.length?this.selectItem(e):this.deactivate(!1)},t.Autocompleter.prototype.selectItem=function(t){var e=t.data("value"),n=t.data("data"),i=this.displayValue(e,n),r=this.beforeUseConverter(i);this.lastProcessedValue_=r,this.lastSelectedValue_=r;var o=this.getDelimiterOffsets(),s=this.options.delimiterChar,a=this.dom.$elem,l=0;this.options.useDelimiter&&(a.val().substring(o.start-1,o.start)==s&&" "!=s&&(i=" "+i),a.val().substring(o.end,o.end+1)!=s&&this.lastKeyPressed_!=this.options.delimiterKeyCode?i+=s:l=1),this.setValue(i),this.setCaret(o.start+i.length+l),this.callHook("onItemSelect",{value:e,data:n}),this.deactivate(!0),a.focus()},t.Autocompleter.prototype.displayValue=function(e,n){return t.isFunction(this.options.displayValue)?this.options.displayValue(e,n):e},t.Autocompleter.prototype.hideResults=function(){this.dom.$results.hide()},t.Autocompleter.prototype.deactivate=function(t){this.finishTimeout_&&clearTimeout(this.finishTimeout_),this.keyTimeout_&&clearTimeout(this.keyTimeout_),t&&(this.lastProcessedValue_!==this.lastSelectedValue_&&(this.options.mustMatch&&this.setValue(""),this.callHook("onNoMatch")),this.active_&&this.callHook("onFinish"),this.lastKeyPressed_=null,this.lastProcessedValue_=null,this.lastSelectedValue_=null,this.active_=!1),this.hideResults()},t.Autocompleter.prototype.selectRange=function(t,e){var n=this.dom.$elem.get(0);if(n.setSelectionRange)n.focus(),n.setSelectionRange(t,e);else if(n.createTextRange){var i=n.createTextRange();i.collapse(!0),i.moveEnd("character",e),i.moveStart("character",t),i.select()}},t.Autocompleter.prototype.setCaret=function(t){this.selectRange(t,t)},t.Autocompleter.prototype.getCaret=function(){var t,e,n,i,r,o,s=this.dom.$elem,a=s[0];return a.createTextRange?(e=document.selection,"textarea"!=a.tagName.toLowerCase()?(t=s.val(),n=e.createRange().duplicate(),n.moveEnd("character",t.length),i=""===n.text?t.length:t.lastIndexOf(n.text),n=e.createRange().duplicate(),n.moveStart("character",-t.length),r=n.text.length):(n=e.createRange(),o=n.duplicate(),o.moveToElementText(a),o.setEndPoint("EndToEnd",n),i=o.text.length-n.text.length,r=i+n.text.length)):(i=s[0].selectionStart,r=s[0].selectionEnd),{start:i,end:r}},t.Autocompleter.prototype.setValue=function(t){if(this.options.useDelimiter){var e=this.dom.$elem.val(),n=this.getDelimiterOffsets();t=e.substring(0,n.start)+t+e.substring(n.end)}this.dom.$elem.val(t)},t.Autocompleter.prototype.getValue=function(t){if(this.options.useDelimiter){var e=this.getDelimiterOffsets();return t.substring(e.start,e.end).trim()}return t},t.Autocompleter.prototype.getDelimiterOffsets=function(){var t=this.dom.$elem.val();if(this.options.useDelimiter){var e=t.substring(0,this.getCaret().start),n=e.lastIndexOf(this.options.delimiterChar)+1,i=t.substring(this.getCaret().start),r=i.indexOf(this.options.delimiterChar);-1==r&&(r=t.length),r+=this.getCaret().start}else n=0,r=t.length;return{start:n,end:r}}}(t)}).call(e,n(0))},229:function(t,e,n){var i,r,o;!function(s,a,l){!function(s){r=[n(0)],i=s,void 0!==(o="function"==typeof i?i.apply(e,r):i)&&(t.exports=o)}(function(t){"use strict";var e,n,i,r,o,l,u,c,h,d,p,f,g,v,m,y,b,w,_,x,C,k,S,T,A,E,O,j,D,M,P,N={},R=0;e=function(){return{common:{type:"line",lineColor:"#00f",fillColor:"#cdf",defaultPixelsPerValue:3,width:"auto",height:"auto",composite:!1,tagValuesAttribute:"values",tagOptionsPrefix:"spark",enableTagOptions:!1,enableHighlight:!0,highlightLighten:1.4,tooltipSkipNull:!0,tooltipPrefix:"",tooltipSuffix:"",disableHiddenCheck:!1,numberFormatter:!1,numberDigitGroupCount:3,numberDigitGroupSep:",",numberDecimalMark:".",disableTooltips:!1,disableInteraction:!1},line:{spotColor:"#f80",highlightSpotColor:"#5f5",highlightLineColor:"#f22",spotRadius:1.5,minSpotColor:"#f80",maxSpotColor:"#f80",lineWidth:1,normalRangeMin:void 0,normalRangeMax:void 0,normalRangeColor:"#ccc",drawNormalOnTop:!1,chartRangeMin:void 0,chartRangeMax:void 0,chartRangeMinX:void 0,chartRangeMaxX:void 0,tooltipFormat:new i('● {{prefix}}{{y}}{{suffix}}')},bar:{barColor:"#3366cc",negBarColor:"#f44",stackedBarColor:["#3366cc","#dc3912","#ff9900","#109618","#66aa00","#dd4477","#0099c6","#990099"],zeroColor:void 0,nullColor:void 0,zeroAxis:!0,barWidth:4,barSpacing:1,chartRangeMax:void 0,chartRangeMin:void 0,chartRangeClip:!1,colorMap:void 0,tooltipFormat:new i('● {{prefix}}{{value}}{{suffix}}')},tristate:{barWidth:4,barSpacing:1,posBarColor:"#6f6",negBarColor:"#f44",zeroBarColor:"#999",colorMap:{},tooltipFormat:new i('● {{value:map}}'),tooltipValueLookups:{map:{"-1":"Loss",0:"Draw",1:"Win"}}},discrete:{lineHeight:"auto",thresholdColor:void 0,thresholdValue:0,chartRangeMax:void 0,chartRangeMin:void 0,chartRangeClip:!1,tooltipFormat:new i("{{prefix}}{{value}}{{suffix}}")},bullet:{targetColor:"#f33",targetWidth:3,performanceColor:"#33f",rangeColors:["#d3dafe","#a8b6ff","#7f94ff"],base:void 0,tooltipFormat:new i("{{fieldkey:fields}} - {{value}}"),tooltipValueLookups:{fields:{r:"Range",p:"Performance",t:"Target"}}},pie:{offset:0,sliceColors:["#3366cc","#dc3912","#ff9900","#109618","#66aa00","#dd4477","#0099c6","#990099"],borderWidth:0,borderColor:"#000",tooltipFormat:new i('● {{value}} ({{percent.1}}%)')},box:{raw:!1,boxLineColor:"#000",boxFillColor:"#cdf",whiskerColor:"#000",outlierLineColor:"#333",outlierFillColor:"#fff",medianColor:"#f00",showOutliers:!0,outlierIQR:1.5,spotRadius:1.5,target:void 0,targetColor:"#4a2",chartRangeMax:void 0,chartRangeMin:void 0,tooltipFormat:new i("{{field:fields}}: {{value}}"),tooltipFormatFieldlistKey:"field",tooltipValueLookups:{fields:{lq:"Lower Quartile",med:"Median",uq:"Upper Quartile",lo:"Left Outlier",ro:"Right Outlier",lw:"Left Whisker",rw:"Right Whisker"}}}}},A='.jqstooltip { position: absolute;left: 0px;top: 0px;visibility: hidden;background: rgb(0, 0, 0) transparent;background-color: rgba(0,0,0,0.6);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000);-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000)";color: white;font: 10px arial, san serif;text-align: left;white-space: nowrap;padding: 5px;border: 1px solid white;z-index: 10000;}.jqsfield { color: white;font: 10px arial, san serif;text-align: left;}',n=function(){var e,n;return e=function(){this.init.apply(this,arguments)},arguments.length>1?(arguments[0]?(e.prototype=t.extend(new arguments[0],arguments[arguments.length-1]),e._super=arguments[0].prototype):e.prototype=arguments[arguments.length-1],arguments.length>2&&(n=Array.prototype.slice.call(arguments,1,-1),n.unshift(e.prototype),t.extend.apply(t,n))):e.prototype=arguments[0],e.prototype.cls=e,e},t.SPFormatClass=i=n({fre:/\{\{([\w.]+?)(:(.+?))?\}\}/g,precre:/(\w+)\.(\d+)/,init:function(t,e){this.format=t,this.fclass=e},render:function(t,e,n){var i,r,o,s,a,l=this,u=t;return this.format.replace(this.fre,function(){var t;return r=arguments[1],o=arguments[3],i=l.precre.exec(r),i?(a=i[2],r=i[1]):a=!1,void 0===(s=u[r])?"":o&&e&&e[o]?(t=e[o],t.get?e[o].get(s)||s:e[o][s]||s):(h(s)&&(s=n.get("numberFormatter")?n.get("numberFormatter")(s):g(s,a,n.get("numberDigitGroupCount"),n.get("numberDigitGroupSep"),n.get("numberDecimalMark"))),s)})}}),t.spformat=function(t,e){return new i(t,e)},r=function(t,e,n){return tn?n:t},o=function(t,e){var n;return 2===e?(n=a.floor(t.length/2),t.length%2?t[n]:(t[n-1]+t[n])/2):t.length%2?(n=(t.length*e+e)/4,n%1?(t[a.floor(n)]+t[a.floor(n)-1])/2:t[n-1]):(n=(t.length*e+2)/4,n%1?(t[a.floor(n)]+t[a.floor(n)-1])/2:t[n-1])},l=function(t){var e;switch(t){case"undefined":t=void 0;break;case"null":t=null;break;case"true":t=!0;break;case"false":t=!1;break;default:e=parseFloat(t),t==e&&(t=e)}return t},u=function(t){var e,n=[];for(e=t.length;e--;)n[e]=l(t[e]);return n},c=function(t,e){var n,i,r=[];for(n=0,i=t.length;n0;a-=i)e.splice(a,0,r);return e.join("")},d=function(t,e,n){var i;for(i=e.length;i--;)if((!n||null!==e[i])&&e[i]!==t)return!1;return!0},f=function(e){return t.isArray(e)?e:[e]},p=function(t){var e;s.createStyleSheet?s.createStyleSheet().cssText=t:(e=s.createElement("style"),e.type="text/css",s.getElementsByTagName("head")[0].appendChild(e),e["string"==typeof s.body.style.WebkitAppearance?"innerText":"innerHTML"]=t)},t.fn.simpledraw=function(e,n,i,r){var o,a;if(i&&(o=this.data("_jqs_vcanvas")))return o;if(!1===t.fn.sparkline.canvas)return!1;if(void 0===t.fn.sparkline.canvas){var l=s.createElement("canvas");if(l.getContext&&l.getContext("2d"))t.fn.sparkline.canvas=function(t,e,n,i){return new D(t,e,n,i)};else{if(!s.namespaces||s.namespaces.v)return t.fn.sparkline.canvas=!1,!1;s.namespaces.add("v","urn:schemas-microsoft-com:vml","#default#VML"),t.fn.sparkline.canvas=function(t,e,n,i){return new M(t,e,n)}}}return void 0===e&&(e=t(this).innerWidth()),void 0===n&&(n=t(this).innerHeight()),o=t.fn.sparkline.canvas(e,n,this,r),a=t(this).data("_jqs_mhandler"),a&&a.registerCanvas(o),o},t.fn.cleardraw=function(){var t=this.data("_jqs_vcanvas");t&&t.reset()},t.RangeMapClass=v=n({init:function(t){var e,n,i=[];for(e in t)t.hasOwnProperty(e)&&"string"==typeof e&&e.indexOf(":")>-1&&(n=e.split(":"),n[0]=0===n[0].length?-1/0:parseFloat(n[0]),n[1]=0===n[1].length?1/0:parseFloat(n[1]),n[2]=t[e],i.push(n));this.map=t,this.rangelist=i||!1},get:function(t){var e,n,i,r=this.rangelist;if(void 0!==(i=this.map[t]))return i;if(r)for(e=r.length;e--;)if(n=r[e],n[0]<=t&&n[1]>=t)return n[2]}}),t.range_map=function(t){return new v(t)},m=n({init:function(e,n){var i=t(e);this.$el=i,this.options=n,this.currentPageX=0,this.currentPageY=0,this.el=e,this.splist=[],this.tooltip=null,this.over=!1,this.displayTooltips=!n.get("disableTooltips"),this.highlightEnabled=!n.get("disableHighlight")},registerSparkline:function(t){this.splist.push(t),this.over&&this.updateDisplay()},registerCanvas:function(e){var n=t(e.canvas);this.canvas=e,this.$canvas=n,n.mouseenter(t.proxy(this.mouseenter,this)),n.mouseleave(t.proxy(this.mouseleave,this)),n.click(t.proxy(this.mouseclick,this))},reset:function(t){this.splist=[],this.tooltip&&t&&(this.tooltip.remove(),this.tooltip=void 0)},mouseclick:function(e){var n=t.Event("sparklineClick");n.originalEvent=e,n.sparklines=this.splist,this.$el.trigger(n)},mouseenter:function(e){t(s.body).unbind("mousemove.jqs"),t(s.body).bind("mousemove.jqs",t.proxy(this.mousemove,this)),this.over=!0,this.currentPageX=e.pageX,this.currentPageY=e.pageY,this.currentEl=e.target,!this.tooltip&&this.displayTooltips&&(this.tooltip=new y(this.options),this.tooltip.updatePosition(e.pageX,e.pageY)),this.updateDisplay()},mouseleave:function(){t(s.body).unbind("mousemove.jqs");var e,n,i=this.splist,r=i.length,o=!1;for(this.over=!1,this.currentEl=null,this.tooltip&&(this.tooltip.remove(),this.tooltip=null),n=0;n
    '}),e.prototype=t.extend({},t.fn.tooltip.Constructor.prototype),e.prototype.constructor=e,e.prototype.getDefaults=function(){return e.DEFAULTS},e.prototype.setContent=function(){var t=this.tip(),e=this.getTitle(),n=this.getContent();t.find(".popover-title")[this.options.html?"html":"text"](e),t.find(".popover-content")[this.options.html?"html":"text"](n),t.removeClass("fade top bottom left right in"),t.find(".popover-title").html()||t.find(".popover-title").hide()},e.prototype.hasContent=function(){return this.getTitle()||this.getContent()},e.prototype.getContent=function(){var t=this.$element,e=this.options;return t.attr("data-content")||("function"==typeof e.content?e.content.call(t[0]):e.content)},e.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},e.prototype.tip=function(){return this.$tip||(this.$tip=t(this.options.template)),this.$tip};var n=t.fn.popover;t.fn.popover=function(n){return this.each(function(){var i=t(this),r=i.data("bs.popover"),o="object"==typeof n&&n;r||i.data("bs.popover",r=new e(this,o)),"string"==typeof n&&r[n]()})},t.fn.popover.Constructor=e,t.fn.popover.noConflict=function(){return t.fn.popover=n,this}}(e)}).call(e,n(0),n(0))},233:function(t,e){t.exports=function(){throw new Error("define cannot be used indirect")}},234:function(t,e,n){"use strict";(function(t,e,i,r,o){function s(t){return t&&t.__esModule?t:{default:t}}function a(e,n,i){function r(t){var e=o(t),n={placeholder:"Click to select",closeOnSelect:!e.is("[MULTIPLE]"),dropdownAutoWidth:!0,containerCssClass:"select2-minwidth"};return t.select2(n)}t.fn.select2&&(void 0===e&&(e=20),void 0===n&&(n=3e3),i=i||o("select"),i.each(function(){var t=o(this).not("[multiple]"),i=t.find("option").length;in||t.hasClass("no-autocomplete")||r(t)}))}function l(){o("select[refresh_on_change='true']").off("change").change(function(){var t=o(this),e=t.val(),n=t.attr("refresh_on_change_values");if(n){n=n.split(",");var i=t.attr("last_selected_value");if(-1===o.inArray(e,n)&&-1===o.inArray(i,n))return}o(window).trigger("refresh_on_change"),o(document).trigger("convert_to_values"),t.get(0).form.submit()}),o(":checkbox[refresh_on_change='true']").off("click").click(function(){var t=o(this),e=t.val(),n=t.attr("refresh_on_change_values");if(n){n=n.split(",");var i=t.attr("last_selected_value");if(-1===o.inArray(e,n)&&-1===o.inArray(i,n))return}o(window).trigger("refresh_on_change"),t.get(0).form.submit()}),o("a[confirm]").off("click").click(function(){return confirm(o(this).attr("confirm"))})}var u=n(176),c=s(u),h=n(202),d=s(h),p=n(203),f=s(p),g=n(235),v=s(g),m=n(201),y=s(m),b=n(204),w=s(b),_=n(58);s(_);window.$=t,window._=i,window.Backbone=r,window.panels=c.default,i.extend(window,d.default),window.async_save_text=f.default,window.make_popupmenu=v.default.make_popupmenu,window.make_popup_menus=v.default.make_popup_menus,window.init_tag_click_function=y.default,window.init_refresh_on_change=l,o(document).ready(function(){function t(){void 0!==Galaxy.root?o.getJSON(Galaxy.root+"api/webhooks/onload/all",function(t){i.each(t,function(t){t.activate&&t.script&&(o("