Enhance history cloning to allow for cloning active or activatable datasets from the original history - resolves ticket # 29.

This commit is contained in:
Greg Von Kuster
2009-07-08 14:10:45 -04:00
parent b2bdb4c50f
commit 9133a4bfc3
6 changed files with 137 additions and 56 deletions
+9 -5
View File
@@ -214,17 +214,21 @@ class History( object ):
if genome_build not in [None, '?']:
self.genome_build = genome_build
self.datasets.append( dataset )
def copy( self, name=None, target_user=None ):
def copy( self, name=None, target_user=None, activatable=False ):
if not name:
name = self.name
if not target_user:
target_user = self.user
new_history = History( name=name, user=target_user )
new_history.flush()
for data in self.datasets:
new_data = data.copy( copy_children=True, target_history=new_history )
new_history.add_dataset( new_data, set_hid = False )
new_data.flush()
if activatable:
hdas = self.activatable_datasets
else:
hdas = self.active_datasets
for hda in hdas:
new_hda = hda.copy( copy_children=True, target_history=new_history )
new_history.add_dataset( new_hda, set_hid = False )
new_hda.flush()
new_history.hid_counter = self.hid_counter
new_history.flush()
return new_history
+15 -8
View File
@@ -310,8 +310,8 @@ class HistoryController( BaseController ):
email=email,
send_to_err=send_to_err )
if params.get( 'share_button', False ):
can_change, cannot_change, no_change_needed = \
self._populate_restricted( trans, user, histories, send_to_users, None )
can_change, cannot_change, no_change_needed, send_to_err = \
self._populate_restricted( trans, user, histories, send_to_users, None, send_to_err )
if can_change or cannot_change:
return trans.fill_template( "/history/share.mako",
histories=histories,
@@ -335,8 +335,8 @@ class HistoryController( BaseController ):
user = trans.get_user()
histories, send_to_users, send_to_err = self._get_histories_and_users( trans, user, id, email )
send_to_err = ''
can_change, cannot_change, no_change_needed = \
self._populate_restricted( trans, user, histories, send_to_users, action )
can_change, cannot_change, no_change_needed, send_to_err = \
self._populate_restricted( trans, user, histories, send_to_users, action, send_to_err )
# Now that we've populated the can_change, cannot_change, and no_change_needed dictionaries,
# we'll populate the histories_for_sharing dictionary from each of them.
histories_for_sharing = {}
@@ -430,7 +430,7 @@ class HistoryController( BaseController ):
elif history not in histories_for_sharing[ send_to_user ]:
histories_for_sharing[ send_to_user ].append( history )
return histories_for_sharing, send_to_err
def _populate_restricted( self, trans, user, histories, send_to_users, action ):
def _populate_restricted( self, trans, user, histories, send_to_users, action, send_to_err ):
# The user may be attempting to share histories whose datasets cannot all be accessed by other users.
# If this is the case, the user sharing the histories can:
# 1) action=='public': choose to make the datasets public if he is permitted to do so
@@ -494,7 +494,7 @@ class HistoryController( BaseController ):
cannot_change[ send_to_user ][ history ] = [ hda ]
else:
cannot_change[ send_to_user ][ history ].append( hda )
return can_change, cannot_change, no_change_needed
return can_change, cannot_change, no_change_needed, send_to_err
def _share_histories( self, trans, user, send_to_err, histories={} ):
# histories looks like: { userA: [ historyX, historyY ], userB: [ historyY ] }
msg = ""
@@ -585,8 +585,12 @@ class HistoryController( BaseController ):
return trans.show_message( "<p>%s" % change_msg, refresh_frames=['history'] )
@web.expose
@web.require_login( "clone shared Galaxy history" )
def clone( self, trans, id ):
def clone( self, trans, id, **kwd ):
history = get_history( trans, id, check_ownership=False )
params = util.Params( kwd )
clone_choice = params.get( 'clone_choice', None )
if not clone_choice:
return trans.fill_template( "/history/clone.mako", history=history )
user = trans.get_user()
if history.user == user:
owner = True
@@ -598,7 +602,10 @@ class HistoryController( BaseController ):
name = "Clone of '%s'" % history.name
if not owner:
name += " shared by '%s'" % history.user.email
new_history = history.copy( name=name, target_user=user )
if clone_choice == 'activatable':
new_history = history.copy( name=name, target_user=user, activatable=True )
elif clone_choice == 'active':
new_history = history.copy( name=name, target_user=user )
# Render the list view
return trans.show_ok_message( 'Clone with name "%s" is now included in your list of stored histories.' % new_history.name )
+25
View File
@@ -0,0 +1,25 @@
<% _=n_ %>
<%inherit file="/base.mako"/>
<%def name="title()">Clone History</%def>
<div class="toolForm">
<div class="toolFormTitle">Clone History</div>
<div class="toolFormBody">
<form action="${h.url_for( controller='history', action='clone' )}" method="post" >
<div class="form-row">
<input type="hidden" name="id" value="${trans.security.encode_id( history.id )}">
You can clone the history such that the clone will include all items in the original
history, or you can eliminate the original history's deleted items from the clone.
</div>
<div class="form-row">
<input type="radio" name="clone_choice" value="activatable"> Clone all history items, including deleted items
</div>
<div class="form-row">
<input type="radio" name="clone_choice" value="active"> Clone only items that are not deleted
</div>
<div class="form-row">
<input type="submit" name="clone_choice_button" value="Clone">
</div>
</form>
</div>
</div>
+1 -1
View File
@@ -18,7 +18,7 @@
${history.name}
<a id="shared-${i}-popup" class="popup-arrow" style="display: none;">&#9660;</a>
<div popupmenu="shared-${i}-popup">
<a class="action-button" href="${h.url_for( controller='history', action='clone', id=trans.security.encode_id( history.id ) )}">Clone</a>
<a class="action-button" href="${h.url_for( controller='history', action='clone', id=trans.security.encode_id( history.id ), clone_choice='activatable' )}">Clone</a>
</div>
</td>
<td>${history.user.email}</td>
+18 -28
View File
@@ -121,10 +121,13 @@ class TwillTestCase( unittest.TestCase ):
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 ):
def check_history_for_string( self, patt, show_deleted=False ):
"""Looks for 'string' in history page"""
self.home()
self.visit_page( "history" )
if show_deleted:
self.visit_page( "history?show_deleted=True" )
else:
self.visit_page( "history" )
for subpatt in patt.split():
tc.find(subpatt)
self.home()
@@ -268,11 +271,6 @@ class TwillTestCase( unittest.TestCase ):
self.home()
def switch_history( self, id='', name='' ):
"""Switches to a history in the current list of histories"""
data_list = self.get_histories_as_data_list()
self.assertTrue( data_list )
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 )
@@ -305,11 +303,15 @@ class TwillTestCase( unittest.TestCase ):
if check_str2:
self.check_page_for_string( check_str2 )
self.home()
def clone_history( self, history_id, check_str1='' ):
def clone_history( self, history_id, clone_choice, check_str1='', check_str_after_submit='' ):
self.home()
self.visit_page( "history/clone?id=%s" % history_id )
if check_str1:
self.check_page_for_string( check_str1 )
tc.fv( '1', 'clone_choice', clone_choice )
tc.submit( 'clone_choice_button' )
if check_str_after_submit:
self.check_page_for_string( check_str_after_submit )
self.home()
def enable_import_via_link( self, history_id, check_str='', check_str_after_submit='' ):
self.home()
@@ -366,34 +368,22 @@ class TwillTestCase( unittest.TestCase ):
self.visit_page( "edit?hid=%d" % hid )
for subpatt in patt.split():
tc.find(subpatt)
def delete_history_item( self, hid, check_str='' ):
def delete_history_item( self, hda_id, check_str='' ):
"""Deletes an item from a history"""
try:
hid = int( hid )
hda_id = int( hda_id )
except:
raise AssertionError, "Invalid hid '%s' - must be int" % hid
hid = str(hid)
data_list = self.get_history_as_data_list()
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_page( "delete?id=%s" % elems[0].get( 'id' ) )
raise AssertionError, "Invalid hda_id '%s' - must be int" % hda_id
self.visit_url( "%s/root/delete?show_deleted_on_refresh=False&id=%s" % ( self.url, hda_id ) )
if check_str:
self.check_page_for_string( check_str )
def undelete_history_item( self, hid, show_deleted=False, check_str='' ):
def undelete_history_item( self, hda_id, check_str='' ):
"""Un-deletes a deleted item in a history"""
try:
hid = int( hid )
hda_id = int( hda_id )
except:
raise AssertionError, "Invalid hid '%s' - must be int" % hid
hid = str( hid )
data_list = self.get_history_as_data_list( 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' ) ) )
raise AssertionError, "Invalid hda_id '%s' - must be int" % hda_id
self.visit_url( "%s/dataset/undelete?id=%s" % ( self.url, hda_id ) )
if check_str:
self.check_page_for_string( check_str )
def display_history_item( self, id, check_str='' ):
+69 -14
View File
@@ -168,11 +168,6 @@ class TestHistory( TwillTestCase ):
# Test sharing history3 with an invalid user
self.share_current_history( 'jack@jill.com',
check_str_after_submit='jack@jill.com is not a valid Galaxy user.' )
def test_025_delete_shared_current_history( self ):
"""Testing deleting the current history after it was shared"""
# Logged in as admin_user
@@ -187,7 +182,8 @@ class TestHistory( TwillTestCase ):
# Shared history3 should be in regular_user1's list of shared histories
self.view_shared_histories( check_str=history3.name, check_str2=admin_user.email )
self.clone_history( self.security.encode_id( history3.id ),
check_str1='is now included in your list of stored histories.' )
'activatable',
check_str_after_submit='is now included in your list of stored histories.' )
global history3_clone1
history3_clone1 = galaxy.model.History.filter( and_( galaxy.model.History.table.c.deleted==False,
galaxy.model.History.table.c.user_id==regular_user1.id ) ) \
@@ -201,8 +197,26 @@ class TestHistory( TwillTestCase ):
# logged in as regular_user1
self.logout()
self.login( email=admin_user.email )
# Current history should be history3, add more datasets to history3, then delete them so we can
# test cloning activatable datasets as well as only the active datasets
self.upload_file( '2.bed', dbkey='hg18' )
hda_2_bed = galaxy.model.HistoryDatasetAssociation \
.filter( and_( galaxy.model.HistoryDatasetAssociation.table.c.history_id==history3.id,
galaxy.model.HistoryDatasetAssociation.table.c.name=='2.bed' ) ) \
.first()
assert hda_2_bed is not None, "Problem retrieving hda_2_bed from database"
self.delete_history_item( str( hda_2_bed.id ) )
self.upload_file( '3.bed', dbkey='hg18' )
hda_3_bed = galaxy.model.HistoryDatasetAssociation \
.filter( and_( galaxy.model.HistoryDatasetAssociation.table.c.history_id==history3.id,
galaxy.model.HistoryDatasetAssociation.table.c.name=='3.bed' ) ) \
.first()
assert hda_3_bed is not None, "Problem retrieving hda_3_bed from database"
self.delete_history_item( str( hda_3_bed.id ) )
# Test cloning activatable datasets
self.clone_history( self.security.encode_id( history3.id ),
check_str1='is now included in your list of stored histories.' )
'activatable',
check_str_after_submit='is now included in your list of stored histories.' )
global history3_clone2
history3_clone2 = galaxy.model.History.filter( and_( galaxy.model.History.table.c.deleted==False,
galaxy.model.History.table.c.user_id==admin_user.id ) ) \
@@ -210,6 +224,42 @@ class TestHistory( TwillTestCase ):
assert history3_clone2 is not None, "Problem retrieving history3_clone2 from database"
# Check list of histories to make sure shared history3 was cloned
self.view_stored_active_histories( check_str="Clone of '%s'" % history3.name )
# Switch to the cloned history to make sure activatable datasets were cloned
self.switch_history( id=self.security.encode_id( history3_clone2.id ), name=history3_clone2.name )
hda_2_bed = galaxy.model.HistoryDatasetAssociation \
.filter( and_( galaxy.model.HistoryDatasetAssociation.table.c.history_id==history3_clone2.id,
galaxy.model.HistoryDatasetAssociation.table.c.name=='2.bed' ) ) \
.first()
assert hda_2_bed is not None, "Problem retrieving hda_2_bed from database"
hda_3_bed = galaxy.model.HistoryDatasetAssociation \
.filter( and_( galaxy.model.HistoryDatasetAssociation.table.c.history_id==history3_clone2.id,
galaxy.model.HistoryDatasetAssociation.table.c.name=='3.bed' ) ) \
.first()
assert hda_3_bed is not None, "Problem retrieving hda_3_bed from database"
# Make sure the deleted datasets are included in the cloned history
check_str = 'This dataset has been deleted. Click undelete id=%d"' % hda_2_bed.id
self.check_history_for_string( check_str, show_deleted=True )
check_str = 'This dataset has been deleted. Click undelete id=%d"' % hda_3_bed.id
self.check_history_for_string( check_str, show_deleted=True )
# Test cloning only active datasets
self.clone_history( self.security.encode_id( history3.id ),
'active',
check_str_after_submit='is now included in your list of stored histories.' )
global history3_clone3
history3_clone3 = galaxy.model.History.filter( and_( galaxy.model.History.table.c.deleted==False,
galaxy.model.History.table.c.user_id==admin_user.id ) ) \
.order_by( desc( galaxy.model.History.table.c.create_time ) ).first()
assert history3_clone3 is not None, "Problem retrieving history3_clone3 from database"
# Check list of histories to make sure shared history3 was cloned
self.view_stored_active_histories( check_str="Clone of '%s'" % history3.name )
# Switch to the cloned history to make sure activatable datasets were cloned
self.switch_history( id=self.security.encode_id( history3_clone3.id ), name=history3_clone3.name )
# Make sure the deleted datasets are NOT included in the cloned history
try:
self.check_history_for_string( 'This dataset has been deleted.', show_deleted=True )
raise AssertionError, "Deleted datasets incorrectly included in cloned history history3_clone3"
except:
pass
def test_040_sharing_mulitple_histories_with_multiple_users( self ):
"""Testing sharing multiple histories containing only public datasets with multiple users"""
# Logged in as admin_user
@@ -304,7 +354,8 @@ class TestHistory( TwillTestCase ):
self.view_shared_histories( check_str=history5.name, check_str2=admin_user.email )
# Clone restricted history5
self.clone_history( self.security.encode_id( history5.id ),
check_str1='is now included in your list of stored histories.' )
'activatable',
check_str_after_submit='is now included in your list of stored histories.' )
global history5_clone1
history5_clone1 = galaxy.model.History.filter( and_( galaxy.model.History.table.c.deleted==False,
galaxy.model.History.table.c.user_id==regular_user1.id ) ) \
@@ -359,7 +410,8 @@ class TestHistory( TwillTestCase ):
self.view_shared_histories( check_str=history5.name, check_str2=admin_user.email )
# Clone restricted history5
self.clone_history( self.security.encode_id( history5.id ),
check_str1='is now included in your list of stored histories.' )
'activatable',
check_str_after_submit='is now included in your list of stored histories.' )
global history5_clone2
history5_clone2 = galaxy.model.History.filter( and_( galaxy.model.History.table.c.deleted==False,
galaxy.model.History.table.c.user_id==regular_user2.id ) ) \
@@ -431,7 +483,8 @@ class TestHistory( TwillTestCase ):
self.view_shared_histories( check_str=history5.name, check_str2=admin_user.email )
# Clone restricted history5
self.clone_history( self.security.encode_id( history5.id ),
check_str1='is now included in your list of stored histories.' )
'activatable',
check_str_after_submit='is now included in your list of stored histories.' )
global history5_clone3
history5_clone3 = galaxy.model.History.filter( and_( galaxy.model.History.table.c.deleted==False,
galaxy.model.History.table.c.user_id==regular_user2.id ) ) \
@@ -468,7 +521,8 @@ class TestHistory( TwillTestCase ):
self.view_shared_histories( check_str=history5.name, check_str2=admin_user.email )
# Clone restricted history5
self.clone_history( self.security.encode_id( history5.id ),
check_str1='is now included in your list of stored histories.' )
'activatable',
check_str_after_submit='is now included in your list of stored histories.' )
global history5_clone4
history5_clone4 = galaxy.model.History.filter( and_( galaxy.model.History.table.c.deleted==False,
galaxy.model.History.table.c.user_id==regular_user3.id ) ) \
@@ -587,16 +641,16 @@ class TestHistory( TwillTestCase ):
self.check_page_for_string( 'hg15' )
self.assertEqual ( len( self.get_history_as_data_list() ), 1 )
# Delete the history item
self.delete_history_item( latest_hda.hid, check_str="Your history is empty" )
self.delete_history_item( str( latest_hda.id ), check_str="Your history is empty" )
self.assertEqual ( len( self.get_history_as_data_list() ), 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"
raise AssertionError, "Inproperly able to delete hda_id 'XXX' which is not an integer"
except:
pass
# Undelete the history item
self.undelete_history_item( latest_hda.hid, show_deleted=True )
self.undelete_history_item( str( latest_hda.id ) )
self.home()
self.visit_url( "%s/history/?show_deleted=False" % self.url )
self.check_page_for_string( '1.bed' )
@@ -673,6 +727,7 @@ class TestHistory( TwillTestCase ):
# Delete histories
self.delete_history( id=self.security.encode_id( history3.id ) )
self.delete_history( id=self.security.encode_id( history3_clone2.id ) )
self.delete_history( id=self.security.encode_id( history3_clone3.id ) )
self.delete_history( id=self.security.encode_id( history4.id ) )
self.delete_history( id=self.security.encode_id( history5.id ) )
# Eliminate Sharing role for: test@bx.psu.edu, test2@bx.psu.edu