mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-10-24 02:04:04 +08:00
HDA & History Managers: move serialization logic for bootstrapping into view, display_by_username_and_slug, and root/history into Managers in order to display dataset collections
This commit is contained in:
@@ -1,4 +1,33 @@
|
||||
""" 'Business logic' independent of web transactions/user context (trans)
|
||||
should be pushed into models - but logic that requires the context trans
|
||||
should be placed under this module.
|
||||
"""
|
||||
Classes that manage resources (models, tools, etc.) by using the current
|
||||
Transaction.
|
||||
|
||||
Encapsulates the intersection of trans (or trans.sa_session), models,
|
||||
and Controllers.
|
||||
|
||||
Responsibilities:
|
||||
model operations that involve the trans/sa_session (CRUD)
|
||||
security:
|
||||
ownership, accessibility
|
||||
common aspect-oriented operations via new mixins:
|
||||
sharable, annotatable, tagable, ratable
|
||||
|
||||
Not responsible for:
|
||||
encoding/decoding ids
|
||||
any http gobblygook
|
||||
formatting of returned data (always python structures)
|
||||
formatting of raised errors
|
||||
|
||||
The goal is to have Controllers only handle:
|
||||
query-string/payload parsing and encoding/decoding ids
|
||||
http
|
||||
return formatting
|
||||
|
||||
and:
|
||||
control, improve namespacing in Controllers
|
||||
DRY for Controller ops (define here - use in both UI/API Controllers)
|
||||
|
||||
In other words, 'Business logic' independent of web transactions/user context
|
||||
(trans) should be pushed into models - but logic that requires the context
|
||||
trans should be placed under this module.
|
||||
"""
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
|
||||
class ModelManager( object ):
|
||||
pass
|
||||
|
||||
class ModelSerializer( object ):
|
||||
pass
|
||||
+179
-5
@@ -1,16 +1,35 @@
|
||||
"""
|
||||
Manager and Serializer for HDAs.
|
||||
|
||||
HistoryDatasetAssociations (HDAs) are datasets contained or created in a
|
||||
history.
|
||||
"""
|
||||
|
||||
from galaxy import exceptions
|
||||
from ..managers import histories
|
||||
|
||||
from galaxy.managers import base as manager_base
|
||||
from galaxy.managers import histories as history_manager
|
||||
|
||||
class HDAManager( object ):
|
||||
import galaxy.web
|
||||
import galaxy.datatypes.metadata
|
||||
from galaxy import objectstore
|
||||
|
||||
class HDAManager( manager_base.ModelManager ):
|
||||
"""
|
||||
Interface/service object for interacting with HDAs.
|
||||
"""
|
||||
|
||||
def __init__( self ):
|
||||
self.histories_mgr = histories.HistoryManager()
|
||||
"""
|
||||
Set up and initialize other managers needed by hdas.
|
||||
"""
|
||||
self.histories_mgr = history_manager.HistoryManager()
|
||||
|
||||
def get( self, trans, unencoded_id, check_ownership=True, check_accessible=True ):
|
||||
"""
|
||||
Get an HDA by its unencoded db id, checking ownership (via its history)
|
||||
or accessibility (via dataset shares/permissions).
|
||||
"""
|
||||
# this is a replacement for UsesHistoryDatasetAssociationMixin because mixins are a bad soln/structure
|
||||
hda = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( unencoded_id )
|
||||
if hda is None:
|
||||
raise exceptions.ObjectNotFound()
|
||||
@@ -19,7 +38,8 @@ class HDAManager( object ):
|
||||
|
||||
def secure( self, trans, hda, check_ownership=True, check_accessible=True ):
|
||||
"""
|
||||
checks if (a) user owns item or (b) item is accessible to user.
|
||||
check ownership (via its history) or accessibility (via dataset
|
||||
shares/permissions).
|
||||
"""
|
||||
# all items are accessible to an admin
|
||||
if trans.user and trans.user_is_admin():
|
||||
@@ -31,12 +51,18 @@ class HDAManager( object ):
|
||||
return hda
|
||||
|
||||
def can_access_dataset( self, trans, hda ):
|
||||
"""
|
||||
Use security agent to see if current user has access to dataset.
|
||||
"""
|
||||
current_user_roles = trans.get_current_user_roles()
|
||||
return trans.app.security_agent.can_access_dataset( current_user_roles, hda.dataset )
|
||||
|
||||
#TODO: is_owner, is_accessible
|
||||
|
||||
def check_ownership( self, trans, hda ):
|
||||
"""
|
||||
Use history to see if current user owns HDA.
|
||||
"""
|
||||
if not trans.user:
|
||||
#if hda.history == trans.history:
|
||||
# return hda
|
||||
@@ -51,6 +77,9 @@ class HDAManager( object ):
|
||||
"HistoryDatasetAssociation is not owned by the current user", type='error' )
|
||||
|
||||
def check_accessible( self, trans, hda ):
|
||||
"""
|
||||
Raise error if HDA is not accessible.
|
||||
"""
|
||||
if trans.user and trans.user_is_admin():
|
||||
return hda
|
||||
# check for access of the containing history...
|
||||
@@ -62,6 +91,151 @@ class HDAManager( object ):
|
||||
"HistoryDatasetAssociation is not accessible to the current user", type='error' )
|
||||
|
||||
def err_if_uploading( self, trans, hda ):
|
||||
"""
|
||||
Raise error if HDA is still uploading.
|
||||
"""
|
||||
if hda.state == trans.model.Dataset.states.UPLOAD:
|
||||
raise exceptions.Conflict( "Please wait until this dataset finishes uploading" )
|
||||
return hda
|
||||
|
||||
def get_hda_dict( self, trans, hda ):
|
||||
"""
|
||||
Return full details of this HDA in dictionary form.
|
||||
"""
|
||||
#precondition: the user's access to this hda has already been checked
|
||||
#TODO:?? postcondition: all ids are encoded (is this really what we want at this level?)
|
||||
expose_dataset_path = trans.user_is_admin() or trans.app.config.expose_dataset_path
|
||||
hda_dict = hda.to_dict( view='element', expose_dataset_path=expose_dataset_path )
|
||||
hda_dict[ 'api_type' ] = "file"
|
||||
|
||||
# Add additional attributes that depend on trans must be added here rather than at the model level.
|
||||
can_access_hda = trans.app.security_agent.can_access_dataset( trans.get_current_user_roles(), hda.dataset )
|
||||
can_access_hda = ( trans.user_is_admin() or can_access_hda )
|
||||
if not can_access_hda:
|
||||
return self.get_inaccessible_hda_dict( trans, hda )
|
||||
hda_dict[ 'accessible' ] = True
|
||||
|
||||
#TODO: I'm unclear as to which access pattern is right
|
||||
hda_dict[ 'annotation' ] = hda.get_item_annotation_str( trans.sa_session, hda.history.user, hda )
|
||||
#annotation = getattr( hda, 'annotation', hda.get_item_annotation_str( trans.sa_session, trans.user, hda ) )
|
||||
|
||||
# ---- return here if deleted AND purged OR can't access
|
||||
purged = ( hda.purged or hda.dataset.purged )
|
||||
if ( hda.deleted and purged ):
|
||||
#TODO: to_dict should really go AFTER this - only summary data
|
||||
return trans.security.encode_dict_ids( hda_dict )
|
||||
|
||||
if expose_dataset_path:
|
||||
try:
|
||||
hda_dict[ 'file_name' ] = hda.file_name
|
||||
except objectstore.ObjectNotFound:
|
||||
log.exception( 'objectstore.ObjectNotFound, HDA %s.', hda.id )
|
||||
|
||||
hda_dict[ 'download_url' ] = galaxy.web.url_for( 'history_contents_display',
|
||||
history_id = trans.security.encode_id( hda.history.id ),
|
||||
history_content_id = trans.security.encode_id( hda.id ) )
|
||||
|
||||
# indeces, assoc. metadata files, etc.
|
||||
meta_files = []
|
||||
for meta_type in hda.metadata.spec.keys():
|
||||
if isinstance( hda.metadata.spec[ meta_type ].param, galaxy.datatypes.metadata.FileParameter ):
|
||||
meta_files.append( dict( file_type=meta_type ) )
|
||||
if meta_files:
|
||||
hda_dict[ 'meta_files' ] = meta_files
|
||||
|
||||
# currently, the viz reg is optional - handle on/off
|
||||
if trans.app.visualizations_registry:
|
||||
hda_dict[ 'visualizations' ] = trans.app.visualizations_registry.get_visualizations( trans, hda )
|
||||
else:
|
||||
hda_dict[ 'visualizations' ] = hda.get_visualizations()
|
||||
#TODO: it may also be wiser to remove from here and add as API call that loads the visualizations
|
||||
# when the visualizations button is clicked (instead of preloading/pre-checking)
|
||||
|
||||
# ---- return here if deleted
|
||||
if hda.deleted and not purged:
|
||||
return trans.security.encode_dict_ids( hda_dict )
|
||||
|
||||
return trans.security.encode_dict_ids( hda_dict )
|
||||
|
||||
def get_inaccessible_hda_dict( self, trans, hda ):
|
||||
"""
|
||||
Return truncated serialization of HDA when inaccessible to user.
|
||||
"""
|
||||
return trans.security.encode_dict_ids({
|
||||
'id' : hda.id,
|
||||
'history_id': hda.history.id,
|
||||
'hid' : hda.hid,
|
||||
'name' : hda.name,
|
||||
'state' : hda.state,
|
||||
'deleted' : hda.deleted,
|
||||
'visible' : hda.visible,
|
||||
'accessible': False
|
||||
})
|
||||
|
||||
def get_hda_dict_with_error( self, trans, hda=None, history_id=None, id=None, error_msg='Error' ):
|
||||
"""
|
||||
Return truncated serialization of HDA when error raised getting
|
||||
details.
|
||||
"""
|
||||
return trans.security.encode_dict_ids({
|
||||
'id' : hda.id if hda else id,
|
||||
'history_id': hda.history.id if hda else history_id,
|
||||
'hid' : hda.hid if hda else '(unknown)',
|
||||
'name' : hda.name if hda else '(unknown)',
|
||||
'error' : error_msg,
|
||||
'state' : trans.model.Dataset.states.NEW
|
||||
})
|
||||
|
||||
def get_display_apps( self, trans, hda ):
|
||||
"""
|
||||
Return dictionary containing new-style display app urls.
|
||||
"""
|
||||
display_apps = []
|
||||
for display_app in hda.get_display_applications( trans ).itervalues():
|
||||
|
||||
app_links = []
|
||||
for link_app in display_app.links.itervalues():
|
||||
app_links.append({
|
||||
'target': link_app.url.get( 'target_frame', '_blank' ),
|
||||
'href' : link_app.get_display_url( hda, trans ),
|
||||
'text' : gettext( link_app.name )
|
||||
})
|
||||
if app_links:
|
||||
display_apps.append( dict( label=display_app.name, links=app_links ) )
|
||||
|
||||
return display_apps
|
||||
|
||||
def get_old_display_applications( self, trans, hda ):
|
||||
"""
|
||||
Return dictionary containing old-style display app urls.
|
||||
"""
|
||||
display_apps = []
|
||||
if not trans.app.config.enable_old_display_applications:
|
||||
return display_apps
|
||||
|
||||
for display_app in hda.datatype.get_display_types():
|
||||
target_frame, display_links = hda.datatype.get_display_links( hda,
|
||||
display_app, trans.app, trans.request.base )
|
||||
|
||||
if len( display_links ) > 0:
|
||||
display_label = hda.datatype.get_display_label( display_app )
|
||||
|
||||
app_links = []
|
||||
for display_name, display_link in display_links:
|
||||
app_links.append({
|
||||
'target': target_frame,
|
||||
'href' : display_link,
|
||||
'text' : gettext( display_name )
|
||||
})
|
||||
if app_links:
|
||||
display_apps.append( dict( label=display_label, links=app_links ) )
|
||||
|
||||
return display_apps
|
||||
|
||||
|
||||
# =============================================================================
|
||||
class HistorySerializer( manager_base.ModelSerializer ):
|
||||
"""
|
||||
Interface/service object for serializing HDAs into dictionaries.
|
||||
"""
|
||||
pass
|
||||
|
||||
@@ -1,9 +1,32 @@
|
||||
"""
|
||||
Manager and Serializer for histories.
|
||||
|
||||
Histories are containers for datasets or dataset collections
|
||||
created (or copied) by users over the course of an analysis.
|
||||
"""
|
||||
|
||||
from galaxy import exceptions
|
||||
from galaxy.model import orm
|
||||
|
||||
from galaxy.managers import base as manager_base
|
||||
import galaxy.managers.hdas
|
||||
|
||||
import galaxy.web
|
||||
import galaxy.dataset_collections.util
|
||||
|
||||
import logging
|
||||
log = logging.getLogger( __name__ )
|
||||
|
||||
|
||||
# =============================================================================
|
||||
class HistoryManager( manager_base.ModelManager ):
|
||||
"""
|
||||
Interface/service object for interacting with HDAs.
|
||||
"""
|
||||
|
||||
class HistoryManager( object ):
|
||||
#TODO: all the following would be more useful if passed the user instead of defaulting to trans.user
|
||||
def __init__( self, *args, **kwargs ):
|
||||
super( HistoryManager, self ).__init__( *args, **kwargs )
|
||||
|
||||
def get( self, trans, unencoded_id, check_ownership=True, check_accessible=True, deleted=None ):
|
||||
"""
|
||||
@@ -44,7 +67,7 @@ class HistoryManager( object ):
|
||||
|
||||
def secure( self, trans, history, check_ownership=True, check_accessible=True ):
|
||||
"""
|
||||
checks if (a) user owns item or (b) item is accessible to user.
|
||||
Checks if (a) user owns item or (b) item is accessible to user.
|
||||
"""
|
||||
# all items are accessible to an admin
|
||||
if trans.user and trans.user_is_admin():
|
||||
@@ -56,15 +79,28 @@ class HistoryManager( object ):
|
||||
return history
|
||||
|
||||
def is_current( self, trans, history ):
|
||||
"""
|
||||
True if the given history is the user's current history.
|
||||
|
||||
Returns False if the session has no current history.
|
||||
"""
|
||||
if trans.history is None:
|
||||
return False
|
||||
return trans.history == history
|
||||
|
||||
def is_owner( self, trans, history ):
|
||||
"""
|
||||
True if the current user is the owner of the given history.
|
||||
"""
|
||||
# anon users are only allowed to view their current history
|
||||
if not trans.user:
|
||||
return self.is_current( trans, history )
|
||||
return trans.user == history.user
|
||||
|
||||
def check_ownership( self, trans, history ):
|
||||
"""
|
||||
Raises error if the current user is not the owner of the history.
|
||||
"""
|
||||
if trans.user and trans.user_is_admin():
|
||||
return history
|
||||
if not trans.user and not self.is_current( trans, history ):
|
||||
@@ -74,6 +110,9 @@ class HistoryManager( object ):
|
||||
raise exceptions.ItemOwnershipException( "History is not owned by the current user", type='error' )
|
||||
|
||||
def is_accessible( self, trans, history ):
|
||||
"""
|
||||
True if the user can access (read) the current history.
|
||||
"""
|
||||
# admin always have access
|
||||
if trans.user and trans.user_is_admin():
|
||||
return True
|
||||
@@ -88,6 +127,103 @@ class HistoryManager( object ):
|
||||
return False
|
||||
|
||||
def check_accessible( self, trans, history ):
|
||||
"""
|
||||
Raises error if the current user can't access the history.
|
||||
"""
|
||||
if self.is_accessible( trans, history ):
|
||||
return history
|
||||
raise exceptions.ItemAccessibilityException( "History is not accessible to the current user", type='error' )
|
||||
|
||||
#TODO: bleh...
|
||||
def _get_history_data( self, trans, history ):
|
||||
"""
|
||||
Returns a dictionary containing ``history`` and ``contents``, serialized
|
||||
history and an array of serialized history contents respectively.
|
||||
"""
|
||||
hda_mgr = galaxy.managers.hdas.HDAManager()
|
||||
collection_dictifier = galaxy.dataset_collections.util.dictify_dataset_collection_instance
|
||||
|
||||
history_dictionary = {}
|
||||
contents_dictionaries = []
|
||||
try:
|
||||
#for content in history.contents_iter( **contents_kwds ):
|
||||
for content in history.contents_iter( types=[ 'dataset', 'dataset_collection' ] ):
|
||||
hda_dict = {}
|
||||
|
||||
if isinstance( content, trans.app.model.HistoryDatasetAssociation ):
|
||||
try:
|
||||
hda_dict = hda_mgr.get_hda_dict( trans, content )
|
||||
except Exception, exc:
|
||||
# don't fail entire list if hda err's, record and move on
|
||||
log.exception( 'Error bootstrapping hda: %s', exc )
|
||||
hda_dict = hda_mgr.get_hda_dict_with_error( trans, content, str( exc ) )
|
||||
|
||||
elif isinstance( content, trans.app.model.HistoryDatasetCollectionAssociation ):
|
||||
try:
|
||||
service = trans.app.dataset_collections_service
|
||||
dataset_collection_instance = service.get_dataset_collection_instance(
|
||||
trans=trans,
|
||||
instance_type='history',
|
||||
id=trans.security.encode_id( content.id ),
|
||||
)
|
||||
hda_dict = collection_dictifier( dataset_collection_instance,
|
||||
security=trans.security, parent=dataset_collection_instance.history, view="element" )
|
||||
|
||||
except Exception, exc:
|
||||
log.exception( "Error in history API at listing dataset collection: %s", exc )
|
||||
#TODO: return some dict with the error
|
||||
|
||||
contents_dictionaries.append( hda_dict )
|
||||
|
||||
# re-use the hdas above to get the history data...
|
||||
history_dictionary = self.get_history_dict( trans, history, contents_dictionaries=contents_dictionaries )
|
||||
|
||||
except Exception, exc:
|
||||
user_id = str( trans.user.id ) if trans.user else '(anonymous)'
|
||||
log.exception( 'Error bootstrapping history for user %s: %s', user_id, str( exc ) )
|
||||
message = ( 'An error occurred getting the history data from the server. '
|
||||
+ 'Please contact a Galaxy administrator if the problem persists.' )
|
||||
history_dictionary[ 'error' ] = message
|
||||
|
||||
return {
|
||||
'history' : history_dictionary,
|
||||
'contents' : contents_dictionaries
|
||||
}
|
||||
|
||||
def get_history_dict( self, trans, history, contents_dictionaries=None ):
|
||||
"""
|
||||
Returns history data in the form of a dictionary.
|
||||
"""
|
||||
#TODO: to serializer
|
||||
history_dict = history.to_dict( view='element', value_mapper={ 'id':trans.security.encode_id })
|
||||
history_dict[ 'user_id' ] = None
|
||||
if history.user_id:
|
||||
history_dict[ 'user_id' ] = trans.security.encode_id( history.user_id )
|
||||
|
||||
history_dict[ 'nice_size' ] = history.get_disk_size( nice_size=True )
|
||||
history_dict[ 'annotation' ] = history.get_item_annotation_str( trans.sa_session, history.user, history )
|
||||
if not history_dict[ 'annotation' ]:
|
||||
history_dict[ 'annotation' ] = ''
|
||||
|
||||
#TODO: item_slug url
|
||||
if history_dict[ 'importable' ] and history_dict[ 'slug' ]:
|
||||
username_and_slug = ( '/' ).join(( 'u', history.user.username, 'h', history_dict[ 'slug' ] ))
|
||||
history_dict[ 'username_and_slug' ] = username_and_slug
|
||||
|
||||
#TODO: re-add
|
||||
#hda_summaries = hda_dictionaries if hda_dictionaries else self.get_hda_summary_dicts( trans, history )
|
||||
##TODO remove the following in v2
|
||||
#( state_counts, state_ids ) = self._get_hda_state_summaries( trans, hda_summaries )
|
||||
#history_dict[ 'state_details' ] = state_counts
|
||||
#history_dict[ 'state_ids' ] = state_ids
|
||||
#history_dict[ 'state' ] = self._get_history_state_from_hdas( trans, history, state_counts )
|
||||
|
||||
return history_dict
|
||||
|
||||
|
||||
# =============================================================================
|
||||
class HistorySerializer( manager_base.ModelSerializer ):
|
||||
"""
|
||||
Interface/service object for serializing histories into dictionaries.
|
||||
"""
|
||||
pass
|
||||
|
||||
@@ -92,7 +92,7 @@ class HistoryContentsController( BaseAPIController, UsesHistoryDatasetAssociatio
|
||||
else:
|
||||
types = [ 'dataset', "dataset_collection" ]
|
||||
|
||||
contents_kwds = {'types': types}
|
||||
contents_kwds = { 'types': types }
|
||||
if ids:
|
||||
ids = map( lambda id: trans.security.decode_id( id ), ids.split( ',' ) )
|
||||
contents_kwds[ 'ids' ] = ids
|
||||
@@ -110,14 +110,14 @@ class HistoryContentsController( BaseAPIController, UsesHistoryDatasetAssociatio
|
||||
details = util.listify( details )
|
||||
|
||||
for content in history.contents_iter( **contents_kwds ):
|
||||
if isinstance(content, trans.app.model.HistoryDatasetAssociation):
|
||||
if isinstance( content, trans.app.model.HistoryDatasetAssociation ):
|
||||
encoded_content_id = trans.security.encode_id( content.id )
|
||||
detailed = details == 'all' or ( encoded_content_id in details )
|
||||
if detailed:
|
||||
rval.append( self._detailed_hda_dict( trans, content ) )
|
||||
else:
|
||||
rval.append( self._summary_hda_dict( trans, history_id, content ) )
|
||||
elif isinstance(content, trans.app.model.HistoryDatasetCollectionAssociation):
|
||||
elif isinstance( content, trans.app.model.HistoryDatasetCollectionAssociation ):
|
||||
rval.append( self.__collection_dict( trans, content ) )
|
||||
return rval
|
||||
|
||||
@@ -149,7 +149,8 @@ class HistoryContentsController( BaseAPIController, UsesHistoryDatasetAssociatio
|
||||
}
|
||||
|
||||
def __collection_dict( self, trans, dataset_collection_instance, view="collection" ):
|
||||
return dictify_dataset_collection_instance( dataset_collection_instance, security=trans.security, parent=dataset_collection_instance.history, view=view )
|
||||
return dictify_dataset_collection_instance( dataset_collection_instance,
|
||||
security=trans.security, parent=dataset_collection_instance.history, view=view )
|
||||
|
||||
def _detailed_hda_dict( self, trans, hda ):
|
||||
"""
|
||||
@@ -201,8 +202,7 @@ class HistoryContentsController( BaseAPIController, UsesHistoryDatasetAssociatio
|
||||
)
|
||||
return self.__collection_dict( trans, dataset_collection_instance, view="element" )
|
||||
except Exception, e:
|
||||
msg = "Error in history API at listing dataset collection: %s" % ( str(e) )
|
||||
log.error( msg, exc_info=True )
|
||||
log.exception( "Error in history API at listing dataset collection: %s", e )
|
||||
trans.response.status = 500
|
||||
return msg
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ from cgi import escape
|
||||
import galaxy.util
|
||||
from galaxy import model
|
||||
from galaxy import web
|
||||
from galaxy import managers
|
||||
from galaxy.datatypes.data import nice_size
|
||||
from galaxy.model.item_attrs import UsesAnnotations, UsesItemRatings
|
||||
from galaxy.model.orm import and_, eagerload_all, func
|
||||
@@ -194,6 +195,12 @@ class HistoryAllPublishedGrid( grids.Grid ):
|
||||
class HistoryController( BaseUIController, SharableMixin, UsesAnnotations, UsesItemRatings,
|
||||
UsesHistoryMixin, UsesHistoryDatasetAssociationMixin, ExportsHistoryMixin,
|
||||
ImportsHistoryMixin ):
|
||||
def __init__( self, app ):
|
||||
super( HistoryController, self ).__init__( app )
|
||||
self.mgrs = util.bunch.Bunch(
|
||||
histories=managers.histories.HistoryManager()
|
||||
)
|
||||
|
||||
@web.expose
|
||||
def index( self, trans ):
|
||||
return ""
|
||||
@@ -204,6 +211,7 @@ class HistoryController( BaseUIController, SharableMixin, UsesAnnotations, UsesI
|
||||
trans.response.set_content_type( 'text/xml' )
|
||||
return trans.fill_template( "/history/list_as_xml.mako" )
|
||||
|
||||
# ......................................................................... lists
|
||||
stored_list_grid = HistoryListGrid()
|
||||
shared_list_grid = SharedHistoryListGrid()
|
||||
published_list_grid = HistoryAllPublishedGrid()
|
||||
@@ -437,6 +445,7 @@ class HistoryController( BaseUIController, SharableMixin, UsesAnnotations, UsesI
|
||||
# Render the list view
|
||||
return self.shared_list_grid( trans, status=status, message=message, **kwargs )
|
||||
|
||||
# ......................................................................... html
|
||||
@web.expose
|
||||
def display_structured( self, trans, id=None ):
|
||||
"""
|
||||
@@ -511,333 +520,6 @@ class HistoryController( BaseUIController, SharableMixin, UsesAnnotations, UsesI
|
||||
#
|
||||
return trans.fill_template( "history/display_structured.mako", items=items, history=history )
|
||||
|
||||
@web.expose
|
||||
def delete_hidden_datasets( self, trans ):
|
||||
"""
|
||||
This method deletes all hidden datasets in the current history.
|
||||
"""
|
||||
count = 0
|
||||
for hda in trans.history.datasets:
|
||||
if not hda.visible and not hda.deleted and not hda.purged:
|
||||
hda.mark_deleted()
|
||||
count += 1
|
||||
trans.sa_session.add( hda )
|
||||
trans.log_event( "HDA id %s has been deleted" % hda.id )
|
||||
trans.sa_session.flush()
|
||||
return trans.show_ok_message( "%d hidden datasets have been deleted" % count, refresh_frames=['history'] )
|
||||
|
||||
@web.expose
|
||||
def purge_deleted_datasets( self, trans ):
|
||||
count = 0
|
||||
if trans.app.config.allow_user_dataset_purge:
|
||||
for hda in trans.history.datasets:
|
||||
if not hda.deleted or hda.purged:
|
||||
continue
|
||||
if trans.user:
|
||||
trans.user.total_disk_usage -= hda.quota_amount( trans.user )
|
||||
hda.purged = True
|
||||
trans.sa_session.add( hda )
|
||||
trans.log_event( "HDA id %s has been purged" % hda.id )
|
||||
trans.sa_session.flush()
|
||||
if hda.dataset.user_can_purge:
|
||||
try:
|
||||
hda.dataset.full_delete()
|
||||
trans.log_event( "Dataset id %s has been purged upon the the purge of HDA id %s" % ( hda.dataset.id, hda.id ) )
|
||||
trans.sa_session.add( hda.dataset )
|
||||
except:
|
||||
log.exception( 'Unable to purge dataset (%s) on purge of hda (%s):' % ( hda.dataset.id, hda.id ) )
|
||||
count += 1
|
||||
return trans.show_ok_message( "%d datasets have been deleted permanently" % count, refresh_frames=['history'] )
|
||||
|
||||
#TODO: use api instead
|
||||
@web.expose
|
||||
def delete_current( self, trans, purge=False ):
|
||||
"""Delete just the active history -- this does not require a logged in user."""
|
||||
history = trans.get_history()
|
||||
if history.users_shared_with:
|
||||
return trans.show_error_message( "History (%s) has been shared with others, unshare it before deleting it. " % history.name )
|
||||
if not history.deleted:
|
||||
history.deleted = True
|
||||
trans.sa_session.add( history )
|
||||
trans.sa_session.flush()
|
||||
trans.log_event( "History id %d marked as deleted" % history.id )
|
||||
if purge and trans.app.config.allow_user_dataset_purge:
|
||||
for hda in history.datasets:
|
||||
if trans.user:
|
||||
trans.user.total_disk_usage -= hda.quota_amount( trans.user )
|
||||
hda.purged = True
|
||||
trans.sa_session.add( hda )
|
||||
trans.log_event( "HDA id %s has been purged" % hda.id )
|
||||
trans.sa_session.flush()
|
||||
if hda.dataset.user_can_purge:
|
||||
try:
|
||||
hda.dataset.full_delete()
|
||||
trans.log_event( "Dataset id %s has been purged upon the the purge of HDA id %s" % ( hda.dataset.id, hda.id ) )
|
||||
trans.sa_session.add( hda.dataset )
|
||||
except:
|
||||
log.exception( 'Unable to purge dataset (%s) on purge of hda (%s):' % ( hda.dataset.id, hda.id ) )
|
||||
history.purged = True
|
||||
self.sa_session.add( history )
|
||||
self.sa_session.flush()
|
||||
for hda in history.datasets:
|
||||
# Not all datasets have jobs associated with them (e.g., datasets imported from libraries).
|
||||
if hda.creating_job_associations:
|
||||
# HDA has associated job, so try marking it deleted.
|
||||
job = hda.creating_job_associations[0].job
|
||||
if job.history_id == history.id and job.state in [ trans.app.model.Job.states.QUEUED, trans.app.model.Job.states.RUNNING, trans.app.model.Job.states.NEW ]:
|
||||
# No need to check other outputs since the job's parent history is this history
|
||||
job.mark_deleted( trans.app.config.track_jobs_in_database )
|
||||
trans.app.job_manager.job_stop_queue.put( job.id )
|
||||
# Regardless of whether it was previously deleted, get or create default history.
|
||||
trans.get_or_create_default_history()
|
||||
return trans.show_ok_message( "History deleted, a new history is active", refresh_frames=['history'] )
|
||||
|
||||
@web.expose
|
||||
def unhide_datasets( self, trans, current=False, ids=None ):
|
||||
"""Unhide the datasets in the active history -- this does not require a logged in user."""
|
||||
if not ids and galaxy.util.string_as_bool( current ):
|
||||
histories = [ trans.get_history() ]
|
||||
refresh_frames = ['history']
|
||||
else:
|
||||
raise NotImplementedError( "You can currently only unhide all the datasets of the current history." )
|
||||
for history in histories:
|
||||
history.unhide_datasets()
|
||||
trans.sa_session.add( history )
|
||||
trans.sa_session.flush()
|
||||
return trans.show_ok_message( "Your datasets have been unhidden.", refresh_frames=refresh_frames )
|
||||
#TODO: used in index.mako
|
||||
|
||||
@web.expose
|
||||
def resume_paused_jobs( self, trans, current=False, ids=None ):
|
||||
"""Resume paused jobs the active history -- this does not require a logged in user."""
|
||||
if not ids and galaxy.util.string_as_bool( current ):
|
||||
histories = [ trans.get_history() ]
|
||||
refresh_frames = ['history']
|
||||
else:
|
||||
raise NotImplementedError( "You can currently only resume all the datasets of the current history." )
|
||||
for history in histories:
|
||||
history.resume_paused_jobs()
|
||||
trans.sa_session.add( history )
|
||||
trans.sa_session.flush()
|
||||
return trans.show_ok_message( "Your jobs have been resumed.", refresh_frames=refresh_frames )
|
||||
#TODO: used in index.mako
|
||||
|
||||
@web.expose
|
||||
@web.require_login( "rate items" )
|
||||
@web.json
|
||||
def rate_async( self, trans, id, rating ):
|
||||
""" Rate a history asynchronously and return updated community data. """
|
||||
history = self.get_history( trans, id, check_ownership=False, check_accessible=True )
|
||||
if not history:
|
||||
return trans.show_error_message( "The specified history does not exist." )
|
||||
# Rate history.
|
||||
history_rating = self.rate_item( trans.sa_session, trans.get_user(), history, rating )
|
||||
return self.get_ave_item_rating_data( trans.sa_session, history )
|
||||
#TODO: used in display_base.mako
|
||||
|
||||
@web.expose
|
||||
# TODO: Remove require_login when users are warned that, if they are not
|
||||
# logged in, this will remove their current history.
|
||||
@web.require_login( "use Galaxy histories" )
|
||||
def import_archive( self, trans, **kwargs ):
|
||||
""" Import a history from a file archive. """
|
||||
# Set archive source and type.
|
||||
archive_file = kwargs.get( 'archive_file', None )
|
||||
archive_url = kwargs.get( 'archive_url', None )
|
||||
archive_source = None
|
||||
if archive_file:
|
||||
archive_source = archive_file
|
||||
archive_type = 'file'
|
||||
elif archive_url:
|
||||
archive_source = archive_url
|
||||
archive_type = 'url'
|
||||
# If no source to create archive from, show form to upload archive or specify URL.
|
||||
if not archive_source:
|
||||
return trans.show_form(
|
||||
web.FormBuilder( web.url_for(controller='history', action='import_archive'), "Import a History from an Archive", submit_text="Submit" ) \
|
||||
.add_input( "text", "Archived History URL", "archive_url", value="", error=None )
|
||||
# TODO: add support for importing via a file.
|
||||
#.add_input( "file", "Archived History File", "archive_file", value=None, error=None )
|
||||
)
|
||||
self.queue_history_import( trans, archive_type=archive_type, archive_source=archive_source )
|
||||
return trans.show_message( "Importing history from '%s'. \
|
||||
This history will be visible when the import is complete" % archive_source )
|
||||
#TODO: used in this file and index.mako
|
||||
|
||||
@web.expose
|
||||
def export_archive( self, trans, id=None, gzip=True, include_hidden=False, include_deleted=False ):
|
||||
""" Export a history to an archive. """
|
||||
#
|
||||
# Get history to export.
|
||||
#
|
||||
if id:
|
||||
history = self.get_history( trans, id, check_ownership=False, check_accessible=True )
|
||||
else:
|
||||
# Use current history.
|
||||
history = trans.history
|
||||
id = trans.security.encode_id( history.id )
|
||||
|
||||
if not history:
|
||||
return trans.show_error_message( "This history does not exist or you cannot export this history." )
|
||||
|
||||
#
|
||||
# If history has already been exported and it has not changed since export, stream it.
|
||||
#
|
||||
jeha = history.latest_export
|
||||
if jeha and jeha.up_to_date:
|
||||
if jeha.ready:
|
||||
return self.serve_ready_history_export( trans, jeha )
|
||||
elif jeha.preparing:
|
||||
return trans.show_message( "Still exporting history %(n)s; please check back soon. Link: <a href='%(s)s'>%(s)s</a>" \
|
||||
% ( { 'n' : history.name, 's' : url_for( controller='history', action="export_archive", id=id, qualified=True ) } ) )
|
||||
|
||||
self.queue_history_export( trans, history, gzip=gzip, include_hidden=include_hidden, include_deleted=include_deleted )
|
||||
url = url_for( controller='history', action="export_archive", id=id, qualified=True )
|
||||
return trans.show_message( "Exporting History '%(n)s'. Use this link to download \
|
||||
the archive or import it to another Galaxy server: \
|
||||
<a href='%(u)s'>%(u)s</a>" % ( { 'n' : history.name, 'u' : url } ) )
|
||||
#TODO: used in this file and index.mako
|
||||
|
||||
@web.expose
|
||||
@web.json
|
||||
@web.require_login( "get history name and link" )
|
||||
def get_name_and_link_async( self, trans, id=None ):
|
||||
""" Returns history's name and link. """
|
||||
history = self.get_history( trans, id, False )
|
||||
if self.create_item_slug( trans.sa_session, history ):
|
||||
trans.sa_session.flush()
|
||||
return_dict = {
|
||||
"name" : history.name,
|
||||
"link" : url_for(controller='history', action="display_by_username_and_slug",
|
||||
username=history.user.username, slug=history.slug ) }
|
||||
return return_dict
|
||||
#TODO: used in page/editor.mako
|
||||
|
||||
@web.expose
|
||||
@web.require_login( "set history's accessible flag" )
|
||||
def set_accessible_async( self, trans, id=None, accessible=False ):
|
||||
""" Set history's importable attribute and slug. """
|
||||
history = self.get_history( trans, id, True )
|
||||
# Only set if importable value would change; this prevents a change in the update_time unless attribute really changed.
|
||||
importable = accessible in ['True', 'true', 't', 'T'];
|
||||
if history and history.importable != importable:
|
||||
if importable:
|
||||
self._make_item_accessible( trans.sa_session, history )
|
||||
else:
|
||||
history.importable = importable
|
||||
trans.sa_session.flush()
|
||||
return
|
||||
#TODO: used in page/editor.mako
|
||||
|
||||
@web.expose
|
||||
def get_item_content_async( self, trans, id ):
|
||||
""" Returns item content in HTML format. """
|
||||
|
||||
history = self.get_history( trans, id, False, True )
|
||||
if history is None:
|
||||
raise web.httpexceptions.HTTPNotFound()
|
||||
|
||||
# Get datasets.
|
||||
datasets = self.get_history_datasets( trans, history )
|
||||
# Get annotations.
|
||||
history.annotation = self.get_item_annotation_str( trans.sa_session, history.user, history )
|
||||
for dataset in datasets:
|
||||
dataset.annotation = self.get_item_annotation_str( trans.sa_session, history.user, dataset )
|
||||
return trans.stream_template_mako( "/history/item_content.mako", item = history, item_data = datasets )
|
||||
#TODO: used in embed_base.mako
|
||||
|
||||
@web.expose
|
||||
def name_autocomplete_data( self, trans, q=None, limit=None, timestamp=None ):
|
||||
"""Return autocomplete data for history names"""
|
||||
user = trans.get_user()
|
||||
if not user:
|
||||
return
|
||||
|
||||
ac_data = ""
|
||||
for history in ( trans.sa_session.query( model.History )
|
||||
.filter_by( user=user )
|
||||
.filter( func.lower( model.History.name ).like(q.lower() + "%") ) ):
|
||||
ac_data = ac_data + history.name + "\n"
|
||||
return ac_data
|
||||
#TODO: used in grid_base.mako
|
||||
|
||||
@web.expose
|
||||
def imp( self, trans, id=None, confirm=False, **kwd ):
|
||||
"""Import another user's history via a shared URL"""
|
||||
msg = ""
|
||||
user = trans.get_user()
|
||||
user_history = trans.get_history()
|
||||
# Set referer message
|
||||
if 'referer' in kwd:
|
||||
referer = kwd['referer']
|
||||
else:
|
||||
referer = trans.request.referer
|
||||
if referer is not "":
|
||||
referer_message = "<a href='%s'>return to the previous page</a>" % referer
|
||||
else:
|
||||
referer_message = "<a href='%s'>go to Galaxy's start page</a>" % url_for( '/' )
|
||||
|
||||
# include all datasets when copying?
|
||||
all_datasets = util.string_as_bool( kwd.get( 'all_datasets', False ) )
|
||||
|
||||
# Do import.
|
||||
if not id:
|
||||
return trans.show_error_message( "You must specify a history you want to import.<br>You can %s." % referer_message, use_panels=True )
|
||||
import_history = self.get_history( trans, id, check_ownership=False, check_accessible=False )
|
||||
if not import_history:
|
||||
return trans.show_error_message( "The specified history does not exist.<br>You can %s." % referer_message, use_panels=True )
|
||||
# History is importable if user is admin or it's accessible. TODO: probably want to have app setting to enable admin access to histories.
|
||||
if not trans.user_is_admin() and not self.security_check( trans, import_history, check_ownership=False, check_accessible=True ):
|
||||
return trans.show_error_message( "You cannot access this history.<br>You can %s." % referer_message, use_panels=True )
|
||||
if user:
|
||||
#dan: I can import my own history.
|
||||
#if import_history.user_id == user.id:
|
||||
# return trans.show_error_message( "You cannot import your own history.<br>You can %s." % referer_message, use_panels=True )
|
||||
new_history = import_history.copy( target_user=user, all_datasets=all_datasets )
|
||||
new_history.name = "imported: " + new_history.name
|
||||
new_history.user_id = user.id
|
||||
galaxy_session = trans.get_galaxy_session()
|
||||
try:
|
||||
association = trans.sa_session.query( trans.app.model.GalaxySessionToHistoryAssociation ) \
|
||||
.filter_by( session_id=galaxy_session.id, history_id=new_history.id ) \
|
||||
.first()
|
||||
except:
|
||||
association = None
|
||||
new_history.add_galaxy_session( galaxy_session, association=association )
|
||||
trans.sa_session.add( new_history )
|
||||
trans.sa_session.flush()
|
||||
# Set imported history to be user's current history.
|
||||
trans.set_history( new_history )
|
||||
return trans.show_ok_message(
|
||||
message="""History "%s" has been imported. <br>You can <a href="%s" onclick="parent.window.location='%s';">start using this history</a> or %s."""
|
||||
% ( new_history.name, web.url_for( '/' ), web.url_for( '/' ), referer_message ), use_panels=True )
|
||||
|
||||
elif not user_history or not user_history.datasets or confirm:
|
||||
#TODO:?? should anon-users be allowed to include deleted datasets when importing?
|
||||
#new_history = import_history.copy( activatable=include_deleted )
|
||||
new_history = import_history.copy()
|
||||
new_history.name = "imported: " + new_history.name
|
||||
new_history.user_id = None
|
||||
galaxy_session = trans.get_galaxy_session()
|
||||
try:
|
||||
association = trans.sa_session.query( trans.app.model.GalaxySessionToHistoryAssociation ) \
|
||||
.filter_by( session_id=galaxy_session.id, history_id=new_history.id ) \
|
||||
.first()
|
||||
except:
|
||||
association = None
|
||||
new_history.add_galaxy_session( galaxy_session, association=association )
|
||||
trans.sa_session.add( new_history )
|
||||
trans.sa_session.flush()
|
||||
trans.set_history( new_history )
|
||||
return trans.show_ok_message(
|
||||
message="""History "%s" has been imported. <br>You can <a href="%s">start using this history</a> or %s."""
|
||||
% ( new_history.name, web.url_for( '/' ), referer_message ), use_panels=True )
|
||||
return trans.show_warn_message( """
|
||||
Warning! If you import this history, you will lose your current
|
||||
history. <br>You can <a href="%s">continue and import this history</a> or %s.
|
||||
""" % ( web.url_for(controller='history', action='imp', id=id, confirm=True, referer=trans.request.referer ), referer_message ), use_panels=True )
|
||||
#TODO: used in history/view, display, embed
|
||||
|
||||
@web.expose
|
||||
def view( self, trans, id=None, show_deleted=False, show_hidden=False, use_panels=True ):
|
||||
"""
|
||||
@@ -870,22 +552,9 @@ class HistoryController( BaseUIController, SharableMixin, UsesAnnotations, UsesI
|
||||
+ " or the owner of this history has not made it accessible." )
|
||||
|
||||
# include all datasets: hidden, deleted, and purged
|
||||
hdas = self.get_history_datasets( trans, history_to_view,
|
||||
show_deleted=True, show_hidden=True, show_purged=True )
|
||||
for hda in hdas:
|
||||
hda_dict = {}
|
||||
try:
|
||||
hda_dict = self.get_hda_dict( trans, hda )
|
||||
|
||||
except Exception, exc:
|
||||
# don't fail entire list if hda err's, record and move on
|
||||
log.error( 'Error bootstrapping hda %d: %s', hda.id, str( exc ), exc_info=True )
|
||||
hda_dict = self.get_hda_dict_with_error( trans, hda, str( exc ) )
|
||||
|
||||
hda_dictionaries.append( hda_dict )
|
||||
|
||||
# re-use the hdas above to get the history data...
|
||||
history_dictionary = self.get_history_dict( trans, history_to_view, hda_dictionaries=hda_dictionaries )
|
||||
history_data = self.mgrs.histories._get_history_data( trans, history_to_view )
|
||||
history_dictionary = history_data[ 'history' ]
|
||||
hda_dictionaries = history_data[ 'contents' ]
|
||||
|
||||
except Exception, exc:
|
||||
user_id = str( trans.user.id ) if trans.user else '(anonymous)'
|
||||
@@ -911,12 +580,6 @@ class HistoryController( BaseUIController, SharableMixin, UsesAnnotations, UsesI
|
||||
# Security check raises error if user cannot access history.
|
||||
self.security_check( trans, history, False, True)
|
||||
|
||||
# Get datasets and annotations
|
||||
datasets = self.get_history_datasets( trans, history )
|
||||
history.annotation = self.get_item_annotation_str( trans.sa_session, history.user, history )
|
||||
for dataset in datasets:
|
||||
dataset.annotation = self.get_item_annotation_str( trans.sa_session, history.user, dataset )
|
||||
|
||||
# Get rating data.
|
||||
user_item_rating = 0
|
||||
if trans.get_user():
|
||||
@@ -928,20 +591,22 @@ class HistoryController( BaseUIController, SharableMixin, UsesAnnotations, UsesI
|
||||
ave_item_rating, num_ratings = self.get_ave_item_rating_data( trans.sa_session, history )
|
||||
|
||||
# create ownership flag for template, dictify models
|
||||
# note: adding original annotation since this is published - get_dict returns user-based annos
|
||||
user_is_owner = trans.user == history.user
|
||||
hda_dicts = []
|
||||
for hda in datasets:
|
||||
hda_dict = self.get_hda_dict( trans, hda )
|
||||
hda_dict[ 'annotation' ] = hda.annotation
|
||||
hda_dicts.append( hda_dict )
|
||||
history_dict = self.get_history_dict( trans, history, hda_dictionaries=hda_dicts )
|
||||
history_dict[ 'annotation' ] = history.annotation
|
||||
history_data = self.mgrs.histories._get_history_data( trans, history )
|
||||
history_dict = history_data[ 'history' ]
|
||||
hda_dicts = history_data[ 'contents' ]
|
||||
|
||||
return trans.stream_template_mako( "history/display.mako", item=history, item_data=datasets,
|
||||
history_dict[ 'annotation' ] = self.get_item_annotation_str( trans.sa_session, history.user, history )
|
||||
# note: adding original annotation since this is published - get_dict returns user-based annos
|
||||
#for hda_dict in hda_dicts:
|
||||
# hda_dict[ 'annotation' ] = hda.annotation
|
||||
# dataset.annotation = self.get_item_annotation_str( trans.sa_session, history.user, dataset )
|
||||
|
||||
return trans.stream_template_mako( "history/display.mako", item=history, item_data=[],
|
||||
user_is_owner=user_is_owner, history_dict=history_dict, hda_dicts=hda_dicts,
|
||||
user_item_rating = user_item_rating, ave_item_rating=ave_item_rating, num_ratings=num_ratings )
|
||||
|
||||
# ......................................................................... sharing & publishing
|
||||
@web.expose
|
||||
@web.require_login( "share Galaxy histories" )
|
||||
def sharing( self, trans, id=None, histories=[], **kwargs ):
|
||||
@@ -1283,6 +948,334 @@ class HistoryController( BaseUIController, SharableMixin, UsesAnnotations, UsesI
|
||||
msg += send_to_err
|
||||
return self.sharing( trans, histories=shared_histories, msg=msg )
|
||||
|
||||
# ......................................................................... actions/orig. async
|
||||
@web.expose
|
||||
def delete_hidden_datasets( self, trans ):
|
||||
"""
|
||||
This method deletes all hidden datasets in the current history.
|
||||
"""
|
||||
count = 0
|
||||
for hda in trans.history.datasets:
|
||||
if not hda.visible and not hda.deleted and not hda.purged:
|
||||
hda.mark_deleted()
|
||||
count += 1
|
||||
trans.sa_session.add( hda )
|
||||
trans.log_event( "HDA id %s has been deleted" % hda.id )
|
||||
trans.sa_session.flush()
|
||||
return trans.show_ok_message( "%d hidden datasets have been deleted" % count, refresh_frames=['history'] )
|
||||
|
||||
@web.expose
|
||||
def purge_deleted_datasets( self, trans ):
|
||||
count = 0
|
||||
if trans.app.config.allow_user_dataset_purge:
|
||||
for hda in trans.history.datasets:
|
||||
if not hda.deleted or hda.purged:
|
||||
continue
|
||||
if trans.user:
|
||||
trans.user.total_disk_usage -= hda.quota_amount( trans.user )
|
||||
hda.purged = True
|
||||
trans.sa_session.add( hda )
|
||||
trans.log_event( "HDA id %s has been purged" % hda.id )
|
||||
trans.sa_session.flush()
|
||||
if hda.dataset.user_can_purge:
|
||||
try:
|
||||
hda.dataset.full_delete()
|
||||
trans.log_event( "Dataset id %s has been purged upon the the purge of HDA id %s" % ( hda.dataset.id, hda.id ) )
|
||||
trans.sa_session.add( hda.dataset )
|
||||
except:
|
||||
log.exception( 'Unable to purge dataset (%s) on purge of hda (%s):' % ( hda.dataset.id, hda.id ) )
|
||||
count += 1
|
||||
return trans.show_ok_message( "%d datasets have been deleted permanently" % count, refresh_frames=['history'] )
|
||||
|
||||
#TODO: use api instead
|
||||
@web.expose
|
||||
def delete_current( self, trans, purge=False ):
|
||||
"""Delete just the active history -- this does not require a logged in user."""
|
||||
history = trans.get_history()
|
||||
if history.users_shared_with:
|
||||
return trans.show_error_message( "History (%s) has been shared with others, unshare it before deleting it. " % history.name )
|
||||
if not history.deleted:
|
||||
history.deleted = True
|
||||
trans.sa_session.add( history )
|
||||
trans.sa_session.flush()
|
||||
trans.log_event( "History id %d marked as deleted" % history.id )
|
||||
if purge and trans.app.config.allow_user_dataset_purge:
|
||||
for hda in history.datasets:
|
||||
if trans.user:
|
||||
trans.user.total_disk_usage -= hda.quota_amount( trans.user )
|
||||
hda.purged = True
|
||||
trans.sa_session.add( hda )
|
||||
trans.log_event( "HDA id %s has been purged" % hda.id )
|
||||
trans.sa_session.flush()
|
||||
if hda.dataset.user_can_purge:
|
||||
try:
|
||||
hda.dataset.full_delete()
|
||||
trans.log_event( "Dataset id %s has been purged upon the the purge of HDA id %s" % ( hda.dataset.id, hda.id ) )
|
||||
trans.sa_session.add( hda.dataset )
|
||||
except:
|
||||
log.exception( 'Unable to purge dataset (%s) on purge of hda (%s):' % ( hda.dataset.id, hda.id ) )
|
||||
history.purged = True
|
||||
self.sa_session.add( history )
|
||||
self.sa_session.flush()
|
||||
for hda in history.datasets:
|
||||
# Not all datasets have jobs associated with them (e.g., datasets imported from libraries).
|
||||
if hda.creating_job_associations:
|
||||
# HDA has associated job, so try marking it deleted.
|
||||
job = hda.creating_job_associations[0].job
|
||||
if job.history_id == history.id and job.state in [ trans.app.model.Job.states.QUEUED, trans.app.model.Job.states.RUNNING, trans.app.model.Job.states.NEW ]:
|
||||
# No need to check other outputs since the job's parent history is this history
|
||||
job.mark_deleted( trans.app.config.track_jobs_in_database )
|
||||
trans.app.job_manager.job_stop_queue.put( job.id )
|
||||
# Regardless of whether it was previously deleted, get or create default history.
|
||||
trans.get_or_create_default_history()
|
||||
return trans.show_ok_message( "History deleted, a new history is active", refresh_frames=['history'] )
|
||||
|
||||
@web.expose
|
||||
def unhide_datasets( self, trans, current=False, ids=None ):
|
||||
"""Unhide the datasets in the active history -- this does not require a logged in user."""
|
||||
if not ids and galaxy.util.string_as_bool( current ):
|
||||
histories = [ trans.get_history() ]
|
||||
refresh_frames = ['history']
|
||||
else:
|
||||
raise NotImplementedError( "You can currently only unhide all the datasets of the current history." )
|
||||
for history in histories:
|
||||
history.unhide_datasets()
|
||||
trans.sa_session.add( history )
|
||||
trans.sa_session.flush()
|
||||
return trans.show_ok_message( "Your datasets have been unhidden.", refresh_frames=refresh_frames )
|
||||
#TODO: used in index.mako
|
||||
|
||||
@web.expose
|
||||
def resume_paused_jobs( self, trans, current=False, ids=None ):
|
||||
"""Resume paused jobs the active history -- this does not require a logged in user."""
|
||||
if not ids and galaxy.util.string_as_bool( current ):
|
||||
histories = [ trans.get_history() ]
|
||||
refresh_frames = ['history']
|
||||
else:
|
||||
raise NotImplementedError( "You can currently only resume all the datasets of the current history." )
|
||||
for history in histories:
|
||||
history.resume_paused_jobs()
|
||||
trans.sa_session.add( history )
|
||||
trans.sa_session.flush()
|
||||
return trans.show_ok_message( "Your jobs have been resumed.", refresh_frames=refresh_frames )
|
||||
#TODO: used in index.mako
|
||||
|
||||
@web.expose
|
||||
@web.require_login( "rate items" )
|
||||
@web.json
|
||||
def rate_async( self, trans, id, rating ):
|
||||
""" Rate a history asynchronously and return updated community data. """
|
||||
history = self.get_history( trans, id, check_ownership=False, check_accessible=True )
|
||||
if not history:
|
||||
return trans.show_error_message( "The specified history does not exist." )
|
||||
# Rate history.
|
||||
history_rating = self.rate_item( trans.sa_session, trans.get_user(), history, rating )
|
||||
return self.get_ave_item_rating_data( trans.sa_session, history )
|
||||
#TODO: used in display_base.mako
|
||||
|
||||
@web.expose
|
||||
# TODO: Remove require_login when users are warned that, if they are not
|
||||
# logged in, this will remove their current history.
|
||||
@web.require_login( "use Galaxy histories" )
|
||||
def import_archive( self, trans, **kwargs ):
|
||||
""" Import a history from a file archive. """
|
||||
# Set archive source and type.
|
||||
archive_file = kwargs.get( 'archive_file', None )
|
||||
archive_url = kwargs.get( 'archive_url', None )
|
||||
archive_source = None
|
||||
if archive_file:
|
||||
archive_source = archive_file
|
||||
archive_type = 'file'
|
||||
elif archive_url:
|
||||
archive_source = archive_url
|
||||
archive_type = 'url'
|
||||
# If no source to create archive from, show form to upload archive or specify URL.
|
||||
if not archive_source:
|
||||
return trans.show_form(
|
||||
web.FormBuilder( web.url_for(controller='history', action='import_archive'), "Import a History from an Archive", submit_text="Submit" ) \
|
||||
.add_input( "text", "Archived History URL", "archive_url", value="", error=None )
|
||||
# TODO: add support for importing via a file.
|
||||
#.add_input( "file", "Archived History File", "archive_file", value=None, error=None )
|
||||
)
|
||||
self.queue_history_import( trans, archive_type=archive_type, archive_source=archive_source )
|
||||
return trans.show_message( "Importing history from '%s'. \
|
||||
This history will be visible when the import is complete" % archive_source )
|
||||
#TODO: used in this file and index.mako
|
||||
|
||||
@web.expose
|
||||
def export_archive( self, trans, id=None, gzip=True, include_hidden=False, include_deleted=False ):
|
||||
""" Export a history to an archive. """
|
||||
#
|
||||
# Get history to export.
|
||||
#
|
||||
if id:
|
||||
history = self.get_history( trans, id, check_ownership=False, check_accessible=True )
|
||||
else:
|
||||
# Use current history.
|
||||
history = trans.history
|
||||
id = trans.security.encode_id( history.id )
|
||||
|
||||
if not history:
|
||||
return trans.show_error_message( "This history does not exist or you cannot export this history." )
|
||||
|
||||
#
|
||||
# If history has already been exported and it has not changed since export, stream it.
|
||||
#
|
||||
jeha = history.latest_export
|
||||
if jeha and jeha.up_to_date:
|
||||
if jeha.ready:
|
||||
return self.serve_ready_history_export( trans, jeha )
|
||||
elif jeha.preparing:
|
||||
return trans.show_message( "Still exporting history %(n)s; please check back soon. Link: <a href='%(s)s'>%(s)s</a>" \
|
||||
% ( { 'n' : history.name, 's' : url_for( controller='history', action="export_archive", id=id, qualified=True ) } ) )
|
||||
|
||||
self.queue_history_export( trans, history, gzip=gzip, include_hidden=include_hidden, include_deleted=include_deleted )
|
||||
url = url_for( controller='history', action="export_archive", id=id, qualified=True )
|
||||
return trans.show_message( "Exporting History '%(n)s'. Use this link to download \
|
||||
the archive or import it to another Galaxy server: \
|
||||
<a href='%(u)s'>%(u)s</a>" % ( { 'n' : history.name, 'u' : url } ) )
|
||||
#TODO: used in this file and index.mako
|
||||
|
||||
@web.expose
|
||||
@web.json
|
||||
@web.require_login( "get history name and link" )
|
||||
def get_name_and_link_async( self, trans, id=None ):
|
||||
""" Returns history's name and link. """
|
||||
history = self.get_history( trans, id, False )
|
||||
if self.create_item_slug( trans.sa_session, history ):
|
||||
trans.sa_session.flush()
|
||||
return_dict = {
|
||||
"name" : history.name,
|
||||
"link" : url_for(controller='history', action="display_by_username_and_slug",
|
||||
username=history.user.username, slug=history.slug ) }
|
||||
return return_dict
|
||||
#TODO: used in page/editor.mako
|
||||
|
||||
@web.expose
|
||||
@web.require_login( "set history's accessible flag" )
|
||||
def set_accessible_async( self, trans, id=None, accessible=False ):
|
||||
""" Set history's importable attribute and slug. """
|
||||
history = self.get_history( trans, id, True )
|
||||
# Only set if importable value would change; this prevents a change in the update_time unless attribute really changed.
|
||||
importable = accessible in ['True', 'true', 't', 'T'];
|
||||
if history and history.importable != importable:
|
||||
if importable:
|
||||
self._make_item_accessible( trans.sa_session, history )
|
||||
else:
|
||||
history.importable = importable
|
||||
trans.sa_session.flush()
|
||||
return
|
||||
#TODO: used in page/editor.mako
|
||||
|
||||
@web.expose
|
||||
def get_item_content_async( self, trans, id ):
|
||||
""" Returns item content in HTML format. """
|
||||
|
||||
history = self.get_history( trans, id, False, True )
|
||||
if history is None:
|
||||
raise web.httpexceptions.HTTPNotFound()
|
||||
|
||||
# Get datasets.
|
||||
datasets = self.get_history_datasets( trans, history )
|
||||
# Get annotations.
|
||||
history.annotation = self.get_item_annotation_str( trans.sa_session, history.user, history )
|
||||
for dataset in datasets:
|
||||
dataset.annotation = self.get_item_annotation_str( trans.sa_session, history.user, dataset )
|
||||
return trans.stream_template_mako( "/history/item_content.mako", item = history, item_data = datasets )
|
||||
#TODO: used in embed_base.mako
|
||||
|
||||
@web.expose
|
||||
def name_autocomplete_data( self, trans, q=None, limit=None, timestamp=None ):
|
||||
"""Return autocomplete data for history names"""
|
||||
user = trans.get_user()
|
||||
if not user:
|
||||
return
|
||||
|
||||
ac_data = ""
|
||||
for history in ( trans.sa_session.query( model.History )
|
||||
.filter_by( user=user )
|
||||
.filter( func.lower( model.History.name ).like(q.lower() + "%") ) ):
|
||||
ac_data = ac_data + history.name + "\n"
|
||||
return ac_data
|
||||
#TODO: used in grid_base.mako
|
||||
|
||||
@web.expose
|
||||
def imp( self, trans, id=None, confirm=False, **kwd ):
|
||||
"""Import another user's history via a shared URL"""
|
||||
msg = ""
|
||||
user = trans.get_user()
|
||||
user_history = trans.get_history()
|
||||
# Set referer message
|
||||
if 'referer' in kwd:
|
||||
referer = kwd['referer']
|
||||
else:
|
||||
referer = trans.request.referer
|
||||
if referer is not "":
|
||||
referer_message = "<a href='%s'>return to the previous page</a>" % referer
|
||||
else:
|
||||
referer_message = "<a href='%s'>go to Galaxy's start page</a>" % url_for( '/' )
|
||||
|
||||
# include all datasets when copying?
|
||||
all_datasets = util.string_as_bool( kwd.get( 'all_datasets', False ) )
|
||||
|
||||
# Do import.
|
||||
if not id:
|
||||
return trans.show_error_message( "You must specify a history you want to import.<br>You can %s." % referer_message, use_panels=True )
|
||||
import_history = self.get_history( trans, id, check_ownership=False, check_accessible=False )
|
||||
if not import_history:
|
||||
return trans.show_error_message( "The specified history does not exist.<br>You can %s." % referer_message, use_panels=True )
|
||||
# History is importable if user is admin or it's accessible. TODO: probably want to have app setting to enable admin access to histories.
|
||||
if not trans.user_is_admin() and not self.security_check( trans, import_history, check_ownership=False, check_accessible=True ):
|
||||
return trans.show_error_message( "You cannot access this history.<br>You can %s." % referer_message, use_panels=True )
|
||||
if user:
|
||||
#dan: I can import my own history.
|
||||
#if import_history.user_id == user.id:
|
||||
# return trans.show_error_message( "You cannot import your own history.<br>You can %s." % referer_message, use_panels=True )
|
||||
new_history = import_history.copy( target_user=user, all_datasets=all_datasets )
|
||||
new_history.name = "imported: " + new_history.name
|
||||
new_history.user_id = user.id
|
||||
galaxy_session = trans.get_galaxy_session()
|
||||
try:
|
||||
association = trans.sa_session.query( trans.app.model.GalaxySessionToHistoryAssociation ) \
|
||||
.filter_by( session_id=galaxy_session.id, history_id=new_history.id ) \
|
||||
.first()
|
||||
except:
|
||||
association = None
|
||||
new_history.add_galaxy_session( galaxy_session, association=association )
|
||||
trans.sa_session.add( new_history )
|
||||
trans.sa_session.flush()
|
||||
# Set imported history to be user's current history.
|
||||
trans.set_history( new_history )
|
||||
return trans.show_ok_message(
|
||||
message="""History "%s" has been imported. <br>You can <a href="%s" onclick="parent.window.location='%s';">start using this history</a> or %s."""
|
||||
% ( new_history.name, web.url_for( '/' ), web.url_for( '/' ), referer_message ), use_panels=True )
|
||||
|
||||
elif not user_history or not user_history.datasets or confirm:
|
||||
#TODO:?? should anon-users be allowed to include deleted datasets when importing?
|
||||
#new_history = import_history.copy( activatable=include_deleted )
|
||||
new_history = import_history.copy()
|
||||
new_history.name = "imported: " + new_history.name
|
||||
new_history.user_id = None
|
||||
galaxy_session = trans.get_galaxy_session()
|
||||
try:
|
||||
association = trans.sa_session.query( trans.app.model.GalaxySessionToHistoryAssociation ) \
|
||||
.filter_by( session_id=galaxy_session.id, history_id=new_history.id ) \
|
||||
.first()
|
||||
except:
|
||||
association = None
|
||||
new_history.add_galaxy_session( galaxy_session, association=association )
|
||||
trans.sa_session.add( new_history )
|
||||
trans.sa_session.flush()
|
||||
trans.set_history( new_history )
|
||||
return trans.show_ok_message(
|
||||
message="""History "%s" has been imported. <br>You can <a href="%s">start using this history</a> or %s."""
|
||||
% ( new_history.name, web.url_for( '/' ), referer_message ), use_panels=True )
|
||||
return trans.show_warn_message( """
|
||||
Warning! If you import this history, you will lose your current
|
||||
history. <br>You can <a href="%s">continue and import this history</a> or %s.
|
||||
""" % ( web.url_for(controller='history', action='imp', id=id, confirm=True, referer=trans.request.referer ), referer_message ), use_panels=True )
|
||||
#TODO: used in history/view, display, embed
|
||||
|
||||
@web.expose
|
||||
@web.require_login( "rename histories" )
|
||||
def rename( self, trans, id=None, name=None, **kwd ):
|
||||
@@ -1361,7 +1354,8 @@ class HistoryController( BaseUIController, SharableMixin, UsesAnnotations, UsesI
|
||||
name += " (active items only)"
|
||||
new_history = history.copy( name=name, target_user=user )
|
||||
if len( histories ) == 1:
|
||||
msg = 'New history "<a href="%s" target="_top">%s</a>" has been created.' % ( url_for( controller="history", action="switch_to_history", hist_id=trans.security.encode_id( new_history.id ) ) , new_history.name )
|
||||
switch_url = url_for( controller="history", action="switch_to_history", hist_id=trans.security.encode_id( new_history.id ) )
|
||||
msg = 'New history "<a href="%s" target="_top">%s</a>" has been created.' % ( switch_url, new_history.name )
|
||||
else:
|
||||
msg = 'Copied and created %d new histories.' % len( histories )
|
||||
return trans.show_ok_message( msg )
|
||||
|
||||
@@ -7,20 +7,28 @@ import urllib
|
||||
|
||||
from paste.httpexceptions import HTTPNotFound, HTTPBadGateway
|
||||
|
||||
from galaxy.web.base.controller import BaseUIController, UsesHistoryDatasetAssociationMixin, UsesHistoryMixin
|
||||
from galaxy import managers
|
||||
|
||||
from galaxy import web
|
||||
from galaxy.web import url_for
|
||||
from galaxy.model.item_attrs import UsesAnnotations
|
||||
from galaxy import util
|
||||
from galaxy.util import listify, Params, string_as_bool, string_as_bool_or_none
|
||||
from galaxy.web.base.controller import BaseUIController, UsesHistoryDatasetAssociationMixin, UsesHistoryMixin
|
||||
from galaxy.util.json import to_json_string
|
||||
|
||||
from galaxy.util.debugging import SimpleProfiler
|
||||
|
||||
import logging
|
||||
log = logging.getLogger( __name__ )
|
||||
|
||||
class RootController( BaseUIController, UsesHistoryMixin, UsesHistoryDatasetAssociationMixin, UsesAnnotations ):
|
||||
"""Controller class that maps to the url root of Galaxy (i.e. '/')."""
|
||||
"""
|
||||
Controller class that maps to the url root of Galaxy (i.e. '/').
|
||||
"""
|
||||
def __init__( self, app ):
|
||||
super( RootController, self ).__init__( app )
|
||||
self.mgrs = util.bunch.Bunch(
|
||||
histories=managers.histories.HistoryManager()
|
||||
)
|
||||
|
||||
@web.expose
|
||||
def default(self, trans, target1=None, target2=None, **kwd):
|
||||
@@ -100,42 +108,6 @@ class RootController( BaseUIController, UsesHistoryMixin, UsesHistoryDatasetAsso
|
||||
show_deleted=string_as_bool( show_deleted ),
|
||||
show_hidden=string_as_bool( show_hidden ) )
|
||||
|
||||
def _get_current_history_data( self, trans ):
|
||||
history_dictionary = {}
|
||||
hda_dictionaries = []
|
||||
|
||||
try:
|
||||
history = trans.get_history( create=True )
|
||||
hdas = self.get_history_datasets( trans, history,
|
||||
show_deleted=True, show_hidden=True, show_purged=True )
|
||||
|
||||
for hda in hdas:
|
||||
hda_dict = {}
|
||||
try:
|
||||
hda_dict = self.get_hda_dict( trans, hda )
|
||||
|
||||
except Exception, exc:
|
||||
# don't fail entire list if hda err's, record and move on
|
||||
log.error( 'Error bootstrapping hda %d: %s', hda.id, str( exc ), exc_info=True )
|
||||
hda_dict = self.get_hda_dict_with_error( trans, hda, str( exc ) )
|
||||
|
||||
hda_dictionaries.append( hda_dict )
|
||||
|
||||
# re-use the hdas above to get the history data...
|
||||
history_dictionary = self.get_history_dict( trans, history, hda_dictionaries=hda_dictionaries )
|
||||
|
||||
except Exception, exc:
|
||||
user_id = str( trans.user.id ) if trans.user else '(anonymous)'
|
||||
log.exception( 'Error bootstrapping history for user %s: %s', user_id, str( exc ) )
|
||||
message = ( 'An error occurred getting the history data from the server. '
|
||||
+ 'Please contact a Galaxy administrator if the problem persists.' )
|
||||
history_dictionary[ 'error' ] = message
|
||||
|
||||
return {
|
||||
'history' : history_dictionary,
|
||||
'hdas' : hda_dictionaries
|
||||
}
|
||||
|
||||
@web.expose
|
||||
def history( self, trans, as_xml=False, show_deleted=None, show_hidden=None, **kwd ):
|
||||
"""
|
||||
@@ -154,11 +126,11 @@ class RootController( BaseUIController, UsesHistoryMixin, UsesHistoryDatasetAsso
|
||||
show_hidden = string_as_bool_or_none( show_hidden )
|
||||
|
||||
history_dictionary = {}
|
||||
hda_dictionaries = []
|
||||
hda_dictionaries = []
|
||||
try:
|
||||
history_data = self._get_current_history_data( trans )
|
||||
history_data = self.mgrs.histories._get_history_data( trans, trans.get_history( create=True ) )
|
||||
history_dictionary = history_data[ 'history' ]
|
||||
hda_dictionaries = history_data[ 'hdas' ]
|
||||
hda_dictionaries = history_data[ 'contents' ]
|
||||
|
||||
except Exception, exc:
|
||||
user_id = str( trans.user.id ) if trans.user else '(anonymous)'
|
||||
@@ -170,6 +142,7 @@ class RootController( BaseUIController, UsesHistoryMixin, UsesHistoryDatasetAsso
|
||||
history = history_dictionary, hdas = hda_dictionaries,
|
||||
show_deleted=show_deleted, show_hidden=show_hidden )
|
||||
|
||||
|
||||
## ---- Dataset display / editing ----------------------------------------
|
||||
@web.expose
|
||||
def display( self, trans, id=None, hid=None, tofile=None, toext=".txt", encoded_id=None, **kwd ):
|
||||
|
||||
Reference in New Issue
Block a user