mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
Extract some mixin's out of trans allowing trans-like functionilty outside web threads...
In particular break out mixins defining context-based utilities for interacting with app, an user, and a history. In downstream work on workflow scheduling this proves nessecary and sufficient to create an context for 'execute'-ing tools (create jobs) outside of web threads in response to workflow requests. Unit tests for some of this.
This commit is contained in:
@@ -491,7 +491,178 @@ class WebApplication( base.WebApplication ):
|
||||
return T( app )
|
||||
|
||||
|
||||
class GalaxyWebTransaction( base.DefaultWebTransaction ):
|
||||
class ProvidesAppContext( object ):
|
||||
""" For transaction-like objects to provide Galaxy convience layer for
|
||||
database and event handling.
|
||||
|
||||
Mixed in class must provide `app` property.
|
||||
"""
|
||||
|
||||
def log_action( self, user=None, action=None, context=None, params=None):
|
||||
"""
|
||||
Application-level logging of user actions.
|
||||
"""
|
||||
if self.app.config.log_actions:
|
||||
action = self.app.model.UserAction(action=action, context=context, params=unicode( to_json_string( params ) ) )
|
||||
try:
|
||||
if user:
|
||||
action.user = user
|
||||
else:
|
||||
action.user = self.user
|
||||
except:
|
||||
action.user = None
|
||||
try:
|
||||
action.session_id = self.galaxy_session.id
|
||||
except:
|
||||
action.session_id = None
|
||||
self.sa_session.add( action )
|
||||
self.sa_session.flush()
|
||||
|
||||
def log_event( self, message, tool_id=None, **kwargs ):
|
||||
"""
|
||||
Application level logging. Still needs fleshing out (log levels and such)
|
||||
Logging events is a config setting - if False, do not log.
|
||||
"""
|
||||
if self.app.config.log_events:
|
||||
event = self.app.model.Event()
|
||||
event.tool_id = tool_id
|
||||
try:
|
||||
event.message = message % kwargs
|
||||
except:
|
||||
event.message = message
|
||||
try:
|
||||
event.history = self.get_history()
|
||||
except:
|
||||
event.history = None
|
||||
try:
|
||||
event.history_id = self.history.id
|
||||
except:
|
||||
event.history_id = None
|
||||
try:
|
||||
event.user = self.user
|
||||
except:
|
||||
event.user = None
|
||||
try:
|
||||
event.session_id = self.galaxy_session.id
|
||||
except:
|
||||
event.session_id = None
|
||||
self.sa_session.add( event )
|
||||
self.sa_session.flush()
|
||||
|
||||
@property
|
||||
def sa_session( self ):
|
||||
"""
|
||||
Returns a SQLAlchemy session -- currently just gets the current
|
||||
session from the threadlocal session context, but this is provided
|
||||
to allow migration toward a more SQLAlchemy 0.4 style of use.
|
||||
"""
|
||||
return self.app.model.context.current
|
||||
|
||||
def expunge_all( self ):
|
||||
app = self.app
|
||||
context = app.model.context
|
||||
context.expunge_all()
|
||||
# This is a bit hacky, should refctor this. Maybe refactor to app -> expunge_all()
|
||||
if hasattr(app, 'install_model'):
|
||||
install_model = app.install_model
|
||||
if install_model != app.model:
|
||||
install_model.context.expunge_all()
|
||||
|
||||
def get_toolbox(self):
|
||||
"""Returns the application toolbox"""
|
||||
return self.app.toolbox
|
||||
|
||||
@property
|
||||
def model( self ):
|
||||
return self.app.model
|
||||
|
||||
@property
|
||||
def install_model( self ):
|
||||
return self.app.install_model
|
||||
|
||||
def request_types(self):
|
||||
if self.sa_session.query( self.app.model.RequestType ).filter_by( deleted=False ).count() > 0:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class ProvidesUserContext( object ):
|
||||
""" For transaction-like objects to provide Galaxy convience layer for
|
||||
reasoning about users.
|
||||
|
||||
Mixed in class must provide `user`, `api_inherit_admin`, and `app`
|
||||
properties.
|
||||
"""
|
||||
|
||||
@property
|
||||
def anonymous( self ):
|
||||
return self.user is None and not self.api_inherit_admin
|
||||
|
||||
def get_current_user_roles( self ):
|
||||
user = self.user
|
||||
if user:
|
||||
roles = user.all_roles()
|
||||
else:
|
||||
roles = []
|
||||
return roles
|
||||
|
||||
def user_is_admin( self ):
|
||||
if self.api_inherit_admin:
|
||||
return True
|
||||
return self.user and self.user.email in self.app.config.admin_users_list
|
||||
|
||||
def user_can_do_run_as( self ):
|
||||
run_as_users = [ user for user in self.app.config.get( "api_allow_run_as", "" ).split( "," ) if user ]
|
||||
if not run_as_users:
|
||||
return False
|
||||
user_in_run_as_users = self.user and self.user.email in run_as_users
|
||||
# Can do if explicitly in list or master_api_key supplied.
|
||||
can_do_run_as = user_in_run_as_users or self.api_inherit_admin
|
||||
return can_do_run_as
|
||||
|
||||
@property
|
||||
def user_ftp_dir( self ):
|
||||
identifier = self.app.config.ftp_upload_dir_identifier
|
||||
return os.path.join( self.app.config.ftp_upload_dir, getattr(self.user, identifier) )
|
||||
|
||||
|
||||
class ProvidesHistoryContext( object ):
|
||||
""" For transaction-like objects to provide Galaxy convience layer for
|
||||
reasoning about histories.
|
||||
|
||||
Mixed in class must provide `user`, `history`, and `app`
|
||||
properties.
|
||||
"""
|
||||
|
||||
def db_dataset_for( self, dbkey ):
|
||||
"""
|
||||
Returns the db_file dataset associated/needed by `dataset`, or `None`.
|
||||
"""
|
||||
# If no history, return None.
|
||||
if self.history is None:
|
||||
return None
|
||||
if isinstance(self.history, Bunch):
|
||||
# The API presents a Bunch for a history. Until the API is
|
||||
# more fully featured for handling this, also return None.
|
||||
return None
|
||||
datasets = self.sa_session.query( self.app.model.HistoryDatasetAssociation ) \
|
||||
.filter_by( deleted=False, history_id=self.history.id, extension="len" )
|
||||
for ds in datasets:
|
||||
if dbkey == ds.dbkey:
|
||||
return ds
|
||||
return None
|
||||
|
||||
@property
|
||||
def db_builds( self ):
|
||||
"""
|
||||
Returns the builds defined by galaxy and the builds defined by
|
||||
the user (chromInfo in history).
|
||||
"""
|
||||
# FIXME: This method should be removed
|
||||
return self.app.genome_builds.get_genome_build_names( trans=self )
|
||||
|
||||
|
||||
class GalaxyWebTransaction( base.DefaultWebTransaction, ProvidesAppContext, ProvidesUserContext, ProvidesHistoryContext ):
|
||||
"""
|
||||
Encapsulates web transaction specific state for the Galaxy application
|
||||
(specifically the user's "cookie" session and history)
|
||||
@@ -552,29 +723,6 @@ class GalaxyWebTransaction( base.DefaultWebTransaction ):
|
||||
t = Translations.load( dirname='locale', locales=locales, domain='ginga' )
|
||||
self.template_context.update( dict( _=t.ugettext, n_=t.ugettext, N_=t.ungettext ) )
|
||||
|
||||
@property
|
||||
def anonymous( self ):
|
||||
return self.user is None and not self.api_inherit_admin
|
||||
|
||||
@property
|
||||
def sa_session( self ):
|
||||
"""
|
||||
Returns a SQLAlchemy session -- currently just gets the current
|
||||
session from the threadlocal session context, but this is provided
|
||||
to allow migration toward a more SQLAlchemy 0.4 style of use.
|
||||
"""
|
||||
return self.app.model.context.current
|
||||
|
||||
def expunge_all( self ):
|
||||
app = self.app
|
||||
context = app.model.context
|
||||
context.expunge_all()
|
||||
# This is a bit hacky, should refctor this. Maybe refactor to app -> expunge_all()
|
||||
if hasattr(app, 'install_model'):
|
||||
install_model = app.install_model
|
||||
if install_model != app.model:
|
||||
install_model.context.expunge_all()
|
||||
|
||||
def get_user( self ):
|
||||
"""Return the current user if logged in or None."""
|
||||
if self.galaxy_session:
|
||||
@@ -592,57 +740,6 @@ class GalaxyWebTransaction( base.DefaultWebTransaction ):
|
||||
|
||||
user = property( get_user, set_user )
|
||||
|
||||
def log_action( self, user=None, action=None, context=None, params=None):
|
||||
"""
|
||||
Application-level logging of user actions.
|
||||
"""
|
||||
if self.app.config.log_actions:
|
||||
action = self.app.model.UserAction(action=action, context=context, params=unicode( to_json_string( params ) ) )
|
||||
try:
|
||||
if user:
|
||||
action.user = user
|
||||
else:
|
||||
action.user = self.user
|
||||
except:
|
||||
action.user = None
|
||||
try:
|
||||
action.session_id = self.galaxy_session.id
|
||||
except:
|
||||
action.session_id = None
|
||||
self.sa_session.add( action )
|
||||
self.sa_session.flush()
|
||||
|
||||
def log_event( self, message, tool_id=None, **kwargs ):
|
||||
"""
|
||||
Application level logging. Still needs fleshing out (log levels and such)
|
||||
Logging events is a config setting - if False, do not log.
|
||||
"""
|
||||
if self.app.config.log_events:
|
||||
event = self.app.model.Event()
|
||||
event.tool_id = tool_id
|
||||
try:
|
||||
event.message = message % kwargs
|
||||
except:
|
||||
event.message = message
|
||||
try:
|
||||
event.history = self.get_history()
|
||||
except:
|
||||
event.history = None
|
||||
try:
|
||||
event.history_id = self.history.id
|
||||
except:
|
||||
event.history_id = None
|
||||
try:
|
||||
event.user = self.user
|
||||
except:
|
||||
event.user = None
|
||||
try:
|
||||
event.session_id = self.galaxy_session.id
|
||||
except:
|
||||
event.session_id = None
|
||||
self.sa_session.add( event )
|
||||
self.sa_session.flush()
|
||||
|
||||
def get_cookie( self, name='galaxysession' ):
|
||||
"""Convenience method for getting a session cookie"""
|
||||
try:
|
||||
@@ -1100,44 +1197,10 @@ class GalaxyWebTransaction( base.DefaultWebTransaction ):
|
||||
self.sa_session.flush()
|
||||
return history
|
||||
|
||||
def get_current_user_roles( self ):
|
||||
user = self.get_user()
|
||||
if user:
|
||||
roles = user.all_roles()
|
||||
else:
|
||||
roles = []
|
||||
return roles
|
||||
|
||||
def user_is_admin( self ):
|
||||
if self.api_inherit_admin:
|
||||
return True
|
||||
return self.user and self.user.email in self.app.config.admin_users_list
|
||||
|
||||
def user_can_do_run_as( self ):
|
||||
run_as_users = [ user for user in self.app.config.get( "api_allow_run_as", "" ).split( "," ) if user ]
|
||||
if not run_as_users:
|
||||
return False
|
||||
user_in_run_as_users = self.user and self.user.email in run_as_users
|
||||
# Can do if explicitly in list or master_api_key supplied.
|
||||
can_do_run_as = user_in_run_as_users or self.api_inherit_admin
|
||||
return can_do_run_as
|
||||
|
||||
def get_toolbox(self):
|
||||
"""Returns the application toolbox"""
|
||||
return self.app.toolbox
|
||||
|
||||
@base.lazy_property
|
||||
def template_context( self ):
|
||||
return dict()
|
||||
|
||||
@property
|
||||
def model( self ):
|
||||
return self.app.model
|
||||
|
||||
@property
|
||||
def install_model( self ):
|
||||
return self.app.install_model
|
||||
|
||||
def make_form_data( self, name, **kwargs ):
|
||||
rval = self.template_context[name] = FormData()
|
||||
rval.values.update( kwargs )
|
||||
@@ -1250,43 +1313,6 @@ class GalaxyWebTransaction( base.DefaultWebTransaction ):
|
||||
searchList=[context or kwargs, dict(caller=self)] )
|
||||
return str(template)
|
||||
|
||||
@property
|
||||
def db_builds( self ):
|
||||
"""
|
||||
Returns the builds defined by galaxy and the builds defined by
|
||||
the user (chromInfo in history).
|
||||
"""
|
||||
# FIXME: This method should be removed
|
||||
return self.app.genome_builds.get_genome_build_names( trans=self )
|
||||
|
||||
@property
|
||||
def user_ftp_dir( self ):
|
||||
identifier = self.app.config.ftp_upload_dir_identifier
|
||||
return os.path.join( self.app.config.ftp_upload_dir, getattr(self.user, identifier) )
|
||||
|
||||
def db_dataset_for( self, dbkey ):
|
||||
"""
|
||||
Returns the db_file dataset associated/needed by `dataset`, or `None`.
|
||||
"""
|
||||
# If no history, return None.
|
||||
if self.history is None:
|
||||
return None
|
||||
if isinstance(self.history, Bunch):
|
||||
# The API presents a Bunch for a history. Until the API is
|
||||
# more fully featured for handling this, also return None.
|
||||
return None
|
||||
datasets = self.sa_session.query( self.app.model.HistoryDatasetAssociation ) \
|
||||
.filter_by( deleted=False, history_id=self.history.id, extension="len" )
|
||||
for ds in datasets:
|
||||
if dbkey == ds.dbkey:
|
||||
return ds
|
||||
return None
|
||||
|
||||
def request_types(self):
|
||||
if self.sa_session.query( self.app.model.RequestType ).filter_by( deleted=False ).count() > 0:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class FormBuilder( object ):
|
||||
"""
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
from galaxy import model
|
||||
from galaxy.model import mapping
|
||||
from galaxy.util import bunch
|
||||
from galaxy.web import framework
|
||||
|
||||
|
||||
class TestTransaction( framework.ProvidesAppContext ):
|
||||
|
||||
def __init__( self ):
|
||||
self.app = TestApp()
|
||||
|
||||
|
||||
def test_logging_events_off():
|
||||
trans = TestTransaction()
|
||||
trans.log_event( "test event 123" )
|
||||
assert len( trans.sa_session.query( model.Event ).all() ) == 0
|
||||
|
||||
|
||||
def test_logging_events_on():
|
||||
trans = TestTransaction()
|
||||
trans.app.config.log_events = True
|
||||
trans.log_event( "test event 123" )
|
||||
events = trans.sa_session.query( model.Event ).all()
|
||||
assert len( events ) == 1
|
||||
assert events[ 0 ].message == "test event 123"
|
||||
|
||||
|
||||
def test_logging_actions_off():
|
||||
trans = TestTransaction()
|
||||
trans.log_action( "test action 123" )
|
||||
assert len( trans.sa_session.query( model.Event ).all() ) == 0
|
||||
|
||||
|
||||
def test_logging_actions_on():
|
||||
trans = TestTransaction()
|
||||
trans.app.config.log_actions = True
|
||||
trans.log_action( None, "test action 123", context="the context", params=dict(foo="bar") )
|
||||
actions = trans.sa_session.query( model.UserAction ).all()
|
||||
assert len( actions ) == 1
|
||||
assert actions[ 0 ].action == "test action 123"
|
||||
|
||||
|
||||
def test_expunge_all():
|
||||
trans = TestTransaction()
|
||||
|
||||
user = model.User( "foo", "bar1" )
|
||||
trans.sa_session.add( user )
|
||||
|
||||
user.password = "bar2"
|
||||
trans.sa_session.flush()
|
||||
|
||||
assert trans.sa_session.query( model.User ).first().password == "bar2"
|
||||
|
||||
trans.sa_session.expunge_all()
|
||||
|
||||
user.password = "bar3"
|
||||
trans.sa_session.flush()
|
||||
|
||||
# Password unchange because not attached to session/context.
|
||||
assert trans.sa_session.query( model.User ).first().password == "bar2"
|
||||
|
||||
|
||||
class TestApp( object ):
|
||||
|
||||
def __init__( self ):
|
||||
self.config = bunch.Bunch(
|
||||
log_events=False,
|
||||
log_actions=False,
|
||||
)
|
||||
self.model = mapping.init(
|
||||
"/tmp",
|
||||
"sqlite:///:memory:",
|
||||
create_tables=True
|
||||
)
|
||||
Reference in New Issue
Block a user