/lib/galaxy/web/controllers/tag.py

https://bitbucket.org/cistrome/cistrome-harvard/ · Python · 185 lines · 157 code · 2 blank · 26 comment · 14 complexity · 54d94894ea604069337d6866f601e96d MD5 · raw file

  1. """
  2. Tags Controller: handles tagging/untagging of entities and provides autocomplete support.
  3. """
  4. import logging
  5. from galaxy.web.base.controller import *
  6. from sqlalchemy.sql.expression import func, and_
  7. from sqlalchemy.sql import select
  8. log = logging.getLogger( __name__ )
  9. class TagsController ( BaseUIController ):
  10. def __init__( self, app ):
  11. BaseUIController.__init__( self, app )
  12. self.tag_handler = app.tag_handler
  13. @web.expose
  14. @web.require_login( "edit item tags" )
  15. def get_tagging_elt_async( self, trans, item_id, item_class, elt_context="" ):
  16. """ Returns HTML for editing an item's tags. """
  17. item = self._get_item( trans, item_class, trans.security.decode_id( item_id ) )
  18. if not item:
  19. return trans.show_error_message( "No item of class %s with id %s " % ( item_class, item_id ) )
  20. return trans.fill_template( "/tagging_common.mako",
  21. tag_type="individual",
  22. user=trans.user,
  23. tagged_item=item,
  24. elt_context=elt_context,
  25. in_form=False,
  26. input_size="22",
  27. tag_click_fn="default_tag_click_fn",
  28. use_toggle_link=False )
  29. @web.expose
  30. @web.require_login( "add tag to an item" )
  31. def add_tag_async( self, trans, item_id=None, item_class=None, new_tag=None, context=None ):
  32. """ Add tag to an item. """
  33. # Apply tag.
  34. item = self._get_item( trans, item_class, trans.security.decode_id( item_id ) )
  35. user = trans.user
  36. self.tag_handler.apply_item_tags( trans, user, item, new_tag.encode( 'utf-8' ) )
  37. trans.sa_session.flush()
  38. # Log.
  39. params = dict( item_id=item.id, item_class=item_class, tag=new_tag )
  40. trans.log_action( user, unicode( "tag" ), context, params )
  41. @web.expose
  42. @web.require_login( "remove tag from an item" )
  43. def remove_tag_async( self, trans, item_id=None, item_class=None, tag_name=None, context=None ):
  44. """ Remove tag from an item. """
  45. # Remove tag.
  46. item = self._get_item( trans, item_class, trans.security.decode_id( item_id ) )
  47. user = trans.user
  48. self.tag_handler.remove_item_tag( trans, user, item, tag_name.encode( 'utf-8' ) )
  49. trans.sa_session.flush()
  50. # Log.
  51. params = dict( item_id=item.id, item_class=item_class, tag=tag_name )
  52. trans.log_action( user, unicode( "untag" ), context, params )
  53. # Retag an item. All previous tags are deleted and new tags are applied.
  54. #@web.expose
  55. @web.require_login( "Apply a new set of tags to an item; previous tags are deleted." )
  56. def retag_async( self, trans, item_id=None, item_class=None, new_tags=None ):
  57. """ Apply a new set of tags to an item; previous tags are deleted. """
  58. # Apply tags.
  59. item = self._get_item( trans, item_class, trans.security.decode_id( item_id ) )
  60. user = trans.user
  61. self.tag_handler.delete_item_tags( trans, item )
  62. self.tag_handler.apply_item_tags( trans, user, item, new_tags.encode( 'utf-8' ) )
  63. trans.sa_session.flush()
  64. @web.expose
  65. @web.require_login( "get autocomplete data for an item's tags" )
  66. def tag_autocomplete_data( self, trans, q=None, limit=None, timestamp=None, item_id=None, item_class=None ):
  67. """ Get autocomplete data for an item's tags. """
  68. # Get item, do security check, and get autocomplete data.
  69. item = None
  70. if item_id is not None:
  71. item = self._get_item( trans, item_class, trans.security.decode_id( item_id ) )
  72. user = trans.user
  73. item_class = self.get_class( item_class )
  74. q = q.encode( 'utf-8' )
  75. if q.find( ":" ) == -1:
  76. return self._get_tag_autocomplete_names( trans, q, limit, timestamp, user, item, item_class )
  77. else:
  78. return self._get_tag_autocomplete_values( trans, q, limit, timestamp, user, item, item_class )
  79. def _get_tag_autocomplete_names( self, trans, q, limit, timestamp, user=None, item=None, item_class=None ):
  80. """
  81. Returns autocomplete data for tag names ordered from most frequently used to
  82. least frequently used.
  83. """
  84. # Get user's item tags and usage counts.
  85. # Get item's class object and item-tag association class.
  86. if item is None and item_class is None:
  87. raise RuntimeError( "Both item and item_class cannot be None" )
  88. elif item is not None:
  89. item_class = item.__class__
  90. item_tag_assoc_class = self.tag_handler.get_tag_assoc_class( item_class )
  91. # Build select statement.
  92. cols_to_select = [ item_tag_assoc_class.table.c.tag_id, func.count( '*' ) ]
  93. from_obj = item_tag_assoc_class.table.join( item_class.table ).join( trans.app.model.Tag.table )
  94. where_clause = and_( trans.app.model.Tag.table.c.name.like( q + "%" ),
  95. item_tag_assoc_class.table.c.user_id == user.id )
  96. order_by = [ func.count( "*" ).desc() ]
  97. group_by = item_tag_assoc_class.table.c.tag_id
  98. # Do query and get result set.
  99. query = select( columns=cols_to_select,
  100. from_obj=from_obj,
  101. whereclause=where_clause,
  102. group_by=group_by,
  103. order_by=order_by,
  104. limit=limit )
  105. result_set = trans.sa_session.execute( query )
  106. # Create and return autocomplete data.
  107. ac_data = "#Header|Your Tags\n"
  108. for row in result_set:
  109. tag = self.tag_handler.get_tag_by_id( trans, row[0] )
  110. # Exclude tags that are already applied to the item.
  111. if ( item is not None ) and ( self.tag_handler.item_has_tag( trans, trans.user, item, tag ) ):
  112. continue
  113. # Add tag to autocomplete data. Use the most frequent name that user
  114. # has employed for the tag.
  115. tag_names = self._get_usernames_for_tag( trans, trans.user, tag, item_class, item_tag_assoc_class )
  116. ac_data += tag_names[0] + "|" + tag_names[0] + "\n"
  117. return ac_data
  118. def _get_tag_autocomplete_values( self, trans, q, limit, timestamp, user=None, item=None, item_class=None ):
  119. """
  120. Returns autocomplete data for tag values ordered from most frequently used to
  121. least frequently used.
  122. """
  123. tag_name_and_value = q.split( ":" )
  124. tag_name = tag_name_and_value[0]
  125. tag_value = tag_name_and_value[1]
  126. tag = self.tag_handler.get_tag_by_name( trans, tag_name )
  127. # Don't autocomplete if tag doesn't exist.
  128. if tag is None:
  129. return ""
  130. # Get item's class object and item-tag association class.
  131. if item is None and item_class is None:
  132. raise RuntimeError( "Both item and item_class cannot be None" )
  133. elif item is not None:
  134. item_class = item.__class__
  135. item_tag_assoc_class = self.tag_handler.get_tag_assoc_class( item_class )
  136. # Build select statement.
  137. cols_to_select = [ item_tag_assoc_class.table.c.value, func.count( '*' ) ]
  138. from_obj = item_tag_assoc_class.table.join( item_class.table ).join( trans.app.model.Tag.table )
  139. where_clause = and_( item_tag_assoc_class.table.c.user_id == user.id,
  140. trans.app.model.Tag.table.c.id == tag.id,
  141. item_tag_assoc_class.table.c.value.like( tag_value + "%" ) )
  142. order_by = [ func.count("*").desc(), item_tag_assoc_class.table.c.value ]
  143. group_by = item_tag_assoc_class.table.c.value
  144. # Do query and get result set.
  145. query = select( columns=cols_to_select,
  146. from_obj=from_obj,
  147. whereclause=where_clause,
  148. group_by=group_by,
  149. order_by=order_by,
  150. limit=limit )
  151. result_set = trans.sa_session.execute( query )
  152. # Create and return autocomplete data.
  153. ac_data = "#Header|Your Values for '%s'\n" % ( tag_name )
  154. tag_uname = self._get_usernames_for_tag( trans, trans.user, tag, item_class, item_tag_assoc_class )[0]
  155. for row in result_set:
  156. ac_data += tag_uname + ":" + row[0] + "|" + row[0] + "\n"
  157. return ac_data
  158. def _get_usernames_for_tag( self, trans, user, tag, item_class, item_tag_assoc_class ):
  159. """
  160. Returns an ordered list of the user names for a tag; list is ordered from
  161. most popular to least popular name.
  162. """
  163. # Build select stmt.
  164. cols_to_select = [ item_tag_assoc_class.table.c.user_tname, func.count( '*' ) ]
  165. where_clause = and_( item_tag_assoc_class.table.c.user_id == user.id,
  166. item_tag_assoc_class.table.c.tag_id == tag.id )
  167. group_by = item_tag_assoc_class.table.c.user_tname
  168. order_by = [ func.count( "*" ).desc() ]
  169. # Do query and get result set.
  170. query = select( columns=cols_to_select,
  171. whereclause=where_clause,
  172. group_by=group_by,
  173. order_by=order_by )
  174. result_set = trans.sa_session.execute( query )
  175. user_tag_names = list()
  176. for row in result_set:
  177. user_tag_names.append( row[0] )
  178. return user_tag_names
  179. def _get_item( self, trans, item_class_name, id ):
  180. """ Get an item based on type and id. """
  181. item_class = self.tag_handler.item_tag_assoc_info[item_class_name].item_class
  182. item = trans.sa_session.query( item_class ).filter( "id=" + str( id ) )[0]
  183. return item