PageRenderTime 57ms CodeModel.GetById 2ms app.highlight 50ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/galaxy/tags/tag_handler.py

https://bitbucket.org/cistrome/cistrome-harvard/
Python | 277 lines | 266 code | 3 blank | 8 comment | 0 complexity | e1c74fe69a0f4d3bc392685911988178 MD5 | raw file
  1import re, logging
  2from sqlalchemy.sql.expression import func, and_
  3from sqlalchemy.sql import select
  4
  5log = logging.getLogger( __name__ )
  6
  7# Item-specific information needed to perform tagging.
  8class ItemTagAssocInfo( object ):
  9    def __init__( self, item_class, tag_assoc_class, item_id_col ):
 10        self.item_class = item_class
 11        self.tag_assoc_class = tag_assoc_class
 12        self.item_id_col = item_id_col
 13
 14class TagHandler( object ):
 15    def __init__( self ):
 16        # Minimum tag length.
 17        self.min_tag_len = 2
 18        # Maximum tag length.
 19        self.max_tag_len = 255
 20        # Tag separator.
 21        self.tag_separators = ',;'
 22        # Hierarchy separator.
 23        self.hierarchy_separator = '.'
 24        # Key-value separator.
 25        self.key_value_separators = "=:"
 26        # Initialize with known classes - add to this in subclasses.
 27        self.item_tag_assoc_info = {}
 28    def get_tag_assoc_class( self, item_class ):
 29        """Returns tag association class for item class."""
 30        return self.item_tag_assoc_info[item_class.__name__].tag_assoc_class
 31    def get_id_col_in_item_tag_assoc_table( self, item_class ):
 32        """Returns item id column in class' item-tag association table."""
 33        return self.item_tag_assoc_info[item_class.__name__].item_id_col
 34    def get_community_tags( self, trans, item=None, limit=None ):
 35        """Returns community tags for an item."""
 36        # Get item-tag association class.
 37        item_class = item.__class__
 38        item_tag_assoc_class = self.get_tag_assoc_class( item_class )
 39        if not item_tag_assoc_class:
 40            return []
 41        # Build select statement.
 42        cols_to_select = [ item_tag_assoc_class.table.c.tag_id, func.count( '*' ) ]
 43        from_obj = item_tag_assoc_class.table.join( item_class.table ).join( trans.app.model.Tag.table )
 44        where_clause = ( self.get_id_col_in_item_tag_assoc_table( item_class ) == item.id )
 45        order_by = [ func.count( "*" ).desc() ]
 46        group_by = item_tag_assoc_class.table.c.tag_id
 47        # Do query and get result set.
 48        query = select( columns=cols_to_select,
 49                        from_obj=from_obj,
 50                        whereclause=where_clause,
 51                        group_by=group_by,
 52                        order_by=order_by,
 53                        limit=limit )
 54        result_set = trans.sa_session.execute( query )
 55        # Return community tags.
 56        community_tags = []
 57        for row in result_set:
 58            tag_id = row[0]
 59            community_tags.append( self.get_tag_by_id( trans, tag_id ) )
 60        return community_tags
 61    def get_tool_tags( self, trans ):
 62        result_set = trans.sa_session.execute( select( columns=[ trans.app.model.ToolTagAssociation.table.c.tag_id ],
 63                                                       from_obj=trans.app.model.ToolTagAssociation.table ).distinct() )
 64
 65        tags = []
 66        for row in result_set:
 67            tag_id = row[0]
 68            tags.append( self.get_tag_by_id( trans, tag_id ) )
 69        return tags
 70    def remove_item_tag( self, trans, user, item, tag_name ):
 71        """Remove a tag from an item."""
 72        # Get item tag association.
 73        item_tag_assoc = self._get_item_tag_assoc( user, item, tag_name )
 74        # Remove association.
 75        if item_tag_assoc:
 76            # Delete association.
 77            trans.sa_session.delete( item_tag_assoc )
 78            item.tags.remove( item_tag_assoc )
 79            return True
 80        return False
 81    def delete_item_tags( self, trans, user, item ):
 82        """Delete tags from an item."""
 83        # Delete item-tag associations.
 84        for tag in item.tags:
 85            trans.sa_session.delete( tag )
 86        # Delete tags from item.
 87        del item.tags[:]
 88    def item_has_tag( self, trans, user, item, tag ):
 89        """Returns true if item is has a given tag."""
 90        # Get tag name.
 91        if isinstance( tag, basestring ):
 92            tag_name = tag
 93        elif isinstance( tag, trans.app.model.Tag ):
 94            tag_name = tag.name
 95        # Check for an item-tag association to see if item has a given tag.
 96        item_tag_assoc = self._get_item_tag_assoc( user, item, tag_name )
 97        if item_tag_assoc:
 98            return True
 99        return False
