diff --git a/lib/galaxy/tags/tag_handler.py b/lib/galaxy/tags/tag_handler.py index 08f7c6d5821..59aee19e533 100644 --- a/lib/galaxy/tags/tag_handler.py +++ b/lib/galaxy/tags/tag_handler.py @@ -1,5 +1,9 @@ from galaxy.model import Tag import re +from sqlalchemy.sql.expression import func, and_ +from sqlalchemy.sql import select +from galaxy.model import History, HistoryTagAssociation, Dataset, DatasetTagAssociation, \ + HistoryDatasetAssociation, HistoryDatasetAssociationTagAssociation, Page, PageTagAssociation class TagHandler( object ): @@ -18,14 +22,56 @@ class TagHandler( object ): # Key-value separator. key_value_separators = "=:" - def __init__(self): - self.tag_assoc_classes = dict() + # Item-specific information needed to perform tagging. + class ItemTagAssocInfo( object ): + def __init__( self, item_class, tag_assoc_class, item_id_col ): + self.item_class = item_class + self.tag_assoc_class = tag_assoc_class + self.item_id_col = item_id_col + + # Initialize with known classes. + item_tag_assoc_info = {} + item_tag_assoc_info["History"] = ItemTagAssocInfo( History, HistoryTagAssociation, HistoryTagAssociation.table.c.history_id ) + item_tag_assoc_info["HistoryDatasetAssociation"] = \ + ItemTagAssocInfo( HistoryDatasetAssociation, HistoryDatasetAssociationTagAssociation, HistoryDatasetAssociationTagAssociation.table.c.history_dataset_association_id ) + item_tag_assoc_info["Page"] = ItemTagAssocInfo( Page, PageTagAssociation, PageTagAssociation.table.c.page_id ) - def add_tag_assoc_class(self, entity_class, tag_assoc_class): - self.tag_assoc_classes[entity_class] = tag_assoc_class + def get_tag_assoc_class(self, item_class): + """ Returns tag association class for item class. """ + return self.item_tag_assoc_info[item_class.__name__].tag_assoc_class - def get_tag_assoc_class(self, entity_class): - return self.tag_assoc_classes[entity_class] + def get_id_col_in_item_tag_assoc_table( self, item_class): + """ Returns item id column in class' item-tag association table. """ + return self.item_tag_assoc_info[item_class.__name__].item_id_col + + def get_community_tags(self, sa_session, item=None, limit=None): + """ Returns community tags for an item. """ + + # Get item-tag association class. + item_class = item.__class__ + item_tag_assoc_class = self.get_tag_assoc_class( item_class ) + if not item_tag_assoc_class: + return [] + + # Build select statement. + cols_to_select = [ item_tag_assoc_class.table.c.tag_id, func.count('*') ] + from_obj = item_tag_assoc_class.table.join(item_class.table).join(Tag.table) + where_clause = ( self.get_id_col_in_item_tag_assoc_table(item_class) == item.id ) + order_by = [ func.count("*").desc() ] + group_by = item_tag_assoc_class.table.c.tag_id + + # Do query and get result set. + query = select(columns=cols_to_select, from_obj=from_obj, + whereclause=where_clause, group_by=group_by, order_by=order_by, limit=limit) + result_set = sa_session.execute(query) + + # Return community tags. + community_tags = [] + for row in result_set: + tag_id = row[0] + community_tags.append( self.get_tag_by_id( sa_session, tag_id ) ) + + return community_tags def remove_item_tag( self, trans, item, tag_name ): """Remove a tag from an item.""" @@ -45,7 +91,7 @@ class TagHandler( object ): """Delete tags from an item.""" # Delete item-tag associations. for tag in item.tags: - trans.sa_ession.delete( tag ) + trans.sa_session.delete( tag ) # Delete tags from item. del item.tags[:] @@ -89,7 +135,7 @@ class TagHandler( object ): continue # Create tag association based on item class. - item_tag_assoc_class = self.tag_assoc_classes[item.__class__] + item_tag_assoc_class = self.get_tag_assoc_class( item.__class__ ) item_tag_assoc = item_tag_assoc_class() # Add tag to association. diff --git a/lib/galaxy/web/controllers/tag.py b/lib/galaxy/web/controllers/tag.py index 8e057e76545..ce0ec1dbc63 100644 --- a/lib/galaxy/web/controllers/tag.py +++ b/lib/galaxy/web/controllers/tag.py @@ -13,19 +13,10 @@ class TagsController ( BaseController ): def __init__(self, app): BaseController.__init__(self, app) - - # Keep a list of taggable classes. - self.taggable_classes = dict() - self.taggable_classes[History.__name__] = History - self.taggable_classes[HistoryDatasetAssociation.__name__] = HistoryDatasetAssociation - self.taggable_classes[Page.__name__] = Page - + # Set up tag handler to recognize the following items: History, HistoryDatasetAssociation, Page, ... self.tag_handler = TagHandler() - self.tag_handler.add_tag_assoc_class(History, HistoryTagAssociation) - self.tag_handler.add_tag_assoc_class(HistoryDatasetAssociation, HistoryDatasetAssociationTagAssociation) - self.tag_handler.add_tag_assoc_class(Page, PageTagAssociation) - + @web.expose @web.require_login( "Add tag to an item." ) def add_tag_async( self, trans, id=None, item_class=None, new_tag=None, context=None ): @@ -91,6 +82,8 @@ class TagsController ( BaseController ): item_class = History elif item_class == 'HistoryDatasetAssociation': item_class = HistoryDatasetAssociation + elif item_class == 'Page': + item_class = Page q = q.encode('utf-8') if q.find(":") == -1: @@ -207,16 +200,16 @@ class TagsController ( BaseController ): def _get_column_for_filtering_item_by_user_id(self, item_class): """ Returns the column to use when filtering by user id. """ - # TODO: make this generic by using a dict() to map from item class to a "user id" column - if item_class is History: - return History.table.c.user_id - elif item_class is HistoryDatasetAssociation: + if item_class is HistoryDatasetAssociation: # Use the user_id associated with the HDA's history. return History.table.c.user_id + else: + # Generically, just use the user_id column of the tagged item's table. + return item_class.table.c.user_id def _get_item(self, trans, item_class_name, id): """ Get an item based on type and id. """ - item_class = self.taggable_classes[item_class_name] + item_class = self.tag_handler.item_tag_assoc_info[item_class_name].item_class item = trans.sa_session.query(item_class).filter("id=" + str(id))[0] return item; @@ -234,3 +227,6 @@ class TagsController ( BaseController ): elif isinstance(item, HistoryDatasetAssociation): # TODO. pass + elif isinstance(item, Page): + # TODO. + pass diff --git a/static/scripts/autocomplete_tagging.js b/static/scripts/autocomplete_tagging.js index ad5a19b5277..b1f61db8f99 100644 --- a/static/scripts/autocomplete_tagging.js +++ b/static/scripts/autocomplete_tagging.js @@ -3,7 +3,22 @@ * @author: Jeremy Goecks * @require: jquery.autocomplete plugin */ -jQuery.fn.autocomplete_tagging = function(elt_id, options) +// +// Initialize "tag click functions" for tags. +// +function init_tag_click_function(tag_elt, click_func) +{ + $(tag_elt).find('.tag-name').each( function() { + $(this).click( function() { + var tag_str = $(this).text(); + var tag_name_and_value = tag_str.split(":") + click_func(tag_name_and_value[0], tag_name_and_value[1]); + return true; + }); + }); +} + +jQuery.fn.autocomplete_tagging = function(options) { // @@ -61,21 +76,17 @@ jQuery.fn.autocomplete_tagging = function(elt_id, options) // Initalize object's elements. // - // Get elements for this object. - var this_obj = $('#' + elt_id); - var id_parts = $(this).attr('id').split("-"); - var obj_id = id_parts[ id_parts.length-1 ]; - var tag_area = this_obj.find('#tag-area-' + obj_id); - var toggle_link = this_obj.find('#toggle-link-' + obj_id); - var tag_input_field = this_obj.find('#tag-input'); + // Get elements for this object. For this_obj, assume the last element with the id is the "this"; this is somewhat of a hack to address the problem + // that there may be two tagging elements for a single item if there are both community and individual tags for an element. + var this_obj = $(this); + var tag_area = this_obj.find('.tag-area'); + var toggle_link = this_obj.find('.toggle-link'); + var tag_input_field = this_obj.find('.tag-input'); var add_tag_button = this_obj.find('.add-tag-button'); // Initialize toggle link. toggle_link.click( function() { - var id = $(this).attr('id').split('-')[2]; - // Take special actions depending on whether toggle is showing or hiding link. - var tag_area = $('#tag-area-' + id); var showing_tag_area = (tag_area.css("display") == "none"); var after_toggle_fn; if (showing_tag_area) @@ -210,7 +221,7 @@ jQuery.fn.autocomplete_tagging = function(elt_id, options) } var autocomplete_options = { selectFirst: false, formatItem : format_item_func, autoFill: false, highlight: false }; - tag_input_field.autocomplete(settings.ajax_autocomplete_tag_url, autocomplete_options); + tag_input_field.autocomplete(settings.ajax_autocomplete_tag_url, autocomplete_options); // Initialize delete tag images for current tags. @@ -218,16 +229,9 @@ jQuery.fn.autocomplete_tagging = function(elt_id, options) init_delete_tag_image( $(this) ); }); - this_obj.find('.tag-name').each( function() { - $(this).click( function() { - var tag_str = $(this).text(); - var tag_name_and_value = tag_str.split(":") - settings.tag_click_fn(tag_name_and_value[0], tag_name_and_value[1]); - return true; - }); - }); - + // Initialize tag click function. + init_tag_click_function($(this), settings.tag_click_fn); // Initialize "add tag" button. add_tag_button.click( function() diff --git a/templates/page/display.mako b/templates/page/display.mako index 5e44e9775e6..ac4de4ffbf6 100644 --- a/templates/page/display.mako +++ b/templates/page/display.mako @@ -146,29 +146,35 @@ }; // - // Function provides text for tagging toggle link. + // Handle click on community tag. // - var get_toggle_link_text = function(tags) + function community_tag_click(tag_name, tag_value) { - var text = ""; - var num_tags = array_length(tags); - if (num_tags != 0) - { - text = num_tags + (num_tags != 1 ? " Tags" : " Tag"); - } - else - { - // No tags. - text = "Add tags to history"; - } - return text; - }; + alert("community tag click: " + tag_name); + } %def> <%def name="stylesheets()"> ${parent.stylesheets()} ${h.css( "base", "history", "autocomplete_tagging" )} + %def> <%def name="init()"> @@ -180,6 +186,7 @@ %> %def> +<%namespace file="../tagging_common.mako" import="render_tagging_element, render_community_tagging_element" /> <%def name="center_panel()"> @@ -191,9 +198,27 @@