/lib/galaxy/web/framework/__init__.py

https://bitbucket.org/ialbert/galaxy-genetrack · Python · 692 lines · 559 code · 18 blank · 115 comment · 64 complexity · a50ed1323a738185bd6f52a268742a61 MD5 · raw file

  1. """
  2. Galaxy web application framework
  3. """
  4. import pkg_resources
  5. import os, sys, time, socket, random, string
  6. pkg_resources.require( "Cheetah" )
  7. from Cheetah.Template import Template
  8. import base
  9. import pickle
  10. from galaxy import util
  11. pkg_resources.require( "simplejson" )
  12. import simplejson
  13. import helpers
  14. pkg_resources.require( "PasteDeploy" )
  15. from paste.deploy.converters import asbool
  16. pkg_resources.require( "Mako" )
  17. import mako.template
  18. import mako.lookup
  19. import mako.runtime
  20. pkg_resources.require( "Babel" )
  21. from babel.support import Translations
  22. pkg_resources.require( "SQLAlchemy >= 0.4" )
  23. from sqlalchemy import and_
  24. import logging
  25. log = logging.getLogger( __name__ )
  26. url_for = base.routes.url_for
  27. UCSC_SERVERS = (
  28. 'hgw1.cse.ucsc.edu',
  29. 'hgw2.cse.ucsc.edu',
  30. 'hgw3.cse.ucsc.edu',
  31. 'hgw4.cse.ucsc.edu',
  32. 'hgw5.cse.ucsc.edu',
  33. 'hgw6.cse.ucsc.edu',
  34. 'hgw7.cse.ucsc.edu',
  35. 'hgw8.cse.ucsc.edu',
  36. )
  37. def expose( func ):
  38. """
  39. Decorator: mark a function as 'exposed' and thus web accessible
  40. """
  41. func.exposed = True
  42. return func
  43. def json( func ):
  44. def decorator( self, trans, *args, **kwargs ):
  45. trans.response.set_content_type( "text/javascript" )
  46. return simplejson.dumps( func( self, trans, *args, **kwargs ) )
  47. if not hasattr(func, '_orig'):
  48. decorator._orig = func
  49. decorator.exposed = True
  50. return decorator
  51. def require_login( verb="perform this action" ):
  52. def argcatcher( func ):
  53. def decorator( self, trans, *args, **kwargs ):
  54. if trans.get_user():
  55. return func( self, trans, *args, **kwargs )
  56. else:
  57. return trans.show_error_message(
  58. "You must be <a target='galaxy_main' href='%s'>logged in</a> to %s</div>"
  59. % ( url_for( controller='user', action='login' ), verb ) )
  60. return decorator
  61. return argcatcher
  62. def require_admin( func ):
  63. def decorator( self, trans, *args, **kwargs ):
  64. admin_users = trans.app.config.get( "admin_users", "" ).split( "," )
  65. if not admin_users:
  66. return trans.show_error_message( "You must be logged in as an administrator to access this feature, but no administrators are set in the Galaxy configuration." )
  67. user = trans.get_user()
  68. if not user:
  69. return trans.show_error_message( "You must be logged in as an administrator to access this feature." )
  70. if not user.email in admin_users:
  71. return trans.show_error_message( "You must be an administrator to access this feature." )
  72. return func( self, trans, *args, **kwargs )
  73. return decorator
  74. NOT_SET = object()
  75. class MessageException( Exception ):
  76. """
  77. Exception to make throwing errors from deep in controllers easier
  78. """
  79. def __init__( self, err_msg, type="info" ):
  80. self.err_msg = err_msg
  81. self.type = type
  82. def error( message ):
  83. raise MessageException( message, type='error' )
  84. def form( *args, **kwargs ):
  85. return FormBuilder( *args, **kwargs )
  86. class WebApplication( base.WebApplication ):
  87. def __init__( self, galaxy_app, session_cookie='galaxysession' ):
  88. base.WebApplication.__init__( self )
  89. self.set_transaction_factory( lambda e: UniverseWebTransaction( e, galaxy_app, self, session_cookie ) )
  90. # Mako support
  91. self.mako_template_lookup = mako.lookup.TemplateLookup(
  92. directories = [ galaxy_app.config.template_path ] ,
  93. module_directory = galaxy_app.config.template_cache,
  94. collection_size = 500,
  95. output_encoding = 'utf-8' )
  96. # Security helper
  97. self.security = galaxy_app.security
  98. def handle_controller_exception( self, e, trans, **kwargs ):
  99. if isinstance( e, MessageException ):
  100. return trans.show_message( e.err_msg, e.type )
  101. def make_body_iterable( self, trans, body ):
  102. if isinstance( body, FormBuilder ):
  103. body = trans.show_form( body )
  104. return base.WebApplication.make_body_iterable( self, trans, body )
  105. class UniverseWebTransaction( base.DefaultWebTransaction ):
  106. """
  107. Encapsulates web transaction specific state for the Universe application
  108. (specifically the user's "cookie" session and history)
  109. """
  110. def __init__( self, environ, app, webapp, session_cookie ):
  111. self.app = app
  112. self.webapp = webapp
  113. self.security = webapp.security
  114. # FIXME: the following 3 attributes are not currently used
  115. # Remove them if they are not going to be...
  116. self.__user = NOT_SET
  117. self.__history = NOT_SET
  118. self.__galaxy_session = NOT_SET
  119. base.DefaultWebTransaction.__init__( self, environ )
  120. self.setup_i18n()
  121. self.sa_session.clear()
  122. self.debug = asbool( self.app.config.get( 'debug', False ) )
  123. # Flag indicating whether we are in workflow building mode (means
  124. # that the current history should not be used for parameter values
  125. # and such).
  126. self.workflow_building_mode = False
  127. # Always have a valid galaxy session
  128. self.__ensure_valid_session( session_cookie )
  129. # Prevent deleted users from accessing Galaxy
  130. if self.app.config.use_remote_user and self.galaxy_session.user.deleted:
  131. self.response.send_redirect( url_for( '/static/user_disabled.html' ) )
  132. if self.app.config.require_login:
  133. self.__ensure_logged_in_user( environ )
  134. def setup_i18n( self ):
  135. if 'HTTP_ACCEPT_LANGUAGE' in self.environ:
  136. # locales looks something like: ['en', 'en-us;q=0.7', 'ja;q=0.3']
  137. locales = self.environ['HTTP_ACCEPT_LANGUAGE'].split( ',' )
  138. locales = [ l.split( ';' )[0] for l in locales ]
  139. else:
  140. # Default to English
  141. locales = 'en'
  142. t = Translations.load( dirname='locale', locales=locales, domain='ginga' )
  143. self.template_context.update ( dict( _=t.ugettext, n_=t.ugettext, N_=t.ungettext ) )
  144. @property
  145. def sa_session( self ):
  146. """
  147. Returns a SQLAlchemy session -- currently just gets the current
  148. session from the threadlocal session context, but this is provided
  149. to allow migration toward a more SQLAlchemy 0.4 style of use.
  150. """
  151. return self.app.model.context.current
  152. def log_event( self, message, tool_id=None, **kwargs ):
  153. """
  154. Application level logging. Still needs fleshing out (log levels and such)
  155. Logging events is a config setting - if False, do not log.
  156. """
  157. if self.app.config.log_events:
  158. event = self.app.model.Event()
  159. event.tool_id = tool_id
  160. try:
  161. event.message = message % kwargs
  162. except:
  163. event.message = message
  164. try:
  165. event.history = self.get_history()
  166. except:
  167. event.history = None
  168. try:
  169. event.history_id = self.history.id
  170. except:
  171. event.history_id = None
  172. try:
  173. event.user = self.user
  174. except:
  175. event.user = None
  176. try:
  177. event.session_id = self.galaxy_session.id
  178. except:
  179. event.session_id = None
  180. event.flush()
  181. def get_cookie( self, name='galaxysession' ):
  182. """Convenience method for getting a session cookie"""
  183. try:
  184. # If we've changed the cookie during the request return the new value
  185. if name in self.response.cookies:
  186. return self.response.cookies[name].value
  187. else:
  188. return self.request.cookies[name].value
  189. except:
  190. return None
  191. def set_cookie( self, value, name='galaxysession', path='/', age=90, version='1' ):
  192. """Convenience method for setting a session cookie"""
  193. # The galaxysession cookie value must be a high entropy 128 bit random number encrypted
  194. # using a server secret key. Any other value is invalid and could pose security issues.
  195. self.response.cookies[name] = value
  196. self.response.cookies[name]['path'] = path
  197. self.response.cookies[name]['max-age'] = 3600 * 24 * age # 90 days
  198. tstamp = time.localtime ( time.time() + 3600 * 24 * age )
  199. self.response.cookies[name]['expires'] = time.strftime( '%a, %d-%b-%Y %H:%M:%S GMT', tstamp )
  200. self.response.cookies[name]['version'] = version
  201. #@property
  202. #def galaxy_session( self ):
  203. # if not self.__galaxy_session:
  204. # self.__ensure_valid_session()
  205. # return self.__galaxy_session
  206. def __ensure_valid_session( self, session_cookie ):
  207. """
  208. Ensure that a valid Galaxy session exists and is available as
  209. trans.session (part of initialization)
  210. Support for universe_session and universe_user cookies has been
  211. removed as of 31 Oct 2008.
  212. """
  213. sa_session = self.sa_session
  214. # Try to load an existing session
  215. secure_id = self.get_cookie( name=session_cookie )
  216. galaxy_session = None
  217. prev_galaxy_session = None
  218. user_for_new_session = None
  219. invalidate_existing_session = False
  220. # Track whether the session has changed so we can avoid calling flush
  221. # in the most common case (session exists and is valid).
  222. galaxy_session_requires_flush = False
  223. if secure_id:
  224. # Decode the cookie value to get the session_key
  225. session_key = self.security.decode_session_key( secure_id )
  226. try:
  227. # Make sure we have a valid UTF-8 string
  228. session_key = session_key.encode( 'utf8' )
  229. except UnicodeDecodeError:
  230. # We'll end up creating a new galaxy_session
  231. session_key = None
  232. if session_key:
  233. # Retrieve the galaxy_session id via the unique session_key
  234. galaxy_session = self.app.model.GalaxySession.filter( and_( self.app.model.GalaxySession.table.c.session_key==session_key,
  235. self.app.model.GalaxySession.table.c.is_valid==True ) ).first()
  236. # If remote user is in use it can invalidate the session, so we need to to check some things now.
  237. if self.app.config.use_remote_user:
  238. assert "HTTP_REMOTE_USER" in self.environ, \
  239. "use_remote_user is set but no HTTP_REMOTE_USER variable"
  240. remote_user_email = self.environ[ 'HTTP_REMOTE_USER' ]
  241. if galaxy_session:
  242. # An existing session, make sure correct association exists
  243. if galaxy_session.user is None:
  244. # No user, associate
  245. galaxy_session.user = self.__get_or_create_remote_user( remote_user_email )
  246. galaxy_session_requires_flush = True
  247. elif galaxy_session.user.email != remote_user_email:
  248. # Session exists but is not associated with the correct remote user
  249. invalidate_existing_session = True
  250. user_for_new_session = self.__get_or_create_remote_user( remote_user_email )
  251. log.warning( "User logged in as '%s' externally, but has a cookie as '%s' invalidating session",
  252. remote_user_email, galaxy_session.user.email )
  253. else:
  254. # No session exists, get/create user for new session
  255. user_for_new_session = self.__get_or_create_remote_user( remote_user_email )
  256. else:
  257. if galaxy_session is not None and galaxy_session.user and galaxy_session.user.external:
  258. # Remote user support is not enabled, but there is an existing
  259. # session with an external user, invalidate
  260. invalidate_existing_session = True
  261. log.warning( "User '%s' is an external user with an existing session, invalidating session since external auth is disabled",
  262. galaxy_session.user.email )
  263. elif galaxy_session is not None and galaxy_session.user is not None and galaxy_session.user.deleted:
  264. invalidate_existing_session = True
  265. log.warning( "User '%s' is marked deleted, invalidating session" % galaxy_session.user.email )
  266. # Do we need to invalidate the session for some reason?
  267. if invalidate_existing_session:
  268. prev_galaxy_session = galaxy_session
  269. prev_galaxy_session.is_valid = False
  270. galaxy_session = None
  271. # No relevant cookies, or couldn't find, or invalid, so create a new session
  272. if galaxy_session is None:
  273. galaxy_session = self.__create_new_session( prev_galaxy_session, user_for_new_session )
  274. galaxy_session_requires_flush = True
  275. self.galaxy_session = galaxy_session
  276. self.__update_session_cookie( name=session_cookie )
  277. else:
  278. self.galaxy_session = galaxy_session
  279. # Do we need to flush the session?
  280. if galaxy_session_requires_flush:
  281. objects_to_flush = [ galaxy_session ]
  282. # FIXME: If prev_session is a proper relation this would not
  283. # be needed.
  284. if prev_galaxy_session:
  285. objects_to_flush.append( prev_galaxy_session )
  286. sa_session.flush( objects_to_flush )
  287. # If the old session was invalid, get a new history with our new session
  288. if invalidate_existing_session:
  289. self.new_history()
  290. def __ensure_logged_in_user( self, environ ):
  291. allowed_paths = (
  292. url_for( controller='root', action='index' ),
  293. url_for( controller='root', action='tool_menu' ),
  294. url_for( controller='root', action='masthead' ),
  295. url_for( controller='root', action='history' ),
  296. url_for( controller='user', action='login' ),
  297. url_for( controller='user', action='create' ),
  298. url_for( controller='user', action='reset_password' ),
  299. url_for( controller='library', action='browse' )
  300. )
  301. display_as = url_for( controller='root', action='display_as' )
  302. if self.galaxy_session.user is None:
  303. if self.app.config.ucsc_display_sites and self.request.path == display_as:
  304. try:
  305. host = socket.gethostbyaddr( self.environ[ 'REMOTE_ADDR' ] )[0]
  306. except( socket.error, socket.herror, socket.gaierror, socket.timeout ):
  307. host = None
  308. if host in UCSC_SERVERS:
  309. return
  310. if self.request.path not in allowed_paths:
  311. self.response.send_redirect( url_for( controller='root', action='index' ) )
  312. def __create_new_session( self, prev_galaxy_session=None, user_for_new_session=None ):
  313. """
  314. Create a new GalaxySession for this request, possibly with a connection
  315. to a previous session (in `prev_galaxy_session`) and an existing user
  316. (in `user_for_new_session`).
  317. Caller is responsible for flushing the returned session.
  318. """
  319. session_key = self.security.get_new_session_key()
  320. galaxy_session = self.app.model.GalaxySession(
  321. session_key=session_key,
  322. is_valid=True,
  323. remote_host = self.request.remote_host,
  324. remote_addr = self.request.remote_addr,
  325. referer = self.request.headers.get( 'Referer', None ) )
  326. if prev_galaxy_session:
  327. # Invalidated an existing session for some reason, keep track
  328. galaxy_session.prev_session_id = prev_galaxy_session.id
  329. if user_for_new_session:
  330. # The new session should be associated with the user
  331. galaxy_session.user = user_for_new_session
  332. return galaxy_session
  333. def __get_or_create_remote_user( self, remote_user_email ):
  334. """
  335. Return the user in $HTTP_REMOTE_USER and create if necessary
  336. """
  337. # remote_user middleware ensures HTTP_REMOTE_USER exists
  338. user = self.app.model.User.filter( self.app.model.User.table.c.email==remote_user_email ).first()
  339. if user:
  340. # GVK: June 29, 2009 - This is to correct the behavior of a previous bug where a private
  341. # role and default user / history permissions were not set for remote users. When a
  342. # remote user authenticates, we'll look for this information, and if missing, create it.
  343. if not self.app.security_agent.get_private_user_role( user ):
  344. self.app.security_agent.create_private_user_role( user )
  345. if not user.default_permissions:
  346. self.app.security_agent.user_set_default_permissions( user, history=True, dataset=True )
  347. elif user is None:
  348. random.seed()
  349. user = self.app.model.User( email=remote_user_email )
  350. user.set_password_cleartext( ''.join( random.sample( string.letters + string.digits, 12 ) ) )
  351. user.external = True
  352. user.flush()
  353. self.app.security_agent.create_private_user_role( user )
  354. # We set default user permissions, before we log in and set the default history permissions
  355. self.app.security_agent.user_set_default_permissions( user )
  356. #self.log_event( "Automatically created account '%s'", user.email )
  357. return user
  358. def __update_session_cookie( self, name='galaxysession' ):
  359. """
  360. Update the session cookie to match the current session.
  361. """
  362. self.set_cookie( self.security.encode_session_key( self.galaxy_session.session_key ), name=name )
  363. def handle_user_login( self, user ):
  364. """
  365. Login a new user (possibly newly created)
  366. - create a new session
  367. - associate new session with user
  368. - if old session had a history and it was not associated with a user, associate it with the new session,
  369. otherwise associate the current session's history with the user
  370. """
  371. # Set the previous session
  372. prev_galaxy_session = self.galaxy_session
  373. prev_galaxy_session.is_valid = False
  374. # Define a new current_session
  375. self.galaxy_session = self.__create_new_session( prev_galaxy_session, user )
  376. # Associated the current user's last accessed history (if exists) with their new session
  377. history = None
  378. try:
  379. users_last_session = user.galaxy_sessions[0]
  380. last_accessed = True
  381. except:
  382. users_last_session = None
  383. last_accessed = False
  384. if users_last_session and users_last_session.current_history:
  385. history = users_last_session.current_history
  386. if not history:
  387. if prev_galaxy_session.current_history:
  388. if prev_galaxy_session.current_history.user is None or prev_galaxy_session.current_history.user == user:
  389. # If the previous galaxy session had a history, associate it with the new
  390. # session, but only if it didn't belong to a different user.
  391. history = prev_galaxy_session.current_history
  392. elif self.galaxy_session.current_history:
  393. history = self.galaxy_session.current_history
  394. else:
  395. history = self.get_history( create=True )
  396. if history not in self.galaxy_session.histories:
  397. self.galaxy_session.add_history( history )
  398. if history.user is None:
  399. history.user = user
  400. self.galaxy_session.current_history = history
  401. if not last_accessed:
  402. # Only set default history permissions if current history is not from a previous session
  403. self.app.security_agent.history_set_default_permissions( history, dataset=True, bypass_manage_permission=True )
  404. self.sa_session.flush( [ prev_galaxy_session, self.galaxy_session, history ] )
  405. # This method is not called from the Galaxy reports, so the cookie will always be galaxysession
  406. self.__update_session_cookie( name='galaxysession' )
  407. def handle_user_logout( self ):
  408. """
  409. Logout the current user:
  410. - invalidate the current session
  411. - create a new session with no user associated
  412. """
  413. prev_galaxy_session = self.galaxy_session
  414. prev_galaxy_session.is_valid = False
  415. self.galaxy_session = self.__create_new_session( prev_galaxy_session )
  416. self.sa_session.flush( [ prev_galaxy_session, self.galaxy_session ] )
  417. # This method is not called from the Galaxy reports, so the cookie will always be galaxysession
  418. self.__update_session_cookie( name='galaxysession' )
  419. def get_galaxy_session( self ):
  420. """
  421. Return the current galaxy session
  422. """
  423. return self.galaxy_session
  424. def get_history( self, create=False ):
  425. """
  426. Load the current history, creating a new one only if there is not
  427. current history and we're told to create"
  428. """
  429. history = self.galaxy_session.current_history
  430. if not history:
  431. if util.string_as_bool( create ):
  432. history = self.new_history()
  433. else:
  434. # Perhaps a bot is running a tool without having logged in to get a history
  435. log.debug( "Error: this request returned None from get_history(): %s" % self.request.browser_url )
  436. return None
  437. return history
  438. def set_history( self, history ):
  439. if history and not history.deleted:
  440. self.galaxy_session.current_history = history
  441. self.sa_session.flush( [ self.galaxy_session ] )
  442. history = property( get_history, set_history )
  443. def new_history( self, name=None ):
  444. """
  445. Create a new history and associate it with the current session and
  446. its associated user (if set).
  447. """
  448. # Create new history
  449. history = self.app.model.History()
  450. if name:
  451. history.name = name
  452. # Associate with session
  453. history.add_galaxy_session( self.galaxy_session )
  454. # Make it the session's current history
  455. self.galaxy_session.current_history = history
  456. # Associate with user
  457. if self.galaxy_session.user:
  458. history.user = self.galaxy_session.user
  459. # Track genome_build with history
  460. history.genome_build = util.dbnames.default_value
  461. # Set the user's default history permissions
  462. self.app.security_agent.history_set_default_permissions( history )
  463. # Save
  464. self.sa_session.flush( [ self.galaxy_session, history ] )
  465. return history
  466. def get_user( self ):
  467. """Return the current user if logged in or None."""
  468. return self.galaxy_session.user
  469. def set_user( self, user ):
  470. """Set the current user."""
  471. self.galaxy_session.user = user
  472. self.sa_session.flush( [ self.galaxy_session ] )
  473. user = property( get_user, set_user )
  474. def get_user_and_roles( self ):
  475. user = self.get_user()
  476. if user:
  477. roles = user.all_roles()
  478. else:
  479. roles = []
  480. return user, roles
  481. def user_is_admin( self ):
  482. admin_users = self.app.config.get( "admin_users", "" ).split( "," )
  483. if self.user and admin_users and self.user.email in admin_users:
  484. return True
  485. return False
  486. def get_toolbox(self):
  487. """Returns the application toolbox"""
  488. return self.app.toolbox
  489. @base.lazy_property
  490. def template_context( self ):
  491. return dict()
  492. @property
  493. def model( self ):
  494. return self.app.model
  495. def make_form_data( self, name, **kwargs ):
  496. rval = self.template_context[name] = FormData()
  497. rval.values.update( kwargs )
  498. return rval
  499. def set_message( self, message ):
  500. """
  501. Convenience method for setting the 'message' element of the template
  502. context.
  503. """
  504. self.template_context['message'] = message
  505. def show_message( self, message, type='info', refresh_frames=[], cont=None ):
  506. """
  507. Convenience method for displaying a simple page with a single message.
  508. `type`: one of "error", "warning", "info", or "done"; determines the
  509. type of dialog box and icon displayed with the message
  510. `refresh_frames`: names of frames in the interface that should be
  511. refreshed when the message is displayed
  512. """
  513. return self.fill_template( "message.mako", message_type=type, message=message, refresh_frames=refresh_frames, cont=cont )
  514. def show_error_message( self, message, refresh_frames=[] ):
  515. """
  516. Convenience method for displaying an error message. See `show_message`.
  517. """
  518. return self.show_message( message, 'error', refresh_frames )
  519. def show_ok_message( self, message, refresh_frames=[] ):
  520. """
  521. Convenience method for displaying an ok message. See `show_message`.
  522. """
  523. return self.show_message( message, 'done', refresh_frames )
  524. def show_warn_message( self, message, refresh_frames=[] ):
  525. """
  526. Convenience method for displaying an warn message. See `show_message`.
  527. """
  528. return self.show_message( message, 'warning', refresh_frames )
  529. def show_form( self, form, header=None, template="form.mako" ):
  530. """
  531. Convenience method for displaying a simple page with a single HTML
  532. form.
  533. """
  534. return self.fill_template( template, form=form, header=header )
  535. def fill_template(self, filename, **kwargs):
  536. """
  537. Fill in a template, putting any keyword arguments on the context.
  538. """
  539. # call get_user so we can invalidate sessions from external users,
  540. # if external auth has been disabled.
  541. self.get_user()
  542. if filename.endswith( ".mako" ):
  543. return self.fill_template_mako( filename, **kwargs )
  544. else:
  545. template = Template( file=os.path.join(self.app.config.template_path, filename),
  546. searchList=[kwargs, self.template_context, dict(caller=self, t=self, h=helpers, util=util, request=self.request, response=self.response, app=self.app)] )
  547. return str( template )
  548. def fill_template_mako( self, filename, **kwargs ):
  549. template = self.webapp.mako_template_lookup.get_template( filename )
  550. template.output_encoding = 'utf-8'
  551. data = dict( caller=self, t=self, trans=self, h=helpers, util=util, request=self.request, response=self.response, app=self.app )
  552. data.update( self.template_context )
  553. data.update( kwargs )
  554. return template.render( **data )
  555. def stream_template_mako( self, filename, **kwargs ):
  556. template = self.webapp.mako_template_lookup.get_template( filename )
  557. template.output_encoding = 'utf-8'
  558. data = dict( caller=self, t=self, trans=self, h=helpers, util=util, request=self.request, response=self.response, app=self.app )
  559. data.update( self.template_context )
  560. data.update( kwargs )
  561. ## return template.render( **data )
  562. def render( environ, start_response ):
  563. response_write = start_response( self.response.wsgi_status(), self.response.wsgi_headeritems() )
  564. class StreamBuffer( object ):
  565. def write( self, d ):
  566. response_write( d.encode( 'utf-8' ) )
  567. buffer = StreamBuffer()
  568. context = mako.runtime.Context( buffer, **data )
  569. template.render_context( context )
  570. return []
  571. return render
  572. def fill_template_string(self, template_string, context=None, **kwargs):
  573. """
  574. Fill in a template, putting any keyword arguments on the context.
  575. """
  576. template = Template( source=template_string,
  577. searchList=[context or kwargs, dict(caller=self)] )
  578. return str(template)
  579. @property
  580. def db_builds( self ):
  581. """
  582. Returns the builds defined by galaxy and the builds defined by
  583. the user (chromInfo in history).
  584. """
  585. dbnames = list()
  586. datasets = self.app.model.HistoryDatasetAssociation \
  587. .filter_by(deleted=False, history_id=self.history.id, extension="len").all()
  588. if len(datasets) > 0:
  589. dbnames.append( (util.dbnames.default_value, '--------- User Defined Builds ----------') )
  590. for dataset in datasets:
  591. dbnames.append( (dataset.dbkey, dataset.name) )
  592. dbnames.extend( util.dbnames )
  593. return dbnames
  594. def db_dataset_for( self, dbkey ):
  595. """
  596. Returns the db_file dataset associated/needed by `dataset`, or `None`.
  597. """
  598. datasets = self.app.model.HistoryDatasetAssociation \
  599. .filter_by(deleted=False, history_id=self.history.id, extension="len").all()
  600. for ds in datasets:
  601. if dbkey == ds.dbkey:
  602. return ds
  603. return None
  604. def request_types(self):
  605. if self.app.model.RequestType.query().all():
  606. return True
  607. return False
  608. class FormBuilder( object ):
  609. """
  610. Simple class describing an HTML form
  611. """
  612. def __init__( self, action="", title="", name="form", submit_text="submit" ):
  613. self.title = title
  614. self.name = name
  615. self.action = action
  616. self.submit_text = submit_text
  617. self.inputs = []
  618. def add_input( self, type, name, label, value=None, error=None, help=None, use_label=True ):
  619. self.inputs.append( FormInput( type, label, name, value, error, help, use_label ) )
  620. return self
  621. def add_text( self, name, label, value=None, error=None, help=None ):
  622. return self.add_input( 'text', label, name, value, error, help )
  623. def add_password( self, name, label, value=None, error=None, help=None ):
  624. return self.add_input( 'password', label, name, value, error, help )
  625. class FormInput( object ):
  626. """
  627. Simple class describing a form input element
  628. """
  629. def __init__( self, type, name, label, value=None, error=None, help=None, use_label=True ):
  630. self.type = type
  631. self.name = name
  632. self.label = label
  633. self.value = value
  634. self.error = error
  635. self.help = help
  636. self.use_label = use_label
  637. class FormData( object ):
  638. """
  639. Class for passing data about a form to a template, very rudimentary, could
  640. be combined with the tool form handling to build something more general.
  641. """
  642. def __init__( self ):
  643. self.values = Bunch()
  644. self.errors = Bunch()
  645. class Bunch( dict ):
  646. """
  647. Bunch based on a dict
  648. """
  649. def __getattr__( self, key ):
  650. if key not in self: raise AttributeError, key
  651. return self[key]
  652. def __setattr__( self, key, value ):
  653. self[key] = value