galaxy-dev
Threads by month
- ----- 2026 -----
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- 10009 discussions
09 Jun '09
details: http://www.bx.psu.edu/hg/galaxy/rev/9a71b89082fe
changeset: 2428:9a71b89082fe
user: Greg Von Kuster <greg(a)bx.psu.edu>
date: Fri Jun 05 11:15:25 2009 -0400
description:
Add ability to share histories with multiple users, along with bug fixes and more functional test coverage for history features.
12 file(s) affected in this change:
lib/galaxy/security/__init__.py
lib/galaxy/tools/actions/upload.py
lib/galaxy/web/controllers/history.py
lib/galaxy/web/controllers/root.py
lib/galaxy/web/framework/__init__.py
scripts/cleanup_datasets/cleanup_datasets.py
templates/history/rename.mako
templates/history/share.mako
test/base/twilltestcase.py
test/functional/test_history_functions.py
test/functional/test_security_and_libraries.py
tool_conf.xml.sample
diffs (1698 lines):
diff -r 73847f425801 -r 9a71b89082fe lib/galaxy/security/__init__.py
--- a/lib/galaxy/security/__init__.py Mon Jun 01 10:27:04 2009 -0400
+++ b/lib/galaxy/security/__init__.py Fri Jun 05 11:15:25 2009 -0400
@@ -51,6 +51,8 @@
def set_dataset_permission( self, dataset, permission ):
raise "Unimplemented Method"
def set_all_library_permissions( self, dataset, permissions ):
+ raise "Unimplemented Method"
+ def dataset_is_public( self, dataset ):
raise "Unimplemented Method"
def make_dataset_public( self, dataset ):
raise "Unimplemented Method"
@@ -296,6 +298,10 @@
# Add the new specific permission on the dataset
for dp in [ self.model.DatasetPermissions( action, dataset, role ) for role in roles ]:
dp.flush()
+ def dataset_is_public( self, dataset ):
+ # A dataset is considered public if there are no "access" actions associated with it. Any
+ # other actions ( 'manage permissions', 'edit metadata' ) are irrelevant.
+ return self.permitted_actions.DATASET_ACCESS.action not in [ a.action for a in dataset.actions ]
def make_dataset_public( self, dataset ):
# A dataset is considered public if there are no "access" actions associated with it. Any
# other actions ( 'manage permissions', 'edit metadata' ) are irrelevant.
diff -r 73847f425801 -r 9a71b89082fe lib/galaxy/tools/actions/upload.py
--- a/lib/galaxy/tools/actions/upload.py Mon Jun 01 10:27:04 2009 -0400
+++ b/lib/galaxy/tools/actions/upload.py Fri Jun 05 11:15:25 2009 -0400
@@ -245,12 +245,14 @@
parts = file_name.split( "." )
if len( parts ) > 1:
ext = parts[1].strip().lower()
- if not( ext == 'ab1' or ext == 'scf' ):
+ if not( ext == 'ab1' or ext == 'scf' or ext == 'novoindex' ):
raise BadFileException( "you attempted to upload an inappropriate file." )
if ext == 'ab1' and file_type != 'ab1':
raise BadFileException( "you must manually set the 'File Format' to 'Ab1' when uploading ab1 files." )
elif ext == 'scf' and file_type != 'scf':
raise BadFileException( "you must manually set the 'File Format' to 'Scf' when uploading scf files." )
+ elif ext == 'novoindex' and file_type != 'novoindex':
+ raise BadFileException( "you must manually set the 'File Format' to 'NovoIndex' when uploading novoindex files." )
data_type = 'binary'
if not data_type:
# We must have a text file
@@ -336,7 +338,7 @@
return ( False, False, None )
zip_file = zipfile.ZipFile( temp_name, "r" )
# Make sure the archive consists of valid files. The current rules are:
- # 1. Archives can only include .ab1, .scf or .txt files
+ # 1. Archives can only include .ab1, .scf, or .txt files
# 2. All file extensions within an archive must be the same
name = zip_file.namelist()[0]
test_ext = name.split( "." )[1].strip().lower()
diff -r 73847f425801 -r 9a71b89082fe lib/galaxy/web/controllers/history.py
--- a/lib/galaxy/web/controllers/history.py Mon Jun 01 10:27:04 2009 -0400
+++ b/lib/galaxy/web/controllers/history.py Fri Jun 05 11:15:25 2009 -0400
@@ -1,6 +1,7 @@
from galaxy.web.base.controller import *
from galaxy.web.framework.helpers import time_ago, iff, grids
-import webhelpers
+from galaxy import util
+import webhelpers, logging
from datetime import datetime
from cgi import escape
@@ -65,7 +66,6 @@
@web.expose
def index( self, trans ):
return ""
-
@web.expose
def list_as_xml( self, trans ):
"""
@@ -78,9 +78,7 @@
@web.expose
@web.require_login( "work with multiple histories" )
def list( self, trans, **kwargs ):
- """
- List all available histories
- """
+ """List all available histories"""
status = message = None
if 'operation' in kwargs:
operation = kwargs['operation'].lower()
@@ -92,9 +90,7 @@
status, message = None, None
refresh_history = False
# Load the histories and ensure they all belong to the current user
- history_ids = kwargs.get( 'id', [] )
- if type( history_ids ) is not list:
- history_ids = [ history_ids ]
+ history_ids = util.listify( kwargs.get( 'id', [] ) )
histories = []
for hid in history_ids:
history = model.History.get( hid )
@@ -117,17 +113,13 @@
trans.sa_session.flush()
# Render the list view
return self.list_grid( trans, status=status, message=message, **kwargs )
-
def _list_delete( self, trans, histories ):
"""Delete histories"""
n_deleted = 0
deleted_current = False
for history in histories:
if not history.deleted:
- # Delete DefaultHistoryPermissions
- for dhp in history.default_permissions:
- dhp.delete()
- dhp.flush()
+ # We'll not eliminate any DefaultHistoryPermissions in case we undelete the history later
# Mark history as deleted in db
history.deleted = True
# If deleting the current history, make a new current.
@@ -144,7 +136,6 @@
message_parts.append( "Your active history was deleted, a new empty history is now active.")
status = INFO
return ( status, " ".join( message_parts ) )
-
def _list_undelete( self, trans, histories ):
"""Undelete histories"""
n_undeleted = 0
@@ -154,6 +145,15 @@
n_already_purged += 1
if history.deleted:
history.deleted = False
+ if not history.default_permissions:
+ # For backward compatibility - for a while we were deleting all DefaultHistoryPermissions on
+ # the history when we deleted the history. We are no longer doing this.
+ # Need to add default DefaultHistoryPermissions since they were deleted when the history was deleted
+ default_action = trans.app.security_agent.permitted_actions.DATASET_MANAGE_PERMISSIONS
+ private_user_role = trans.app.security_agent.get_private_user_role( history.user )
+ default_permissions = {}
+ default_permissions[ default_action ] = [ private_user_role ]
+ trans.app.security_agent.history_set_default_permissions( history, default_permissions )
n_undeleted += 1
trans.log_event( "History id %d marked as undeleted" % history.id )
status = SUCCESS
@@ -164,7 +164,6 @@
message_parts.append( "%d have already been purged and cannot be undeleted." % n_already_purged )
status = WARNING
return status, "".join( message_parts )
-
def _list_switch( self, trans, histories ):
"""Switch to a new different history"""
new_history = histories[0]
@@ -179,13 +178,9 @@
trans.log_event( "History switched to id: %s, name: '%s'" % (str(new_history.id), new_history.name ) )
# No message
return None, None
-
@web.expose
def delete_current( self, trans ):
- """
- Delete just the active history -- this does not require a logged
- in user.
- """
+ """Delete just the active history -- this does not require a logged in user."""
history = trans.get_history()
if not history.deleted:
history.deleted = True
@@ -195,7 +190,6 @@
# history active
trans.new_history()
return trans.show_ok_message( "History deleted, a new history is active" )
-
@web.expose
def rename_async( self, trans, id=None, new_name=None ):
history = model.History.get( id )
@@ -209,11 +203,9 @@
# Rename
history.name = new_name
trans.sa_session.flush()
-
- ## These have been moved from 'root' but not cleaned up
-
@web.expose
def imp( self, trans, id=None, confirm=False, **kwd ):
+ # TODO clean this up and make sure functionally correct
msg = ""
user = trans.get_user()
user_history = trans.get_history()
@@ -262,97 +254,159 @@
Warning! If you import this history, you will lose your current
history. Click <a href="%s">here</a> to confirm.
""" % web.url_for( id=id, confirm=True ) )
-
@web.expose
@web.require_login( "share histories with other users" )
def share( self, trans, id=None, email="", **kwd ):
- send_to_err = ""
- if not id:
- id = trans.get_history().id
- if not isinstance( id, list ):
- id = [ id ]
- histories = []
- history_names = []
- for hid in id:
- histories.append( trans.app.model.History.get( hid ) )
- history_names.append(histories[-1].name)
- if not email:
- return trans.fill_template("/history/share.mako", histories=histories, email=email, send_to_err=send_to_err)
- user = trans.get_user()
- send_to_user = trans.app.model.User.filter( trans.app.model.User.table.c.email==email ).first()
+ # If a history contains both datasets that can be shared and others that cannot be shared with the desired user,
+ # then the entire history is shared, and the protected datasets will be visible, but inaccessible ( greyed out )
+ # in the shared history
params = util.Params( kwd )
action = params.get( 'action', None )
if action == "no_share":
- trans.response.send_redirect( url_for( action='history_options' ) )
- if not send_to_user:
- send_to_err = "No such user"
- elif user.email == email:
- send_to_err = "You can't send histories to yourself"
- else:
- if 'history_share_btn' in kwd or action != 'share':
- # The user is attempting to share a history whose datasets cannot all be accessed by the other user. In this case,
- # the user sharing the history can chose to make the datasets public ( action == 'public' ) if he has the authority
- # to do so, or automatically create a new "sharing role" that allows the user to share his private datasets only with the
- # desired user ( action == 'private' ).
- can_change = {}
- cannot_change = {}
+ trans.response.send_redirect( url_for( controller='root', action='history_options' ) )
+ if not id:
+ id = trans.get_history().id
+ id = util.listify( id )
+ send_to_err = ""
+ histories = []
+ for hid in id:
+ histories.append( trans.app.model.History.get( hid ) )
+ if not email:
+ return trans.fill_template( "/history/share.mako", histories=histories, email=email, send_to_err=send_to_err )
+ user = trans.get_user()
+ send_to_users = []
+ for email_address in util.listify( email ):
+ email_address = email_address.strip()
+ if email_address:
+ if email_address == user.email:
+ send_to_err += "You can't send histories to yourself. "
+ else:
+ send_to_user = trans.app.model.User.filter( trans.app.model.User.table.c.email==email_address ).first()
+ if send_to_user:
+ send_to_users.append( send_to_user )
+ else:
+ send_to_err += "%s is not a valid Galaxy user. " % email_address
+ if not send_to_users:
+ if not send_to_err:
+ send_to_err += "%s is not a valid Galaxy user. " % email
+ return trans.fill_template( "/history/share.mako", histories=histories, email=email, send_to_err=send_to_err )
+ if params.get( 'share_proceed_button', False ) and action == 'share':
+ # We need to filter out all histories that cannot be shared
+ filtered_histories = {}
+ for history in histories:
+ for send_to_user in send_to_users:
+ # Only deal with datasets that have not been purged
+ for hda in history.activatable_datasets:
+ # The history can be shared if it contains at least 1 public dataset or 1 dataset that the
+ # other user can access. Inaccessible datasets contained in the history will be displayed
+ # in the shared history, but "greyed out", so they cannot be viewed or used.
+ if trans.app.security_agent.dataset_is_public( hda.dataset ) or \
+ trans.app.security_agent.allow_action( send_to_user,
+ trans.app.security_agent.permitted_actions.DATASET_ACCESS,
+ dataset=hda ):
+ if send_to_user in filtered_histories:
+ filtered_histories[ send_to_user ].append( history )
+ else:
+ filtered_histories[ send_to_user ] = [ history ]
+ break
+ return self._share_histories( trans, user, send_to_users, send_to_err, filtered_histories=filtered_histories )
+ elif params.get( 'history_share_btn', False ) or action != 'share':
+ # The user is attempting to share histories whose datasets cannot all be accessed by other users. In this case,
+ # the user sharing the histories can:
+ # 1) action=='public': chose to make the datasets public if he is permitted to do so
+ # 2) action=='private': automatically create a new "sharing role" allowing protected
+ # datasets to be accessed only by the desired users
+ # 3) action=='share': share only what can be shared when no permissions are changed - this case is handled above
+ # 4) action=='no_share': Do not share anything - this case is handled above.
+ can_change = {}
+ cannot_change = {}
+ no_change_needed = {}
+ for history in histories:
+ # Only deal with datasets that have not been purged
+ for hda in history.activatable_datasets:
+ if trans.app.security_agent.dataset_is_public( hda.dataset ):
+ if history not in no_change_needed:
+ no_change_needed[ history ] = [ hda ]
+ else:
+ no_change_needed[ history ].append( hda )
+ elif not trans.app.security_agent.allow_action( send_to_user,
+ trans.app.security_agent.permitted_actions.DATASET_ACCESS,
+ dataset=hda ):
+ # The user with which we are sharing the history does not have access permission on the current dataset
+ if trans.app.security_agent.allow_action( user,
+ trans.app.security_agent.permitted_actions.DATASET_MANAGE_PERMISSIONS,
+ dataset=hda ) and not hda.dataset.library_associations:
+ # The current user has authority to change permissions on the current dataset because
+ # they have permission to manage permissions on the dataset and the dataset is not associated
+ # with a library.
+ if action == "private":
+ trans.app.security_agent.privately_share_dataset( hda.dataset, users=[ user, send_to_user ] )
+ elif action == "public":
+ trans.app.security_agent.make_dataset_public( hda.dataset )
+ elif history not in can_change:
+ # Build the set of histories / datasets on which the current user has authority
+ # to "manage permissions". This is used in /history/share.mako
+ can_change[ history ] = [ hda ]
+ else:
+ can_change[ history ].append( hda )
+ else:
+ if action in [ "private", "public" ]:
+ # Don't change stuff that the user doesn't have permission to change
+ continue
+ elif history not in cannot_change:
+ # Build the set of histories / datasets on which the current user does
+ # not have authority to "manage permissions". This is used in /history/share.mako
+ cannot_change[ history ] = [ hda ]
+ else:
+ cannot_change[ history ].append( hda )
+ if can_change or cannot_change:
+ return trans.fill_template( "/history/share.mako",
+ histories=histories,
+ email=email,
+ send_to_err=send_to_err,
+ can_change=can_change,
+ cannot_change=cannot_change,
+ no_change_needed=no_change_needed )
+ return self._share_histories( trans, user, send_to_users, send_to_err, histories=histories )
+ return trans.fill_template( "/history/share.mako", histories=histories, email=email, send_to_err=send_to_err )
+ def _share_histories( self, trans, user, send_to_users, send_to_err, histories=[], filtered_histories={} ):
+ msg = ""
+ if not send_to_users:
+ msg = "No users have been specified with which to share histories"
+ sent_to_emails = []
+ for sent_to_user in send_to_users:
+ sent_to_emails.append( sent_to_user.email )
+ emails = ",".join( e for e in sent_to_emails )
+ if not histories and not filtered_histories:
+ msg = "No histories can be sent to (%s) without changing dataset permissions associating a sharing role with them" % emails
+ elif histories:
+ history_names = []
+ for history in histories:
+ history_names.append( history.name )
+ for send_to_user in send_to_users:
+ new_history = history.copy( target_user=send_to_user )
+ new_history.name = history.name + " from " + user.email
+ new_history.user_id = send_to_user.id
+ self.app.model.flush()
+ msg = "Histories (%s) have been shared with: %s. " % ( ",".join( history_names ), emails )
+ elif filtered_histories:
+ # filtered_histories is a dictionary like: { user: [ history, history ], user: [ history ] }
+ for send_to_user, histories in filtered_histories.items():
+ history_names = []
for history in histories:
- for hda in history.activatable_datasets:
- # Only deal with datasets that have not been purged
- if not trans.app.security_agent.allow_action( send_to_user,
- trans.app.security_agent.permitted_actions.DATASET_ACCESS,
- dataset=hda ):
- # The user with which we are sharing the history does not have access permission on the current dataset
- if trans.app.security_agent.allow_action( user,
- trans.app.security_agent.permitted_actions.DATASET_MANAGE_PERMISSIONS,
- dataset=hda ) and not hda.dataset.library_associations:
- # The current user has authority to change permissions on the current dataset because
- # they have permission to manage permissions on the dataset and the dataset is not associated
- # with a library.
- if action == "private":
- trans.app.security_agent.privately_share_dataset( hda.dataset, users=[ user, send_to_user ] )
- elif action == "public":
- trans.app.security_agent.make_dataset_public( hda.dataset )
- elif history not in can_change:
- # Build the set of histories / datasets on which the current user has authority
- # to "manage permissions". This is used in /history/share.mako
- can_change[ history ] = [ hda ]
- else:
- can_change[ history ].append( hda )
- else:
- if action in [ "private", "public" ]:
- # Don't change stuff that the user doesn't have permission to change
- continue
- elif history not in cannot_change:
- # Build the set of histories / datasets on which the current user does
- # not have authority to "manage permissions". This is used in /history/share.mako
- cannot_change[ history ] = [ hda ]
- else:
- cannot_change[ history ].append( hda )
- if can_change or cannot_change:
- return trans.fill_template( "/history/share.mako",
- histories=histories,
- email=email,
- send_to_err=send_to_err,
- can_change=can_change,
- cannot_change=cannot_change )
- for history in histories:
- new_history = history.copy( target_user=send_to_user )
- new_history.name = history.name + " from " + user.email
- new_history.user_id = send_to_user.id
- trans.log_event( "History share, id: %s, name: '%s': to new id: %s" % ( str( history.id ), history.name, str( new_history.id ) ) )
- self.app.model.flush()
- return trans.show_message( "History (%s) has been shared with: %s" % ( ",".join( history_names ),email ) )
- return trans.fill_template( "/history/share.mako", histories=histories, email=email, send_to_err=send_to_err )
-
+ history_names.append( history.name )
+ new_history = history.copy( target_user=send_to_user )
+ new_history.name = history.name + " from " + user.email
+ new_history.user_id = send_to_user.id
+ self.app.model.flush()
+ msg += "Histories (%s) have been shared with: %s. " % ( ",".join( history_names ), send_to_user.email )
+ if send_to_err:
+ msg += send_to_err
+ return trans.show_message( msg )
@web.expose
@web.require_login( "rename histories" )
def rename( self, trans, id=None, name=None, **kwd ):
- if trans.app.memory_usage:
- # Keep track of memory usage
- m0 = self.app.memory_usage.memory()
user = trans.get_user()
-
if not isinstance( id, list ):
if id != None:
id = [ id ]
@@ -387,7 +441,4 @@
change_msg = change_msg + "<p>You must specify a valid name for History: "+cur_names[i]+"</p>"
else:
change_msg = change_msg + "<p>History: "+cur_names[i]+" does not appear to belong to you.</p>"
- if self.app.memory_usage:
- m1 = trans.app.memory_usage.memory( m0, pretty=True )
- log.info( "End of root/history_rename, memory used increased by %s" % m1 )
- return trans.show_message( "<p>%s" % change_msg, refresh_frames=['history'] )
\ No newline at end of file
+ return trans.show_message( "<p>%s" % change_msg, refresh_frames=['history'] )
diff -r 73847f425801 -r 9a71b89082fe lib/galaxy/web/controllers/root.py
--- a/lib/galaxy/web/controllers/root.py Mon Jun 01 10:27:04 2009 -0400
+++ b/lib/galaxy/web/controllers/root.py Fri Jun 05 11:15:25 2009 -0400
@@ -349,14 +349,12 @@
"""Displays a list of history related actions"""
return trans.fill_template( "/history/options.mako",
user = trans.get_user(), history = trans.get_history() )
-
@web.expose
def history_delete( self, trans, id ):
"""
Backward compatibility with check_galaxy script.
"""
return trans.webapp.controllers['history'].list( trans, id, operation='delete' )
-
@web.expose
def clear_history( self, trans ):
"""Clears the history for a user"""
@@ -367,7 +365,6 @@
self.app.model.flush()
trans.log_event( "History id %s cleared" % (str(history.id)) )
trans.response.send_redirect( url_for("/index" ) )
-
@web.expose
def history_import( self, trans, id=None, confirm=False, **kwd ):
msg = ""
@@ -417,13 +414,11 @@
Warning! If you import this history, you will lose your current
history. Click <a href="%s">here</a> to confirm.
""" % web.url_for( id=id, confirm=True ) )
-
@web.expose
- def history_new( self, trans ):
- trans.new_history()
+ def history_new( self, trans, name=None ):
+ trans.new_history( name=name )
trans.log_event( "Created new History, id: %s." % str(trans.get_history().id) )
return trans.show_message( "New history created", refresh_frames = ['history'] )
-
@web.expose
def history_add_to( self, trans, history_id=None, file_data=None, name="Data Added to History",info=None,ext="txt",dbkey="?",copy_access_from=None,**kwd ):
"""Adds a POSTed file to a History"""
@@ -455,13 +450,22 @@
except Exception, e:
trans.log_event( "Failed to add dataset to history: %s" % ( e ) )
return trans.show_error_message("Adding File to History has Failed")
-
@web.expose
- def history_set_default_permissions( self, trans, **kwd ):
- """Sets the user's default permissions for the current history"""
+ def history_set_default_permissions( self, trans, id=None, **kwd ):
+ """Sets the permissions on a history"""
if trans.user:
if 'update_roles_button' in kwd:
- history = trans.get_history()
+ history = None
+ if id:
+ try:
+ id = int( id )
+ except:
+ id = None
+ if id:
+ history = trans.app.model.History.get( id )
+ if not history:
+ # If we haven't retrieved a history, use the current one
+ history = trans.get_history()
p = util.Params( kwd )
permissions = {}
for k, v in trans.app.model.Dataset.permitted_actions.items():
@@ -478,7 +482,6 @@
else:
#user not logged in, history group must be only public
return trans.show_error_message( "You must be logged in to change a history's default permissions." )
-
@web.expose
def dataset_make_primary( self, trans, id=None):
"""Copies a dataset and makes primary"""
diff -r 73847f425801 -r 9a71b89082fe lib/galaxy/web/framework/__init__.py
--- a/lib/galaxy/web/framework/__init__.py Mon Jun 01 10:27:04 2009 -0400
+++ b/lib/galaxy/web/framework/__init__.py Fri Jun 05 11:15:25 2009 -0400
@@ -432,13 +432,15 @@
self.galaxy_session.current_history = history
self.sa_session.flush( [ self.galaxy_session ] )
history = property( get_history, set_history )
- def new_history( self ):
+ def new_history( self, name=None ):
"""
Create a new history and associate it with the current session and
its associated user (if set).
"""
# Create new history
history = self.app.model.History()
+ if name:
+ history.name = name
# Associate with session
history.add_galaxy_session( self.galaxy_session )
# Make it the session's current history
diff -r 73847f425801 -r 9a71b89082fe scripts/cleanup_datasets/cleanup_datasets.py
--- a/scripts/cleanup_datasets/cleanup_datasets.py Mon Jun 01 10:27:04 2009 -0400
+++ b/scripts/cleanup_datasets/cleanup_datasets.py Fri Jun 05 11:15:25 2009 -0400
@@ -123,6 +123,11 @@
for dataset_assoc in history.datasets:
_purge_dataset_instance( dataset_assoc, app, remove_from_disk, info_only = info_only ) #mark a DatasetInstance as deleted, clear associated files, and mark the Dataset as deleted if it is deletable
if not info_only:
+ # TODO: should the Delete DefaultHistoryPermissions be deleted here? This was incorrectly
+ # done in the _list_delete() method of the history controller, so copied it here. Not sure
+ # if we should ever delete info like this from the db though, so commented out for now...
+ #for dhp in history.default_permissions:
+ # dhp.delete()
history.purged = True
print "%d" % history.id
history_count += 1
@@ -226,7 +231,6 @@
print "# This Dataset (%i) is not deletable, associated Metadata Files will not be removed.\n" % ( dataset.id )
else:
# Mark all associated MetadataFiles as deleted and purged and remove them from disk
- print "The following metadata files attached to associations of Dataset '%s' have been purged:" % dataset.id
metadata_files = []
#lets create a list of metadata files, then perform actions on them
for hda in dataset.history_associations:
@@ -236,6 +240,7 @@
for metadata_file in app.model.MetadataFile.filter( app.model.MetadataFile.table.c.lda_id==lda.id ).all():
metadata_files.append( metadata_file )
for metadata_file in metadata_files:
+ print "# The following metadata files attached to associations of Dataset '%s' have been purged:" % dataset.id
if not info_only:
if remove_from_disk:
try:
@@ -248,7 +253,6 @@
print "%s" % metadata_file.file_name
print
dataset.deleted = True
- #dataset.flush()
app.model.flush()
def _purge_dataset( dataset, remove_from_disk, info_only = False ):
@@ -259,6 +263,7 @@
if not info_only:
# Remove files from disk and update the database
if remove_from_disk:
+ # TODO: should permissions on the dataset be deleted here?
os.unlink( dataset.file_name )
# Remove associated extra files from disk if they exist
if dataset.extra_files_path and os.path.exists( dataset.extra_files_path ):
@@ -286,6 +291,7 @@
for sub_folder in folder.folders:
_purge_folder( sub_folder, app, remove_from_disk, info_only = info_only )
if not info_only:
+ # TODO: should the folder permissions be deleted here?
folder.purged = True
folder.flush()
diff -r 73847f425801 -r 9a71b89082fe templates/history/rename.mako
--- a/templates/history/rename.mako Mon Jun 01 10:27:04 2009 -0400
+++ b/templates/history/rename.mako Fri Jun 05 11:15:25 2009 -0400
@@ -4,17 +4,38 @@
<div class="toolForm">
<div class="toolFormTitle">${_('Rename History')}</div>
- <div class="toolFormBody">
- <form action="${h.url_for( controller='history', action='rename' )}" method="post" >
- <table>
- <tr><th>${_('Current Name')}</th><th>${_('New Name')}</th></tr>
- %for history in histories:
- <tr><td>${history.name}<input type="hidden" name="id" value="${history.id}"></td><td><input type="text" name="name" value="${history.name}" size="40"></td></tr>
- %endfor
- <tr><td colspan="2"><input type="submit" name="history_rename_btn" value="${_('Rename Histories')}"></td></tr>
- </table>
- </form>
- </div>
+ <div class="toolFormBody">
+ <form action="${h.url_for( controller='history', action='rename' )}" method="post" >
+ <table class="grid">
+ %for history in histories:
+ <tr>
+ <td>
+ <div class="form-row">
+ <input type="hidden" name="id" value="${history.id}">
+ <label>${_('Current Name')}</label>
+ <div style="float: left; width: 250px; margin-right: 10px;">
+ ${history.name}
+ </div>
+ </div>
+ </td>
+ <td>
+ <div class="form-row">
+ <label>${_('New Name')}</label>
+ <div style="float: left; width: 250px; margin-right: 10px;">
+ <input type="text" name="name" value="${history.name}" size="40">
+ </div>
+ </div>
+ </td>
+ </tr>
+ %endfor
+ <tr>
+ <td colspan="2">
+ <div class="form-row">
+ <input type="submit" name="history_rename_btn" value="${_('Rename Histories')}">
+ </div>
+ </td>
+ </tr>
+ </table>
+ </form>
+ </div>
</div>
-</body>
-</html>
\ No newline at end of file
diff -r 73847f425801 -r 9a71b89082fe templates/history/share.mako
--- a/templates/history/share.mako Mon Jun 01 10:27:04 2009 -0400
+++ b/templates/history/share.mako Fri Jun 05 11:15:25 2009 -0400
@@ -2,117 +2,173 @@
<%inherit file="/base.mako"/>
<%def name="title()">Share histories</%def>
-%if not can_change and not cannot_change:
- <div class="toolForm">
- <div class="toolFormTitle">${_('Share histories')}</div>
- <table>
+<div class="toolForm">
+ <div class="toolFormTitle">Share ${len( histories)} histories</div>
+ <div class="toolFormBody">
+ %if not can_change and not cannot_change:
<form action="${h.url_for( controller="history", action='share' )}" method="post" >
- <tr><th>${_('History Name:')}</td><th>${_('Number of Datasets:')}</th><th>${_('Share Link')}</th></tr>
%for history in histories:
- <tr>
- <td align="center">${history.name}<input type="hidden" name="id" value="${history.id}"></td>
- <td align="center">
- %if len( history.datasets ) < 1:
- <div class="warningmark">${_('This history contains no data.')}</div>
- %else:
- ${len(history.datasets)}
- %endif
- </td>
- <td align="center"><a href="${h.url_for( controller='history', action='imp', id=trans.security.encode_id(history.id) )}">${_('copy link to share')}</a></td>
- </tr>
+ <div class="toolForm">
+ <div class="form-row">
+ <label>${_('History Name:')}</label>
+ <div style="float: left; width: 250px; margin-right: 10px;">
+ ${history.name}<input type="hidden" name="id" value="${history.id}">
+ </div>
+ </div>
+ <div style="clear: both"></div>
+ <div class="form-row">
+ <label>${_('Number of Datasets:')}</label>
+ <div style="float: left; width: 250px; margin-right: 10px;">
+ %if len( history.datasets ) < 1:
+ <div class="warningmark">${_('This history contains no data.')}</div>
+ %else:
+ ${len(history.datasets)}
+ %endif
+ </td>
+ </div>
+ </div>
+ ## TODO: this feature is not currently working
+ ##<div style="clear: both"></div>
+ ##<div class="form-row">
+ ## <label>${_('Share Link')}</label>
+ ## <div style="float: left; width: 250px; margin-right: 10px;">
+ ## <a href="${h.url_for( controller='history', action='imp', id=trans.security.encode_id(history.id) )}">${_('copy link to share')}</a>
+ ## </div>
+ ##</div>
+ ##<div style="clear: both"></div>
+ <p/>
+ </div>
%endfor
- <tr><td>${_('Email of User to share with:')}</td><td><input type="text" name="email" value="${email}" size="40"></td></tr>
+ <p/>
+ <div style="clear: both"></div>
+ <div class="form-row">
+ <label>Galaxy user emails with which to share histories</label>
+ <div style="float: left; width: 250px; margin-right: 10px;">
+ <input type="text" name="email" value="${email}" size="40">
+ </div>
+ <div class="toolParamHelp" style="clear: both;">
+ Enter a Galaxy user email address or a comma-separated list of addresses if sharing with multiple users
+ </div>
+ </div>
%if send_to_err:
- <tr><td colspan="100%"><div class="errormessage">${send_to_err}</div></td></tr>
+ <div style="clear: both"></div>
+ <div class="form-row">
+ <div class="errormessage">${send_to_err}</div>
+ </div>
%endif
- <tr><td colspan="2" align="right"><input type="submit" name="history_share_btn" value="Submit"></td></tr>
+ <div style="clear: both"></div>
+ <div class="form-row">
+ <input type="submit" name="history_share_btn" value="Submit">
+ </div>
</form>
- </table>
+ %else:
+ <form action="${h.url_for( controller='history', action='share' )}" method="post">
+ %for history in histories:
+ <input type="hidden" name="id" value="${history.id}">
+ %endfor
+ <input type="hidden" name="email" value="${email}">
+ %if no_change_needed:
+ <div style="clear: both"></div>
+ <div class="form-row">
+ <div class="donemessage">
+ The following datasets can be shared with ${email} with no changes
+ </div>
+ </div>
+ %for history, hdas in no_change_needed.items():
+ <div class="form-row">
+ <label>History</label>
+ ${history.name}
+ </div>
+ <div style="clear: both"></div>
+ <div class="form-row">
+ <label>Datasets</label>
+ </div>
+ %for hda in hdas:
+ <div class="form-row">
+ ${hda.name}
+ </div>
+ %endfor
+ %endfor
+ %endif
+ %if can_change:
+ <div style="clear: both"></div>
+ <div class="form-row">
+ <div class="warningmessage">
+ The following datasets can be shared with ${email} by updating their permissions
+ </div>
+ </div>
+ %for history, hdas in can_change.items():
+ <div class="form-row">
+ <label>History</label>
+ ${history.name}
+ </div>
+ <div style="clear: both"></div>
+ <div class="form-row">
+ <label>Datasets</label>
+ </div>
+ %for hda in hdas:
+ <div class="form-row">
+ ${hda.name}
+ </div>
+ %endfor
+ %endfor
+ %endif
+ %if cannot_change:
+ <div style="clear: both"></div>
+ <div class="form-row">
+ <div class="errormessage">
+ The following datasets cannot be shared with ${email} because you are not authorized to
+ change the permissions on them
+ </div>
+ </div>
+ %for history, hdas in cannot_change.items():
+ <div class="form-row">
+ <label>History</label>
+ ${history.name}
+ </div>
+ <div style="clear: both"></div>
+ <div class="form-row">
+ <label>Datasets</label>
+ </div>
+ %for hda in hdas:
+ <div class="form-row">
+ ${hda.name}
+ </div>
+ %endfor
+ %endfor
+ %endif
+ <div class="form-row">
+ <label>How would you like to proceed?</label>
+ </div>
+ %if can_change:
+ <div class="form-row">
+ <input type="radio" name="action" value="public"> Make datasets public so anyone can access them
+ %if cannot_change:
+ (where possible)
+ %endif
+ </div>
+ <div class="form-row">
+ <input type="radio" name="action" value="private"> Make datasets private to me and the user(s) with whom I am sharing
+ %if cannot_change:
+ (where possible)
+ %endif
+ </div>
+ %endif
+ %if no_change_needed:
+ <div class="form-row">
+ <input type="radio" name="action" value="share"> Share anyway
+ %if can_change:
+ (don't change any permissions)
+ %endif
+ </div>
+ %endif
+ <div class="form-row">
+ <input type="radio" name="action" value="no_share"> Don't share
+ </div>
+ <div class="form-row">
+ <input type="submit" name="share_proceed_button" value="Go"><br/>
+ </div>
+ </form>
+ %endif
</div>
-%else:
- <style type="text/css">
- th
- {
- text-align: left;
- }
- td
- {
- vertical-align: top;
- }
- </style>
- <form action="${h.url_for( controller='history', action='share' )}" method="post">
- %for history in histories:
- <input type="hidden" name="id" value="${history.id}">
- %endfor
- <input type="hidden" name="email" value="${email}">
- <div class="warningmessage">
- The history or histories you've chosen to share contain datasets that the user with which you're sharing does not have permission to access.
- These datasets are shown below. Datasets that the user has permission to access are not shown.
- </div>
- <p/>
- %if can_change:
- <div class="donemessage">
- The following datasets can be shared with ${email} by updating their permissions:
- <p/>
- <table cellpadding="0" cellspacing="8" border="0">
- <tr><th>Histories</th><th>Datasets</th></tr>
- %for history, datasets in can_change.items():
- <tr>
- <td>${history.name}</td>
- <td>
- %for dataset in datasets:
- ${dataset.name}<br/>
- %endfor
- </td>
- </tr>
- %endfor
- </table>
- </div>
- <p/>
- %endif
- %if cannot_change:
- <div class="errormessage">
- The following datasets cannot be shared with ${email} because you are not authorized to change the permissions on them.
- <p/>
- <table cellpadding="0" cellspacing="8" border="0">
- <tr><th>Histories</th><th>Datasets</th></tr>
- %for history, datasets in cannot_change.items():
- <tr>
- <td>${history.name}</td>
- <td>
- %for dataset in datasets:
- ${dataset.name}<br/>
- %endfor
- </td>
- </tr>
- %endfor
- </table>
- </div>
- <p/>
- %endif
- <div>
- <b>How would you like to proceed?</b>
- <p/>
- %if can_change:
- <input type="radio" name="action" value="public"> Set datasets above to public access
- %if cannot_change:
- (where possible)
- %endif
- <br/>
- <input type="radio" name="action" value="private"> Set datasets above to private access for me and the user(s) with whom I am sharing
- %if cannot_change:
- (where possible)
- %endif
- <br/>
- %endif
- <input type="radio" name="action" value="share"> Share anyway
- %if can_change:
- (don't change any permissions)
- %endif
- <br/>
- <input type="radio" name="action" value="no_share"> Don't share<br/>
- <br/>
- <input type="submit" name="submit" value="Ok"><br/>
- </div>
- </form>
-%endif
+</div>
diff -r 73847f425801 -r 9a71b89082fe test/base/twilltestcase.py
--- a/test/base/twilltestcase.py Mon Jun 01 10:27:04 2009 -0400
+++ b/test/base/twilltestcase.py Fri Jun 05 11:15:25 2009 -0400
@@ -103,41 +103,42 @@
page = self.last_page()
if page.find( 'error' ) > -1:
raise AssertionError('Errors in the history for user %s' % self.user )
-
def check_history_for_string( self, patt ):
"""Looks for 'string' in history page"""
self.home()
self.visit_page( "history" )
for subpatt in patt.split():
tc.find(subpatt)
-
+ self.home()
def clear_history( self ):
"""Empties a history of all datasets"""
self.visit_page( "clear_history" )
self.check_history_for_string( 'Your history is empty' )
-
- def delete_history( self, id=None ):
- """Deletes a history"""
+ self.home()
+ def delete_history( self, id='' ):
+ """Deletes one or more histories"""
history_list = self.get_histories()
self.assertTrue( history_list )
- if id is None:
+ num_deleted = 1
+ if not id:
history = history_list[0]
id = history.get( 'id' )
- id = str( id )
- self.visit_page( "history/list?operation=delete&id=%s" %(id) )
-
+ else:
+ num_deleted = len( id.split( ',' ) )
+ self.visit_page( "history/list?operation=delete&id=%s" % ( id ) )
+ check_str = 'Deleted %d histories' % num_deleted
+ self.check_page_for_string( check_str )
+ self.home()
def get_histories( self ):
"""Returns all histories"""
tree = self.histories_as_xml_tree()
data_list = [ elem for elem in tree.findall("data") ]
return data_list
-
def get_history( self, show_deleted=False ):
"""Returns a history"""
tree = self.history_as_xml_tree( show_deleted=show_deleted )
data_list = [ elem for elem in tree.findall("data") ]
return data_list
-
def history_as_xml_tree( self, show_deleted=False ):
"""Returns a parsed xml object of a history"""
self.home()
@@ -145,7 +146,6 @@
xml = self.last_page()
tree = ElementTree.fromstring(xml)
return tree
-
def histories_as_xml_tree( self ):
"""Returns a parsed xml object of all histories"""
self.home()
@@ -153,97 +153,96 @@
xml = self.last_page()
tree = ElementTree.fromstring(xml)
return tree
-
- def history_options( self ):
+ def history_options( self, check_str='', upload=False ):
"""Mimics user clicking on history options link"""
self.visit_page( "history_options" )
-
- def new_history( self ):
+ if check_str:
+ self.check_page_for_string( check_str )
+ else:
+ self.check_page_for_string( 'Rename</a> current history' )
+ self.check_page_for_string( 'List</a> previously stored histories' )
+ self.check_page_for_string( 'Construct workflow</a> from the current history' )
+ self.check_page_for_string( 'Share</a> current history' )
+ # Tests for changing default history permissions are done in test_security_and_libraries.py
+ self.check_page_for_string( 'Change default permissions</a> for the current history' )
+ self.check_page_for_string( 'Show deleted</a> datasets in history' )
+ self.check_page_for_string( 'Delete</a> current history' )
+ # Need to add a history item in order to create a new empty history
+ try:
+ self.check_page_for_string( 'Create</a> a new empty history' )
+ raise AssertionError, "Incorrectly able to create a new empty history when the current history is empty."
+ except:
+ pass
+ if upload:
+ self.upload_file( '1.bed', dbkey='hg18' )
+ self.home()
+ self.visit_page( "history_options" )
+ self.check_page_for_string( 'Create</a> a new empty history' )
+ self.home()
+ def new_history( self, name=None ):
"""Creates a new, empty history"""
- self.visit_page( "history_new" )
+ if name:
+ self.visit_url( "%s/history_new?name=%s" % ( self.url, str( name ) ) )
+ else:
+ self.visit_url( "%s/history_new" % self.url )
self.check_history_for_string('Your history is empty')
-
- def rename_history( self, id=None, name='NewTestHistory' ):
+ self.home()
+ def rename_history( self, id, old_name, new_name ):
"""Rename an existing history"""
- history_list = self.get_histories()
- self.assertTrue( history_list )
- if id is None: # take last id
- elem = history_list[-1]
- else:
- i = history_list.index( id )
- self.assertTrue( i )
- elem = history_list[i]
- id = elem.get( 'id' )
- self.assertTrue( id )
- old_name = elem.get( 'name' )
- self.assertTrue( old_name )
- id = str( id )
- self.visit_page( "history/rename?id=%s&name=%s" %(id, name) )
- return id, old_name, name
-
+ self.home()
+ self.visit_page( "history/rename?id=%s&name=%s" %( id, new_name ) )
+ check_str = 'History: %s renamed to: %s' % ( old_name, new_name )
+ self.check_page_for_string( check_str )
+ self.home()
def set_history( self ):
"""Sets the history (stores the cookies for this run)"""
if self.history_id:
self.visit_page( "history?id=%s" % self.history_id )
else:
self.new_history()
- def share_history( self, id=None, email='test2(a)bx.psu.edu' ):
- """Share a history with a different user"""
- history_list = self.get_histories()
- self.assertTrue( history_list )
- if id is None: # take last id
- elem = history_list[-1]
- else:
- i = history_list.index( id )
- self.assertTrue( i )
- elem = history_list[i]
- id = elem.get( 'id' )
- self.assertTrue( id )
- id = str( id )
- name = elem.get( 'name' )
- self.assertTrue( name )
+ self.home()
+ def share_history( self, id, email, check_str, check_str2='', action=None, action_check_str=None ):
+ """Share a history different users"""
self.visit_url( "%s/history/share?id=%s&email=%s&history_share_btn=Submit" % ( self.url, id, email ) )
- self.check_page_for_string( 'History (%s) has been shared with: %s' % ( name, email ) )
- return id, name, email
- def share_history_containing_private_datasets( self, history_id, email='test(a)bx.psu.edu' ):
- """Attempt to share a history containing private datasets with a different user"""
- self.visit_url( "%s/history/share?id=%s&email=%s&history_share_btn=Submit" % ( self.url, history_id, email ) )
- self.last_page()
- self.check_page_for_string( "The history or histories you've chosen to share contain datasets" )
- self.check_page_for_string( "How would you like to proceed?" )
+ self.check_page_for_string( check_str )
+ if check_str2:
+ self.check_page_for_string( check_str2 )
+ if action:
+ # If we have an action, then we are sharing datasets with users that do not have access permissions on them
+ tc.fv( '1', 'action', action )
+ tc.submit( "share_proceed_button" )
+ if action_check_str:
+ self.check_page_for_string( action_check_str )
self.home()
- def make_datasets_public( self, history_id, email='test(a)bx.psu.edu' ):
- """Make private datasets public in order to share a history with a different user"""
- self.visit_url( "%s/history/share?id=%s&email=%s&action=public&submit=Ok" % ( self.url, history_id, email ) )
- self.last_page()
- check_str = "History (Unnamed history) has been shared with: %s" % email
- self.check_page_for_string( check_str )
- self.home()
- def privately_share_dataset( self, history_id, email='test(a)bx.psu.edu' ):
- """Make private datasets public in order to share a history with a different user"""
- self.visit_url( "%s/history/share?id=%s&email=%s&action=private&submit=Ok" % ( self.url, history_id, email ) )
- self.last_page()
- check_str = "History (Unnamed history) has been shared with: %s" % email
- self.check_page_for_string( check_str )
- self.home()
- def switch_history( self, hid=None ):
+ def switch_history( self, id='', name='' ):
"""Switches to a history in the current list of histories"""
data_list = self.get_histories()
self.assertTrue( data_list )
- if hid is None: # take last hid
- elem = data_list[-1]
- hid = elem.get('hid')
- if hid < 0:
- hid = len(data_list) + hid + 1
- hid = str(hid)
- elems = [ elem for elem in data_list if elem.get('hid') == hid ]
- self.assertEqual(len(elems), 1)
- self.visit_page( "history/list?operation=switch&id=%s" % elems[0].get('id') )
-
- def view_stored_histories( self, check_str='' ):
+ if not id:
+ history = history_list[0]
+ id = history.get( 'id' )
+ self.visit_url( "%s/history/list?operation=switch&id=%s" % ( self.url, id ) )
+ if name:
+ self.check_history_for_string( name )
+ self.home()
+ def view_stored_active_histories( self, check_str='' ):
self.visit_page( "history/list" )
+ self.check_page_for_string( 'Stored histories' )
+ self.check_page_for_string( '<input type="checkbox" name="id" value=' )
+ self.check_page_for_string( 'operation=Rename&id' )
+ self.check_page_for_string( 'operation=Switch&id' )
+ self.check_page_for_string( 'operation=Delete&id' )
if check_str:
self.check_page_for_string( check_str )
+ self.home()
+ def view_stored_deleted_histories( self, check_str='' ):
+ self.visit_page( "history/list?f-deleted=True" )
+ self.check_page_for_string( 'Stored histories' )
+ self.check_page_for_string( '<input type="checkbox" name="id" value=' )
+ self.check_page_for_string( 'operation=Undelete&id' )
+ if check_str:
+ self.check_page_for_string( check_str )
+ self.home()
# Functions associated with datasets (history items) and meta data
def get_job_stderr( self, id ):
@@ -299,9 +298,14 @@
self.visit_url( "%s/dataset/undelete?id=%s" % ( self.url, elems[0].get( 'id' ) ) )
if check_str:
self.check_page_for_string( check_str )
+ def display_history_item( self, id, check_str='' ):
+ """Displays a history item - simulates eye icon click"""
+ self.visit_url( '%s/datasets/%s/display/index' % ( self.url, id ) )
+ if check_str:
+ self.check_page_for_string( check_str )
+ self.home()
def edit_metadata( self, hid=None, form_no=0, **kwd ):
- """
- Edits the metadata associated with a history item."""
+ """Edits the metadata associated with a history item."""
# There are currently 4 forms on the edit page:
# 0. name="edit_attributes"
# 1. name="auto_detect"
@@ -324,7 +328,6 @@
button = "change" #Change data type form
if kwd:
self.submit_form( form_no=form_no, button=button, **kwd)
-
def get_dataset_ids_in_history( self ):
"""Returns the ids of datasets in a history"""
data_list = self.get_history()
diff -r 73847f425801 -r 9a71b89082fe test/functional/test_history_functions.py
--- a/test/functional/test_history_functions.py Mon Jun 01 10:27:04 2009 -0400
+++ b/test/functional/test_history_functions.py Fri Jun 05 11:15:25 2009 -0400
@@ -4,74 +4,367 @@
class TestHistory( TwillTestCase ):
- def test_00_history_options_when_not_logged_in( self ):
+ def test_000_history_options_when_not_logged_in( self ):
"""Testing history options when not logged in"""
- self.logout() #Ensure we are not logged in
- self.history_options()
- self.check_page_for_string( 'logged in</a> to store or switch histories.' )
- self.login( email='test2(a)bx.psu.edu' ) #Just to make sure we have created this account since it is used to share histories
self.logout()
- def test_05_new_history_then_delete( self ):
- """Testing creating a new history and then deleting it"""
- self.login()
- self.new_history()
- if len(self.get_history()) > 0:
- raise AssertionError("test_new_history_then_delete failed")
- self.delete_history()
- self.check_page_for_string( 'Deleted 1 histories' )
- def test_10_history_options_when_logged_in( self ):
+ check_str = 'logged in</a> to store or switch histories.'
+ self.history_options( check_str=check_str )
+ # Make sure we have created the following accounts
+ self.login( email='test1(a)bx.psu.edu' )
+ global regular_user1
+ regular_user1 = galaxy.model.User.filter( galaxy.model.User.table.c.email=='test1(a)bx.psu.edu' ).first()
+ assert regular_user1 is not None, 'Problem retrieving user with email "test1(a)bx.psu.edu" from the database'
+ self.logout()
+ self.login( email='test2(a)bx.psu.edu' )
+ global regular_user2
+ regular_user2 = galaxy.model.User.filter( galaxy.model.User.table.c.email=='test2(a)bx.psu.edu' ).first()
+ assert regular_user2 is not None, 'Problem retrieving user with email "test2(a)bx.psu.edu" from the database'
+ self.logout()
+ self.login( email='test3(a)bx.psu.edu' )
+ global regular_user3
+ regular_user3 = galaxy.model.User.filter( galaxy.model.User.table.c.email=='test3(a)bx.psu.edu' ).first()
+ assert regular_user3 is not None, 'Problem retrieving user with email "test3(a)bx.psu.edu" from the database'
+ self.logout()
+ def test_005_deleting_histories( self ):
+ """Testing deleting histories"""
+ self.login( email='test(a)bx.psu.edu' )
+ global admin_user
+ admin_user = galaxy.model.User.filter( galaxy.model.User.table.c.email=='test(a)bx.psu.edu' ).first()
+ assert admin_user is not None, 'Problem retrieving user with email "test(a)bx.psu.edu" from the database'
+ # Get the admin_user private role
+ global admin_user_private_role
+ admin_user_private_role = None
+ for role in admin_user.all_roles():
+ if role.name == admin_user.email and role.description == 'Private Role for %s' % admin_user.email:
+ admin_user_private_role = role
+ break
+ if not admin_user_private_role:
+ raise AssertionError( "Private role not found for user '%s'" % admin_user.email )
+ latest_history = galaxy.model.History.query().order_by( desc( galaxy.model.History.table.c.create_time ) ).first()
+ assert latest_history is not None, "Problem retrieving latest history from database"
+ assert not latest_history.deleted, "After login, associated history is deleted"
+ self.delete_history( str( latest_history.id ) )
+ latest_history.refresh()
+ if not latest_history.deleted:
+ raise AssertionError, "Problem deleting history id %d" % latest_history.id
+ # We'll now test deleting a list of histories
+ # After deleting the current history, a new one should have been created
+ global history1
+ history1 = galaxy.model.History.query().order_by( desc( galaxy.model.History.table.c.create_time ) ).first()
+ assert history1 is not None, "Problem retrieving history1 from database"
+ self.upload_file( '1.bed', dbkey='hg18' )
+ self.new_history( name='history2' )
+ global history2
+ history2 = galaxy.model.History.query().order_by( desc( galaxy.model.History.table.c.create_time ) ).first()
+ assert history2 is not None, "Problem retrieving history2 from database"
+ self.upload_file( '2.bed', dbkey='hg18' )
+ ids = '%s,%s' % ( str( history1.id ), str( history2.id ) )
+ self.delete_history( ids )
+ try:
+ self.view_stored_active_histories( check_str=history1.name )
+ raise AssertionError, "History %s is displayed in the active history list after it was deleted" % history1.name
+ except:
+ pass
+ self.view_stored_deleted_histories( check_str=history1.name )
+ try:
+ self.view_stored_active_histories( check_str=history2.name )
+ raise AssertionError, "History %s is displayed in the active history list after it was deleted" % history2.name
+ except:
+ pass
+ self.view_stored_deleted_histories( check_str=history2.name )
+ history1.refresh()
+ if not history1.deleted:
+ raise AssertionError, "Problem deleting history id %d" % history1.id
+ if not history1.default_permissions:
+ raise AssertionError, "Default permissions were incorrectly deleted from the db for history id %d when it was deleted" % history1.id
+ history2.refresh()
+ if not history2.deleted:
+ raise AssertionError, "Problem deleting history id %d" % history2.id
+ if not history2.default_permissions:
+ raise AssertionError, "Default permissions were incorrectly deleted from the db for history id %d when it was deleted" % history2.id
+ def test_010_history_options_when_logged_in( self ):
"""Testing history options when logged in"""
self.history_options()
- self.check_page_for_string( 'Rename</a> current history' )
- self.check_page_for_string( 'List</a> previously stored histories' )
- self.check_page_for_string( 'Construct workflow</a> from the current history' )
- self.check_page_for_string( 'Share</a> current history' )
- # Tests for changing default history permissions are done in test_security_and_libraries.py
- self.check_page_for_string( 'Change default permissions</a> for the current history' )
- self.check_page_for_string( 'Show deleted</a> datasets in history' )
- self.check_page_for_string( 'Delete</a> current history' )
- # Need to add a history item in order to create a new empty history
+ def test_015_history_rename( self ):
+ """Testing renaming a history"""
+ global history3
+ history3 = galaxy.model.History.query().order_by( desc( galaxy.model.History.table.c.create_time ) ).first()
+ assert history3 is not None, "Problem retrieving history3 from database"
+ if history3.deleted:
+ raise AssertionError, "History id %d deleted when it should not be" % latest_history.id
+ self.rename_history( str( history3.id ), history3.name, new_name='history3' )
+ def test_020_history_list( self ):
+ """Testing viewing previously stored histories"""
+ self.view_stored_active_histories()
+ def test_025_history_share( self ):
+ """Testing sharing histories containing only public datasets"""
+ history3.refresh()
+ self.upload_file( '1.bed', dbkey='hg18' )
+ # Test sharing a history with yourself
+ check_str = "You can't send histories to yourself."
+ self.share_history( str( history3.id ), 'test(a)bx.psu.edu', check_str )
+ # Share a history with 1 valid user
+ name = history3.name
+ email = 'test1(a)bx.psu.edu'
+ check_str = 'Histories (%s) have been shared with: %s' % ( name, email )
+ self.share_history( str( history3.id ), email, check_str )
+ # We need to keep track of all shared histories so they can later be deleted
+ global history3_copy1
+ history3_copy1 = galaxy.model.History.query().order_by( desc( galaxy.model.History.table.c.create_time ) ).first()
+ assert history3_copy1 is not None, "Problem retrieving history3_copy1 from database"
+ self.logout()
+ self.login( email='test1(a)bx.psu.edu' )
+ check_str = '%s from test(a)bx.psu.edu' % history3.name
+ self.view_stored_active_histories( check_str=check_str )
+ self.logout()
+ self.login( email='test(a)bx.psu.edu' )
+ # Need to delete history3_copy1
+ history3_copy1.deleted = True
+ history3_copy1.flush()
+ # Test sharing a history with an invalid user
+ email = 'jack(a)jill.com'
+ check_str = '%s is not a valid Galaxy user.' % email
+ self.share_history( str( history3.id ), email, check_str )
+ # Test sharing multiple histories with multiple users
+ self.new_history()
+ global history4
+ history4 = galaxy.model.History.query().order_by( desc( galaxy.model.History.table.c.create_time ) ).first()
+ assert history4 is not None, "Problem retrieving history4 from database"
+ self.rename_history( str( history4.id ), history4.name, new_name='history4' )
+ history4.refresh()
+ self.upload_file( '2.bed', dbkey='hg18' )
+ id = '%s,%s' % ( str( history3.id ), str( history4.id ) )
+ name = '%s,%s' % ( history3.name, history4.name )
+ email = 'test2@bx.psu.edu,test3@bx.psu.edu'
+ check_str = 'Histories (%s) have been shared with: %s' % ( name, email )
+ self.share_history( id, email, check_str )
+ # We need to keep track of all shared histories so they can later be deleted
+ history3_copy_name = "%s from %s" % ( history3.name, admin_user.email )
+ history3_copies = galaxy.model.History \
+ .filter( and_( galaxy.model.History.table.c.name==history3_copy_name,
+ galaxy.model.History.table.c.deleted==False ) ) \
+ .order_by( desc( galaxy.model.History.table.c.create_time ) ) \
+ .limit( 2 ) \
+ .all()
+ history3_copy2 = history3_copies[0]
+ history3_copy3 = history3_copies[1]
+ history4_copy_name = "%s from %s" % ( history4.name, admin_user.email )
+ history4_copyies = galaxy.model.History \
+ .filter( and_( galaxy.model.History.table.c.name==history4_copy_name,
+ galaxy.model.History.table.c.deleted==False ) ) \
+ .order_by( desc( galaxy.model.History.table.c.create_time ) ) \
+ .limit( 2 ) \
+ .all()
+ history4_copy1 = history4_copyies[0]
+ history4_copy2 = history4_copyies[1]
+ self.logout()
+ self.login( email='test2(a)bx.psu.edu' )
+ check_str = '%s from %s' % ( history3.name, admin_user.email )
+ self.view_stored_active_histories( check_str=check_str )
+ check_str = '%s from %s' % ( history4.name, admin_user.email )
+ self.view_stored_active_histories( check_str=check_str )
+ self.logout()
+ self.login( email='test3(a)bx.psu.edu' )
+ check_str = '%s from %s' % ( history3.name, admin_user.email )
+ self.view_stored_active_histories( check_str=check_str )
+ check_str = '%s from %s' % ( history4.name, admin_user.email )
+ self.view_stored_active_histories( check_str=check_str )
+ self.logout()
+ self.login( email='test(a)bx.psu.edu' )
+ # Need to delete the copied histories, so later test runs are valid
+ history3_copy2.deleted = True
+ history3_copy2.flush()
+ history3_copy3.deleted = True
+ history3_copy3.flush()
+ history4_copy1.deleted = True
+ history4_copy1.flush()
+ history4_copy1.deleted = True
+ history4_copy1.flush()
+ history4_copy2.deleted = True
+ history4_copy2.flush()
+ def test_030_change_permissions_on_current_history( self ):
+ """Testing changing permissions on the current history"""
+ global history5
+ history5 = galaxy.model.History.query().order_by( desc( galaxy.model.History.table.c.create_time ) ).first()
+ assert history5 is not None, "Problem retrieving history5 from database"
+ self.rename_history( str( history5.id ), history5.name, new_name='history5' )
+ history5.refresh()
+ # Due to the limitations of twill ( not functional with the permissions forms ), we're forced
+ # to do this manually. At this point, we just want to restrict the access permission on history5
+ # to the admin_user
+ global access_action
+ access_action = galaxy.model.Dataset.permitted_actions.DATASET_ACCESS.action
+ dhp = galaxy.model.DefaultHistoryPermissions( history5, access_action, admin_user_private_role )
+ dhp.flush()
+ self.upload_file( '1.bed', dbkey='hg18' )
+ history5_dataset1 = None
+ for hda in history5.datasets:
+ if hda.name == '1.bed':
+ history5_dataset1 = hda.dataset
+ assert history5_dataset1 is not None, "Problem retrieving history5_dataset1 from the database"
+ # The permissions on the dataset should be restricted from sharing with anyone due to the
+ # inherited history permissions
+ restricted = False
+ for action in history5_dataset1.actions:
+ if action.action == access_action:
+ restricted = True
+ break
+ if not restricted:
+ raise AssertionError, "The 'access' permission is not set for history5_dataset1.actions"
+ def test_035_sharing_history_by_making_datasets_public( self ):
+ """Testing sharing a restricted history by making the datasets public"""
+ check_str = 'The following datasets can be shared with %s by updating their permissions' % regular_user1.email
+ action_check_str = 'Histories (%s) have been shared with: %s' % ( history5.name, regular_user1.email )
+ self.share_history( str( history5.id ), regular_user1.email, check_str, action='public', action_check_str=action_check_str )
+ history5_copy1 = galaxy.model.History.query().order_by( desc( galaxy.model.History.table.c.create_time ) ).first()
+ assert history5_copy1 is not None, "Problem retrieving history5_copy1 from database"
+ self.logout()
+ self.login( email=regular_user1.email )
+ self.visit_url( "%s/history/list" % self.url )
+ self.check_page_for_string( history5_copy1.name )
+ # Need to delete history5_copy1 on the history list page for regular_user1
+ history5_copy1.deleted = True
+ history5_copy1.flush()
+ self.logout()
+ self.login( email=admin_user.email )
+ def test_040_sharing_history_by_making_new_sharing_role( self ):
+ """Testing sharing a restricted history by associating a new sharing role with protected datasets"""
+ self.switch_history( id=str( history5.id ), name=history5.name )
+ # At this point, history5 should have 1 item, 1.bed, which is public. We'll add another
+ # item which will be private to admin_user due to the permissions on history5
+ self.upload_file( '2.bed', dbkey='hg18' )
+ check_str = 'The following datasets can be shared with %s with no changes' % regular_user1.email
+ check_str2 = 'The following datasets can be shared with %s by updating their permissions' % regular_user1.email
+ action_check_str = 'Histories (%s) have been shared with: %s' % ( history5.name, regular_user1.email )
+ self.share_history( str( history5.id ),
+ regular_user1.email,
+ check_str,
+ check_str2=check_str2,
+ action='private',
+ action_check_str=action_check_str )
+ history5_copy2 = galaxy.model.History.query().order_by( desc( galaxy.model.History.table.c.create_time ) ).first()
+ assert history5_copy2 is not None, "Problem retrieving history5_copy2 from database"
+ # We should now have a new sharing role
+ global sharing_role
+ role_name = 'Sharing role for: %s, %s' % ( admin_user.email, regular_user1.email )
+ sharing_role = galaxy.model.Role.filter( galaxy.model.Role.table.c.name==role_name ).first()
+ assert sharing_role is not None, "Problem retrieving sharing_role from the database"
+ self.logout()
+ self.login( email=regular_user1.email )
+ self.visit_url( "%s/history/list" % self.url )
+ self.check_page_for_string( history5_copy2.name )
+ self.switch_history( id=str( history5_copy2.id ), name=history5_copy2.name )
+ # Make sure both datasets are in the history
+ self.check_history_for_string( '1.bed' )
+ self.check_history_for_string( '2.bed' )
+ # Get both new hdas from the db that were created for the shared history
+ hda_1_bed = galaxy.model.HistoryDatasetAssociation \
+ .filter( and_( galaxy.model.HistoryDatasetAssociation.table.c.history_id==history5_copy2.id,
+ galaxy.model.HistoryDatasetAssociation.table.c.name=='1.bed' ) ) \
+ .first()
+ assert hda_1_bed is not None, "Problem retrieving hda_1_bed from database"
+ hda_2_bed = galaxy.model.HistoryDatasetAssociation \
+ .filter( and_( galaxy.model.HistoryDatasetAssociation.table.c.history_id==history5_copy2.id,
+ galaxy.model.HistoryDatasetAssociation.table.c.name=='2.bed' ) ) \
+ .first()
+ assert hda_2_bed is not None, "Problem retrieving hda_2_bed from database"
+ # Make sure 1.bed is accessible since it is public
+ self.display_history_item( str( hda_1_bed.id ), check_str='chr1' )
+ # Make sure 2.bed is accessible since it is associated with a sharing role
+ self.display_history_item( str( hda_2_bed.id ), check_str='chr1' )
+ # Need to delete history5_copy2 on the history list page for regular_user1
+ history5_copy2.deleted = True
+ history5_copy2.flush()
+ self.logout()
+ self.login( email=admin_user.email )
+ def test_045_sharing_private_history_with_multiple_users_by_changing_no_permissions( self ):
+ """Testing sharing a restricted history with multiple users, making no permission changes"""
+ # History5 can be shared with any user, since it contains a public dataset. However, only
+ # regular_user1 should be able to access history5's 2.bed dataset since it is associated with a
+ # sharing role, and regular_user2 should be able to access history5's 1.bed, but not 2.bed even
+ # though they can see it in their shared history.
+ self.switch_history( id=str( history5.id ), name=history5.name )
+ email = '%s,%s' % ( regular_user1.email, regular_user2.email )
+ check_str = 'The following datasets can be shared with %s with no changes' % email
+ check_str2 = 'The following datasets can be shared with %s by updating their permissions' % email
+ action_check_str = 'Histories (%s) have been shared with: %s' % ( history5.name, regular_user1.email )
+ self.share_history( str( history5.id ),
+ email,
+ check_str,
+ check_str2=check_str2,
+ action='share',
+ action_check_str=action_check_str )
+ # We need to keep track of all shared histories so they can later be deleted
+ history5_copy_name = "%s from %s" % ( history5.name, admin_user.email )
+ history5_copies = galaxy.model.History \
+ .filter( and_( galaxy.model.History.table.c.name==history5_copy_name,
+ galaxy.model.History.table.c.deleted==False ) ) \
+ .order_by( desc( galaxy.model.History.table.c.create_time ) ) \
+ .limit( 2 ) \
+ .all()
+ history5_copy3 = history5_copies[0]
+ assert history5_copy3 is not None, "Problem retrieving history5_copy3 from database"
+ history5_copy4 = history5_copies[1]
+ assert history5_copy4 is not None, "Problem retrieving history5_copy4 from database"
+ # Make sure test1(a)bx.psu.edu received a copy of history5 with both datasets accessible
+ self.login( email=regular_user1.email )
+ check_str = '%s from %s' % ( history5.name, admin_user.email )
+ self.view_stored_active_histories( check_str=check_str )
+ self.switch_history( id=str( history5_copy3.id ), name=history5_copy3.name )
+ self.check_history_for_string( '1.bed' )
+ self.check_history_for_string( '2.bed' )
+ self.logout()
+ # Make sure test2(a)bx.psu.edu received a copy of history5, with only 1.bed accessible
+ self.login( email=regular_user2.email )
+ self.view_stored_active_histories( check_str=check_str )
+ self.switch_history( id=str( history5_copy4.id ), name=history5_copy4.name )
+ self.check_history_for_string( '1.bed' )
+ self.check_history_for_string( '2.bed' )
+ # Get both new hdas from the db that were created for the shared history
+ hda_1_bed = galaxy.model.HistoryDatasetAssociation \
+ .filter( and_( galaxy.model.HistoryDatasetAssociation.table.c.history_id==history5_copy4.id,
+ galaxy.model.HistoryDatasetAssociation.table.c.name=='1.bed' ) ) \
+ .first()
+ assert hda_1_bed is not None, "Problem retrieving hda_1_bed from database"
+ hda_2_bed = galaxy.model.HistoryDatasetAssociation \
+ .filter( and_( galaxy.model.HistoryDatasetAssociation.table.c.history_id==history5_copy4.id,
+ galaxy.model.HistoryDatasetAssociation.table.c.name=='2.bed' ) ) \
+ .first()
+ assert hda_2_bed is not None, "Problem retrieving hda_2_bed from database"
+ # Make sure 1.bed is accessible since it is public
+ self.display_history_item( str( hda_1_bed.id ), check_str='chr1' )
+ # Make sure 2.bed is not accessible since it is protected
try:
- self.check_page_for_string( 'Create</a> a new empty history' )
- raise AssertionError, "Incorrectly able to create a new empty history when the current history is empty."
+ self.display_history_item( str( hda_2_bed.id ), check_str='chr1' )
+ raise AssertionError, "History item 2.bed is accessible by user %s when is should not be" % regular_user2.email
except:
pass
- self.upload_file( '1.bed', dbkey='hg18' )
- self.history_options()
- self.check_page_for_string( 'Create</a> a new empty history' )
- def test_15_history_rename( self ):
- """Testing renaming a history"""
- id, old_name, new_name = self.rename_history()
- self.check_page_for_string( 'History: %s renamed to: %s' %(old_name, new_name) )
- def test_20_history_list( self ):
- """Testing viewing previously stored histories"""
- self.view_stored_histories()
- self.check_page_for_string( 'Stored histories' )
- self.check_page_for_string( '<input type="checkbox" name="id" value=' )
- self.check_page_for_string( 'operation=Rename&id' )
- self.check_page_for_string( 'operation=Switch&id' )
- self.check_page_for_string( 'operation=Delete&id' )
- def test_25_history_share( self ):
- """Testing sharing a history with another user"""
- self.upload_file('1.bed', dbkey='hg18')
- id, name, email = self.share_history()
- self.logout()
- self.login( email=email )
- self.home()
- check_str = 'Unnamed history from test(a)bx.psu.edu'
- self.view_stored_histories( check_str=check_str )
- histories = self.get_histories()
- for history in histories:
- if history.get( 'name' ) == 'Unnamed history from test(a)bx.psu.edu':
- id = history.get( 'id' )
- break
- self.assertTrue( id )
- self.delete_history( id )
+ self.check_history_for_string( 'You do not have permission to view this dataset' )
self.logout()
self.login( email='test(a)bx.psu.edu' )
- def test_30_history_show_and_hide_deleted_datasets( self ):
+ # Need to delete the copied histories, so later test runs are valid
+ history5_copy3.deleted = True
+ history5_copy3.flush()
+ history5_copy4.deleted = True
+ history5_copy4.flush()
+
+
+
+ def test_050_sharing_private_history_by_choosing_to_not_share( self ):
+ """Testing sharing a restricted history with multiple users by choosing not to share"""
+ self.switch_history( id=str( history5.id ), name=history5.name )
+ email = '%s,%s' % ( regular_user1.email, regular_user2.email )
+ check_str = 'The following datasets can be shared with %s with no changes' % email
+ check_str2 = 'The following datasets can be shared with %s by updating their permissions' % email
+ action_check_str = 'History Options'
+ self.share_history( str( history5.id ),
+ email,
+ check_str,
+ check_str2=check_str2,
+ action='no_share' )
+ def test_055_history_show_and_hide_deleted_datasets( self ):
"""Testing displaying deleted history items"""
- self.new_history()
+ self.new_history( name='temp_history1' )
self.upload_file('1.bed', dbkey='hg18')
latest_hda = galaxy.model.HistoryDatasetAssociation.query() \
.order_by( desc( galaxy.model.HistoryDatasetAssociation.table.c.create_time ) ).first()
@@ -85,9 +378,9 @@
self.home()
self.visit_url( "%s/history/?show_deleted=False" % self.url )
self.check_page_for_string( 'Your history is empty' )
- def test_35_deleting_and_undeleting_history_items( self ):
+ def test_060_deleting_and_undeleting_history_items( self ):
"""Testing deleting and un-deleting history items"""
- self.new_history()
+ self.new_history( name='temp_history2' )
# Add a new history item
self.upload_file( '1.bed', dbkey='hg15' )
self.home()
@@ -110,6 +403,8 @@
self.visit_url( "%s/history/?show_deleted=False" % self.url )
self.check_page_for_string( '1.bed' )
self.check_page_for_string( 'hg15' )
- def test_9999_clean_up( self ):
- self.delete_history()
- self.logout()
+ def test_065_reset_data_for_later_test_runs( self ):
+ """Reseting data to enable later test runs to pass"""
+ self.delete_history( id=str( history3.id ) )
+ self.delete_history( id=str( history4.id ) )
+ self.delete_history( id=str( history5.id ) )
diff -r 73847f425801 -r 9a71b89082fe test/functional/test_security_and_libraries.py
--- a/test/functional/test_security_and_libraries.py Mon Jun 01 10:27:04 2009 -0400
+++ b/test/functional/test_security_and_libraries.py Fri Jun 05 11:15:25 2009 -0400
@@ -40,7 +40,7 @@
global admin_user
admin_user = galaxy.model.User.filter( galaxy.model.User.table.c.email=='test(a)bx.psu.edu' ).first()
assert admin_user is not None, 'Problem retrieving user with email "test(a)bx.psu.edu" from the database'
- # Get the admin user's privat role for later use
+ # Get the admin user's private role for later use
global admin_user_private_role
admin_user_private_role = None
for role in admin_user.all_roles():
@@ -136,7 +136,7 @@
dps.append( dp.action )
# Sort actions for later comparison
dps.sort()
- # Compare DatasetPermissionss with permissions_in - should be the same
+ # Compare DatasetPermissions with permissions_in - should be the same
if dps != actions_in:
raise AssertionError( 'DatasetPermissionss "%s" for dataset id %d differ from changed default permissions "%s"' \
% ( str( dps ), latest_dataset.id, str( actions_in ) ) )
@@ -145,14 +145,26 @@
raise AssertionError( 'DatasetPermissionss "%s" for dataset id %d differ from DefaultHistoryPermissions "%s" for history id %d' \
% ( str( dps ), latest_dataset.id, str( dhps ), latest_history.id ) )
# Since the dataset in the history is now private, we can test sharing with another user
- self.share_history_containing_private_datasets( str( latest_history.id ), email=admin_user.email )
# Test making the dataset in the history public
- self.make_datasets_public( str( latest_history.id ), email=admin_user.email )
+ check_str = 'The following datasets can be shared with %s by updating their permissions' % admin_user.email
+ action_check_str = 'Histories (%s) have been shared with: %s' % ( latest_history.name, admin_user.email )
+ self.share_history( str( latest_history.id ),
+ admin_user.email,
+ check_str,
+ action='public',
+ action_check_str=action_check_str )
# Add another dataset to the history, it should be private since that is now our default
self.upload_file( '2.bed' )
- self.share_history_containing_private_datasets( str( latest_history.id ), email=admin_user.email )
- # Test creating a new sharing role for sharing the private datasets
- self.privately_share_dataset( str( latest_history.id ), email=admin_user.email )
+ # Test creating a new sharing role for the private dataset
+ check_str = 'The following datasets can be shared with %s with no changes' % admin_user.email
+ check_str2 = 'The following datasets can be shared with %s by updating their permissions' % admin_user.email
+ action_check_str = 'Histories (%s) have been shared with: %s' % ( latest_history.name, admin_user.email )
+ self.share_history( str( latest_history.id ),
+ admin_user.email,
+ check_str,
+ check_str2=check_str2,
+ action='private',
+ action_check_str=action_check_str )
role_type = 'sharing'
role_name = 'Sharing role for: %s, %s' % ( regular_user1.email, admin_user.email )
global sharing_role
diff -r 73847f425801 -r 9a71b89082fe tool_conf.xml.sample
--- a/tool_conf.xml.sample Mon Jun 01 10:27:04 2009 -0400
+++ b/tool_conf.xml.sample Fri Jun 05 11:15:25 2009 -0400
@@ -13,6 +13,7 @@
<tool file="data_source/wormbase_test.xml" />
<tool file="data_source/flymine.xml" />
<tool file="data_source/flymine_test.xml" />
+ <tool file="data_source/eupathdb.xml" />
<tool file="data_source/encode_db.xml" />
<tool file="data_source/epigraph_import.xml" />
<tool file="data_source/epigraph_import_test.xml" />
1
0
sh run.sh seems to work okay until hitting this block of report...
Any ideas what this socket business is about? It looks to me like
possibly something python-specific.
Starting server in PID 11963.
Traceback (most recent call last):
File "./scripts/paster.py", line 27, in ?
command.run()
File "/root/src/galaxy-f7336991d0ee/eggs/py2.4-noplatform/
PasteScript-1.3.6-py2.4.egg/paste/script/command.py", line 78, in run
File "/root/src/galaxy-f7336991d0ee/eggs/py2.4-noplatform/
PasteScript-1.3.6-py2.4.egg/paste/script/command.py", line 117, in
invoke
File "/root/src/galaxy-f7336991d0ee/eggs/py2.4-noplatform/
PasteScript-1.3.6-py2.4.egg/paste/script/command.py", line 212, in run
File "/root/src/galaxy-f7336991d0ee/eggs/py2.4-noplatform/
PasteScript-1.3.6-py2.4.egg/paste/script/serve.py", line 232, in command
File "/root/src/galaxy-f7336991d0ee/eggs/py2.4-noplatform/
PasteDeploy-1.3.1-py2.4.egg/paste/deploy/loadwsgi.py", line 139, in
server_wrapper
File "/root/src/galaxy-f7336991d0ee/eggs/py2.4-noplatform/
PasteDeploy-1.3.1-py2.4.egg/paste/deploy/util/fixtypeerror.py", line
57, in fix_call
File "/root/src/galaxy-f7336991d0ee/eggs/py2.4-noplatform/
Paste-1.5.1-py2.4.egg/paste/httpserver.py", line 1307, in server_runner
File "/root/src/galaxy-f7336991d0ee/eggs/py2.4-noplatform/
Paste-1.5.1-py2.4.egg/paste/httpserver.py", line 1257, in serve
File "/root/src/galaxy-f7336991d0ee/eggs/py2.4-noplatform/
Paste-1.5.1-py2.4.egg/paste/httpserver.py", line 1107, in __init__
File "/root/src/galaxy-f7336991d0ee/eggs/py2.4-noplatform/
Paste-1.5.1-py2.4.egg/paste/httpserver.py", line 1087, in __init__
File "/root/src/galaxy-f7336991d0ee/eggs/py2.4-noplatform/
Paste-1.5.1-py2.4.egg/paste/httpserver.py", line 328, in __init__
File "/usr/lib64/python2.4/SocketServer.py", line 330, in __init__
self.server_bind()
File "/usr/lib64/python2.4/BaseHTTPServer.py", line 101, in
server_bind
SocketServer.TCPServer.server_bind(self)
File "/usr/lib64/python2.4/SocketServer.py", line 341, in server_bind
self.socket.bind(self.server_address)
File "<string>", line 1, in bind
socket.error: (98, 'Address already in use')
galaxy.jobs INFO 2009-06-05 02:15:43,692 sending stop signal to worker
thread
galaxy.jobs INFO 2009-06-05 02:15:43,692 job queue stopped
galaxy.jobs.runners.local INFO 2009-06-05 02:15:43,693 sending stop
signal to worker threads
galaxy.jobs.runners.local INFO 2009-06-05 02:15:43,694 local job
runner stopped
galaxy.jobs INFO 2009-06-05 02:15:43,694 sending stop signal to worker
thread
galaxy.jobs INFO 2009-06-05 02:15:43,695 job stopper stopped
2
7
Hi,
Regarding the install instructions at:
* http://g2.trac.bx.psu.edu/wiki/HowToInstall
We're wondering about the step:
* hg clone http://www.bx.psu.edu/hg/galaxy galaxy_dist
Previously we had been using svn to access the sources (some months
ago now). At this point I believe we got sources corresponding to the
development version of galaxy(http://test.g2.bx.psu.edu/). Since we
are doing module development we think it may be more appropriate to
use a release version (http://main.g2.bx.psu.edu/) It would be nice
to confirm that this is what galaxy_dist from the above step actually
corresponds to. If not then it would be good to know this too so we
can adjust our approach to updates accordingly.
Thanks for your attention.
Stewart
--
Stewart Stevens
Software Developer - Integrated Genomics
University of Otago
Department of Biochemistry
Tel: +64(0)3 479 7863
Fax: +64(0)3 479 7866
2
1
01 Jun '09
details: http://www.bx.psu.edu/hg/galaxy/rev/e50551db6e60
changeset: 2426:e50551db6e60
user: Greg Von Kuster <greg(a)bx.psu.edu>
date: Fri May 29 16:36:43 2009 -0400
description:
Add missing functional tests for history features.
4 file(s) affected in this change:
lib/galaxy/web/controllers/root.py
templates/root/history_as_xml.mako
test/base/twilltestcase.py
test/functional/test_history_functions.py
diffs (264 lines):
diff -r c8a4a4ea0f2c -r e50551db6e60 lib/galaxy/web/controllers/root.py
--- a/lib/galaxy/web/controllers/root.py Fri May 29 13:19:42 2009 -0400
+++ b/lib/galaxy/web/controllers/root.py Fri May 29 16:36:43 2009 -0400
@@ -59,7 +59,7 @@
return trans.fill_template( '/no_access.mako', message = 'Please log in to access Galaxy histories.' )
if as_xml:
trans.response.set_content_type('text/xml')
- return trans.fill_template_mako( "root/history_as_xml.mako", history=history )
+ return trans.fill_template_mako( "root/history_as_xml.mako", history=history, show_deleted=util.string_as_bool( show_deleted ) )
else:
template = "root/history.mako"
return trans.fill_template( "root/history.mako", history = history, show_deleted = util.string_as_bool( show_deleted ) )
diff -r c8a4a4ea0f2c -r e50551db6e60 templates/root/history_as_xml.mako
--- a/templates/root/history_as_xml.mako Fri May 29 13:19:42 2009 -0400
+++ b/templates/root/history_as_xml.mako Fri May 29 16:36:43 2009 -0400
@@ -1,8 +1,16 @@
<?xml version="1.0"?>
<history>
- %for data in history.active_datasets:
- <data id="${data.id}" hid="${data.hid}" name="${data.name}" state="${data.state}" dbkey="${data.dbkey}">
- ${_(data.blurb)}
- </data>
- %endfor
+ %if show_deleted:
+ %for data in history.activatable_datasets:
+ <data id="${data.id}" hid="${data.hid}" name="${data.name}" state="${data.state}" dbkey="${data.dbkey}">
+ ${_(data.blurb)}
+ </data>
+ %endfor
+ %else:
+ %for data in history.active_datasets:
+ <data id="${data.id}" hid="${data.hid}" name="${data.name}" state="${data.state}" dbkey="${data.dbkey}">
+ ${_(data.blurb)}
+ </data>
+ %endfor
+ %endif
</history>
diff -r c8a4a4ea0f2c -r e50551db6e60 test/base/twilltestcase.py
--- a/test/base/twilltestcase.py Fri May 29 13:19:42 2009 -0400
+++ b/test/base/twilltestcase.py Fri May 29 16:36:43 2009 -0400
@@ -98,6 +98,7 @@
# Functions associated with histories
def check_history_for_errors( self ):
"""Raises an exception if there are errors in a history"""
+ self.home()
self.visit_page( "history" )
page = self.last_page()
if page.find( 'error' ) > -1:
@@ -105,6 +106,7 @@
def check_history_for_string( self, patt ):
"""Looks for 'string' in history page"""
+ self.home()
self.visit_page( "history" )
for subpatt in patt.split():
tc.find(subpatt)
@@ -130,16 +132,16 @@
data_list = [ elem for elem in tree.findall("data") ]
return data_list
- def get_history( self ):
+ def get_history( self, show_deleted=False ):
"""Returns a history"""
- tree = self.history_as_xml_tree()
+ tree = self.history_as_xml_tree( show_deleted=show_deleted )
data_list = [ elem for elem in tree.findall("data") ]
return data_list
- def history_as_xml_tree( self ):
+ def history_as_xml_tree( self, show_deleted=False ):
"""Returns a parsed xml object of a history"""
self.home()
- self.visit_page( 'history?as_xml=True' )
+ self.visit_page( 'history?as_xml=True&show_deleted=%s' % show_deleted )
xml = self.last_page()
tree = ElementTree.fromstring(xml)
return tree
@@ -201,6 +203,7 @@
name = elem.get( 'name' )
self.assertTrue( name )
self.visit_url( "%s/history/share?id=%s&email=%s&history_share_btn=Submit" % ( self.url, id, email ) )
+ self.check_page_for_string( 'History (%s) has been shared with: %s' % ( name, email ) )
return id, name, email
def share_history_containing_private_datasets( self, history_id, email='test(a)bx.psu.edu' ):
"""Attempt to share a history containing private datasets with a different user"""
@@ -237,8 +240,10 @@
self.assertEqual(len(elems), 1)
self.visit_page( "history/list?operation=switch&id=%s" % elems[0].get('id') )
- def view_stored_histories( self ):
+ def view_stored_histories( self, check_str='' ):
self.visit_page( "history/list" )
+ if check_str:
+ self.check_page_for_string( check_str )
# Functions associated with datasets (history items) and meta data
def get_job_stderr( self, id ):
@@ -264,16 +269,36 @@
self.visit_page( "edit?hid=%d" % hid )
for subpatt in patt.split():
tc.find(subpatt)
-
- def delete_history_item( self, hid ):
+ def delete_history_item( self, hid, check_str='' ):
"""Deletes an item from a history"""
+ try:
+ hid = int( hid )
+ except:
+ raise AssertionError, "Invalid hid '%s' - must be int" % hid
hid = str(hid)
data_list = self.get_history()
self.assertTrue( data_list )
- elems = [ elem for elem in data_list if elem.get('hid') == hid ]
- self.assertEqual(len(elems), 1)
- self.visit_page( "delete?id=%s" % elems[0].get('id') )
-
+ elems = [ elem for elem in data_list if elem.get( 'hid' ) == hid ]
+ self.assertEqual( len( elems ), 1 )
+ self.home()
+ self.visit_page( "delete?id=%s" % elems[0].get( 'id' ) )
+ if check_str:
+ self.check_page_for_string( check_str )
+ def undelete_history_item( self, hid, show_deleted=False, check_str='' ):
+ """Un-deletes a deleted item in a history"""
+ try:
+ hid = int( hid )
+ except:
+ raise AssertionError, "Invalid hid '%s' - must be int" % hid
+ hid = str( hid )
+ data_list = self.get_history( show_deleted=show_deleted )
+ self.assertTrue( data_list )
+ elems = [ elem for elem in data_list if elem.get( 'hid' ) == hid ]
+ self.assertEqual( len( elems ), 1 )
+ self.home()
+ self.visit_url( "%s/dataset/undelete?id=%s" % ( self.url, elems[0].get( 'id' ) ) )
+ if check_str:
+ self.check_page_for_string( check_str )
def edit_metadata( self, hid=None, form_no=0, **kwd ):
"""
Edits the metadata associated with a history item."""
diff -r c8a4a4ea0f2c -r e50551db6e60 test/functional/test_history_functions.py
--- a/test/functional/test_history_functions.py Fri May 29 13:19:42 2009 -0400
+++ b/test/functional/test_history_functions.py Fri May 29 16:36:43 2009 -0400
@@ -1,4 +1,6 @@
-from base.twilltestcase import TwillTestCase
+import galaxy.model
+from galaxy.model.orm import *
+from base.twilltestcase import *
class TestHistory( TwillTestCase ):
@@ -20,15 +22,28 @@
def test_10_history_options_when_logged_in( self ):
"""Testing history options when logged in"""
self.history_options()
- self.check_page_for_string( 'Rename</a> current history')
- self.check_page_for_string( 'List</a> previously stored histories')
- self.check_page_for_string( 'Share</a> current history')
- self.check_page_for_string( 'Delete</a> current history')
- def test_10_history_rename( self ):
+ self.check_page_for_string( 'Rename</a> current history' )
+ self.check_page_for_string( 'List</a> previously stored histories' )
+ self.check_page_for_string( 'Construct workflow</a> from the current history' )
+ self.check_page_for_string( 'Share</a> current history' )
+ # Tests for changing default history permissions are done in test_security_and_libraries.py
+ self.check_page_for_string( 'Change default permissions</a> for the current history' )
+ self.check_page_for_string( 'Show deleted</a> datasets in history' )
+ self.check_page_for_string( 'Delete</a> current history' )
+ # Need to add a history item in order to create a new empty history
+ try:
+ self.check_page_for_string( 'Create</a> a new empty history' )
+ raise AssertionError, "Incorrectly able to create a new empty history when the current history is empty."
+ except:
+ pass
+ self.upload_file( '1.bed', dbkey='hg18' )
+ self.history_options()
+ self.check_page_for_string( 'Create</a> a new empty history' )
+ def test_15_history_rename( self ):
"""Testing renaming a history"""
id, old_name, new_name = self.rename_history()
self.check_page_for_string( 'History: %s renamed to: %s' %(old_name, new_name) )
- def test_15_history_view( self ):
+ def test_20_history_list( self ):
"""Testing viewing previously stored histories"""
self.view_stored_histories()
self.check_page_for_string( 'Stored histories' )
@@ -36,24 +51,15 @@
self.check_page_for_string( 'operation=Rename&id' )
self.check_page_for_string( 'operation=Switch&id' )
self.check_page_for_string( 'operation=Delete&id' )
- def test_20_delete_history_item( self ):
- """Testing deleting history item"""
- self.upload_file('1.bed', dbkey='hg15')
- self.check_history_for_string('hg15 bed')
- self.assertEqual ( len(self.get_history()), 1)
- self.delete_history_item(1)
- self.check_history_for_string("Your history is empty")
- self.assertEqual ( len(self.get_history()), 0)
- def test_25_share_history( self ):
+ def test_25_history_share( self ):
"""Testing sharing a history with another user"""
self.upload_file('1.bed', dbkey='hg18')
id, name, email = self.share_history()
- self.last_page()
- self.check_page_for_string( 'History (%s) has been shared with: %s' %(name, email) )
self.logout()
- self.login( email='test2(a)bx.psu.edu' )
- self.view_stored_histories()
- self.check_page_for_string( 'Unnamed history from test(a)bx.psu.edu' )
+ self.login( email=email )
+ self.home()
+ check_str = 'Unnamed history from test(a)bx.psu.edu'
+ self.view_stored_histories( check_str=check_str )
histories = self.get_histories()
for history in histories:
if history.get( 'name' ) == 'Unnamed history from test(a)bx.psu.edu':
@@ -63,6 +69,47 @@
self.delete_history( id )
self.logout()
self.login( email='test(a)bx.psu.edu' )
+ def test_30_history_show_and_hide_deleted_datasets( self ):
+ """Testing displaying deleted history items"""
+ self.new_history()
+ self.upload_file('1.bed', dbkey='hg18')
+ latest_hda = galaxy.model.HistoryDatasetAssociation.query() \
+ .order_by( desc( galaxy.model.HistoryDatasetAssociation.table.c.create_time ) ).first()
+ self.home()
+ self.visit_url( "%s/root/delete?show_deleted_on_refresh=False&id=%s" % ( self.url, str( latest_hda.id ) ) )
+ self.check_history_for_string( 'Your history is empty' )
+ self.home()
+ self.visit_url( "%s/history/?show_deleted=True" % self.url )
+ self.check_page_for_string( 'This dataset has been deleted.' )
+ self.check_page_for_string( '1.bed' )
+ self.home()
+ self.visit_url( "%s/history/?show_deleted=False" % self.url )
+ self.check_page_for_string( 'Your history is empty' )
+ def test_35_deleting_and_undeleting_history_items( self ):
+ """Testing deleting and un-deleting history items"""
+ self.new_history()
+ # Add a new history item
+ self.upload_file( '1.bed', dbkey='hg15' )
+ self.home()
+ self.visit_url( "%s/history/?show_deleted=False" % self.url )
+ self.check_page_for_string( '1.bed' )
+ self.check_page_for_string( 'hg15' )
+ self.assertEqual ( len( self.get_history() ), 1 )
+ # Delete the history item
+ self.delete_history_item( 1, check_str="Your history is empty" )
+ self.assertEqual ( len( self.get_history() ), 0 )
+ # Try deleting an invalid hid
+ try:
+ self.delete_history_item( 'XXX' )
+ raise AssertionError, "Inproperly able to delete hid 'XXX' which is not an integer"
+ except:
+ pass
+ # Undelete the history item
+ self.undelete_history_item( 1, show_deleted=True )
+ self.home()
+ self.visit_url( "%s/history/?show_deleted=False" % self.url )
+ self.check_page_for_string( '1.bed' )
+ self.check_page_for_string( 'hg15' )
def test_9999_clean_up( self ):
self.delete_history()
self.logout()
1
0
01 Jun '09
details: http://www.bx.psu.edu/hg/galaxy/rev/0fc19c283f78
changeset: 2424:0fc19c283f78
user: guru
date: Fri May 29 11:07:18 2009 -0400
description:
Bug fixes for histogram liftover and grouping tools.
6 file(s) affected in this change:
tools/extract/liftOver_wrapper.py
tools/extract/liftOver_wrapper.xml
tools/plotting/histogram.py
tools/plotting/histogram2.xml
tools/stats/grouping.py
tools/stats/grouping.xml
diffs (102 lines):
diff -r 7a6f75bf4be5 -r 0fc19c283f78 tools/extract/liftOver_wrapper.py
--- a/tools/extract/liftOver_wrapper.py Thu May 28 15:18:49 2009 -0400
+++ b/tools/extract/liftOver_wrapper.py Fri May 29 11:07:18 2009 -0400
@@ -29,7 +29,7 @@
if in_dbkey == "?":
stop_err( "Input dataset genome build unspecified, click the pencil icon in the history item to specify it." )
-cmd_line = "liftOver -minMatch=" + minMatch + " " + infile + " " + mapfilepath + " " + outfile1 + " " + outfile2 + " > /dev/null 2>&1"
+cmd_line = "liftOver -minMatch=" + str(minMatch) + " " + infile + " " + mapfilepath + " " + outfile1 + " " + outfile2 + " > /dev/null 2>&1"
if not os.path.isfile( mapfilepath ):
stop_err( "%s mapping is not currently available." % ( mapfilepath.split('/')[-1].split('.')[0] ) )
diff -r 7a6f75bf4be5 -r 0fc19c283f78 tools/extract/liftOver_wrapper.xml
--- a/tools/extract/liftOver_wrapper.xml Thu May 28 15:18:49 2009 -0400
+++ b/tools/extract/liftOver_wrapper.xml Fri May 29 11:07:18 2009 -0400
@@ -1,4 +1,4 @@
-<tool id="liftOver1" name="Convert genome coordinates">
+<tool id="liftOver1" name="Convert genome coordinates" version="1.0.1">
<description> between assemblies and genomes</description>
<command interpreter="python">liftOver_wrapper.py $input "$out_file1" "$out_file2" $dbkey $to_dbkey $minMatch</command>
<inputs>
diff -r 7a6f75bf4be5 -r 0fc19c283f78 tools/plotting/histogram.py
--- a/tools/plotting/histogram.py Thu May 28 15:18:49 2009 -0400
+++ b/tools/plotting/histogram.py Fri May 29 11:07:18 2009 -0400
@@ -32,7 +32,7 @@
skipped_lines = 0
first_invalid_line = 0
invalid_value = ''
-
+ i = 0
for i, line in enumerate( file( in_fname ) ):
valid = True
line = line.rstrip('\r\n')
@@ -79,7 +79,10 @@
except Exception, exc:
stop_err( "%s" %str( exc ) )
else:
- stop_err( "All values in column %s are non-numeric." %sys.argv[3] )
+ if i == 0:
+ stop_err("Input dataset is empty.")
+ else:
+ stop_err( "All values in column %s are non-numeric." %sys.argv[3] )
print "Histogram of column %s. " %sys.argv[3]
if skipped_lines > 0:
diff -r 7a6f75bf4be5 -r 0fc19c283f78 tools/plotting/histogram2.xml
--- a/tools/plotting/histogram2.xml Thu May 28 15:18:49 2009 -0400
+++ b/tools/plotting/histogram2.xml Fri May 29 11:07:18 2009 -0400
@@ -1,4 +1,4 @@
-<tool id="histogram_rpy" name="Histogram">
+<tool id="histogram_rpy" name="Histogram" version="1.0.1">
<description>of a numeric column</description>
<command interpreter="python">histogram.py $input $out_file1 $numerical_column "$title" "$xlab" $breaks $density</command>
<inputs>
diff -r 7a6f75bf4be5 -r 0fc19c283f78 tools/stats/grouping.py
--- a/tools/stats/grouping.py Thu May 28 15:18:49 2009 -0400
+++ b/tools/stats/grouping.py Fri May 29 11:07:18 2009 -0400
@@ -149,9 +149,15 @@
rout = eval( rfunc )( rout )
if op in ['c', 'cuniq']:
if op == 'c':
- out_str += "\t" + ','.join(rout)
+ if type(rout) == type([]):
+ out_str += "\t" + ','.join(rout)
+ else:
+ out_str += "\t" + str(rout)
else:
- out_str += "\t" + ','.join(list(set(rout)))
+ if type(rout) == type([]):
+ out_str += "\t" + ','.join(list(set(rout)))
+ else:
+ out_str += "\t" + str(rout)
else:
out_str += "\t" + str(rout)
@@ -211,9 +217,15 @@
rout = eval( rfunc )( rout )
if op in ['c','cuniq']:
if op == 'c':
- out_str += "\t" + ','.join(rout)
+ if type(rout) == type([]):
+ out_str += "\t" + ','.join(rout)
+ else:
+ out_str += "\t" + str(rout)
else:
- out_str += "\t" + ','.join(list(set(rout)))
+ if type(rout) == type([]):
+ out_str += "\t" + ','.join(list(set(rout)))
+ else:
+ out_str += "\t" + str(rout)
else:
out_str += "\t" + str( rout )
except:
diff -r 7a6f75bf4be5 -r 0fc19c283f78 tools/stats/grouping.xml
--- a/tools/stats/grouping.xml Thu May 28 15:18:49 2009 -0400
+++ b/tools/stats/grouping.xml Fri May 29 11:07:18 2009 -0400
@@ -1,4 +1,4 @@
-<tool id="Grouping1" name="Group" version="1.6.0">
+<tool id="Grouping1" name="Group" version="1.7.0">
<description>data by a column and perform aggregate operation on other columns.</description>
<command interpreter="python">
grouping.py
1
0
01 Jun '09
details: http://www.bx.psu.edu/hg/galaxy/rev/c8a4a4ea0f2c
changeset: 2425:c8a4a4ea0f2c
user: Nate Coraor <nate(a)bx.psu.edu>
date: Fri May 29 13:19:42 2009 -0400
description:
Allow the configuration of a job walltime (currently only supported in PBS)
4 file(s) affected in this change:
lib/galaxy/config.py
lib/galaxy/jobs/runners/pbs.py
lib/galaxy/util/bunch.py
universe_wsgi.ini.sample
diffs (223 lines):
diff -r 0fc19c283f78 -r c8a4a4ea0f2c lib/galaxy/config.py
--- a/lib/galaxy/config.py Fri May 29 11:07:18 2009 -0400
+++ b/lib/galaxy/config.py Fri May 29 13:19:42 2009 -0400
@@ -55,6 +55,7 @@
self.job_working_directory = resolve_path( kwargs.get( "job_working_directory", "database/job_working_directory" ), self.root )
self.outputs_to_working_directory = string_as_bool( kwargs.get( 'outputs_to_working_directory', False ) )
self.output_size_limit = int( kwargs.get( 'output_size_limit', 0 ) )
+ self.job_walltime = kwargs.get( 'job_walltime', None )
self.admin_users = kwargs.get( "admin_users", "" )
self.sendmail_path = kwargs.get('sendmail_path',"/usr/sbin/sendmail")
self.mailing_join_addr = kwargs.get('mailing_join_addr',"galaxy-user-join(a)bx.psu.edu")
@@ -123,7 +124,7 @@
"""
admin_users = self.get( "admin_users", "" ).split( "," )
return ( user is not None and user.email in admin_users )
-
+
def get_database_engine_options( kwargs ):
"""
Allow options for the SQLAlchemy database engine to be passed by using
diff -r 0fc19c283f78 -r c8a4a4ea0f2c lib/galaxy/jobs/runners/pbs.py
--- a/lib/galaxy/jobs/runners/pbs.py Fri May 29 11:07:18 2009 -0400
+++ b/lib/galaxy/jobs/runners/pbs.py Fri May 29 13:19:42 2009 -0400
@@ -1,8 +1,10 @@
import os, logging, threading, time
+from datetime import timedelta
from Queue import Queue, Empty
from galaxy import model
from galaxy.datatypes.data import nice_size
+from galaxy.util.bunch import Bunch
from paste.deploy.converters import asbool
@@ -88,6 +90,10 @@
# set the default server during startup
self.default_pbs_server = None
self.determine_pbs_server( 'pbs:///' )
+ self.job_walltime = None
+ if self.app.config.job_walltime is not None:
+ h, m, s = [ int( v ) for v in self.app.config.job_walltime.split( ':' ) ]
+ self.job_walltime = timedelta( 0, s, 0, 0, m, h )
self.monitor_thread = threading.Thread( target=self.monitor )
self.monitor_thread.start()
self.work_queue = Queue()
@@ -138,8 +144,8 @@
self.queue_job( obj )
elif op == 'finish':
self.finish_job( obj )
- elif op == 'fail_oversize_job':
- self.fail_oversize_job( obj )
+ elif op == 'fail':
+ self.fail_job( obj )
except:
log.exception( "Uncaught exception %sing job" % op )
@@ -297,7 +303,7 @@
"""
new_watched = []
# reduce pbs load by batching status queries
- ( failures, states ) = self.check_all_jobs()
+ ( failures, statuses ) = self.check_all_jobs()
for pbs_job_state in self.watched:
job_id = pbs_job_state.job_id
galaxy_job_id = pbs_job_state.job_wrapper.job_id
@@ -307,28 +313,43 @@
log.debug( "(%s/%s) Skipping state check because PBS server connection failed" % ( galaxy_job_id, job_id ) )
new_watched.append( pbs_job_state )
continue
- if states.has_key( job_id ):
- state = states[job_id]
- if state != old_state:
- log.debug("(%s/%s) job state changed from %s to %s" % ( galaxy_job_id, job_id, old_state, state ) )
- if state == "R" and not pbs_job_state.running:
+ if statuses.has_key( job_id ):
+ status = statuses[job_id]
+ if status.job_state != old_state:
+ log.debug("(%s/%s) job state changed from %s to %s" % ( galaxy_job_id, job_id, old_state, status.job_state ) )
+ if status.job_state == "R" and not pbs_job_state.running:
pbs_job_state.running = True
pbs_job_state.job_wrapper.change_state( model.Job.states.RUNNING )
- if self.app.config.output_size_limit > 0 and state == "R" and (pbs_job_state.check_count % 10) == 0:
- # Every 10th time a job is checked, check the size of its outputs.
- fail = False
- for outfile, size in pbs_job_state.job_wrapper.check_output_sizes():
- if size > self.app.config.output_size_limit:
- pbs_job_state.fail_message = 'Job output grew too large (greater than %s), please try different job parameters or' \
- % nice_size( self.app.config.output_size_limit )
- log.warning( '(%s/%s) Dequeueing job due to output %s growing larger than %s limit' \
- % ( galaxy_job_id, job_id, os.path.basename( outfile ), nice_size( self.app.config.output_size_limit ) ) )
- self.work_queue.put( ( 'fail_oversize_job', pbs_job_state ) )
- fail = True
- break
- if fail:
- continue
- pbs_job_state.old_state = state
+ if status.job_state == "R" and ( pbs_job_state.check_count % 20 ) == 0:
+ # Every 20th time the job status is checked, do limit checks (if configured)
+ if self.app.config.output_size_limit > 0:
+ # Check the size of the job outputs
+ fail = False
+ for outfile, size in pbs_job_state.job_wrapper.check_output_sizes():
+ if size > self.app.config.output_size_limit:
+ pbs_job_state.fail_message = 'Job output grew too large (greater than %s), please try different job parameters or' \
+ % nice_size( self.app.config.output_size_limit )
+ log.warning( '(%s/%s) Dequeueing job due to output %s growing larger than %s limit' \
+ % ( galaxy_job_id, job_id, os.path.basename( outfile ), nice_size( self.app.config.output_size_limit ) ) )
+ self.work_queue.put( ( 'fail', pbs_job_state ) )
+ fail = True
+ break
+ if fail:
+ continue
+ if self.job_walltime is not None:
+ # Check the job's execution time
+ if status.get( 'resources_used', False ):
+ # resources_used may not be in the status for new jobs
+ h, m, s = [ int( i ) for i in status.resources_used.walltime.split( ':' ) ]
+ time_executing = timedelta( 0, s, 0, 0, m, h )
+ if time_executing > self.job_walltime:
+ pbs_job_state.fail_message = 'Job ran longer than maximum allowed execution time (%s), please try different job parameters or' \
+ % self.app.config.job_walltime
+ log.warning( '(%s/%s) Dequeueing job since walltime has been reached' \
+ % ( galaxy_job_id, job_id ) )
+ self.work_queue.put( ( 'fail', pbs_job_state ) )
+ continue
+ pbs_job_state.old_state = status.job_state
new_watched.append( pbs_job_state )
else:
try:
@@ -350,11 +371,12 @@
def check_all_jobs( self ):
"""
Returns a list of servers that failed to be contacted and a dict
- of "job_id : state" pairs.
+ of "job_id : status" pairs (where status is a bunchified version
+ of the API's structure.
"""
servers = []
failures = []
- states = {}
+ statuses = {}
for pbs_job_state in self.watched:
pbs_server_name = self.determine_pbs_server( pbs_job_state.runner_url )
if pbs_server_name not in servers:
@@ -366,13 +388,27 @@
log.debug("connection to PBS server %s for state check failed" % pbs_server_name )
failures.append( pbs_server_name )
continue
- stat_attrl = pbs.new_attrl(1)
- stat_attrl[0].name = 'job_state'
+ stat_attrl = pbs.new_attrl(2)
+ stat_attrl[0].name = pbs.ATTR_state
+ stat_attrl[1].name = pbs.ATTR_used
jobs = pbs.pbs_statjob( c, None, stat_attrl, None )
pbs.pbs_disconnect( c )
- for job in jobs:
- states[job.name] = job.attribs[0].value
- return( ( failures, states ) )
+ statuses.update( self.convert_statjob_to_bunches( jobs ) )
+ return( ( failures, statuses ) )
+
+ def convert_statjob_to_bunches( self, statjob_out ):
+ statuses = {}
+ for job in statjob_out:
+ status = {}
+ for attrib in job.attribs:
+ if attrib.resource is None:
+ status[ attrib.name ] = attrib.value
+ else:
+ if attrib.name not in status:
+ status[ attrib.name ] = Bunch()
+ status[ attrib.name ][ attrib.resource ] = attrib.value
+ statuses[ job.name ] = Bunch( **status )
+ return statuses
def check_single_job( self, pbs_server_name, job_id ):
"""
@@ -384,7 +420,7 @@
log.debug("connection to PBS server %s for state check failed" % pbs_server_name )
return None
stat_attrl = pbs.new_attrl(1)
- stat_attrl[0].name = 'job_state'
+ stat_attrl[0].name = pbs.ATTR_state
jobs = pbs.pbs_statjob( c, job_id, stat_attrl, None )
pbs.pbs_disconnect( c )
return jobs[0].attribs[0].value
@@ -417,7 +453,7 @@
# clean up the pbs files
self.cleanup( ( ofile, efile, job_file ) )
- def fail_oversize_job( self, pbs_job_state ):
+ def fail_job( self, pbs_job_state ):
"""
Seperated out so we can use the worker threads for it.
"""
diff -r 0fc19c283f78 -r c8a4a4ea0f2c lib/galaxy/util/bunch.py
--- a/lib/galaxy/util/bunch.py Fri May 29 11:07:18 2009 -0400
+++ b/lib/galaxy/util/bunch.py Fri May 29 13:19:42 2009 -0400
@@ -21,4 +21,7 @@
return '%s' % self.__dict__
def __nonzero__(self):
- return bool(self.__dict__)
\ No newline at end of file
+ return bool(self.__dict__)
+
+ def __setitem__(self, k, v):
+ self.__dict__.__setitem__(k, v)
diff -r 0fc19c283f78 -r c8a4a4ea0f2c universe_wsgi.ini.sample
--- a/universe_wsgi.ini.sample Fri May 29 11:07:18 2009 -0400
+++ b/universe_wsgi.ini.sample Fri May 29 13:19:42 2009 -0400
@@ -171,6 +171,11 @@
# Job queue cleanup interval in minutes. Currently only used by RoundRobin
job_queue_cleanup_interval = 30
+# Jobs can be killed after a certain amount of execution time. Format is in
+# hh:mm:ss. Currently only implemented for PBS. Leave commented for
+# unlimited.
+#job_walltime = 10:00:00
+
# Clustering Galaxy is not a straightforward process and requires a lot of
# pre-configuration. See the ClusteringGalaxy Wiki before attempting to set
# any of these options:
1
0
29 May '09
details: http://www.bx.psu.edu/hg/galaxy/rev/186fcb977e75
changeset: 2421:186fcb977e75
user: Greg Von Kuster <greg(a)bx.psu.edu>
date: Thu May 28 11:10:44 2009 -0400
description:
Add a defaulttimeout setting of 10 minutes to the urlopen() call for requests to remote data sources.
1 file(s) affected in this change:
tools/data_source/data_source.py
diffs (43 lines):
diff -r 5c2d30d3fb04 -r 186fcb977e75 tools/data_source/data_source.py
--- a/tools/data_source/data_source.py Wed May 27 14:19:59 2009 -0400
+++ b/tools/data_source/data_source.py Thu May 28 11:10:44 2009 -0400
@@ -1,8 +1,8 @@
#!/usr/bin/env python
-#Retreives data from UCSC and stores in a file. UCSC parameters are provided in the input/output file.
-import urllib, sys, os, gzip, tempfile, shutil
+# Retrieves data from external data source applications and stores in a dataset file.
+# Data source application parameters are temporarily stored in the dataset file.
+import socket, urllib, sys, os, gzip, tempfile, shutil
from galaxy import eggs
-#from galaxy.datatypes import data
from galaxy.util import gzip_magic
assert sys.version_info[:2] >= ( 2, 4 )
@@ -34,15 +34,23 @@
open( filename, 'w' ).write( "" )
stop_err( 'The remote data source application has not sent back a URL parameter in the request.' )
URL_method = params.get( 'URL_method', None )
- out = open( filename, 'w' )
- CHUNK_SIZE = 2**20 # 1Mb
+ CHUNK_SIZE = 2**20 # 1Mb
+ # The Python support for fetching resources from the web is layered. urllib uses the httplib
+ # library, which in turn uses the socket library. As of Python 2.3 you can specify how long
+ # a socket should wait for a response before timing out. By default the socket module has no
+ # timeout and can hang. Currently, the socket timeout is not exposed at the httplib or urllib2
+ # levels. However, you can set the default timeout ( in seconds ) globally for all sockets by
+ # doing the following.
+ socket.setdefaulttimeout( 600 )
+ # The following calls to urllib2.urlopen() will use the above default timeout
try:
if not URL_method or URL_method == 'get':
page = urllib.urlopen( URL )
elif URL_method == 'post':
page = urllib.urlopen( URL, urllib.urlencode( params ) )
- except:
- stop_err( 'It appears that the remote data source application is currently off line. Please try again later.' )
+ except Exception, e:
+ stop_err( 'The remote data source application may be off line, please try again later. Error: %s' % str( e ) )
+ out = open( filename, 'w' )
while 1:
chunk = page.read( CHUNK_SIZE )
if not chunk:
1
0
29 May '09
details: http://www.bx.psu.edu/hg/galaxy/rev/a07e84e73b45
changeset: 2422:a07e84e73b45
user: Dan Blankenberg <dan(a)bx.psu.edu>
date: Thu May 28 13:58:56 2009 -0400
description:
Fix for DrillDownParameter when no option is selected and saved in worflow editing.
1 file(s) affected in this change:
lib/galaxy/tools/parameters/basic.py
diffs (15 lines):
diff -r 186fcb977e75 -r a07e84e73b45 lib/galaxy/tools/parameters/basic.py
--- a/lib/galaxy/tools/parameters/basic.py Thu May 28 11:10:44 2009 -0400
+++ b/lib/galaxy/tools/parameters/basic.py Thu May 28 13:58:56 2009 -0400
@@ -947,7 +947,10 @@
def from_html( self, value, trans=None, other_values={} ):
if self.need_late_validation( trans, other_values ):
if self.multiple:
- value = value.split( "\n" )
+ if value == '': #No option selected
+ value = None
+ else:
+ value = value.split( "\n" )
return UnvalidatedValue( value )
if not value: return None
if not isinstance( value, list ):
1
0
details: http://www.bx.psu.edu/hg/galaxy/rev/7a6f75bf4be5
changeset: 2423:7a6f75bf4be5
user: Nate Coraor <nate(a)bx.psu.edu>
date: Thu May 28 15:18:49 2009 -0400
description:
Fix the cleanup_datasets script
1 file(s) affected in this change:
scripts/cleanup_datasets/cleanup_datasets.py
diffs (17 lines):
diff -r a07e84e73b45 -r 7a6f75bf4be5 scripts/cleanup_datasets/cleanup_datasets.py
--- a/scripts/cleanup_datasets/cleanup_datasets.py Thu May 28 13:58:56 2009 -0400
+++ b/scripts/cleanup_datasets/cleanup_datasets.py Thu May 28 15:18:49 2009 -0400
@@ -264,8 +264,13 @@
if dataset.extra_files_path and os.path.exists( dataset.extra_files_path ):
shutil.rmtree( dataset.extra_files_path ) #we need to delete the directory and its contents; os.unlink would always fail on a directory
dataset.purged = True
+ dataset.flush()
else:
print "# This dataset (%i) is not purgable, the file (%s) will not be removed.\n" % ( dataset.id, dataset.file_name )
+ except OSError, exc:
+ print "# Error, file has already been removed: %s" % str( exc )
+ dataset.purged = True
+ dataset.flush()
except Exception, exc:
print "# Error, exception: %s caught attempting to purge %s\n" %( str( exc ), dataset.file_name )
else:
1
0
27 May '09
details: http://www.bx.psu.edu/hg/galaxy/rev/5c2d30d3fb04
changeset: 2420:5c2d30d3fb04
user: Greg Von Kuster <greg(a)bx.psu.edu>
date: Wed May 27 14:19:59 2009 -0400
description:
Add tool config for GrameneMart data source.
3 file(s) affected in this change:
tool_conf.xml.main
tool_conf.xml.sample
tools/data_source/gramene_mart.xml
diffs (67 lines):
diff -r 1acbcbcebeb3 -r 5c2d30d3fb04 tool_conf.xml.main
--- a/tool_conf.xml.main Wed May 27 11:15:35 2009 -0400
+++ b/tool_conf.xml.main Wed May 27 14:19:59 2009 -0400
@@ -6,6 +6,7 @@
<tool file="data_source/ucsc_tablebrowser_archaea.xml" />
<tool file="data_source/microbial_import.xml" />
<tool file="data_source/biomart.xml" />
+ <tool file="data_source/gramene_mart.xml" />
<tool file="data_source/flymine.xml" />
<tool file="data_source/encode_db.xml" />
<tool file="data_source/epigraph_import.xml" />
diff -r 1acbcbcebeb3 -r 5c2d30d3fb04 tool_conf.xml.sample
--- a/tool_conf.xml.sample Wed May 27 11:15:35 2009 -0400
+++ b/tool_conf.xml.sample Wed May 27 14:19:59 2009 -0400
@@ -8,6 +8,7 @@
<tool file="data_source/microbial_import.xml" />
<tool file="data_source/biomart.xml" />
<tool file="data_source/biomart_test.xml" />
+ <tool file="data_source/gramene_mart.xml" />
<tool file="data_source/wormbase.xml" />
<tool file="data_source/wormbase_test.xml" />
<tool file="data_source/flymine.xml" />
diff -r 1acbcbcebeb3 -r 5c2d30d3fb04 tools/data_source/gramene_mart.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/data_source/gramene_mart.xml Wed May 27 14:19:59 2009 -0400
@@ -0,0 +1,41 @@
+<?xml version="1.0"?>
+<!--
+ If the value of 'URL_method' is 'get', the request will consist of the value of 'URL' coming back in
+ the initial response. If value of 'URL_method' is 'post', any additional params coming back in the
+ initial response ( in addition to 'URL' ) will be encoded and appended to URL and a post will be performed.
+
+ TODO: Hack to get biomart to work - the 'add_to_URL' param can be eliminated when the Biomart team encodes URL prior to sending, meanwhile
+ everything including and beyond the first '&' is truncated from URL. They said they'll let us know when this is fixed at their end.
+-->
+<tool name="GrameneMart" id="gramenemart" tool_type="data_source" URL_method="get" version="1.0.1">
+ <description> Central server</description>
+ <command interpreter="python">data_source.py $output</command>
+ <inputs action="http://www.gramene.org/biomart/martview" check_values="false" method="get" target="_top">
+ <display>go to GrameneMart Central $GALAXY_URL</display>
+ <param name="GALAXY_URL" type="baseurl" value="/tool_runner/biomart" />
+ </inputs>
+ <request_param_translation>
+ <request_param galaxy_name="URL" remote_name="URL" missing="">
+ <add_to_url>
+ <param_from_source name="_export" missing="1" />
+ <param_from_source name="GALAXY_URL" missing="0" />
+ </add_to_url>
+ </request_param>
+ <request_param galaxy_name="data_type" remote_name="exportView_outputformat" missing="tabular">
+ <data_type_translation>
+ <format galaxy_format="tabular" remote_format="TSV" />
+ </data_type_translation>
+ </request_param>
+ <request_param galaxy_name="dbkey" remote_name="dbkey" missing="?" />
+ <request_param galaxy_name="organism" remote_name="organism" missing="" />
+ <request_param galaxy_name="table" remote_name="table" missing="" />
+ <request_param galaxy_name="description" remote_name="description" missing="" />
+ <request_param galaxy_name="name" remote_name="name" missing="Biomart query" />
+ <request_param galaxy_name="info" remote_name="info" missing="" />
+ </request_param_translation>
+ <uihints minwidth="800"/>
+ <outputs>
+ <data name="output" format="tabular" />
+ </outputs>
+ <options sanitize="False" refresh="True"/>
+</tool>
1
0