diff --git a/client/galaxy/scripts/mvc/history/history-view-edit.js b/client/galaxy/scripts/mvc/history/history-view-edit.js index 342518c34bd..b31ab4ca857 100644 --- a/client/galaxy/scripts/mvc/history/history-view-edit.js +++ b/client/galaxy/scripts/mvc/history/history-view-edit.js @@ -483,8 +483,7 @@ var HistoryViewEdit = _super.extend( this.$list().before([this._renderDropTargetHelp(), $dropTarget]); for (var evName in dropHandlers) { if (dropHandlers.hasOwnProperty(evName)) { - //console.debug( evName, dropHandlers[ evName ] ); - $dropTarget.on(evName, dropHandlers[evName]); + $dropTarget.get(0).addEventListener(evName, dropHandlers[evName]); } } return this; @@ -551,8 +550,7 @@ var HistoryViewEdit = _super.extend( drop: function(ev) { ev.preventDefault(); //ev.stopPropagation(); - - var dataTransfer = ev.originalEvent.dataTransfer; + var dataTransfer = ev.dataTransfer; var data = dataTransfer.getData("text"); dataTransfer.dropEffect = "move"; diff --git a/client/galaxy/scripts/mvc/workflow/workflow-manager.js b/client/galaxy/scripts/mvc/workflow/workflow-manager.js index 60b4b494725..afb8059ddbd 100644 --- a/client/galaxy/scripts/mvc/workflow/workflow-manager.js +++ b/client/galaxy/scripts/mvc/workflow/workflow-manager.js @@ -187,7 +187,7 @@ class Workflow { id: node.id, type: node.type, content_id: node.content_id, - tool_version: node.config_form.version, + tool_version: node.config_form ? node.config_form.version : null, tool_state: node.tool_state, errors: node.errors, input_connections: input_connections, diff --git a/lib/galaxy/actions/library.py b/lib/galaxy/actions/library.py index aec0c500566..432ff9359bd 100644 --- a/lib/galaxy/actions/library.py +++ b/lib/galaxy/actions/library.py @@ -248,7 +248,7 @@ class LibraryActions(object): uploaded_dataset.dbkey = params.get('dbkey', None) uploaded_dataset.to_posix_lines = params.get('to_posix_lines', None) uploaded_dataset.space_to_tab = params.get('space_to_tab', None) - uploaded_dataset.tag_using_filenames = params.get('tag_using_filenames', True) + uploaded_dataset.tag_using_filenames = params.get('tag_using_filenames', False) uploaded_dataset.purge_source = getattr(trans.app.config, 'ftp_upload_purge', True) if in_folder: uploaded_dataset.in_folder = in_folder diff --git a/lib/galaxy/datatypes/data.py b/lib/galaxy/datatypes/data.py index e17b6f68be5..46c74648da0 100644 --- a/lib/galaxy/datatypes/data.py +++ b/lib/galaxy/datatypes/data.py @@ -430,7 +430,7 @@ class Data(object): else: trans.response.set_content_type("text/html") return trans.stream_template_mako("/dataset/large_file.mako", - truncated_data=open(data.file_name).read(max_peek_size), + truncated_data=open(data.file_name, 'rb').read(max_peek_size), data=data) def _yield_user_file_content(self, trans, from_dataset, filename): diff --git a/lib/galaxy/datatypes/genetics.py b/lib/galaxy/datatypes/genetics.py index ab22d49e13d..90b16536503 100644 --- a/lib/galaxy/datatypes/genetics.py +++ b/lib/galaxy/datatypes/genetics.py @@ -68,7 +68,7 @@ class GenomeGraphs(Tabular): """ Returns file """ - return open(dataset.file_name, 'r') + return open(dataset.file_name, 'rb') def ucsc_links(self, dataset, type, app, base_url): """ diff --git a/lib/galaxy/datatypes/interval.py b/lib/galaxy/datatypes/interval.py index 63c664db2b5..4af4f88fca0 100644 --- a/lib/galaxy/datatypes/interval.py +++ b/lib/galaxy/datatypes/interval.py @@ -100,42 +100,38 @@ class Interval(Tabular): else: # Header lines in Interval files are optional. For example, BED is Interval but has no header. # We'll make a best guess at the location of the metadata columns. - metadata_is_set = False elems = line.split('\t') if len(elems) > 2: - for str in data.col1_startswith: - if line.lower().startswith(str): - if overwrite or not dataset.metadata.element_is_set('chromCol'): - dataset.metadata.chromCol = 1 - try: - int(elems[1]) - if overwrite or not dataset.metadata.element_is_set('startCol'): - dataset.metadata.startCol = 2 - except Exception: - pass # Metadata default will be used - try: - int(elems[2]) - if overwrite or not dataset.metadata.element_is_set('endCol'): - dataset.metadata.endCol = 3 - except Exception: - pass # Metadata default will be used - # we no longer want to guess that this column is the 'name', name must now be set manually for interval files - # we will still guess at the strand, as we can make a more educated guess - # if len( elems ) > 3: - # try: - # int( elems[3] ) - # except Exception: - # if overwrite or not dataset.metadata.element_is_set( 'nameCol' ): - # dataset.metadata.nameCol = 4 - if len(elems) < 6 or elems[5] not in data.valid_strand: - if overwrite or not dataset.metadata.element_is_set('strandCol'): - dataset.metadata.strandCol = 0 - else: - if overwrite or not dataset.metadata.element_is_set('strandCol'): - dataset.metadata.strandCol = 6 - metadata_is_set = True - break - if metadata_is_set or (i - empty_line_count) > num_check_lines: + if overwrite or not dataset.metadata.element_is_set('chromCol'): + dataset.metadata.chromCol = 1 + try: + int(elems[1]) + if overwrite or not dataset.metadata.element_is_set('startCol'): + dataset.metadata.startCol = 2 + except Exception: + pass # Metadata default will be used + try: + int(elems[2]) + if overwrite or not dataset.metadata.element_is_set('endCol'): + dataset.metadata.endCol = 3 + except Exception: + pass # Metadata default will be used + # we no longer want to guess that this column is the 'name', name must now be set manually for interval files + # we will still guess at the strand, as we can make a more educated guess + # if len( elems ) > 3: + # try: + # int( elems[3] ) + # except Exception: + # if overwrite or not dataset.metadata.element_is_set( 'nameCol' ): + # dataset.metadata.nameCol = 4 + if len(elems) < 6 or elems[5] not in data.valid_strand: + if overwrite or not dataset.metadata.element_is_set('strandCol'): + dataset.metadata.strandCol = 0 + else: + if overwrite or not dataset.metadata.element_is_set('strandCol'): + dataset.metadata.strandCol = 6 + break + if (i - empty_line_count) > num_check_lines: break # Our metadata is set or we examined 100 non-empty lines, so break out of the outer loop else: empty_line_count += 1 @@ -238,7 +234,7 @@ class Interval(Tabular): for elems in util.file_iter(dataset.file_name): tmp = [elems[c], elems[s], elems[e]] fh.write('%s\n' % '\t'.join(tmp)) - return open(fh.name) + return open(fh.name, 'rb') def display_peek(self, dataset): """Returns formated html of peek""" @@ -371,7 +367,7 @@ class BedGraph(Interval): Returns file contents as is with no modifications. TODO: this is a functional stub and will need to be enhanced moving forward to provide additional support for bedgraph. """ - return open(dataset.file_name) + return open(dataset.file_name, 'rb') def get_estimated_display_viewport(self, dataset, chrom_col=0, start_col=1, end_col=2): """ @@ -403,26 +399,20 @@ class Bed(Interval): i = 0 if dataset.has_data(): for i, line in enumerate(open(dataset.file_name)): - metadata_set = False line = line.rstrip('\r\n') if line and not line.startswith('#'): elems = line.split('\t') if len(elems) > 2: - for startswith in data.col1_startswith: - if line.lower().startswith(startswith): - if len(elems) > 3: - if overwrite or not dataset.metadata.element_is_set('nameCol'): - dataset.metadata.nameCol = 4 - if len(elems) < 6: - if overwrite or not dataset.metadata.element_is_set('strandCol'): - dataset.metadata.strandCol = 0 - else: - if overwrite or not dataset.metadata.element_is_set('strandCol'): - dataset.metadata.strandCol = 6 - metadata_set = True - break - if metadata_set: - break + if len(elems) > 3: + if overwrite or not dataset.metadata.element_is_set('nameCol'): + dataset.metadata.nameCol = 4 + if len(elems) < 6: + if overwrite or not dataset.metadata.element_is_set('strandCol'): + dataset.metadata.strandCol = 0 + else: + if overwrite or not dataset.metadata.element_is_set('strandCol'): + dataset.metadata.strandCol = 6 + break Tabular.set_meta(self, dataset, overwrite=overwrite, skip=i) def as_ucsc_display_file(self, dataset, **kwd): @@ -459,7 +449,7 @@ class Bed(Interval): break try: - return open(dataset.file_name) + return open(dataset.file_name, 'rb') except Exception: return "This item contains no content" @@ -494,75 +484,67 @@ class Bed(Interval): for hdr in headers: if hdr[0] == '': continue - valid_col1 = False if len(hdr) < 3 or len(hdr) > 12: return False - for str in data.col1_startswith: - if hdr[0].lower().startswith(str): - valid_col1 = True - break - if valid_col1: + try: + int(hdr[1]) + int(hdr[2]) + except Exception: + return False + if len(hdr) > 4: + # hdr[3] is a string, 'name', which defines the name of the BED line - difficult to test for this. + # hdr[4] is an int, 'score', a score between 0 and 1000. try: - int(hdr[1]) - int(hdr[2]) + if int(hdr[4]) < 0 or int(hdr[4]) > 1000: + return False except Exception: return False - if len(hdr) > 4: - # hdr[3] is a string, 'name', which defines the name of the BED line - difficult to test for this. - # hdr[4] is an int, 'score', a score between 0 and 1000. + if len(hdr) > 5: + # hdr[5] is strand + if hdr[5] not in data.valid_strand: + return False + if len(hdr) > 6: + # hdr[6] is thickStart, the starting position at which the feature is drawn thickly. + try: + int(hdr[6]) + except Exception: + return False + if len(hdr) > 7: + # hdr[7] is thickEnd, the ending position at which the feature is drawn thickly + try: + int(hdr[7]) + except Exception: + return False + if len(hdr) > 8: + # hdr[8] is itemRgb, an RGB value of the form R,G,B (e.g. 255,0,0). However, this could also be an int (e.g., 0) + try: + int(hdr[8]) + except Exception: try: - if int(hdr[4]) < 0 or int(hdr[4]) > 1000: - return False + hdr[8].split(',') except Exception: return False - if len(hdr) > 5: - # hdr[5] is strand - if hdr[5] not in data.valid_strand: - return False - if len(hdr) > 6: - # hdr[6] is thickStart, the starting position at which the feature is drawn thickly. - try: - int(hdr[6]) - except Exception: - return False - if len(hdr) > 7: - # hdr[7] is thickEnd, the ending position at which the feature is drawn thickly - try: - int(hdr[7]) - except Exception: - return False - if len(hdr) > 8: - # hdr[8] is itemRgb, an RGB value of the form R,G,B (e.g. 255,0,0). However, this could also be an int (e.g., 0) - try: - int(hdr[8]) - except Exception: - try: - hdr[8].split(',') - except Exception: - return False - if len(hdr) > 9: - # hdr[9] is blockCount, the number of blocks (exons) in the BED line. - try: - block_count = int(hdr[9]) - except Exception: - return False - if len(hdr) > 10: - # hdr[10] is blockSizes - A comma-separated list of the block sizes. - # Sometimes the blosck_sizes and block_starts lists end in extra commas - try: - block_sizes = hdr[10].rstrip(',').split(',') - except Exception: - return False - if len(hdr) > 11: - # hdr[11] is blockStarts - A comma-separated list of block starts. - try: - block_starts = hdr[11].rstrip(',').split(',') - except Exception: - return False - if len(block_sizes) != block_count or len(block_starts) != block_count: - return False - else: - return False + if len(hdr) > 9: + # hdr[9] is blockCount, the number of blocks (exons) in the BED line. + try: + block_count = int(hdr[9]) + except Exception: + return False + if len(hdr) > 10: + # hdr[10] is blockSizes - A comma-separated list of the block sizes. + # Sometimes the blosck_sizes and block_starts lists end in extra commas + try: + block_sizes = hdr[10].rstrip(',').split(',') + except Exception: + return False + if len(hdr) > 11: + # hdr[11] is blockStarts - A comma-separated list of block starts. + try: + block_starts = hdr[11].rstrip(',').split(',') + except Exception: + return False + if len(block_sizes) != block_count or len(block_starts) != block_count: + return False return True except Exception: return False @@ -1201,16 +1183,14 @@ class Wiggle(Tabular, _RemoteCallMixin): if line and not line.startswith('#'): elems = line.split('\t') try: - float(elems[0]) # "Wiggle track data values can be integer or real, positive or negative values" + # variableStep format is nucleotide position\tvalue\n, + # fixedStep is value\n + # "Wiggle track data values can be integer or real, positive or negative values" + float(elems[0]) break except Exception: - do_break = False - for col_startswith in data.col1_startswith: - if elems[0].lower().startswith(col_startswith): - do_break = True - break - if do_break: - break + # We are either in the track definition line or in a declaration line + pass if self.max_optional_metadata_filesize >= 0 and dataset.get_size() > self.max_optional_metadata_filesize: # we'll arbitrarily only use the first 100 data lines in this wig file to calculate tabular attributes (column types) # this should be sufficient, except when we have mixed wig track types (bed, variable, fixed), diff --git a/lib/galaxy/datatypes/sniff.py b/lib/galaxy/datatypes/sniff.py index 8c8c9aea3fd..0f44b2e81f4 100644 --- a/lib/galaxy/datatypes/sniff.py +++ b/lib/galaxy/datatypes/sniff.py @@ -199,27 +199,19 @@ def convert_newlines_sep2tabs(fname, in_place=True, patt=r"\s+", tmp_dir=None, t def iter_headers(fname_or_file_prefix, sep, count=60, comment_designator=None): + idx = 0 if isinstance(fname_or_file_prefix, FilePrefix): - idx = 0 - for line in fname_or_file_prefix.line_iterator(): - line = line.rstrip('\n\r') - if comment_designator is not None and comment_designator != '' and line.startswith(comment_designator): - continue - yield line.split(sep) - idx += 1 - if idx == count: - break + file_iterator = fname_or_file_prefix.line_iterator() else: - with compression_utils.get_fileobj(fname_or_file_prefix) as in_file: - idx = 0 - for line in in_file: - line = line.rstrip('\n\r') - if comment_designator is not None and comment_designator != '' and line.startswith(comment_designator): - continue - yield line.split(sep) - idx += 1 - if idx == count: - break + file_iterator = compression_utils.get_fileobj(fname_or_file_prefix) + for line in file_iterator: + line = line.rstrip('\n\r') + if comment_designator is not None and comment_designator != '' and line.startswith(comment_designator): + continue + yield line.split(sep) + idx += 1 + if idx == count: + break def get_headers(fname_or_file_prefix, sep, count=60, comment_designator=None): diff --git a/lib/galaxy/datatypes/tabular.py b/lib/galaxy/datatypes/tabular.py index 5fd331627d3..4bb7640a804 100644 --- a/lib/galaxy/datatypes/tabular.py +++ b/lib/galaxy/datatypes/tabular.py @@ -394,10 +394,10 @@ class Tabular(TabularData): dataset.metadata.delimiter = '\t' def as_gbrowse_display_file(self, dataset, **kwd): - return open(dataset.file_name) + return open(dataset.file_name, 'rb') def as_ucsc_display_file(self, dataset, **kwd): - return open(dataset.file_name) + return open(dataset.file_name, 'rb') class Taxonomy(Tabular): diff --git a/lib/galaxy/exceptions/__init__.py b/lib/galaxy/exceptions/__init__.py index be9a1d5dee1..9f47371b36d 100644 --- a/lib/galaxy/exceptions/__init__.py +++ b/lib/galaxy/exceptions/__init__.py @@ -103,6 +103,10 @@ class ToolMissingException(MessageException): status_code = 400 err_code = error_codes.USER_TOOL_MISSING_PROBLEM + def __init__(self, err_msg=None, type="info", tool_id=None, **extra_error_info): + super(ToolMissingException, self).__init__(err_msg, type, **extra_error_info) + self.tool_id = tool_id + class RequestParameterInvalidException(MessageException): status_code = 400 diff --git a/lib/galaxy/managers/workflows.py b/lib/galaxy/managers/workflows.py index 6a3f15e833a..067b9a4f7fa 100644 --- a/lib/galaxy/managers/workflows.py +++ b/lib/galaxy/managers/workflows.py @@ -505,9 +505,10 @@ class WorkflowContentsManager(UsesAnnotations): for step in workflow.steps: try: module_injector.inject(step, steps=workflow.steps, exact_tools=False) - except exceptions.ToolMissingException: - if step.tool_id not in missing_tools: - missing_tools.append(step.tool_id) + except exceptions.ToolMissingException as e: + # FIXME: if a subworkflow lacks multiple tools we report only the first missing tool + if e.tool_id not in missing_tools: + missing_tools.append(e.tool_id) continue if step.upgrade_messages: has_upgrade_messages = True diff --git a/lib/galaxy/webapps/galaxy/api/library_contents.py b/lib/galaxy/webapps/galaxy/api/library_contents.py index 81c86d15dac..4fb2c7de9d2 100644 --- a/lib/galaxy/webapps/galaxy/api/library_contents.py +++ b/lib/galaxy/webapps/galaxy/api/library_contents.py @@ -230,6 +230,8 @@ class LibraryContentsController(BaseAPIController, UsesLibraryMixin, UsesLibrary # The rest of the security happens in the library_common controller. real_folder_id = trans.security.encode_id(parent.id) + payload['tag_using_filenames'] = util.string_as_bool(payload.get('tag_using_filenames', None)) + # are we copying an HDA to the library folder? # we'll need the id and any message to attach, then branch to that private function from_hda_id, from_hdca_id, ldda_message = (payload.pop('from_hda_id', None), payload.pop('from_hdca_id', None), payload.pop('ldda_message', '')) diff --git a/lib/galaxy/webapps/galaxy/api/library_datasets.py b/lib/galaxy/webapps/galaxy/api/library_datasets.py index 3302b9cdd9e..cb4d2eec9e2 100644 --- a/lib/galaxy/webapps/galaxy/api/library_datasets.py +++ b/lib/galaxy/webapps/galaxy/api/library_datasets.py @@ -706,7 +706,7 @@ class LibraryDatasetsController(BaseAPIController, UsesVisualizationMixin, Libra fname = ''.join(c in util.FILENAME_VALID_CHARS and c or '_' for c in fname)[0:150] trans.response.headers["Content-Disposition"] = 'attachment; filename="%s"' % fname try: - return open(dataset.file_name) + return open(dataset.file_name, 'rb') except Exception: raise exceptions.InternalServerError("This dataset contains no content.") else: diff --git a/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py b/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py index da50198f54f..79aae81b5a0 100644 --- a/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py +++ b/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py @@ -276,7 +276,7 @@ class AdminToolshed(AdminGalaxy): mimetype = trans.app.datatypes_registry.get_mimetype_by_extension(extension) if mimetype: trans.response.set_content_type(mimetype) - return open(path_to_file, 'r') + return open(path_to_file, 'rb') return None @web.expose diff --git a/lib/galaxy/webapps/galaxy/controllers/dataset.py b/lib/galaxy/webapps/galaxy/controllers/dataset.py index 11cdd2f9b43..c16aaa96c93 100644 --- a/lib/galaxy/webapps/galaxy/controllers/dataset.py +++ b/lib/galaxy/webapps/galaxy/controllers/dataset.py @@ -186,7 +186,7 @@ class DatasetInterface(BaseUIController, UsesAnnotations, UsesItemRatings, UsesE file_ext = data.metadata.spec.get(metadata_name).get("file_ext", metadata_name) trans.response.headers["Content-Type"] = "application/octet-stream" trans.response.headers["Content-Disposition"] = 'attachment; filename="Galaxy%s-[%s].%s"' % (data.hid, fname, file_ext) - return open(data.metadata.get(metadata_name).file_name) + return open(data.metadata.get(metadata_name).file_name, 'rb') def _check_dataset(self, trans, hda_id): # DEPRECATION: We still support unencoded ids for backward compatibility @@ -677,7 +677,7 @@ class DatasetInterface(BaseUIController, UsesAnnotations, UsesItemRatings, UsesE # TODO: figure out a way to display images in display template. if isinstance(dataset.datatype, datatypes.binary.Binary) or isinstance(dataset.datatype, datatypes.images.Image) or isinstance(dataset.datatype, datatypes.text.Html): trans.response.set_content_type(dataset.get_mime()) - return open(dataset.file_name) + return open(dataset.file_name, 'rb') else: # Get rating data. user_item_rating = 0 diff --git a/lib/galaxy/webapps/galaxy/controllers/root.py b/lib/galaxy/webapps/galaxy/controllers/root.py index 5851f879cfd..82910b344fa 100644 --- a/lib/galaxy/webapps/galaxy/controllers/root.py +++ b/lib/galaxy/webapps/galaxy/controllers/root.py @@ -216,7 +216,7 @@ class RootController(controller.JSAppLauncher, UsesAnnotations): trans.response.headers["Content-Disposition"] = 'attachment; filename="GalaxyHistoryItem-%s-[%s]%s"' % (data.hid, fname, toext) trans.log_event("Display dataset id: %s" % str(id)) try: - return open(data.file_name) + return open(data.file_name, 'rb') except Exception: return "This dataset contains no content" else: diff --git a/lib/galaxy/webapps/galaxy/controllers/workflow.py b/lib/galaxy/webapps/galaxy/controllers/workflow.py index fdcce648d47..de142ca2cb6 100644 --- a/lib/galaxy/webapps/galaxy/controllers/workflow.py +++ b/lib/galaxy/webapps/galaxy/controllers/workflow.py @@ -601,7 +601,7 @@ class WorkflowController(BaseUIController, SharableMixin, UsesStoredWorkflowMixi session.flush() try: - workflow, errors = workflow_contents_manager.update_workflow_from_dict( + workflow, errors = workflow_contents_manager.update_workflow_from_raw_description( trans, stored_workflow, workflow_data, diff --git a/lib/galaxy/webapps/tool_shed/controllers/repository.py b/lib/galaxy/webapps/tool_shed/controllers/repository.py index 0935c15ca5f..40eb328e4fe 100644 --- a/lib/galaxy/webapps/tool_shed/controllers/repository.py +++ b/lib/galaxy/webapps/tool_shed/controllers/repository.py @@ -870,7 +870,7 @@ class RepositoryController(BaseUIController, ratings_util.ItemRatings): mimetype = trans.app.datatypes_registry.get_mimetype_by_extension(extension) if mimetype: trans.response.set_content_type(mimetype) - return open(path_to_file, 'r') + return open(path_to_file, 'rb') return None @web.expose diff --git a/lib/galaxy/workflow/modules.py b/lib/galaxy/workflow/modules.py index 707695844fb..87a9b69acd3 100644 --- a/lib/galaxy/workflow/modules.py +++ b/lib/galaxy/workflow/modules.py @@ -144,7 +144,7 @@ class WorkflowModule(object): else: self.state.inputs = safe_loads(state) or {} - def get_errors(self): + def get_errors(self, **kwargs): """ This returns a step related error message as string or None """ return None @@ -414,6 +414,16 @@ class SubWorkflowModule(WorkflowModule): inputs.append(input) return inputs + def get_modules(self): + return [module_factory.from_workflow_step(self.trans, step) for step in self.subworkflow.steps] + + def get_errors(self, **kwargs): + errors = (module.get_errors(include_tool_id=True) for module in self.get_modules()) + errors = [e for e in errors if e] + if any(errors): + return errors + return None + def get_all_outputs(self, data_only=False): outputs = [] if hasattr(self.subworkflow, 'workflow_outputs'): @@ -425,20 +435,23 @@ class SubWorkflowModule(WorkflowModule): tooltip=False) for order_index in sorted(subworkflow_dict['steps']): step = subworkflow_dict['steps'][order_index] - data_outputs = subworkflow_dict['steps'][order_index]['outputs'] + data_outputs = step['outputs'] for workflow_output in step['workflow_outputs']: label = workflow_output['label'] if not label: label = "%s:%s" % (order_index, workflow_output['output_name']) for data_output in data_outputs: - if data_output['name'] == workflow_output['output_name']: + if data_output['name'] == workflow_output['output_name'] or data_output['uuid'] == workflow_output['uuid']: data_output['label'] = label data_output['name'] = label # That's the right data_output break else: - # This hopefully can't happen, but let's be clear - raise Exception("Workflow output '%s' defined, but not listed among data outputs" % workflow_output['output_name']) + # This can happen when importing workflows with missing tools. + # We can't raise an exception here, as that would prevent loading + # the workflow. + log.error("Workflow output '%s' defined, but not listed among data outputs" % workflow_output['output_name']) + continue outputs.append(data_output) return outputs @@ -841,8 +854,12 @@ class ToolModule(WorkflowModule): # ---- Configuration time ----------------------------------------------- - def get_errors(self): - return None if self.tool else "Tool is not installed." + def get_errors(self, include_tool_id=False, **kwargs): + if not self.tool: + if include_tool_id: + return "%s is not installed" % self.tool_id + else: + return "Tool is not installed" def get_inputs(self): return self.tool.inputs if self.tool else {} @@ -982,7 +999,8 @@ class ToolModule(WorkflowModule): return ConnectedValue() visit_input_values(self.tool.inputs, self.state.inputs, callback) else: - raise ToolMissingException("Tool %s missing. Cannot add dummy datasets." % self.tool_id) + raise ToolMissingException("Tool %s missing. Cannot add dummy datasets." % self.tool_id, + tool_id=self.tool_id) def get_post_job_actions(self, incoming): return ActionBox.handle_incoming(incoming) @@ -1082,7 +1100,8 @@ class ToolModule(WorkflowModule): state.inputs[RUNTIME_STEP_META_STATE_KEY] = step_metadata_runtime_state return state, step_errors else: - raise ToolMissingException("Tool %s missing. Cannot compute runtime state." % self.tool_id) + raise ToolMissingException("Tool %s missing. Cannot compute runtime state." % self.tool_id, + tool_id=self.tool_id) def decode_runtime_state(self, runtime_state): """ Take runtime state from persisted invocation and convert it @@ -1094,7 +1113,8 @@ class ToolModule(WorkflowModule): self.__restore_step_meta_runtime_state(loads(runtime_state[RUNTIME_STEP_META_STATE_KEY])) return state else: - raise ToolMissingException("Tool %s missing. Cannot recover runtime state." % self.tool_id) + raise ToolMissingException("Tool %s missing. Cannot recover runtime state." % self.tool_id, + tool_id=self.tool_id) def execute(self, trans, progress, invocation_step, use_cached_job=False): invocation = invocation_step.workflow_invocation diff --git a/tools/data_source/data_source.py b/tools/data_source/data_source.py index 8dc33aee626..eaaa1f2b66f 100644 --- a/tools/data_source/data_source.py +++ b/tools/data_source/data_source.py @@ -89,7 +89,7 @@ def __main__(): if not URL_method or URL_method == 'get': page = urlopen(cur_URL) elif URL_method == 'post': - page = urlopen(cur_URL, urlencode(params)) + page = urlopen(cur_URL, urlencode(params).encode("utf-8")) except Exception as e: stop_err('The remote data source application may be off line, please try again later. Error: %s' % str(e)) if max_file_size: