1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/3e00d002e7ba/
Changeset: 3e00d002e7ba
User: jmchilton
Date: 2014-08-27 16:49:15
Summary: Reduce workflow step state and module population duplication.
Move all operations prior to workflow invocation (and a few other places state and modules are used) relating to populating the non-persistent attributes of `WorkflowStep`s (e.g. module, state, upgrade_messages, and input_connections_by_name) into a single function on a new class.
This should fix a couple of small errors as well - input_connections_by_name was being mis-calculated in a few places (though potentially never used down those roads) and some error handling code in the workflow editor template was expecting step.module to be populated with something (even if None) and it was not always.
Affected #: 4 files
diff -r 7c566a68d2d07375c53006191671dfed04332f55 -r 3e00d002e7ba1fdd9d914bcd68589aad5c1e21ff lib/galaxy/web/base/controller.py
--- a/lib/galaxy/web/base/controller.py
+++ b/lib/galaxy/web/base/controller.py
@@ -26,7 +26,7 @@
from galaxy.web import error, url_for
from galaxy.web.form_builder import AddressField, CheckboxField, SelectField, TextArea, TextField
from galaxy.web.form_builder import build_select_field, HistoryField, PasswordField, WorkflowField, WorkflowMappingField
-from galaxy.workflow.modules import module_factory
+from galaxy.workflow.modules import module_factory, WorkflowModuleInjector, MissingToolException
from galaxy.model.orm import eagerload, eagerload_all, desc
from galaxy.security.validate_user_input import validate_publicname
from galaxy.util.sanitize_html import sanitize_html
@@ -1625,30 +1625,13 @@
def get_stored_workflow_steps( self, trans, stored_workflow ):
""" Restores states for a stored workflow's steps. """
+ module_injector = WorkflowModuleInjector( trans )
for step in stored_workflow.latest_workflow.steps:
- step.upgrade_messages = {}
- if step.type == 'tool' or step.type is None:
- # Restore the tool state for the step
- module = module_factory.from_workflow_step( trans, step )
- if module:
- #Check if tool was upgraded
- step.upgrade_messages = module.check_and_update_state()
- # Any connected input needs to have value DummyDataset (these
- # are not persisted so we need to do it every time)
- module.add_dummy_datasets( connections=step.input_connections )
- # Store state with the step
- step.module = module
- step.state = module.state
- else:
- step.upgrade_messages = "Unknown Tool ID"
- step.module = None
- step.state = None
- else:
- ## Non-tool specific stuff?
- step.module = module_factory.from_workflow_step( trans, step )
- step.state = step.module.get_runtime_state()
- # Connections by input name
- step.input_connections_by_name = dict( ( conn.input_name, conn ) for conn in step.input_connections )
+ try:
+ module_injector.inject( step )
+ except MissingToolException:
+ # Now upgrade_messages is a string instead of a dict, why?
+ step.upgrade_messages = "Unknown Tool ID"
def _import_shared_workflow( self, trans, stored):
""" """
diff -r 7c566a68d2d07375c53006191671dfed04332f55 -r 3e00d002e7ba1fdd9d914bcd68589aad5c1e21ff lib/galaxy/webapps/galaxy/controllers/workflow.py
--- a/lib/galaxy/webapps/galaxy/controllers/workflow.py
+++ b/lib/galaxy/webapps/galaxy/controllers/workflow.py
@@ -24,6 +24,8 @@
from galaxy.web.framework import form
from galaxy.web.framework.helpers import grids, time_ago
from galaxy.web.framework.helpers import to_unicode
+from galaxy.workflow.modules import WorkflowModuleInjector
+from galaxy.workflow.modules import MissingToolException
from galaxy.workflow.modules import module_factory, is_tool_module_type
from galaxy.workflow.run import invoke
from galaxy.workflow.run import WorkflowRunConfig
@@ -1269,6 +1271,7 @@
error("That history does not exist.")
try: # use a try/finally block to restore the user's current history
default_target_history = trans.get_history()
+ module_injector = WorkflowModuleInjector( trans )
if kwargs:
# If kwargs were provided, the states for each step should have
# been POSTed
@@ -1276,45 +1279,15 @@
invocations = []
for (kwargs, multi_input_keys) in _expand_multiple_inputs(kwargs):
for step in workflow.steps:
- step.upgrade_messages = {}
- # Connections by input name
- input_connections_by_name = {}
- for conn in step.input_connections:
- input_name = conn.input_name
- if not input_name in input_connections_by_name:
- input_connections_by_name[input_name] = []
- input_connections_by_name[input_name].append(conn)
- step.input_connections_by_name = input_connections_by_name
# Extract just the arguments for this step by prefix
p = "%s|" % step.id
l = len(p)
step_args = dict( ( k[l:], v ) for ( k, v ) in kwargs.iteritems() if k.startswith( p ) )
- step_errors = None
- if step.type == 'tool' or step.type is None:
- module = module_factory.from_workflow_step( trans, step )
- # Fix any missing parameters
- step.upgrade_messages = module.check_and_update_state()
- if step.upgrade_messages:
- has_upgrade_messages = True
- # Any connected input needs to have value DummyDataset (these
- # are not persisted so we need to do it every time)
- module.add_dummy_datasets( connections=step.input_connections )
- # Get the tool
- tool = module.tool
- # Get the state
- step.state = state = module.state
- # Get old errors
- old_errors = state.inputs.pop( "__errors__", {} )
- # Update the state
- step_errors = tool.update_state( trans, tool.inputs, step.state.inputs, step_args,
- update_only=True, old_errors=old_errors )
- else:
- # Fix this for multiple inputs
- module = step.module = module_factory.from_workflow_step( trans, step )
- state = step.state = module.decode_runtime_state( trans, step_args.pop( "tool_state" ) )
- step_errors = module.update_runtime_state( trans, state, step_args )
+ step_errors = module_injector.inject( step, step_args )
+ if step.upgrade_messages:
+ has_upgrade_messages = True
if step_errors:
- errors[step.id] = state.inputs["__errors__"] = step_errors
+ errors[step.id] = step.state.inputs["__errors__"] = step_errors
if 'run_workflow' in kwargs and not errors:
new_history = None
if 'new_history' in kwargs:
@@ -1361,36 +1334,20 @@
# Prepare each step
missing_tools = []
for step in workflow.steps:
- step.upgrade_messages = {}
- # Contruct modules
+ try:
+ module_injector.inject( step )
+ except MissingToolException:
+ if step.tool_id not in missing_tools:
+ missing_tools.append(step.tool_id)
+ continue
+ if step.upgrade_messages:
+ has_upgrade_messages = True
if step.type == 'tool' or step.type is None:
- # Restore the tool state for the step
- step.module = module_factory.from_workflow_step( trans, step )
- if not step.module:
- if step.tool_id not in missing_tools:
- missing_tools.append(step.tool_id)
- continue
- step.upgrade_messages = step.module.check_and_update_state()
- if step.upgrade_messages:
- has_upgrade_messages = True
- if step.type == 'tool' and step.module.version_changes:
+ if step.module.version_changes:
step_version_changes.extend(step.module.version_changes)
- # Any connected input needs to have value DummyDataset (these
- # are not persisted so we need to do it every time)
- step.module.add_dummy_datasets( connections=step.input_connections )
- # Store state with the step
- step.state = step.module.state
# Error dict
if step.tool_errors:
- # has_errors is never used.
- # has_errors = True
errors[step.id] = step.tool_errors
- else:
- ## Non-tool specific stuff?
- step.module = module_factory.from_workflow_step( trans, step )
- step.state = step.module.get_runtime_state()
- # Connections by input name
- step.input_connections_by_name = dict( ( conn.input_name, conn ) for conn in step.input_connections )
if missing_tools:
stored.annotation = self.get_item_annotation_str( trans.sa_session, trans.user, stored )
return trans.fill_template(
@@ -1464,32 +1421,17 @@
trans.sa_session.add(m)
# Prepare each step
trans.sa_session.flush()
+ module_injector = WorkflowModuleInjector( trans )
for step in workflow.steps:
step.upgrade_messages = {}
# Contruct modules
+ module_injector.inject( step )
+ if step.upgrade_messages:
+ has_upgrade_messages = True
if step.type == 'tool' or step.type is None:
- # Restore the tool state for the step
- step.module = module_factory.from_workflow_step( trans, step )
- # Fix any missing parameters
- step.upgrade_messages = step.module.check_and_update_state()
- if step.upgrade_messages:
- has_upgrade_messages = True
- # Any connected input needs to have value DummyDataset (these
- # are not persisted so we need to do it every time)
- step.module.add_dummy_datasets( connections=step.input_connections )
- # Store state with the step
- step.state = step.module.state
# Error dict
if step.tool_errors:
- # has_errors is never used
- # has_errors = True
errors[step.id] = step.tool_errors
- else:
- ## Non-tool specific stuff?
- step.module = module_factory.from_workflow_step( trans, step )
- step.state = step.module.get_runtime_state()
- # Connections by input name
- step.input_connections_by_name = dict( ( conn.input_name, conn ) for conn in step.input_connections )
# Render the form
return trans.fill_template(
"workflow/tag_outputs.mako",
diff -r 7c566a68d2d07375c53006191671dfed04332f55 -r 3e00d002e7ba1fdd9d914bcd68589aad5c1e21ff lib/galaxy/workflow/modules.py
--- a/lib/galaxy/workflow/modules.py
+++ b/lib/galaxy/workflow/modules.py
@@ -559,3 +559,81 @@
tool=ToolModule,
)
module_factory = WorkflowModuleFactory( module_types )
+
+
+class MissingToolException( Exception ):
+ """ WorkflowModuleInjector will raise this if the tool corresponding to the
+ module is missing. """
+
+
+class WorkflowModuleInjector(object):
+ """ Injects workflow step objects from the ORM with appropriate module and
+ module generated/influenced state. """
+
+ def __init__( self, trans ):
+ self.trans = trans
+
+ def inject( self, step, step_args=None ):
+ """ Pre-condition: `step` is an ORM object coming from the database, if
+ supplied `step_args` is the representation of the inputs for that step
+ supplied via web form.
+
+ Post-condition: The supplied `step` has new non-persistent attributes
+ useful during workflow invocation. These include 'upgrade_messages',
+ 'state', 'input_connections_by_name', and 'module'.
+
+ If step_args is provided from a web form this is applied to generate
+ 'state' else it is just obtained from the database.
+ """
+ trans = self.trans
+
+ step_errors = None
+
+ step.upgrade_messages = {}
+
+ # Make connection information available on each step by input name.
+ input_connections_by_name = {}
+ for conn in step.input_connections:
+ input_name = conn.input_name
+ if not input_name in input_connections_by_name:
+ input_connections_by_name[input_name] = []
+ input_connections_by_name[input_name].append(conn)
+ step.input_connections_by_name = input_connections_by_name
+
+ # Populate module.
+ module = step.module = module_factory.from_workflow_step( trans, step )
+
+ # Calculating step errors and state depends on whether step is a tool step or not.
+ if step.type == 'tool' or step.type is None:
+ if not module:
+ step.module = None
+ step.state = None
+ raise MissingToolException()
+
+ # Fix any missing parameters
+ step.upgrade_messages = module.check_and_update_state()
+
+ # Any connected input needs to have value DummyDataset (these
+ # are not persisted so we need to do it every time)
+ module.add_dummy_datasets( connections=step.input_connections )
+
+ state = module.state
+ step.state = state
+ if step_args is not None:
+ # Get the tool
+ tool = module.tool
+ # Get old errors
+ old_errors = state.inputs.pop( "__errors__", {} )
+ # Update the state
+ step_errors = tool.update_state( trans, tool.inputs, step.state.inputs, step_args,
+ update_only=True, old_errors=old_errors )
+
+ else:
+ if step_args:
+ # Fix this for multiple inputs
+ state = step.state = module.decode_runtime_state( trans, step_args.pop( "tool_state" ) )
+ step_errors = module.update_runtime_state( trans, state, step_args )
+ else:
+ step.state = step.module.get_runtime_state()
+
+ return step_errors
diff -r 7c566a68d2d07375c53006191671dfed04332f55 -r 3e00d002e7ba1fdd9d914bcd68589aad5c1e21ff lib/galaxy/workflow/run.py
--- a/lib/galaxy/workflow/run.py
+++ b/lib/galaxy/workflow/run.py
@@ -229,24 +229,10 @@
def _populate_state( self ):
# Build the state for each step
+ module_injector = modules.WorkflowModuleInjector( self.trans )
for step in self.workflow.steps:
- step_errors = None
- input_connections_by_name = {}
- for conn in step.input_connections:
- input_name = conn.input_name
- if not input_name in input_connections_by_name:
- input_connections_by_name[input_name] = []
- input_connections_by_name[input_name].append(conn)
- step.input_connections_by_name = input_connections_by_name
-
+ step_errors = module_injector.inject( step )
if step.type == 'tool' or step.type is None:
- step.module = modules.module_factory.from_workflow_step( self.trans, step )
- # Check for missing parameters
- step.upgrade_messages = step.module.check_and_update_state()
- # Any connected input needs to have value DummyDataset (these
- # are not persisted so we need to do it every time)
- step.module.add_dummy_datasets( connections=step.input_connections )
- step.state = step.module.state
_update_step_parameters( step, self.param_map )
if step.tool_errors:
message = "Workflow cannot be run because of validation errors in some steps: %s" % step_errors
@@ -254,9 +240,6 @@
if step.upgrade_messages:
message = "Workflow cannot be run because of step upgrade messages: %s" % step.upgrade_messages
raise exceptions.MessageException( message )
- else:
- step.module = modules.module_factory.from_workflow_step( self.trans, step )
- step.state = step.module.get_runtime_state()
def _update_step_parameters(step, normalized_param_map):
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/51de5214e6f9/
Changeset: 51de5214e6f9
Branch: stable
User: natefoo
Date: 2014-08-26 15:00:14
Summary: Update tag latest_2014.08.11 for changeset 9a4b43021fd1
Affected #: 1 file
diff -r 9a4b43021fd1c5808c17329da7b6acbfd9ae27ed -r 51de5214e6f9e59b9bad5a05ae6a79d6e8944a9e .hgtags
--- a/.hgtags
+++ b/.hgtags
@@ -18,4 +18,4 @@
81fbe25bd02edcd53065e8e4476dd1dfb5a72cf2 latest_2013.11.04
2a756ca2cb1826db7796018e77d12e2dd7b67603 latest_2014.02.10
ca45b78adb4152fc6e7395514d46eba6b7d0b838 release_2014.08.11
-7e22f35798522100ff03e1fdd4eced962b292360 latest_2014.08.11
+9a4b43021fd1c5808c17329da7b6acbfd9ae27ed latest_2014.08.11
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/9a4b43021fd1/
Changeset: 9a4b43021fd1
Branch: stable
User: jmchilton
Date: 2014-08-25 18:36:34
Summary: Bugfix for 0750859: Don't generate duplicate workflow slugs for private workflows.
See full conversation on https://bitbucket.org/galaxy/galaxy-central/commits/07508592cb65845474b9f3e….
Would like to better and allow slugs of deleted items to be reused - they cannot be for workflows for instance. Because the slugs are not really handled uniformly though - pages do not have this problem and deleted pages can have their slugs reused because this is included in a check in the web controller which directly assigns valid slugs at that point. Slugs for deleted histories cannot be re-used - but they couldn't be prior to this commit - so this isn't making things worse in that respect.
Affected #: 1 file
diff -r d86834553f0e2d3636b3f545f61ae3250ead31e9 -r 9a4b43021fd1c5808c17329da7b6acbfd9ae27ed lib/galaxy/web/base/controller.py
--- a/lib/galaxy/web/base/controller.py
+++ b/lib/galaxy/web/base/controller.py
@@ -27,7 +27,7 @@
from galaxy.web.form_builder import AddressField, CheckboxField, SelectField, TextArea, TextField
from galaxy.web.form_builder import build_select_field, HistoryField, PasswordField, WorkflowField, WorkflowMappingField
from galaxy.workflow.modules import module_factory
-from galaxy.model.orm import eagerload, eagerload_all, desc
+from galaxy.model.orm import eagerload, eagerload_all, desc, not_
from galaxy.security.validate_user_input import validate_publicname
from galaxy.util.sanitize_html import sanitize_html
from galaxy.model.item_attrs import Dictifiable, UsesAnnotations
@@ -2694,7 +2694,7 @@
item = self.get_item( trans, id )
if item:
# Only update slug if slug is not already in use.
- if trans.sa_session.query( item.__class__ ).filter_by( user=item.user, slug=new_slug, importable=True ).count() == 0:
+ if trans.sa_session.query( item.__class__ ).filter_by( user=item.user, slug=new_slug ).count() == 0:
item.slug = new_slug
trans.sa_session.flush()
@@ -2729,7 +2729,9 @@
# add integer to end.
new_slug = slug_base
count = 1
- while sa_session.query( item.__class__ ).filter_by( user=item.user, slug=new_slug, importable=True ).count() != 0:
+ # Ensure unique across model class and user and don't include this item
+ # in the check in case it has previously been assigned a valid slug.
+ while sa_session.query( item.__class__ ).filter( item.__class__.user == item.user, item.__class__.slug == new_slug, item.__class__.id != item.id).count() != 0:
# Slug taken; choose a new slug based on count. This approach can
# handle numerous items with the same name gracefully.
new_slug = '%s-%i' % ( slug_base, count )
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.