100    def apply_item_tag( self, trans, user, item, name, value=None ):
101        # Use lowercase name for searching/creating tag.
102        lc_name = name.lower()
103        # Get or create item-tag association.
104        item_tag_assoc = self._get_item_tag_assoc( user, item, lc_name )
105        if not item_tag_assoc:
106            # Create item-tag association.
107            # Create tag; if None, skip the tag (and log error).
108            tag = self._get_or_create_tag( trans, lc_name )
109            if not tag:
110                log.warn( "Failed to create tag with name %s" % lc_name )
111                return
112            # Create tag association based on item class.
113            item_tag_assoc_class = self.get_tag_assoc_class( item.__class__ )
114            item_tag_assoc = item_tag_assoc_class()
115            # Add tag to association.
116            item.tags.append( item_tag_assoc )
117            item_tag_assoc.tag = tag
118            item_tag_assoc.user = user
119        # Apply attributes to item-tag association. Strip whitespace from user name and tag.
120        lc_value = None
121        if value:
122            lc_value = value.lower()
123        item_tag_assoc.user_tname = name
124        item_tag_assoc.user_value = value
125        item_tag_assoc.value = lc_value
126        return item_tag_assoc
127    def apply_item_tags( self, trans, user, item, tags_str ):
128        """Apply tags to an item."""
129        # Parse tags.
130        parsed_tags = self.parse_tags( tags_str )
131        # Apply each tag.
132        for name, value in parsed_tags.items():
133            self.apply_item_tag( trans, user, item, name, value )
134    def get_tags_str( self, tags ):
135        """Build a string from an item's tags."""
136        # Return empty string if there are no tags.
137        if not tags:
138            return ""
139        # Create string of tags.
140        tags_str_list = list()
141        for tag in tags:
142            tag_str = tag.user_tname
143            if tag.value is not None:
144                tag_str += ":" + tag.user_value
145            tags_str_list.append( tag_str )
146        return ", ".join( tags_str_list )
147    def get_tag_by_id( self, trans, tag_id ):
148        """Get a Tag object from a tag id."""
149        return trans.sa_session.query( trans.app.model.Tag ).filter_by( id=tag_id ).first()
150    def get_tag_by_name( self, trans, tag_name ):
151        """Get a Tag object from a tag name (string)."""
152        if tag_name:
153            return trans.sa_session.query( trans.app.model.Tag ).filter_by( name=tag_name.lower() ).first()
154        return None
155    def _create_tag( self, trans, tag_str ):
156        """Create a Tag object from a tag string."""
157        tag_hierarchy = tag_str.split( self.hierarchy_separator )
158        tag_prefix = ""
159        parent_tag = None
160        for sub_tag in tag_hierarchy:
161            # Get or create subtag.
162            tag_name = tag_prefix + self._scrub_tag_name( sub_tag )
163            tag = trans.sa_session.query( trans.app.model.Tag ).filter_by( name=tag_name).first()
164            if not tag:
165                tag = trans.app.model.Tag( type=0, name=tag_name )
166            # Set tag parent.
167            tag.parent = parent_tag
168            # Update parent and tag prefix.
169            parent_tag = tag
170            tag_prefix = tag.name + self.hierarchy_separator
171        return tag
172    def _get_or_create_tag( self, trans, tag_str ):
173        """Get or create a Tag object from a tag string."""
174        # Scrub tag; if tag is None after being scrubbed, return None.
175        scrubbed_tag_str = self._scrub_tag_name( tag_str )
176        if not scrubbed_tag_str:
177            return None
178        # Get item tag.
179        tag = self.get_tag_by_name( trans, scrubbed_tag_str )
180        # Create tag if necessary.
181        if tag is None:
182            tag = self._create_tag( trans, scrubbed_tag_str )
183        return tag
184    def _get_item_tag_assoc( self, user, item, tag_name ):
185        """
186        Return ItemTagAssociation object for a user, item, and tag string; returns None if there is
187        no such association.
188        """
189        scrubbed_tag_name = self._scrub_tag_name( tag_name )
190        for item_tag_assoc in item.tags:
191            if ( item_tag_assoc.user == user ) and ( item_tag_assoc.user_tname == scrubbed_tag_name ):
192                return item_tag_assoc
193        return None
194    def parse_tags( self, tag_str ):
195        """
196        Returns a list of raw (tag-name, value) pairs derived from a string; method scrubs tag names and values as well.
197        Return value is a dictionary where tag-names are keys.
198        """
199        # Gracefully handle None.
200        if not tag_str:
201            return dict()
202        # Split tags based on separators.
203        reg_exp = re.compile( '[' + self.tag_separators + ']' )
204        raw_tags = reg_exp.split( tag_str )
205        # Extract name-value pairs.
206        name_value_pairs = dict()
207        for raw_tag in raw_tags:
208            nv_pair = self._get_name_value_pair( raw_tag )
209            scrubbed_name = self._scrub_tag_name( nv_pair[0] )
210            scrubbed_value = self._scrub_tag_value( nv_pair[1] )
211            name_value_pairs[scrubbed_name] = scrubbed_value
212        return name_value_pairs
213    def _scrub_tag_value( self, value ):
214        """Scrub a tag value."""
215        # Gracefully handle None:
216        if not value:
217            return None
218        # Remove whitespace from value.
219        reg_exp = re.compile( '\s' )
220        scrubbed_value = re.sub( reg_exp, "", value )
221        return scrubbed_value
222    def _scrub_tag_name( self, name ):
223        """Scrub a tag name."""
224        # Gracefully handle None:
225        if not name:
226            return None
227        # Remove whitespace from name.
228        reg_exp = re.compile( '\s' )
229        scrubbed_name = re.sub( reg_exp, "", name )
230        # Ignore starting ':' char.
231        if scrubbed_name.startswith( self.hierarchy_separator ):
232            scrubbed_name = scrubbed_name[1:]
233        # If name is too short or too long, return None.
234        if len( scrubbed_name ) < self.min_tag_len or len( scrubbed_name ) > self.max_tag_len:
235            return None
236        return scrubbed_name
237    def _scrub_tag_name_list( self, tag_name_list ):
238        """Scrub a tag name list."""
239        scrubbed_tag_list = list()
240        for tag in tag_name_list:
241            scrubbed_tag_list.append( self._scrub_tag_name( tag ) )
242        return scrubbed_tag_list
243    def _get_name_value_pair( self, tag_str ):
244        """Get name, value pair from a tag string."""
245        # Use regular expression to parse name, value.
246        reg_exp = re.compile( "[" + self.key_value_separators + "]" )
247        name_value_pair = reg_exp.split( tag_str )
248        # Add empty slot if tag does not have value.
249        if len( name_value_pair ) < 2:
250            name_value_pair.append( None )
251        return name_value_pair
252
253class GalaxyTagHandler( TagHandler ):
254    def __init__( self ):
255        from galaxy import model
256        TagHandler.__init__( self )
257        self.item_tag_assoc_info["History"] = ItemTagAssocInfo( model.History,
258                                                                model.HistoryTagAssociation,
259                                                                model.HistoryTagAssociation.table.c.history_id )
260        self.item_tag_assoc_info["HistoryDatasetAssociation"] = \
261            ItemTagAssocInfo( model.HistoryDatasetAssociation,
262                              model.HistoryDatasetAssociationTagAssociation,
263                              model.HistoryDatasetAssociationTagAssociation.table.c.history_dataset_association_id )
264        self.item_tag_assoc_info["Page"] = ItemTagAssocInfo( model.Page,
265                                                             model.PageTagAssociation,
266                                                             model.PageTagAssociation.table.c.page_id )
267        self.item_tag_assoc_info["StoredWorkflow"] = ItemTagAssocInfo( model.StoredWorkflow,
268                                                                       model.StoredWorkflowTagAssociation,
269                                                                       model.StoredWorkflowTagAssociation.table.c.stored_workflow_id )
270        self.item_tag_assoc_info["Visualization"] = ItemTagAssocInfo( model.Visualization,
271                                                                      model.VisualizationTagAssociation,
272                                                                      model.VisualizationTagAssociation.table.c.visualization_id )
273
274class CommunityTagHandler( TagHandler ):
275    def __init__( self ):
276        from galaxy.webapps.tool_shed import model
277        TagHandler.__init__( self )