[2] | 1 | from sqlalchemy.sql.expression import func |
---|
| 2 | # Cannot import galaxy.model b/c it creates a circular import graph. |
---|
| 3 | import galaxy |
---|
| 4 | import logging |
---|
| 5 | log = logging.getLogger( __name__ ) |
---|
| 6 | |
---|
| 7 | class RuntimeException( Exception ): |
---|
| 8 | pass |
---|
| 9 | |
---|
| 10 | class UsesItemRatings: |
---|
| 11 | """ |
---|
| 12 | Mixin for getting and setting item ratings. |
---|
| 13 | |
---|
| 14 | Class makes two assumptions: |
---|
| 15 | (1) item-rating association table is named <item_class>RatingAssocation |
---|
| 16 | (2) item-rating association table has a column with a foreign key referencing |
---|
| 17 | item table that contains the item's id. |
---|
| 18 | """ |
---|
| 19 | def get_ave_item_rating_data( self, db_session, item, webapp_model=None ): |
---|
| 20 | """ Returns the average rating for an item.""" |
---|
| 21 | if webapp_model is None: |
---|
| 22 | webapp_model = galaxy.model |
---|
| 23 | item_rating_assoc_class = self._get_item_rating_assoc_class( item, webapp_model=webapp_model ) |
---|
| 24 | if not item_rating_assoc_class: |
---|
| 25 | raise RuntimeException( "Item does not have ratings: %s" % item.__class__.__name__ ) |
---|
| 26 | item_id_filter = self._get_item_id_filter_str( item, item_rating_assoc_class ) |
---|
| 27 | ave_rating = db_session.query( func.avg( item_rating_assoc_class.rating ) ).filter( item_id_filter ).scalar() |
---|
| 28 | # Convert ave_rating to float; note: if there are no item ratings, ave rating is None. |
---|
| 29 | if ave_rating: |
---|
| 30 | ave_rating = float( ave_rating ) |
---|
| 31 | else: |
---|
| 32 | ave_rating = 0 |
---|
| 33 | num_ratings = int( db_session.query( func.count( item_rating_assoc_class.rating ) ).filter( item_id_filter ).scalar() ) |
---|
| 34 | return ( ave_rating, num_ratings ) |
---|
| 35 | |
---|
| 36 | def rate_item( self, db_session, user, item, rating, webapp_model=None ): |
---|
| 37 | """ Rate an item. Return type is <item_class>RatingAssociation. """ |
---|
| 38 | if webapp_model is None: |
---|
| 39 | webapp_model = galaxy.model |
---|
| 40 | item_rating = self.get_user_item_rating( db_session, user, item, webapp_model=webapp_model ) |
---|
| 41 | if not item_rating: |
---|
| 42 | # User has not yet rated item; create rating. |
---|
| 43 | item_rating_assoc_class = self._get_item_rating_assoc_class( item, webapp_model=webapp_model ) |
---|
| 44 | item_rating = item_rating_assoc_class() |
---|
| 45 | item_rating.user = user |
---|
| 46 | item_rating.set_item( item ) |
---|
| 47 | item_rating.rating = rating |
---|
| 48 | db_session.add( item_rating ) |
---|
| 49 | db_session.flush() |
---|
| 50 | elif item_rating.rating != rating: |
---|
| 51 | # User has rated item; update rating. |
---|
| 52 | item_rating.rating = rating |
---|
| 53 | db_session.flush() |
---|
| 54 | return item_rating |
---|
| 55 | |
---|
| 56 | def get_user_item_rating( self, db_session, user, item, webapp_model=None ): |
---|
| 57 | """ Returns user's rating for an item. Return type is <item_class>RatingAssociation. """ |
---|
| 58 | if webapp_model is None: |
---|
| 59 | webapp_model = galaxy.model |
---|
| 60 | item_rating_assoc_class = self._get_item_rating_assoc_class( item, webapp_model=webapp_model ) |
---|
| 61 | if not item_rating_assoc_class: |
---|
| 62 | raise RuntimeException( "Item does not have ratings: %s" % item.__class__.__name__ ) |
---|
| 63 | |
---|
| 64 | # Query rating table by user and item id. |
---|
| 65 | item_id_filter = self._get_item_id_filter_str( item, item_rating_assoc_class ) |
---|
| 66 | return db_session.query( item_rating_assoc_class ).filter_by( user=user ).filter( item_id_filter ).first() |
---|
| 67 | |
---|
| 68 | def _get_item_rating_assoc_class( self, item, webapp_model=None ): |
---|
| 69 | """ Returns an item's item-rating association class. """ |
---|
| 70 | if webapp_model is None: |
---|
| 71 | webapp_model = galaxy.model |
---|
| 72 | item_rating_assoc_class = '%sRatingAssociation' % item.__class__.__name__ |
---|
| 73 | return getattr( webapp_model, item_rating_assoc_class, None ) |
---|
| 74 | |
---|
| 75 | def _get_item_id_filter_str( self, item, item_rating_assoc_class, webapp_model=None ): |
---|
| 76 | # Get foreign key in item-rating association table that references item table. |
---|
| 77 | if webapp_model is None: |
---|
| 78 | webapp_model = galaxy.model |
---|
| 79 | item_fk = None |
---|
| 80 | for fk in item_rating_assoc_class.table.foreign_keys: |
---|
| 81 | if fk.references( item.table ): |
---|
| 82 | item_fk = fk |
---|
| 83 | break |
---|
| 84 | |
---|
| 85 | if not item_fk: |
---|
| 86 | raise RuntimeException( "Cannot find item id column in item-rating association table: %s, %s" % item_rating_assoc_class.__name__, item_rating_assoc_class.table.name ) |
---|
| 87 | |
---|
| 88 | # TODO: can we provide a better filter than a raw string? |
---|
| 89 | return "%s=%i" % ( item_fk.parent.name, item.id ) |
---|
| 90 | |
---|
| 91 | class UsesAnnotations: |
---|
| 92 | """ Mixin for getting and setting item annotations. """ |
---|
| 93 | def get_item_annotation_str( self, db_session, user, item ): |
---|
| 94 | """ Returns a user's annotation string for an item. """ |
---|
| 95 | annotation_obj = self.get_item_annotation_obj( db_session, user, item ) |
---|
| 96 | if annotation_obj: |
---|
| 97 | return annotation_obj.annotation |
---|
| 98 | return None |
---|
| 99 | |
---|
| 100 | def get_item_annotation_obj( self, db_session, user, item ): |
---|
| 101 | """ Returns a user's annotation object for an item. """ |
---|
| 102 | # Get annotation association class. |
---|
| 103 | annotation_assoc_class = self._get_annotation_assoc_class( item ) |
---|
| 104 | if not annotation_assoc_class: |
---|
| 105 | return None |
---|
| 106 | |
---|
| 107 | # Get annotation association object. |
---|
| 108 | annotation_assoc = db_session.query( annotation_assoc_class ).filter_by( user=user ) |
---|
| 109 | |
---|
| 110 | # TODO: use filtering like that in _get_item_id_filter_str() |
---|
| 111 | if item.__class__ == galaxy.model.History: |
---|
| 112 | annotation_assoc = annotation_assoc.filter_by( history=item ) |
---|
| 113 | elif item.__class__ == galaxy.model.HistoryDatasetAssociation: |
---|
| 114 | annotation_assoc = annotation_assoc.filter_by( hda=item ) |
---|
| 115 | elif item.__class__ == galaxy.model.StoredWorkflow: |
---|
| 116 | annotation_assoc = annotation_assoc.filter_by( stored_workflow=item ) |
---|
| 117 | elif item.__class__ == galaxy.model.WorkflowStep: |
---|
| 118 | annotation_assoc = annotation_assoc.filter_by( workflow_step=item ) |
---|
| 119 | elif item.__class__ == galaxy.model.Page: |
---|
| 120 | annotation_assoc = annotation_assoc.filter_by( page=item ) |
---|
| 121 | elif item.__class__ == galaxy.model.Visualization: |
---|
| 122 | annotation_assoc = annotation_assoc.filter_by( visualization=item ) |
---|
| 123 | return annotation_assoc.first() |
---|
| 124 | |
---|
| 125 | def add_item_annotation( self, db_session, user, item, annotation ): |
---|
| 126 | """ Add or update an item's annotation; a user can only have a single annotation for an item. """ |
---|
| 127 | # Get/create annotation association object. |
---|
| 128 | annotation_assoc = self.get_item_annotation_obj( db_session, user, item ) |
---|
| 129 | if not annotation_assoc: |
---|
| 130 | annotation_assoc_class = self._get_annotation_assoc_class( item ) |
---|
| 131 | if not annotation_assoc_class: |
---|
| 132 | return None |
---|
| 133 | annotation_assoc = annotation_assoc_class() |
---|
| 134 | item.annotations.append( annotation_assoc ) |
---|
| 135 | annotation_assoc.user = user |
---|
| 136 | # Set annotation. |
---|
| 137 | annotation_assoc.annotation = annotation |
---|
| 138 | return annotation_assoc |
---|
| 139 | |
---|
| 140 | def copy_item_annotation( self, db_session, source_user, source_item, target_user, target_item ): |
---|
| 141 | """ Copy an annotation from a user/item source to a user/item target. """ |
---|
| 142 | if source_user and target_user: |
---|
| 143 | annotation_str = self.get_item_annotation_str( db_session, source_user, source_item ) |
---|
| 144 | if annotation_str: |
---|
| 145 | annotation = self.add_item_annotation( db_session, target_user, target_item, annotation_str ) |
---|
| 146 | return annotation |
---|
| 147 | return None |
---|
| 148 | |
---|
| 149 | def _get_annotation_assoc_class( self, item ): |
---|
| 150 | """ Returns an item's item-annotation association class. """ |
---|
| 151 | class_name = '%sAnnotationAssociation' % item.__class__.__name__ |
---|
| 152 | return getattr( galaxy.model, class_name, None ) |
---|