From 47f0f822f01cbca4fd4cb89d18dbd682bf84eeae Mon Sep 17 00:00:00 2001 From: Kyle Ellrott Date: Wed, 20 Aug 2014 14:10:43 -0700 Subject: [PATCH] Refactoring work to add in Workflow UUIDs --- lib/galaxy/model/__init__.py | 12 +++- lib/galaxy/model/mapping.py | 3 +- .../migrate/versions/0121_workflow_uuids.py | 55 +++++++++++++++++++ lib/galaxy/web/base/controller.py | 2 + lib/galaxy/webapps/galaxy/api/workflows.py | 12 ++++ scripts/cleanup_datasets/populate_uuid.py | 6 ++ 6 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 lib/galaxy/model/migrate/versions/0121_workflow_uuids.py diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index 72e687fa1c4..b10980f6888 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -2974,6 +2974,7 @@ class StoredWorkflow( object, Dictifiable): tag_str += ":" + tag.user_value tags_str_list.append( tag_str ) rval['tags'] = tags_str_list + rval['latest_workflow_uuid'] = ( lambda uuid: str( uuid ) if self.latest_workflow.uuid else None )( self.latest_workflow.uuid ) return rval @@ -2982,12 +2983,16 @@ class Workflow( object, Dictifiable ): dict_collection_visible_keys = ( 'name', 'has_cycles', 'has_errors' ) dict_element_visible_keys = ( 'name', 'has_cycles', 'has_errors' ) - def __init__( self ): + def __init__( self, uuid=None ): self.user = None self.name = None self.has_cycles = None self.has_errors = None self.steps = [] + if uuid is None: + self.uuid = uuid4() + else: + self.uuid = UUID(str(uuid)) def has_outputs_defined(self): """ @@ -2998,6 +3003,11 @@ class Workflow( object, Dictifiable ): return True return False + def to_dict( self, view='collection', value_mapper=None): + rval = super( Workflow, self ).to_dict( view=view, value_mapper = value_mapper ) + rval['uuid'] = ( lambda uuid: str( uuid ) if uuid else None )( self.uuid ) + return rval + class WorkflowStep( object ): diff --git a/lib/galaxy/model/mapping.py b/lib/galaxy/model/mapping.py index 4164ecbe2d4..8f7ca819444 100644 --- a/lib/galaxy/model/mapping.py +++ b/lib/galaxy/model/mapping.py @@ -690,7 +690,8 @@ model.Workflow.table = Table( "workflow", metadata, Column( "stored_workflow_id", Integer, ForeignKey( "stored_workflow.id" ), index=True, nullable=False ), Column( "name", TEXT ), Column( "has_cycles", Boolean ), - Column( "has_errors", Boolean ) + Column( "has_errors", Boolean ), + Column( "uuid", UUIDType, nullable=True ) ) model.WorkflowStep.table = Table( "workflow_step", metadata, diff --git a/lib/galaxy/model/migrate/versions/0121_workflow_uuids.py b/lib/galaxy/model/migrate/versions/0121_workflow_uuids.py new file mode 100644 index 00000000000..19f0c8e39f8 --- /dev/null +++ b/lib/galaxy/model/migrate/versions/0121_workflow_uuids.py @@ -0,0 +1,55 @@ +""" +Add UUIDs to workflows +""" + +from sqlalchemy import * +from sqlalchemy.orm import * +from migrate import * +from migrate.changeset import * +from galaxy.model.custom_types import UUIDType, TrimmedString + +import logging +log = logging.getLogger( __name__ ) + +metadata = MetaData() + + + +""" +Because both workflow and job requests can be determined +based the a fixed data structure, their IDs are based on +hashing the data structure +""" +workflow_uuid_column = Column( "uuid", UUIDType, nullable=True ) + + +def display_migration_details(): + print "This migration script adds a UUID column to workflows" + +def upgrade(migrate_engine): + print __doc__ + metadata.bind = migrate_engine + metadata.reflect() + + # Add the uuid colum to the workflow table + try: + workflow_table = Table( "workflow", metadata, autoload=True ) + workflow_uuid_column.create( workflow_table ) + assert workflow_uuid_column is workflow_table.c.uuid + except Exception, e: + print str(e) + log.error( "Adding column 'uuid' to workflow table failed: %s" % str( e ) ) + return + +def downgrade(migrate_engine): + metadata.bind = migrate_engine + metadata.reflect() + + # Drop the workflow table's uuid column. + try: + workflow_table = Table( "workflow", metadata, autoload=True ) + workflow_uuid = workflow_table.c.uuid + workflow_uuid.drop() + except Exception, e: + log.debug( "Dropping 'uuid' column from workflow table failed: %s" % ( str( e ) ) ) + diff --git a/lib/galaxy/web/base/controller.py b/lib/galaxy/web/base/controller.py index d1fe1fe785d..f88969276ed 100644 --- a/lib/galaxy/web/base/controller.py +++ b/lib/galaxy/web/base/controller.py @@ -1791,6 +1791,8 @@ class UsesStoredWorkflowMixin( SharableItemSecurityMixin, UsesAnnotations ): data['format-version'] = "0.1" data['name'] = workflow.name data['annotation'] = annotation_str + if workflow.uuid is not None: + data['uuid'] = str(workflow.uuid) data['steps'] = {} # For each step, rebuild the form and encode the state for step in workflow.steps: diff --git a/lib/galaxy/webapps/galaxy/api/workflows.py b/lib/galaxy/webapps/galaxy/api/workflows.py index 934d2110984..7b045a6487f 100644 --- a/lib/galaxy/webapps/galaxy/api/workflows.py +++ b/lib/galaxy/webapps/galaxy/api/workflows.py @@ -4,6 +4,7 @@ API operations for Workflows from __future__ import absolute_import +import uuid import logging from sqlalchemy import desc, or_ from galaxy import exceptions, util @@ -437,6 +438,17 @@ class WorkflowsAPIController(BaseAPIController, UsesStoredWorkflowMixin, UsesHis query = trans.sa_session.query( trans.app.model.StoredWorkflow ) stored_workflow = query.get( workflow_id ) except Exception: + try: + #see if they have passed in the UUID for a workflow that is attached to a stored workflow + workflow_uuid = uuid.UUID(workflow_id) + stored_workflow = trans.sa_session.query(trans.app.model.StoredWorkflow).filter( and_( + trans.app.model.StoredWorkflow.latest_workflow_id == trans.app.model.Workflow.id, + trans.app.model.Workflow.uuid == workflow_uuid + )).first() + if stored_workflow is None: + raise exceptions.ObjectNotFound( "Workflow not found: %s" % workflow_id ) + except: + pass #let the outer raise exception happen raise exceptions.ObjectNotFound( "No such workflow found - invalid workflow identifier." ) if stored_workflow is None: raise exceptions.ObjectNotFound( "No such workflow found." ) diff --git a/scripts/cleanup_datasets/populate_uuid.py b/scripts/cleanup_datasets/populate_uuid.py index 495d97a622c..4ee46dbf5d9 100644 --- a/scripts/cleanup_datasets/populate_uuid.py +++ b/scripts/cleanup_datasets/populate_uuid.py @@ -33,6 +33,12 @@ def main(): row.uuid = uuid.uuid4() print "Setting dataset:", row.id, " UUID to ", row.uuid model.context.flush() + + for row in model.context.query( model.Workflow ): + if row.uuid is None: + row.uuid = uuid.uuid4() + print "Setting Workflow:", row.id, " UUID to ", row.uuid + model.context.flush() if __name__ == "__main__":