galaxy-dev
Threads by month
- ----- 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
- 10007 discussions
14 Aug '09
details: http://www.bx.psu.edu/hg/galaxy/rev/ad5232a4dbc5
changeset: 2557:ad5232a4dbc5
user: Dan Blankenberg <dan(a)bx.psu.edu>
date: Thu Aug 13 11:00:00 2009 -0400
description:
When setting metadata external is configured, using the 'Auto-detect' function on a hda will now launch an external job process.
13 file(s) affected in this change:
lib/galaxy/app.py
lib/galaxy/datatypes/registry.py
lib/galaxy/jobs/__init__.py
lib/galaxy/jobs/runners/local.py
lib/galaxy/model/__init__.py
lib/galaxy/tools/__init__.py
lib/galaxy/tools/actions/metadata.py
lib/galaxy/tools/actions/upload.py
lib/galaxy/web/controllers/root.py
static/june_2007_style/blue/history.css
static/june_2007_style/history.css.tmpl
templates/mobile/history/detail.mako
templates/root/history_common.mako
diffs (287 lines):
diff -r 846f6a7ee80c -r ad5232a4dbc5 lib/galaxy/app.py
--- a/lib/galaxy/app.py Wed Aug 12 15:56:03 2009 -0400
+++ b/lib/galaxy/app.py Thu Aug 13 11:00:00 2009 -0400
@@ -37,6 +37,8 @@
self.toolbox = tools.ToolBox( self.config.tool_config, self.config.tool_path, self )
# Load datatype converters
self.datatypes_registry.load_datatype_converters( self.toolbox )
+ #load external metadata tool
+ self.datatypes_registry.load_external_metadata_tool( self.toolbox )
# Load datatype indexers
self.datatypes_registry.load_datatype_indexers( self.toolbox )
#Load security policy
diff -r 846f6a7ee80c -r ad5232a4dbc5 lib/galaxy/datatypes/registry.py
--- a/lib/galaxy/datatypes/registry.py Wed Aug 12 15:56:03 2009 -0400
+++ b/lib/galaxy/datatypes/registry.py Thu Aug 13 11:00:00 2009 -0400
@@ -1,7 +1,7 @@
"""
Provides mapping between extensions and datatypes, mime-types, etc.
"""
-import os
+import os, tempfile
import logging
import data, tabular, interval, images, sequence, qualityscore, genetics, xml, coverage, tracks, chrominfo
import galaxy.util
@@ -18,6 +18,7 @@
self.datatype_converters = odict()
self.datatype_indexers = odict()
self.converters = []
+ self.set_external_metadata_tool = None
self.indexers = []
self.sniff_order = []
self.upload_file_formats = []
@@ -251,6 +252,31 @@
self.datatype_converters[source_datatype][target_datatype] = converter
self.log.debug( "Loaded converter: %s", converter.id )
+ def load_external_metadata_tool( self, toolbox ):
+ """Adds a tool which is used to set external metadata"""
+ #we need to be able to add a job to the queue to set metadata. The queue will currently only accept jobs with an associated tool.
+ #We'll create a special tool to be used for Auto-Detecting metadata; this is less than ideal, but effective
+ #Properly building a tool without relying on parsing an XML file is near impossible...so we'll create a temporary file
+ tool_xml_text = """
+ <tool id="__SET_METADATA__" name="Set External Metadata" version="1.0.0" tool_type="set_metadata">
+ <type class="SetMetadataTool" module="galaxy.tools"/>
+ <action module="galaxy.tools.actions.metadata" class="SetMetadataToolAction"/>
+ <command>$__SET_EXTERNAL_METADATA_COMMAND_LINE__</command>
+ <inputs>
+ <param format="data" name="input1" type="data" label="File to set metadata on."/>
+ <param name="__ORIGINAL_DATASET_STATE__" type="hidden" value=""/>
+ <param name="__SET_EXTERNAL_METADATA_COMMAND_LINE__" type="hidden" value=""/>
+ </inputs>
+ </tool>
+ """
+ tmp_name = tempfile.NamedTemporaryFile()
+ tmp_name.write( tool_xml_text )
+ tmp_name.flush()
+ set_meta_tool = toolbox.load_tool( tmp_name.name )
+ toolbox.tools_by_id[ set_meta_tool.id ] = set_meta_tool
+ self.set_external_metadata_tool = set_meta_tool
+ self.log.debug( "Loaded external metadata tool: %s", self.set_external_metadata_tool.id )
+
def load_datatype_indexers( self, toolbox ):
"""Adds indexers from self.indexers to the toolbox from app"""
for elem in self.indexers:
diff -r 846f6a7ee80c -r ad5232a4dbc5 lib/galaxy/jobs/__init__.py
--- a/lib/galaxy/jobs/__init__.py Wed Aug 12 15:56:03 2009 -0400
+++ b/lib/galaxy/jobs/__init__.py Thu Aug 13 11:00:00 2009 -0400
@@ -274,7 +274,7 @@
elif idata.state == idata.states.ERROR:
job_wrapper.fail( "input data %d is in error state" % ( idata.hid ) )
return JOB_INPUT_ERROR
- elif idata.state != idata.states.OK:
+ elif idata.state != idata.states.OK and not ( idata.state == idata.states.SETTING_METADATA and job.tool_id is not None and job.tool_id == self.app.datatypes_registry.set_external_metadata_tool.id ):
# need to requeue
return JOB_WAIT
return JOB_READY
@@ -543,7 +543,7 @@
# Certain tools require tasks to be completed after job execution
# ( this used to be performed in the "exec_after_process" hook, but hooks are deprecated ).
if self.tool.tool_type is not None:
- self.tool.exec_after_process( self.queue.app, inp_data, out_data, param_dict )
+ self.tool.exec_after_process( self.queue.app, inp_data, out_data, param_dict, job = job )
# Call 'exec_after_process' hook
self.tool.call_hook( 'exec_after_process', self.queue.app, inp_data=inp_data,
out_data=out_data, param_dict=param_dict,
diff -r 846f6a7ee80c -r ad5232a4dbc5 lib/galaxy/jobs/runners/local.py
--- a/lib/galaxy/jobs/runners/local.py Wed Aug 12 15:56:03 2009 -0400
+++ b/lib/galaxy/jobs/runners/local.py Thu Aug 13 11:00:00 2009 -0400
@@ -104,7 +104,7 @@
#run the metadata setting script here
#this is terminatable when output dataset/job is deleted
#so that long running set_meta()s can be cancelled without having to reboot the server
- if job_wrapper.get_state() not in [ model.Job.states.ERROR, model.Job.states.DELETED ] and self.app.config.set_metadata_externally:
+ if job_wrapper.get_state() not in [ model.Job.states.ERROR, model.Job.states.DELETED ] and self.app.config.set_metadata_externally and job_wrapper.output_paths:
external_metadata_script = job_wrapper.setup_external_metadata( output_fnames = job_wrapper.get_output_fnames(), kwds = { 'overwrite' : False } ) #we don't want to overwrite metadata that was copied over in init_meta(), as per established behavior
log.debug( 'executing external set_meta script for job %d: %s' % ( job_wrapper.job_id, external_metadata_script ) )
external_metadata_proc = subprocess.Popen( args = external_metadata_script,
diff -r 846f6a7ee80c -r ad5232a4dbc5 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py Wed Aug 12 15:56:03 2009 -0400
+++ b/lib/galaxy/model/__init__.py Thu Aug 13 11:00:00 2009 -0400
@@ -330,7 +330,8 @@
OK = 'ok',
EMPTY = 'empty',
ERROR = 'error',
- DISCARDED = 'discarded' )
+ DISCARDED = 'discarded',
+ SETTING_METADATA = 'setting_metadata' )
permitted_actions = get_permitted_actions( filter='DATASET' )
file_path = "/tmp/"
engine = None
diff -r 846f6a7ee80c -r ad5232a4dbc5 lib/galaxy/tools/__init__.py
--- a/lib/galaxy/tools/__init__.py Wed Aug 12 15:56:03 2009 -0400
+++ b/lib/galaxy/tools/__init__.py Thu Aug 13 11:00:00 2009 -0400
@@ -1468,7 +1468,7 @@
out_data[ name ] = data
return out_data
- def exec_after_process( self, app, inp_data, out_data, param_dict ):
+ def exec_after_process( self, app, inp_data, out_data, param_dict, job = None ):
if self.tool_type == 'data_source':
name, data = out_data.items()[0]
data.set_size()
@@ -1572,6 +1572,18 @@
dataset.history.add( new_data )
new_data.flush()
return primary_datasets
+
+class SetMetadataTool( Tool ):
+ def exec_after_process( self, app, inp_data, out_data, param_dict, job = None ):
+ for name, dataset in inp_data.iteritems():
+ external_metadata = galaxy.datatypes.metadata.JobExternalOutputMetadataWrapper( job )
+ if external_metadata.external_metadata_set_successfully( dataset ):
+ dataset.metadata.from_JSON_dict( external_metadata.get_output_filenames_by_dataset( dataset ).filename_out )
+ # If setting external metadata has failed, how can we inform the user?
+ # For now, we'll leave the default metadata and set the state back to its original.
+ dataset.datatype.after_edit( dataset )
+ dataset.state = param_dict.get( '__ORIGINAL_DATASET_STATE__' )
+ dataset.flush()
# ---- Utility classes to be factored out -----------------------------------
diff -r 846f6a7ee80c -r ad5232a4dbc5 lib/galaxy/tools/actions/metadata.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/galaxy/tools/actions/metadata.py Thu Aug 13 11:00:00 2009 -0400
@@ -0,0 +1,48 @@
+from . import ToolAction
+from galaxy.datatypes.metadata import JobExternalOutputMetadataWrapper
+
+import logging
+log = logging.getLogger( __name__ )
+
+class SetMetadataToolAction( ToolAction ):
+ """Tool action used for setting external metadata on an existing dataset"""
+
+ def execute( self, tool, trans, incoming = {}, set_output_hid = False ):
+ for name, value in incoming.iteritems():
+ if isinstance( value, trans.app.model.HistoryDatasetAssociation ):
+ dataset = value
+ dataset_name = name
+ break
+ else:
+ raise Exception( 'The dataset to set metadata on could not be determined.' )
+
+ # Create the job object
+ job = trans.app.model.Job()
+ job.session_id = trans.get_galaxy_session().id
+ job.history_id = trans.history.id
+ job.tool_id = tool.id
+ try:
+ # For backward compatibility, some tools may not have versions yet.
+ job.tool_version = tool.version
+ except:
+ job.tool_version = "1.0.0"
+ job.flush() #ensure job.id is available
+
+ #add parameters to job_parameter table
+ incoming[ '__ORIGINAL_DATASET_STATE__' ] = dataset.state #store original dataset state, so we can restore it. A seperate table might be better (no chance of 'loosing' the original state)?
+ external_metadata_wrapper = JobExternalOutputMetadataWrapper( job )
+ cmd_line = external_metadata_wrapper.setup_external_metadata( dataset, exec_dir = None, tmp_dir = trans.app.config.new_file_path, dataset_files_path = trans.app.model.Dataset.file_path, output_fnames = None, config_root = None, datatypes_config = None, kwds = { 'overwrite' : True } )
+ incoming[ '__SET_EXTERNAL_METADATA_COMMAND_LINE__' ] = cmd_line
+ for name, value in tool.params_to_strings( incoming, trans.app ).iteritems():
+ job.add_parameter( name, value )
+ #add the dataset to job_to_input_dataset table
+ job.add_input_dataset( dataset_name, dataset )
+ #Need a special state here to show that metadata is being set and also allow the job to run
+ # i.e. if state was set to 'running' the set metadata job would never run, as it would wait for input (the dataset to set metadata on) to be in a ready state
+ dataset.state = dataset.states.SETTING_METADATA
+ trans.app.model.flush()
+
+ # Queue the job for execution
+ trans.app.job_queue.put( job.id, tool )
+ trans.log_event( "Added set external metadata job to the job queue, id: %s" % str(job.id), tool_id=job.tool_id )
+ return []
diff -r 846f6a7ee80c -r ad5232a4dbc5 lib/galaxy/tools/actions/upload.py
--- a/lib/galaxy/tools/actions/upload.py Wed Aug 12 15:56:03 2009 -0400
+++ b/lib/galaxy/tools/actions/upload.py Thu Aug 13 11:00:00 2009 -0400
@@ -1,4 +1,5 @@
import os, shutil, urllib, StringIO, re, gzip, tempfile, shutil, zipfile
+from . import ToolAction
from galaxy import datatypes, jobs
from galaxy.datatypes import sniff
from galaxy import model, util
@@ -8,7 +9,7 @@
import logging
log = logging.getLogger( __name__ )
-class UploadToolAction( object ):
+class UploadToolAction( ToolAction ):
# Action for uploading files
def __init__( self ):
self.empty = False
diff -r 846f6a7ee80c -r ad5232a4dbc5 lib/galaxy/web/controllers/root.py
--- a/lib/galaxy/web/controllers/root.py Wed Aug 12 15:56:03 2009 -0400
+++ b/lib/galaxy/web/controllers/root.py Thu Aug 13 11:00:00 2009 -0400
@@ -297,10 +297,15 @@
if name not in [ 'name', 'info', 'dbkey' ]:
if spec.get( 'default' ):
setattr( data.metadata, name, spec.unwrap( spec.get( 'default' ) ) )
- data.set_meta()
- data.datatype.after_edit( data )
+ if trans.app.config.set_metadata_externally:
+ msg = 'Attributes have been queued to be updated'
+ trans.app.datatypes_registry.set_external_metadata_tool.tool_action.execute( trans.app.datatypes_registry.set_external_metadata_tool, trans, incoming = { 'input1':data } )
+ else:
+ msg = 'Attributes updated'
+ data.set_meta()
+ data.datatype.after_edit( data )
trans.app.model.flush()
- return trans.show_ok_message( "Attributes updated", refresh_frames=['history'] )
+ return trans.show_ok_message( msg, refresh_frames=['history'] )
elif params.convert_data:
target_type = kwd.get("target_type", None)
if target_type:
diff -r 846f6a7ee80c -r ad5232a4dbc5 static/june_2007_style/blue/history.css
--- a/static/june_2007_style/blue/history.css Wed Aug 12 15:56:03 2009 -0400
+++ b/static/june_2007_style/blue/history.css Thu Aug 13 11:00:00 2009 -0400
@@ -13,6 +13,8 @@
div.historyItem-empty .state-icon{background:url(history-states.png) no-repeat 0px -25px;}
div.historyItem-running{border-color:#AAAA66;background:#FFFFCC;}
div.historyItem-running .state-icon{background-image:url(data_running.gif);}
+div.historyItem-setting_metadata{border-color:#AAAA66;background:#FFFFCC;}
+div.historyItem-setting_metadata .state-icon{background-image:url(data_running.gif);}
div.historyItem-upload{border-color:#6666AA;background:#CCCCFF;}
div.historyItem-upload .state-icon{background-image:url(data_upload.gif);}
div.historyItem-queued{background:#EEEEEE;}
diff -r 846f6a7ee80c -r ad5232a4dbc5 static/june_2007_style/history.css.tmpl
--- a/static/june_2007_style/history.css.tmpl Wed Aug 12 15:56:03 2009 -0400
+++ b/static/june_2007_style/history.css.tmpl Thu Aug 13 11:00:00 2009 -0400
@@ -72,6 +72,14 @@
}
div.historyItem-running {
+ border-color: $history_running_border;
+ background: $history_running_bg;
+ .state-icon {
+ background-image: url(data_running.gif);
+ }
+}
+
+div.historyItem-setting_metadata {
border-color: $history_running_border;
background: $history_running_bg;
.state-icon {
diff -r 846f6a7ee80c -r ad5232a4dbc5 templates/mobile/history/detail.mako
--- a/templates/mobile/history/detail.mako Wed Aug 12 15:56:03 2009 -0400
+++ b/templates/mobile/history/detail.mako Thu Aug 13 11:00:00 2009 -0400
@@ -51,6 +51,8 @@
<div>
The job creating this dataset was cancelled before completion.
</div>
+ %elif data_state == 'setting_metadata':
+ <div>Metadata is being Auto-Detected.</div>
%elif data_state == "empty":
<div>No data: <i>${data.display_info()}</i></div>
%elif data_state == "ok":
diff -r 846f6a7ee80c -r ad5232a4dbc5 templates/root/history_common.mako
--- a/templates/root/history_common.mako Wed Aug 12 15:56:03 2009 -0400
+++ b/templates/root/history_common.mako Thu Aug 13 11:00:00 2009 -0400
@@ -58,6 +58,8 @@
<div>
The job creating this dataset was cancelled before completion.
</div>
+ %elif data_state == 'setting_metadata':
+ <div>${_('Metadata is being Auto-Detected.')}</div>
%elif data_state == "empty":
<div>${_('No data: ')}<i>${data.display_info()}</i></div>
%elif data_state == "ok":
1
0
Hello,
I've recently upgrade to the latest version, and after Greg and Ross explained the security model (in small words, so I'll understand), I played a bit with the new model.
I have some usability issue with the new way sharing histories work.
It might be because we are relatively a small and tight user base, and the model was designed to work with disperse teams from around them world, where security must be enforced by an appointed security administrator. We don't have security administrator. We need galaxy to be secured, but without the hassle of administration - and everything should 'just work'.
The following describe four issues (the description is exaggerated on purpose, please take no offense)
Regards,
gordon.
1) Steps needed to share histories
----------------------------------
Usage scenario: User A wants to share datasets with User B.
'Old way' (with public datasets, no security):
1. Login as User A.
2. Select History.
3. Click "options"
4. Click "share this history"
5. Enter email of user B
6. Click "Share".
Outcome:
user B has the new history in his histories list, and can view all files.
'New way' (still with public datasets, no security):
1. Login as User A.
2. Select History.
3. Click "Options"
4. click "share"
5. Enter email of user B.
6. click "share"
Outcome:
user B doesn't have the history in his histories list.
What User B needs to do:
1. click "options"
2. click the *other* list link (there are two of them, both named 'link', and one has to actually read the rest of the sentence to know what's the different between them).
3. Find the needed shared history, no dates or options to sort by.
4. click on the tiny triangle button, which will show a pop-up menu with only one button 'clone' (which he's going to click anyway, so why not make it available as a huge button on the main page?).
5. click 'clone'.
6. User sees a technical question about cloning deleted items. there's no default selection, so you can't just click "clone". you have to read and understand what's going on.
Don't underestimate the annoyance of this:
You advertise galaxy as easy to use for biologists, who don't need to understand the technical aspects - so why make them read technical details and choose these options?
7. Only then, the shared+cloned history appears in the history list.
With security model in place (meaning datasets are not public), things get more confusing.
User A wants to share history with user B.
User A's implied wish: "I want User B to view my files".
When User A clicks "share", enters the email and click "share", he is then presented with four permission related options.
The last one is "Don't Share".
This is kind of funny. it reminds me of clicking the "start" button to shutdown the computer in you-know-which operating system.
The scenario of selecting "Don't Share" and clicking "Go" button - this is a no-op. why is it needed ?
If this wasn't a web-application - then yes, this modal dialog would require a 'cancel' button.
But this is a web-application. just click on another link instead of clicking "go".
A "go" button implies something will be done.
The second-to-last is "Share Anyway (don't change any permission)".
This is a problem waiting to happen - you are explicitly allowing the user to do something that you know will not work.
>From a technical perspective, that's OK - a knowledgeable user can later on manually set permissions, or can later on whine to the administrator about permission problems (or my favorite: User A tells User B to ask the admin to give him permissions).
IMHO, a program should not allow a user to do anything that will definitely not work.
The first option is "make datasets public".
This is bad option for two reasons:
1. User A don't want to make his datasets public (otherwise he would not set permissions on them). All he wants is to allow User B to view his files.
2. If permissions problem persist, Users will get accustomed to just 'make everything public' because that's the easy solution. and this will make the whole security thing redundant.
Also remember that PUBLIC means anybody can view them - anybody, not just people you shared it with. This is not obvious. this means "no security".
The second option is "make datasets private to me and the user...".
This seems like what the user actually wants, but it has strange side effects (see item 3, below).
2) Deleting shared history
--------------------------
Scenario: User A wants to delete the current history.
the history happens to be shared.
1. Click "options"
2. Click "delete current history"
3. Message box appears: "Are you sure?". click yes.
4. Red warning appears: "History has been shared, unshare it before deleting it".
1. There's no link to 'unshare' it, how do I do that?
There's no "unshare" button anywhere.
You have to go the the list of histories, find the history in the list, click on the "shared" link, then "unshare" it.
quite unintuitive...
2. If I shared the history with twenty users, I need to click the little triangle button and then the "unshare" link for each of them.
quite annoying.
Why not have an "unshare all" button ?
3. There's no confirmation needed to unshare a history, no user intervention.
So theoretically, there's no reason that a "delete" operation would not automatically unshare all shares, then delete the history.
5. This is the real confusing part:
To me, "share" means: "allow other users to view the files in this history",
and "unshare" means: "don't allow other users to view the files in the history".
But in the new galaxy model, "share" means: allow other users to clone the history and then view my files",
and "unshare" means: "don't allow other users to clone (or clone again) the history".
The old galaxy had no concept of "unshare" - and that's logical - once you shared your files - you can't take them away.
In the new Galaxy, "unshare" is not really unsharing: if another user has cloned the shared history, he still has access to your files (de-facto: they are still shared).
the other user simply can't clone your history again.
--------
3) Side Effect of Ad-Hoc sharing roles
---------------------------------------
Here are the exact steps I made, and two problems that happen with them.
##
## Creating base configuration
##
$ hg clone http://www.bx.psu.edu/hg/galaxy galaxy1
$ cd galaxy1
# (my default is python 2.6, too bad for me)
$ sed -i 's/^python /python2.5 /' run.sh
$ sed -i 's/^python /python2.5 /' setup.sh
$ sh setup.sh
$ sh run.sh
---
In galaxy
#
# First user
#
User -> Register
username: gordon1(a)cshl.edu
User -> Preferences -> Change Default Permissions for new histories
Roles Associated: [Added 'gordon1(a)cshl.edu']
(click 'save')
User -> Logout
#
# Second User
#
User -> Register
username: gordon2(a)cshl.edu
User -> Preferences -> Change Default Permissions for new histories
Roles Associated: [Added 'gordon2(a)cshl.edu']
(click 'save')
User -> Logout
#
# Third User
#
User -> Register
username: gordon3(a)cshl.edu
User -> Preferences -> Change Default Permissions for new histories
Roles Associated: [Added 'gordon3(a)cshl.edu']
(click 'save')
User -> Logout
-------------------
This is the base configuration.
The following test cases start from this configuration.
-------------------
User->Login username: gordon1(a)cshl.edu
# History 1
Options -> delete current history (because of subtle permission issue, see item 4, below)
Rename History: "His 1 of Gordon1"
Get Data->Upload File: (pasted text: "Data 1 of His 1 of Gordon 1")
Rename Dataset to "Data 1 of His 1 of Gordon 1"
Options -> Share Current History
Username: 'gordon2(a)cshl.edu'
(click 'submit')
Permissions dialog: How would you like to proceed?
- "Make datasets private to me and the users ..." (option 2)
(click 'go')
# Note: the second file is uploaded AFTER the share.
Get Data->Upload File: (pasted text: "Data 2 of His 1 of Gordon 1")
Rename Dataset to "Data 2 of His 1 of Gordon 1"
User -> Logout
#
# New as the second user
#
User->Login username: gordon2(a)cshl.edu
Options->List Shared histories (the 'other' list link)
(seeing two histories shared from gordon1)
On "His1", click the little triangle button, then 'clone'.
Select "clone all history items" (option 1), click "clone"
Options -> List
(two histories, 1 unnamed, and one cloned).
Switch to the cloned history.
#
# Problem 1:
#
In the second user's history pane:
The first dataset is OK, the second one is blocked.
Technically this is correct, because the second dataset was created after the share, and the new ad-hoc role was not applied to it.
but from a usability POV, this is confusing - the history was cloned by the second user AFTER the first user created the files.
In real life:
1. User A shares a History with User B.
2. User A adds more files to the history
3. User A tells user B: "I've added more files, clone the history again"
4. User B clones the history (with the new items), but the new items are blocked.
In the 'previous' version of galaxy (before the changeset that introduced the share mechanism), the "share" action literally meant "Copy the current content of the this history to other users" - and you could do it multiple times, with different contents of the same history. This is not the case anymore.
Also, going back to the first user (gordon1) and re-sharing the history doesn't work (a red warning appears that this history is already shared). A non-technical user (who doesn't care about roles/groups/permissions and just wants things to work) will be very frustrated. The technical solution of visiting every dataset and adding the role is maybe good for one or two datasets, but not to twelve datasets that were created by a workflow.
#
# Problem 2 :
# (this might be a bug)
# continuing from the previously described state
User -> Logout
User -> Login, username: gordon1(a)cshl.edu (first user)
Options -> List Histories
Switch to "His 1 of Gordon1"
This history contains two datasets:
Dataset 1 ( "Data 1 of His 1 Of Gordon 1" ) has:
[access] roles associated: "Sharing role for: gordon1(a)cshl.edu, gordon2(a)cshl.edu"
roles not associated: "gordon1(a)cshl.edu"
Dataset 2 ( "Data 2 of his 1 of gordon 1") has:
[access] roles associated: "gordon1(a)cshl.edu"
roles not associated: "sharing role for: gordon1(a)cshl.edu, gordon2(a)cshl.edu".
Options -> Share
username: gordon3(a)cshl.edu (third user)
(click 'submit')
Permissions dialog: How would you like to proceed?
- "Make datasets private to me and the users ..." (option 2)
(click 'go')
The roles of all the datasets is reset to 'sharing role for: gordon1(a)cshl.edu, gordon3(a)cshl.edu"
Meaning that the second user (gordon2), which was previously allowed to view at least one dataset,
is now deprived of even this privilege.
Even if this is technically correct, from a usability POV it is very confusing.
What the first user did:
1. share history with second user (implied: I want the second user to view my files).
2. share history with third user (implied: I want the third user to also view my files).
But the outcome is that the second user is now blocked.
4) Subtle security bug/feature:
-------------------------------
The first time a NEW user logs on, an empty history is created.
Going to USER->Preferences->Change default permissions
Does not change the permissions of current history, and there's no button to create a new history (because this is an empty history).
So the files in the current history will be public.
While technically correct, this behavior is confusing.
What the user wants is: "everything I create from now on has X permissions",
But what technically is done is: "New histories will have X permissions, but you are currently in an old history which uses the old settings".
---------------------------
Thanks for reading so far.
2
3
13 Aug '09
details: http://www.bx.psu.edu/hg/galaxy/rev/846f6a7ee80c
changeset: 2556:846f6a7ee80c
user: Greg Von Kuster <greg(a)bx.psu.edu>
date: Wed Aug 12 15:56:03 2009 -0400
description:
1) Enhancements for sharing histories based on feedback from Assaf Gordon ( resolves ticket # 125 ):
- Make "Histories shared with you by others" page a grid
- Eliminate the option to clone deleted or all items for the user to which the history has been shared
- Eliminate the "don't share" option when sharing histories
- When the "share anyway" option is chosen, make sure there is something in "no_change_needed" dict
2) Fix library view template display for ldda information page ( resolves ticket 128 )
13 file(s) affected in this change:
lib/galaxy/web/controllers/admin.py
lib/galaxy/web/controllers/history.py
lib/galaxy/web/controllers/library.py
lib/galaxy/web/framework/helpers/grids.py
templates/admin/requests/grid.mako
templates/history/grid.mako
templates/history/list_shared.mako
templates/history/options.mako
templates/history/share.mako
templates/history/shared_grid.mako
templates/history/stored_grid.mako
test/base/twilltestcase.py
test/functional/test_history_functions.py
diffs (1262 lines):
diff -r 2630316ff75e -r 846f6a7ee80c lib/galaxy/web/controllers/admin.py
--- a/lib/galaxy/web/controllers/admin.py Tue Aug 11 16:56:47 2009 -0400
+++ b/lib/galaxy/web/controllers/admin.py Wed Aug 12 15:56:03 2009 -0400
@@ -1214,7 +1214,6 @@
template = info_association.template
# See if we have any field contents
info = info_association.info
- log.debug("####In library_dataset_dataset_association, info.content: %s" % str( info.content))
if info:
field_contents = {}
for index, value in enumerate( info.content ):
diff -r 2630316ff75e -r 846f6a7ee80c lib/galaxy/web/controllers/history.py
--- a/lib/galaxy/web/controllers/history.py Tue Aug 11 16:56:47 2009 -0400
+++ b/lib/galaxy/web/controllers/history.py Wed Aug 12 15:56:03 2009 -0400
@@ -12,7 +12,6 @@
# States for passing messages
SUCCESS, INFO, WARNING, ERROR = "done", "info", "warning", "error"
-
class HistoryListGrid( grids.Grid ):
# Custom column types
@@ -70,8 +69,43 @@
def apply_default_filter( self, trans, query ):
return query.filter_by( user=trans.user, purged=False )
+class SharedHistoryListGrid( grids.Grid ):
+ # Custom column types
+ class DatasetsByStateColumn( grids.GridColumn ):
+ def get_value( self, trans, grid, history ):
+ rval = []
+ for state in ( 'ok', 'running', 'queued', 'error' ):
+ total = sum( 1 for d in history.active_datasets if d.state == state )
+ if total:
+ rval.append( '<div class="count-box state-color-%s">%s</div>' % ( state, total ) )
+ else:
+ rval.append( '' )
+ return rval
+ class SharedByColumn( grids.GridColumn ):
+ def get_value( self, trans, grid, history ):
+ return history.user.email
+ # Grid definition
+ title = "Histories shared with you by others"
+ model_class = model.History
+ default_sort_key = "-update_time"
+ columns = [
+ grids.GridColumn( "Name", key="name" ),
+ DatasetsByStateColumn( "Datasets (by state)", ncells=4 ),
+ grids.GridColumn( "Created", key="create_time", format=time_ago ),
+ grids.GridColumn( "Last Updated", key="update_time", format=time_ago ),
+ SharedByColumn( "Shared by", key="user_id" )
+ ]
+ operations = [
+ grids.GridOperation( "Clone" ),
+ grids.GridOperation( "Unshare" )
+ ]
+ standard_filters = []
+ def build_initial_query( self, session ):
+ return session.query( self.model_class ).join( 'users_shared_with' )
+ def apply_default_filter( self, trans, query ):
+ return query.filter( model.HistoryUserShareAssociation.user == trans.user )
+
class HistoryController( BaseController ):
-
@web.expose
def index( self, trans ):
return ""
@@ -80,7 +114,8 @@
"""XML history list for functional tests"""
return trans.fill_template( "/history/list_as_xml.mako" )
- list_grid = HistoryListGrid()
+ stored_list_grid = HistoryListGrid()
+ shared_list_grid = SharedHistoryListGrid()
@web.expose
@web.require_login( "work with multiple histories" )
@@ -91,7 +126,6 @@
if 'operation' in kwargs:
history_ids = util.listify( kwargs.get( 'id', [] ) )
histories = []
- shared_by_others = []
operation = kwargs['operation'].lower()
if operation == "share":
return self.share( trans, **kwargs )
@@ -127,7 +161,7 @@
status, message = self._list_undelete( trans, histories )
trans.sa_session.flush()
# Render the list view
- return self.list_grid( trans, status=status, message=message, template='/history/grid.mako', **kwargs )
+ return self.stored_list_grid( trans, status=status, message=message, template='/history/stored_grid.mako', **kwargs )
def _list_delete( self, trans, histories ):
"""Delete histories"""
n_deleted = 0
@@ -195,18 +229,38 @@
# No message
return None, None
@web.expose
- def list_shared( self, trans, **kwd ):
+ def list_shared( self, trans, **kwargs ):
"""List histories shared with current user by others"""
- params = util.Params( kwd )
- msg = util.restore_text( params.get( 'msg', '' ) )
- shared_by_others = trans.sa_session \
- .query( model.HistoryUserShareAssociation ) \
- .filter_by( user=trans.user ) \
- .join( 'history' ) \
- .filter( model.History.deleted == False ) \
- .order_by( desc( model.History.update_time ) ) \
- .all()
- return trans.fill_template( "/history/list_shared.mako", shared_by_others=shared_by_others, msg=msg, messagetype='done' )
+ msg = util.restore_text( kwargs.get( 'msg', '' ) )
+ status = message = None
+ if 'operation' in kwargs:
+ id = kwargs.get( 'id', None )
+ operation = kwargs['operation'].lower()
+ if operation == "clone":
+ if not id:
+ message = "Select a history to clone"
+ return self.shared_list_grid( trans, status='error', message=message, template='/history/shared_grid.mako', **kwargs )
+ # When cloning shared histories, only copy active datasets
+ new_kwargs = { 'clone_choice' : 'active' }
+ return self.clone( trans, id, **new_kwargs )
+ elif operation == 'unshare':
+ if not id:
+ message = "Select a history to unshare"
+ return self.shared_list_grid( trans, status='error', message=message, template='/history/shared_grid.mako', **kwargs )
+ ids = util.listify( id )
+ histories = []
+ for history_id in ids:
+ history = get_history( trans, history_id, check_ownership=False )
+ histories.append( history )
+ for history in histories:
+ # Current user is the user with which the histories were shared
+ association = trans.app.model.HistoryUserShareAssociation.filter_by( user=trans.user, history=history ).one()
+ association.delete()
+ association.flush()
+ message = "Unshared %d shared histories" % len( ids )
+ status = 'done'
+ # Render the list view
+ return self.shared_list_grid( trans, status=status, message=message, template='/history/shared_grid.mako', **kwargs )
@web.expose
def delete_current( self, trans ):
"""Delete just the active history -- this does not require a logged in user."""
@@ -323,6 +377,9 @@
can_change, cannot_change, no_change_needed, unique_no_change_needed, send_to_err = \
self._populate_restricted( trans, user, histories, send_to_users, None, send_to_err, unique=True )
send_to_err += err_msg
+ if cannot_change and not no_change_needed and not can_change:
+ send_to_err = "The histories you are sharing do not contain any datasets that can be accessed by the users with which you are sharing."
+ return trans.fill_template( "/history/share.mako", histories=histories, email=email, send_to_err=send_to_err )
if can_change or cannot_change:
return trans.fill_template( "/history/share.mako",
histories=histories,
@@ -350,8 +407,6 @@
email=email,
err_msg=err_msg,
share_button=True ) )
- if action == "no_share":
- trans.response.send_redirect( url_for( controller='root', action='history_options' ) )
user = trans.get_user()
histories, send_to_users, send_to_err = self._get_histories_and_users( trans, user, id, email )
send_to_err = ''
@@ -629,29 +684,38 @@
@web.expose
@web.require_login( "clone shared Galaxy history" )
def clone( self, trans, id, **kwd ):
- history = get_history( trans, id, check_ownership=False )
+ """Clone a list of histories"""
params = util.Params( kwd )
+ ids = util.listify( id )
+ histories = []
+ for history_id in ids:
+ history = get_history( trans, history_id, check_ownership=False )
+ histories.append( history )
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
+ for history in histories:
+ if history.user == user:
+ owner = True
+ else:
+ if trans.sa_session.query( trans.app.model.HistoryUserShareAssociation ) \
+ .filter_by( user=user, history=history ).count() == 0:
+ return trans.show_error_message( "The history you are attempting to clone is not owned by you or shared with you. " )
+ owner = False
+ name = "Clone of '%s'" % history.name
+ if not owner:
+ name += " shared by '%s'" % history.user.email
+ if clone_choice == 'activatable':
+ new_history = history.copy( name=name, target_user=user, activatable=True )
+ elif clone_choice == 'active':
+ name += " (active items only)"
+ new_history = history.copy( name=name, target_user=user )
+ if len( histories ) == 1:
+ msg = 'Clone with name "%s" is now included in your previously stored histories.' % new_history.name
else:
- if trans.sa_session.query( trans.app.model.HistoryUserShareAssociation ) \
- .filter_by( user=user, history=history ).count() == 0:
- return trans.show_error_message( "The history you are attempting to clone is not owned by you or shared with you. " )
- owner = False
- name = "Clone of '%s'" % history.name
- if not owner:
- name += " shared by '%s'" % history.user.email
- if clone_choice == 'activatable':
- new_history = history.copy( name=name, target_user=user, activatable=True )
- elif clone_choice == 'active':
- name += " (active items only)"
- 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 )
+ msg = '%d cloned histories are now included in your previously stored histories.' % len( histories )
+ return trans.show_ok_message( msg )
## ---- Utility methods -------------------------------------------------------
diff -r 2630316ff75e -r 846f6a7ee80c lib/galaxy/web/controllers/library.py
--- a/lib/galaxy/web/controllers/library.py Tue Aug 11 16:56:47 2009 -0400
+++ b/lib/galaxy/web/controllers/library.py Wed Aug 12 15:56:03 2009 -0400
@@ -469,7 +469,7 @@
msg=util.sanitize_text( msg ),
messagetype='error' ) )
# See if we have any associated templates
- info_association = folder.get_info_association()
+ info_association = ldda.get_info_association()
if info_association:
template = info_association.template
# See if we have any field contents
diff -r 2630316ff75e -r 846f6a7ee80c lib/galaxy/web/framework/helpers/grids.py
--- a/lib/galaxy/web/framework/helpers/grids.py Tue Aug 11 16:56:47 2009 -0400
+++ b/lib/galaxy/web/framework/helpers/grids.py Wed Aug 12 15:56:03 2009 -0400
@@ -156,9 +156,7 @@
elif column_filter == "All":
del filter_args[self.key]
return query
-
-
-
+
class GridOperation( object ):
def __init__( self, label, key=None, condition=None, allow_multiple=True ):
self.label = label
diff -r 2630316ff75e -r 846f6a7ee80c templates/admin/requests/grid.mako
--- a/templates/admin/requests/grid.mako Tue Aug 11 16:56:47 2009 -0400
+++ b/templates/admin/requests/grid.mako Wed Aug 12 15:56:03 2009 -0400
@@ -101,7 +101,7 @@
%if not len(query.all()):
- There are no request(s).
+ There are no requests.
%else:
<form name="history_actions" action="${url()}" method="post" >
<table class="grid">
diff -r 2630316ff75e -r 846f6a7ee80c templates/history/grid.mako
--- a/templates/history/grid.mako Tue Aug 11 16:56:47 2009 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,194 +0,0 @@
-<%inherit file="/base.mako"/>
-<%def name="title()">${grid.title}</%def>
-
-%if message:
- <p>
- <div class="${message_type}message transient-message">${message}</div>
- <div style="clear: both"></div>
- </p>
-%endif
-
-<%def name="javascripts()">
- ${parent.javascripts()}
- <script type="text/javascript">
- ## TODO: generalize and move into galaxy.base.js
- $(document).ready(function() {
- $(".grid").each( function() {
- var grid = this;
- var checkboxes = $(this).find("input.grid-row-select-checkbox");
- var update = $(this).find( "span.grid-selected-count" );
- $(checkboxes).each( function() {
- $(this).change( function() {
- var n = $(checkboxes).filter("[checked]").size();
- update.text( n );
- });
- })
- });
- });
- ## Can this be moved into base.mako?
- %if refresh_frames:
- %if 'masthead' in refresh_frames:
- ## Refresh masthead == user changes (backward compatibility)
- if ( parent.user_changed ) {
- %if trans.user:
- parent.user_changed( "${trans.user.email}", ${int( app.config.is_admin_user( trans.user ) )} );
- %else:
- parent.user_changed( null, false );
- %endif
- }
- %endif
- %if 'history' in refresh_frames:
- if ( parent.frames && parent.frames.galaxy_history ) {
- parent.frames.galaxy_history.location.href="${h.url_for( controller='root', action='history')}";
- if ( parent.force_right_panel ) {
- parent.force_right_panel( 'show' );
- }
- }
- %endif
- %if 'tools' in refresh_frames:
- if ( parent.frames && parent.frames.galaxy_tools ) {
- parent.frames.galaxy_tools.location.href="${h.url_for( controller='root', action='tool_menu')}";
- if ( parent.force_left_panel ) {
- parent.force_left_panel( 'show' );
- }
- }
- %endif
- %endif
- </script>
-</%def>
-
-<%def name="stylesheets()">
- <link href="${h.url_for('/static/style/base.css')}" rel="stylesheet" type="text/css" />
- <style>
- ## Not generic to all grids -- move to base?
- .count-box {
- min-width: 1.1em;
- padding: 5px;
- border-width: 1px;
- border-style: solid;
- text-align: center;
- display: inline-block;
- }
- </style>
-</%def>
-
-<div class="grid-header">
- <h2>${grid.title}</h2>
- <span class="title">Filter:</span>
- %for i, filter in enumerate( grid.standard_filters ):
- %if i > 0:
- <span>|</span>
- %endif
- <span class="filter"><a href="${url( filter.get_url_args() )}">${filter.label}</a></span>
- %endfor
-</div>
-
-<form name="history_actions" action="${url()}" method="post" >
- <table class="grid">
- <thead>
- <tr>
- <th></th>
- %for column in grid.columns:
- %if column.visible:
- <%
- href = ""
- extra = ""
- if column.sortable:
- if sort_key == column.key:
- if sort_order == "asc":
- href = url( sort=( "-" + column.key ) )
- extra = "↓"
- else:
- href = url( sort=( column.key ) )
- extra = "↑"
- else:
- href = url( sort=column.key )
- %>
- <th\
- %if column.ncells > 1:
- colspan="${column.ncells}"
- %endif
- >
- %if href:
- <a href="${href}">${column.label}</a>
- %else:
- ${column.label}
- %endif
- <span>${extra}</span>
- </th>
- %endif
- %endfor
- <th></th>
- </tr>
- </thead>
- <tbody>
- %for i, item in enumerate( query ):
- <tr \
- %if current_item == item:
- class="current" \
- %endif
- >
- ## Item selection column
- <td style="width: 1.5em;">
- <input type="checkbox" name="id" value=${trans.security.encode_id( item.id )} class="grid-row-select-checkbox" />
- </td>
- ## Data columns
- %for column in grid.columns:
- %if column.visible:
- <%
- # Link
- link = column.get_link( trans, grid, item )
- if link:
- href = url( **link )
- else:
- href = None
- # Value (coerced to list so we can loop)
- value = column.get_value( trans, grid, item )
- if column.ncells == 1:
- value = [ value ]
- %>
- %for cellnum, v in enumerate( value ):
- <%
- # Attach popup menu?
- if column.attach_popup and cellnum == 0:
- extra = '<a id="grid-%d-popup" class="popup-arrow" style="display: none;">▼</a>' % i
- else:
- extra = ""
- %>
- %if href:
- <td><a href="${href}">${v}</a> ${extra}</td>
- %else:
- <td >${v}${extra}</td>
- %endif
- </td>
- %endfor
- %endif
- %endfor
- ## Actions column
- <td>
- <div popupmenu="grid-${i}-popup">
- %for operation in grid.operations:
- %if operation.allowed( item ):
- <a class="action-button" href="${url( operation=operation.label, id=item.id )}">${operation.label}</a>
- %endif
- %endfor
- </div>
- </td>
- </tr>
- %endfor
- </tbody>
- <tfoot>
- <tr>
- <td></td>
- <td colspan="100">
- For <span class="grid-selected-count"></span> selected histories:
- %for operation in grid.operations:
- %if operation.allow_multiple:
- <input type="submit" name="operation" value="${operation.label}" class="action-button">
- %endif
- %endfor
- </td>
- </tr>
- </tfoot>
- </table>
-</form>
diff -r 2630316ff75e -r 846f6a7ee80c templates/history/list_shared.mako
--- a/templates/history/list_shared.mako Tue Aug 11 16:56:47 2009 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-<%inherit file="/base.mako"/>
-<%namespace file="/message.mako" import="render_msg" />
-
-%if msg:
- ${render_msg( msg, messagetype )}
-%endif
-
-%if shared_by_others:
- <table class="colored" border="0" cellspacing="0" cellpadding="0" width="100%">
- <tr class="header">
- <th>Name</th>
- <th>Owner</th>
- </tr>
- %for i, association in enumerate( shared_by_others ):
- <% history = association.history %>
- <tr>
- <td>
- ${history.name}
- <a id="shared-${i}-popup" class="popup-arrow" style="display: none;">▼</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>
- </div>
- </td>
- <td>${history.user.email}</td>
- </tr>
- %endfor
- </table>
-%else:
- No histories have been shared with you.
-%endif
diff -r 2630316ff75e -r 846f6a7ee80c templates/history/options.mako
--- a/templates/history/options.mako Tue Aug 11 16:56:47 2009 -0400
+++ b/templates/history/options.mako Wed Aug 12 15:56:03 2009 -0400
@@ -12,7 +12,7 @@
<ul>
%if user:
- <li><a href="${h.url_for( controller='history', action='list')}" target="galaxy_main">List</a> previously stored histories</li>
+ <li><a href="${h.url_for( controller='history', action='list')}" target="galaxy_main">Previously</a> stored histories</li>
%if len( history.active_datasets ) > 0:
<li><a href="${h.url_for( controller='root', action='history_new' )}">Create</a> a new empty history</li>
<li><a href="${h.url_for( controller='workflow', action='build_from_current_history' )}">Construct workflow</a> from current history</li>
@@ -27,6 +27,6 @@
<li><a href="${h.url_for( controller='history', action='rename', id=trans.security.encode_id( history.id ) )}" target="galaxy_main">Rename</a> current history (stored as "${history.name}")</li>
<li><a href="${h.url_for( controller='history', action='delete_current' )}" confirm="Are you sure you want to delete the current history?">Delete</a> current history</div>
%if user and user.histories_shared_by_others:
- <li><a href="${h.url_for( controller='history', action='list_shared')}" target="galaxy_main">List</a> histories shared with you by others</li>
+ <li><a href="${h.url_for( controller='history', action='list_shared')}" target="galaxy_main">Histories</a> shared with you by others</li>
%endif
</ul>
diff -r 2630316ff75e -r 846f6a7ee80c templates/history/share.mako
--- a/templates/history/share.mako Tue Aug 11 16:56:47 2009 -0400
+++ b/templates/history/share.mako Wed Aug 12 15:56:03 2009 -0400
@@ -57,134 +57,136 @@
</form>
%else:
## We are sharing restricted histories
- <form name='share_restricted' id=share_restricted' action="${h.url_for( controller='history', action='share_restricted' )}" method="post">
- %if send_to_err:
- <div style="clear: both"></div>
- <div class="form-row">
- <div class="errormessage">${send_to_err}</div>
- </div>
- %endif
- ## Needed for rebuilding dicts
- <input type="hidden" name="email" value="${email}" size="40">
- %for history in histories:
- <input type="hidden" name="id" value="${trans.security.encode_id( history.id )}">
- %endfor
- %if no_change_needed:
- ## no_change_needed looks like: {historyX : [hda, hda], historyY : [hda] }
- <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>
+ %if no_change_needed or can_change:
+ <form name='share_restricted' id=share_restricted' action="${h.url_for( controller='history', action='share_restricted' )}" method="post">
+ %if send_to_err:
<div style="clear: both"></div>
<div class="form-row">
- <label>Datasets</label>
+ <div class="errormessage">${send_to_err}</div>
</div>
- %for hda in hdas:
- <div class="form-row">
- ${hda.name}
- %if hda.deleted:
- (deleted)
- %endif
- </div>
- %endfor
+ %endif
+ ## Needed for rebuilding dicts
+ <input type="hidden" name="email" value="${email}" size="40">
+ %for history in histories:
+ <input type="hidden" name="id" value="${trans.security.encode_id( history.id )}">
%endfor
- %endif
- %if can_change:
- ## can_change looks like: {historyX : [hda, hda], historyY : [hda] }
- <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>
+ %if no_change_needed:
+ ## no_change_needed looks like: {historyX : [hda, hda], historyY : [hda] }
<div style="clear: both"></div>
<div class="form-row">
- <label>Datasets</label>
- </div>
- %for hda in hdas:
+ <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">
- ${hda.name}
- %if hda.deleted:
- (deleted)
- %endif
+ <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}
+ %if hda.deleted:
+ (deleted)
+ %endif
+ </div>
+ %endfor
%endfor
- %endfor
- %endif
- %if cannot_change:
- ## cannot_change looks like: {historyX : [hda, hda], historyY : [hda] }
- <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>
+ %endif
+ %if can_change:
+ ## can_change looks like: {historyX : [hda, hda], historyY : [hda] }
<div style="clear: both"></div>
<div class="form-row">
- <label>Datasets</label>
+ <div class="warningmessage">
+ The following datasets can be shared with ${email} by updating their permissions
+ </div>
</div>
- %for hda in hdas:
+ %for history, hdas in can_change.items():
<div class="form-row">
- ${hda.name}
- %if hda.deleted:
- (deleted)
- %endif
+ <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}
+ %if hda.deleted:
+ (deleted)
+ %endif
+ </div>
+ %endfor
%endfor
- %endfor
- %endif
- <div class="toolFormTitle"></div>
- <div class="form-row">
- Deleted items can be eliminated by the users with which you are sharing the history.
- </div>
- <div class="form-row">
- <label>How would you like to proceed?</label>
- </div>
- %if can_change:
+ %endif
+ %if cannot_change:
+ ## cannot_change looks like: {historyX : [hda, hda], historyY : [hda] }
+ <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}
+ %if hda.deleted:
+ (deleted)
+ %endif
+ </div>
+ %endfor
+ %endfor
+ %endif
+ <div class="toolFormTitle"></div>
<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
+ <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">
+ %if no_change_needed:
+ <input type="radio" name="action" value="private"> Make datasets private to me and the user(s) with whom I am sharing
+ %else:
+ <input type="radio" name="action" value="private" checked> Make datasets private to me and the user(s) with whom I am sharing
+ %endif
+ %if cannot_change:
+ (where possible)
+ %endif
+ </div>
+ %endif
+ %if no_change_needed:
+ <div class="form-row">
+ <input type="radio" name="action" value="share_anyway" checked> Share anyway
+ %if can_change:
+ (don't change any permissions)
+ %endif
+ </div>
+ %endif
<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
+ <input type="submit" name="share_restricted_button" value="Go"><br/>
</div>
- %endif
- <div class="form-row">
- <input type="radio" name="action" value="share_anyway"> Share anyway
- %if can_change:
- (don't change any permissions)
- %endif
- </div>
- <div class="form-row">
- <input type="radio" name="action" value="no_share" checked> Don't share
- </div>
- <div class="form-row">
- <input type="submit" name="share_restricted_button" value="Go"><br/>
- </div>
- </form>
+ </form>
+ %endif
%endif
</div>
</div>
diff -r 2630316ff75e -r 846f6a7ee80c templates/history/shared_grid.mako
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/history/shared_grid.mako Wed Aug 12 15:56:03 2009 -0400
@@ -0,0 +1,197 @@
+<%inherit file="/base.mako"/>
+<%namespace file="/message.mako" import="render_msg" />
+
+<%def name="title()">${grid.title}</%def>
+
+<%def name="javascripts()">
+ ${parent.javascripts()}
+ <script type="text/javascript">
+ ## TODO: generalize and move into galaxy.base.js
+ $(document).ready(function() {
+ $(".grid").each( function() {
+ var grid = this;
+ var checkboxes = $(this).find("input.grid-row-select-checkbox");
+ var update = $(this).find( "span.grid-selected-count" );
+ $(checkboxes).each( function() {
+ $(this).change( function() {
+ var n = $(checkboxes).filter("[checked]").size();
+ update.text( n );
+ });
+ })
+ });
+ });
+ ## Can this be moved into base.mako?
+ %if refresh_frames:
+ %if 'masthead' in refresh_frames:
+ ## Refresh masthead == user changes (backward compatibility)
+ if ( parent.user_changed ) {
+ %if trans.user:
+ parent.user_changed( "${trans.user.email}", ${int( app.config.is_admin_user( trans.user ) )} );
+ %else:
+ parent.user_changed( null, false );
+ %endif
+ }
+ %endif
+ %if 'history' in refresh_frames:
+ if ( parent.frames && parent.frames.galaxy_history ) {
+ parent.frames.galaxy_history.location.href="${h.url_for( controller='root', action='history')}";
+ if ( parent.force_right_panel ) {
+ parent.force_right_panel( 'show' );
+ }
+ }
+ %endif
+ %if 'tools' in refresh_frames:
+ if ( parent.frames && parent.frames.galaxy_tools ) {
+ parent.frames.galaxy_tools.location.href="${h.url_for( controller='root', action='tool_menu')}";
+ if ( parent.force_left_panel ) {
+ parent.force_left_panel( 'show' );
+ }
+ }
+ %endif
+ %endif
+ </script>
+</%def>
+
+<%def name="stylesheets()">
+ <link href="${h.url_for('/static/style/base.css')}" rel="stylesheet" type="text/css" />
+ <style>
+ ## Not generic to all grids -- move to base?
+ .count-box {
+ min-width: 1.1em;
+ padding: 5px;
+ border-width: 1px;
+ border-style: solid;
+ text-align: center;
+ display: inline-block;
+ }
+ </style>
+</%def>
+
+%if grid.standard_filters:
+ <div class="grid-header">
+ <h2>${grid.title}</h2>
+ <span class="title">Filter:</span>
+ %for i, filter in enumerate( grid.standard_filters ):
+ %if i > 0:
+ <span>|</span>
+ %endif
+ <span class="filter"><a href="${url( filter.get_url_args() )}">${filter.label}</a></span>
+ %endfor
+ </div>
+%endif
+
+%if message:
+ <p>
+ <div class="${message_type}message transient-message">${message}</div>
+ <div style="clear: both"></div>
+ </p>
+%endif
+%if msg:
+ ${render_msg( msg, messagetype )}
+%endif
+
+<form name="history_shared_by_others" action="${url()}" method="post" >
+ <table class="grid">
+ <thead>
+ <tr>
+ <th></th>
+ %for column in grid.columns:
+ %if column.visible:
+ <%
+ href = ""
+ extra = ""
+ if column.sortable:
+ if sort_key == column.key:
+ if sort_order == "asc":
+ href = url( sort=( "-" + column.key ) )
+ extra = "↓"
+ else:
+ href = url( sort=( column.key ) )
+ extra = "↑"
+ else:
+ href = url( sort=column.key )
+ %>
+ <th\
+ %if column.ncells > 1:
+ colspan="${column.ncells}"
+ %endif
+ >
+ %if href:
+ <a href="${href}">${column.label}</a>
+ %else:
+ ${column.label}
+ %endif
+ <span>${extra}</span>
+ </th>
+ %endif
+ %endfor
+ <th></th>
+ </tr>
+ </thead>
+ <tbody>
+ %for i, history in enumerate( query ):
+ <tr>
+ ## Item selection column
+ <td style="width: 1.5em;">
+ <input type="checkbox" name="id" value=${trans.security.encode_id( history.id )} class="grid-row-select-checkbox" />
+ </td>
+ ## Data columns
+ %for column in grid.columns:
+ %if column.visible:
+ <%
+ # Link
+ link = column.get_link( trans, grid, history )
+ if link:
+ href = url( **link )
+ else:
+ href = None
+ # Value (coerced to list so we can loop)
+ value = column.get_value( trans, grid, history )
+ if column.ncells == 1:
+ value = [ value ]
+ %>
+ %for cellnum, v in enumerate( value ):
+ <%
+ # Attach popup menu?
+ if column.attach_popup and cellnum == 0:
+ extra = '<a id="grid-%d-popup" class="popup-arrow" style="display: none;">▼</a>' % i
+ else:
+ extra = ""
+ %>
+ %if href:
+ <td><a href="${href}">${v}</a> ${extra}</td>
+ %else:
+ <td >${v}${extra}</td>
+ %endif
+ </td>
+ %endfor
+ %endif
+ %endfor
+ ## Actions column
+ <td>
+ <div popupmenu="grid-${i}-popup">
+ %for operation in grid.operations:
+ %if operation.allowed( history ):
+ <a class="action-button" href="${url( operation=operation.label, id=history.id )}">${operation.label}</a>
+ %endif
+ %endfor
+ </div>
+ </td>
+ </tr>
+ %endfor
+ </tbody>
+ <tfoot>
+ <tr>
+ <td></td>
+ <td colspan="100">
+ For <span class="grid-selected-count"></span> selected histories:
+ %for operation in grid.operations:
+ %if operation.allow_multiple:
+ <input type="submit" name="operation" value="${operation.label}" class="action-button">
+ %endif
+ %endfor
+ </td>
+ </tr>
+ </tfoot>
+ </table>
+</form>
diff -r 2630316ff75e -r 846f6a7ee80c templates/history/stored_grid.mako
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/history/stored_grid.mako Wed Aug 12 15:56:03 2009 -0400
@@ -0,0 +1,196 @@
+<%inherit file="/base.mako"/>
+<%def name="title()">${grid.title}</%def>
+
+%if message:
+ <p>
+ <div class="${message_type}message transient-message">${message}</div>
+ <div style="clear: both"></div>
+ </p>
+%endif
+
+<%def name="javascripts()">
+ ${parent.javascripts()}
+ <script type="text/javascript">
+ ## TODO: generalize and move into galaxy.base.js
+ $(document).ready(function() {
+ $(".grid").each( function() {
+ var grid = this;
+ var checkboxes = $(this).find("input.grid-row-select-checkbox");
+ var update = $(this).find( "span.grid-selected-count" );
+ $(checkboxes).each( function() {
+ $(this).change( function() {
+ var n = $(checkboxes).filter("[checked]").size();
+ update.text( n );
+ });
+ })
+ });
+ });
+ ## Can this be moved into base.mako?
+ %if refresh_frames:
+ %if 'masthead' in refresh_frames:
+ ## Refresh masthead == user changes (backward compatibility)
+ if ( parent.user_changed ) {
+ %if trans.user:
+ parent.user_changed( "${trans.user.email}", ${int( app.config.is_admin_user( trans.user ) )} );
+ %else:
+ parent.user_changed( null, false );
+ %endif
+ }
+ %endif
+ %if 'history' in refresh_frames:
+ if ( parent.frames && parent.frames.galaxy_history ) {
+ parent.frames.galaxy_history.location.href="${h.url_for( controller='root', action='history')}";
+ if ( parent.force_right_panel ) {
+ parent.force_right_panel( 'show' );
+ }
+ }
+ %endif
+ %if 'tools' in refresh_frames:
+ if ( parent.frames && parent.frames.galaxy_tools ) {
+ parent.frames.galaxy_tools.location.href="${h.url_for( controller='root', action='tool_menu')}";
+ if ( parent.force_left_panel ) {
+ parent.force_left_panel( 'show' );
+ }
+ }
+ %endif
+ %endif
+ </script>
+</%def>
+
+<%def name="stylesheets()">
+ <link href="${h.url_for('/static/style/base.css')}" rel="stylesheet" type="text/css" />
+ <style>
+ ## Not generic to all grids -- move to base?
+ .count-box {
+ min-width: 1.1em;
+ padding: 5px;
+ border-width: 1px;
+ border-style: solid;
+ text-align: center;
+ display: inline-block;
+ }
+ </style>
+</%def>
+
+%if grid.standard_filters:
+ <div class="grid-header">
+ <h2>${grid.title}</h2>
+ <span class="title">Filter:</span>
+ %for i, filter in enumerate( grid.standard_filters ):
+ %if i > 0:
+ <span>|</span>
+ %endif
+ <span class="filter"><a href="${url( filter.get_url_args() )}">${filter.label}</a></span>
+ %endfor
+ </div>
+%endif
+
+<form name="history_actions" action="${url()}" method="post" >
+ <table class="grid">
+ <thead>
+ <tr>
+ <th></th>
+ %for column in grid.columns:
+ %if column.visible:
+ <%
+ href = ""
+ extra = ""
+ if column.sortable:
+ if sort_key == column.key:
+ if sort_order == "asc":
+ href = url( sort=( "-" + column.key ) )
+ extra = "↓"
+ else:
+ href = url( sort=( column.key ) )
+ extra = "↑"
+ else:
+ href = url( sort=column.key )
+ %>
+ <th\
+ %if column.ncells > 1:
+ colspan="${column.ncells}"
+ %endif
+ >
+ %if href:
+ <a href="${href}">${column.label}</a>
+ %else:
+ ${column.label}
+ %endif
+ <span>${extra}</span>
+ </th>
+ %endif
+ %endfor
+ <th></th>
+ </tr>
+ </thead>
+ <tbody>
+ %for i, item in enumerate( query ):
+ <tr \
+ %if current_item == item:
+ class="current" \
+ %endif
+ >
+ ## Item selection column
+ <td style="width: 1.5em;">
+ <input type="checkbox" name="id" value=${trans.security.encode_id( item.id )} class="grid-row-select-checkbox" />
+ </td>
+ ## Data columns
+ %for column in grid.columns:
+ %if column.visible:
+ <%
+ # Link
+ link = column.get_link( trans, grid, item )
+ if link:
+ href = url( **link )
+ else:
+ href = None
+ # Value (coerced to list so we can loop)
+ value = column.get_value( trans, grid, item )
+ if column.ncells == 1:
+ value = [ value ]
+ %>
+ %for cellnum, v in enumerate( value ):
+ <%
+ # Attach popup menu?
+ if column.attach_popup and cellnum == 0:
+ extra = '<a id="grid-%d-popup" class="popup-arrow" style="display: none;">▼</a>' % i
+ else:
+ extra = ""
+ %>
+ %if href:
+ <td><a href="${href}">${v}</a> ${extra}</td>
+ %else:
+ <td >${v}${extra}</td>
+ %endif
+ </td>
+ %endfor
+ %endif
+ %endfor
+ ## Actions column
+ <td>
+ <div popupmenu="grid-${i}-popup">
+ %for operation in grid.operations:
+ %if operation.allowed( item ):
+ <a class="action-button" href="${url( operation=operation.label, id=item.id )}">${operation.label}</a>
+ %endif
+ %endfor
+ </div>
+ </td>
+ </tr>
+ %endfor
+ </tbody>
+ <tfoot>
+ <tr>
+ <td></td>
+ <td colspan="100">
+ For <span class="grid-selected-count"></span> selected histories:
+ %for operation in grid.operations:
+ %if operation.allow_multiple:
+ <input type="submit" name="operation" value="${operation.label}" class="action-button">
+ %endif
+ %endfor
+ </td>
+ </tr>
+ </tfoot>
+ </table>
+</form>
diff -r 2630316ff75e -r 846f6a7ee80c test/base/twilltestcase.py
--- a/test/base/twilltestcase.py Tue Aug 11 16:56:47 2009 -0400
+++ b/test/base/twilltestcase.py Wed Aug 12 15:56:03 2009 -0400
@@ -182,7 +182,7 @@
self.home()
self.visit_page( "root/history_options" )
if user:
- self.check_page_for_string( 'List</a> previously stored histories' )
+ self.check_page_for_string( 'Previously</a> stored histories' )
if active_datasets:
self.check_page_for_string( 'Create</a> a new empty history' )
self.check_page_for_string( 'Construct workflow</a> from current history' )
@@ -190,7 +190,7 @@
self.check_page_for_string( 'Share</a> current history' )
self.check_page_for_string( 'Change default permissions</a> for current history' )
if histories_shared_by_others:
- self.check_page_for_string( 'List</a> histories shared with you by others' )
+ self.check_page_for_string( 'Histories</a> shared with you by others' )
if activatable_datasets:
self.check_page_for_string( 'Show deleted</a> datasets in current history' )
self.check_page_for_string( 'Rename</a> current history' )
diff -r 2630316ff75e -r 846f6a7ee80c test/functional/test_history_functions.py
--- a/test/functional/test_history_functions.py Tue Aug 11 16:56:47 2009 -0400
+++ b/test/functional/test_history_functions.py Wed Aug 12 15:56:03 2009 -0400
@@ -183,7 +183,7 @@
self.view_shared_histories( check_str=history3.name, check_str2=admin_user.email )
self.clone_history( self.security.encode_id( history3.id ),
'activatable',
- check_str_after_submit='is now included in your list of stored histories.' )
+ check_str_after_submit='is now included in your previously 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 ) ) \
@@ -216,7 +216,7 @@
# Test cloning activatable datasets
self.clone_history( self.security.encode_id( history3.id ),
'activatable',
- check_str_after_submit='is now included in your list of stored histories.' )
+ check_str_after_submit='is now included in your previously 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 ) ) \
@@ -244,7 +244,7 @@
# 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.' )
+ check_str_after_submit='is now included in your previously 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 ) ) \
@@ -355,7 +355,7 @@
# Clone restricted history5
self.clone_history( self.security.encode_id( history5.id ),
'activatable',
- check_str_after_submit='is now included in your list of stored histories.' )
+ check_str_after_submit='is now included in your previously 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 ) ) \
@@ -411,7 +411,7 @@
# Clone restricted history5
self.clone_history( self.security.encode_id( history5.id ),
'activatable',
- check_str_after_submit='is now included in your list of stored histories.' )
+ check_str_after_submit='is now included in your previously 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 ) ) \
@@ -484,7 +484,7 @@
# Clone restricted history5
self.clone_history( self.security.encode_id( history5.id ),
'activatable',
- check_str_after_submit='is now included in your list of stored histories.' )
+ check_str_after_submit='is now included in your previously 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 ) ) \
@@ -522,7 +522,7 @@
# Clone restricted history5
self.clone_history( self.security.encode_id( history5.id ),
'activatable',
- check_str_after_submit='is now included in your list of stored histories.' )
+ check_str_after_submit='is now included in your previously 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 ) ) \
@@ -591,15 +591,6 @@
pass
self.logout()
self.login( email=admin_user.email )
- email = '%s,%s' % ( regular_user2.email, regular_user3.email )
- check_str_after_submit = 'The following datasets can be shared with %s with no changes' % email
- check_str_after_submit2 = 'The following datasets can be shared with %s by updating their permissions' % email
- action_check_str_after_submit = 'History Options'
- self.share_current_history( email,
- check_str_after_submit=check_str_after_submit,
- check_str_after_submit2=check_str_after_submit2,
- action='no_share',
- action_check_str_after_submit=action_check_str_after_submit )
def test_070_history_show_and_hide_deleted_datasets( self ):
"""Testing displaying deleted history items"""
# Logged in as admin_user
1
0
details: http://www.bx.psu.edu/hg/galaxy/rev/2630316ff75e
changeset: 2555:2630316ff75e
user: Dan Blankenberg <dan(a)bx.psu.edu>
date: Tue Aug 11 16:56:47 2009 -0400
description:
Fix error reporting template.
1 file(s) affected in this change:
templates/dataset/errors.mako
diffs (15 lines):
diff -r 690bbab07115 -r 2630316ff75e templates/dataset/errors.mako
--- a/templates/dataset/errors.mako Tue Aug 11 14:59:23 2009 -0400
+++ b/templates/dataset/errors.mako Tue Aug 11 16:56:47 2009 -0400
@@ -63,7 +63,10 @@
</div>
<div class="form-row">
<label>Message</label>
- <textarea name="message", rows="10" cols="40" />
+ <textarea name="message" rows="10" cols="40"></textarea>
+ </div>
+ <div class="form-row">
+ <input type="submit" value="Report"/>
</div>
</form>
</div>
1
0
12 Aug '09
details: http://www.bx.psu.edu/hg/galaxy/rev/690bbab07115
changeset: 2554:690bbab07115
user: Dan Blankenberg <dan(a)bx.psu.edu>
date: Tue Aug 11 14:59:23 2009 -0400
description:
Fix a bug with specifying a Tool Class different from the base. The element object returned by root.find('type') has a boolean value of False even when the element exists (value is None when it does not.)
1 file(s) affected in this change:
lib/galaxy/tools/__init__.py
diffs (12 lines):
diff -r 228dd6f56b68 -r 690bbab07115 lib/galaxy/tools/__init__.py
--- a/lib/galaxy/tools/__init__.py Tue Aug 11 13:21:20 2009 -0400
+++ b/lib/galaxy/tools/__init__.py Tue Aug 11 14:59:23 2009 -0400
@@ -128,7 +128,7 @@
tree = util.parse_xml( config_file )
root = tree.getroot()
# Allow specifying a different tool subclass to instantiate
- if root.find( "type" ):
+ if root.find( "type" ) is not None:
type_elem = root.find( "type" )
module = type_elem.get( 'module', 'galaxy.tools' )
cls = type_elem.get( 'class' )
1
0
12 Aug '09
details: http://www.bx.psu.edu/hg/galaxy/rev/228dd6f56b68
changeset: 2553:228dd6f56b68
user: rc
date: Tue Aug 11 13:21:20 2009 -0400
description:
Added browse button on csv importers in LIMS
Fixed a bug in creating request types
9 file(s) affected in this change:
lib/galaxy/web/controllers/admin.py
lib/galaxy/web/controllers/forms.py
lib/galaxy/web/controllers/library.py
lib/galaxy/web/controllers/requests.py
templates/admin/forms/create_form.mako
templates/admin/requests/add_states.mako
templates/admin/requests/create_request_type.mako
templates/admin/requests/manage_request_types.mako
templates/requests/show_request.mako
diffs (332 lines):
diff -r c021935a25ac -r 228dd6f56b68 lib/galaxy/web/controllers/admin.py
--- a/lib/galaxy/web/controllers/admin.py Mon Aug 10 14:00:47 2009 -0400
+++ b/lib/galaxy/web/controllers/admin.py Tue Aug 11 13:21:20 2009 -0400
@@ -710,7 +710,7 @@
library=trans.app.model.Library.get( id ),
deleted=deleted,
created_ldda_ids=created_ldda_ids,
- forms=get_all_forms( trans ),
+ forms=get_all_forms( trans, filter=dict(deleted=False) ),
msg=msg,
messagetype=messagetype,
show_deleted=show_deleted )
@@ -1628,7 +1628,7 @@
library_item_desc = 'library'
response_action = 'browse_library'
response_id = library_id
- forms = get_all_forms( trans )
+ forms = get_all_forms( trans, filter=dict(deleted=False) )
if not forms:
msg = "There are no forms on which to base the template, so create a form and "
msg += "try again to add the information template to the %s." % library_item_desc
@@ -1982,19 +1982,20 @@
messagetype = params.get( 'messagetype', 'done' )
if params.get( 'create', False ):
return trans.fill_template( '/admin/requests/create_request_type.mako',
- forms=get_all_forms( trans ),
+ forms=get_all_forms( trans,
+ filter=dict(deleted=False) ),
msg=msg,
messagetype=messagetype)
- elif params.get( 'add_states', False ):
+ elif params.get( 'define_states_button', False ):
return trans.fill_template( '/admin/requests/add_states.mako',
- sample_type_name=util.restore_text( params.name ),
+ request_type_name=util.restore_text( params.name ),
desc=util.restore_text( params.description ),
num_states=int(util.restore_text( params.num_states )),
request_form_id=int(util.restore_text( params.request_form_id )),
sample_form_id=int(util.restore_text( params.sample_form_id )),
msg=msg,
messagetype=messagetype)
- elif params.get( 'save_new', False ):
+ elif params.get( 'save_request_type', False ):
st, msg = self._save_request_type(trans, **kwd)
if not st:
return trans.fill_template( '/admin/requests/create_request_type.mako',
@@ -2005,7 +2006,7 @@
action='manage_request_types',
msg='Request type <b>%s</b> has been created' % st.name,
messagetype='done') )
- elif params.get('edit', False) == 'True':
+ elif params.get('view', False):
rt = trans.app.model.RequestType.get(int(util.restore_text( params.id )))
ss_list = trans.app.model.SampleState.filter(trans.app.model.SampleState.table.c.request_type_id == rt.id).all()
return trans.fill_template( '/admin/requests/view_request_type.mako',
@@ -2039,9 +2040,6 @@
for ss in ss_list:
ss.delete()
ss.flush()
- # unsubmitted state
- #ss = trans.app.model.SampleState('Unsubmitted', 'Sample not yet submitted', rt)
- ##ss.flush()
for i in range( num_states ):
name = util.restore_text( params.get( 'new_element_name_%i' % i, None ))
desc = util.restore_text( params.get( 'new_element_description_%i' % i, None ))
@@ -2075,5 +2073,5 @@
rt.flush()
return trans.response.send_redirect( web.url_for( controller='admin',
action='manage_request_types',
- msg='Request type <b>%s</b> has been deleted' % rt.name,
+ msg='Request type <b>%s</b> has been undeleted' % rt.name,
messagetype='done') )
diff -r c021935a25ac -r 228dd6f56b68 lib/galaxy/web/controllers/forms.py
--- a/lib/galaxy/web/controllers/forms.py Mon Aug 10 14:00:47 2009 -0400
+++ b/lib/galaxy/web/controllers/forms.py Tue Aug 11 13:21:20 2009 -0400
@@ -62,7 +62,7 @@
self.current_form[ 'fields' ] = []
inputs = [ ( 'Name', TextField( 'name', 40, self.current_form[ 'name' ] ) ),
( 'Description', TextField( 'description', 40, self.current_form[ 'desc' ] ) ),
- ( 'Import from csv file (Optional)', TextField( 'csv_file', 40, '' ) ) ]
+ ( 'Import from csv file (Optional)', FileField( 'file_data', 40, '' ) ) ]
return trans.fill_template( '/admin/forms/create_form.mako',
inputs=inputs,
msg=msg,
@@ -123,13 +123,13 @@
return self.__show( trans=trans, form=fd, msg=msg, messagetype=messagetype, empty_form=True, **kwd )
# Delete a field
elif params.get( 'remove_button', False ):
- self.__update_current_form( **kwd )
+ self.__update_current_form( trans, **kwd )
index = int( kwd[ 'remove_button' ].split( ' ' )[2] ) - 1
self.__remove_field( index )
return self.__show( trans=trans, form=fd, msg=msg, messagetype=messagetype, **kwd )
# Save changes
elif params.get( 'save_changes_button', False ):
- self.__update_current_form( **kwd )
+ self.__update_current_form( trans, **kwd )
fd_new, msg = self.__save_form( trans, fd.form_definition_current.id, **kwd )
if not fd_new:
return self.__show( trans=trans, form=fd, msg=msg, messagetype='error', **kwd )
@@ -139,7 +139,7 @@
return self.__show( trans=trans, form=fd, msg=msg, messagetype=messagetype, **kwd )
#Add a field
elif params.get( 'add_field_button', False ):
- self.__update_current_form( **kwd )
+ self.__update_current_form( trans, **kwd )
self.__add_field()
# show the form again with one empty field
return self.__show( trans=trans, form=fd, msg=msg, messagetype=messagetype, **kwd )
@@ -151,7 +151,7 @@
messagetype=messagetype )
# Refresh page, SelectField is selected/deselected as the type of a field
elif params.get( 'refresh', False ):
- self.__update_current_form( **kwd )
+ self.__update_current_form( trans, **kwd )
return self.__show( trans=trans, form=fd, msg=msg, messagetype=messagetype, **kwd )
# Remove SelectField option
elif params.get( 'select_box_options', False ) == 'remove':
@@ -235,29 +235,28 @@
if not util.restore_text(params.get( 'field_name_%i' % i, None )):
return None, "All the field label(s) must be completed."
return True, ''
- def __get_form(self, **kwd):
+ def __get_form(self, trans, **kwd):
params = util.Params( kwd )
name = util.restore_text( params.name )
desc = util.restore_text( params.description ) or ""
- if params.get( 'csv_file', None ):
- csv_file = util.restore_text( params.get( 'csv_file', '' ) )
- if csv_file:
- fields = self.__import_fields(csv_file)
- else:
+ csv_file = params.get( 'file_data', '' )
+ if csv_file == '':
# set form fields
fields = []
for i in range( len(self.current_form['fields']) ):
fields.append(self.__get_field(i, **kwd))
fields = fields
+ else:
+ fields = self.__import_fields(trans, csv_file)
return name, desc, fields
- def __update_current_form(self, **kwd):
- name, desc, fields = self.__get_form(**kwd)
+ def __update_current_form(self, trans, **kwd):
+ name, desc, fields = self.__get_form(trans, **kwd)
self.current_form = {}
self.current_form['name'] = name
self.current_form['desc'] = desc
self.current_form['fields'] = fields
- def __import_fields(self, csv_file):
+ def __import_fields(self, trans, csv_file):
'''
"company","name of the company", "True", "required", "TextField",,
"due date","turnaround time", "True", "optional", "SelectField","24 hours, 1 week, 1 month"
@@ -265,7 +264,7 @@
import csv
fields = []
try:
- reader = csv.reader(open(csv_file))
+ reader = csv.reader(csv_file.file)
for row in reader:
options = row[5].split(',')
fields.append({'label': row[0],
@@ -291,7 +290,7 @@
if not flag:
return None, msg
fd = trans.app.model.FormDefinition()
- fd.name, fd.desc, fd.fields = self.__get_form(**kwd)
+ fd.name, fd.desc, fd.fields = self.__get_form(trans, **kwd)
if fdc_id: # save changes to the existing form
# change the pointer in the form_definition_current table to point
# to this new record
@@ -415,9 +414,11 @@
'''
if all_versions:
return trans.app.model.FormDefinition.query().all()
+ if filter:
+ fdc_list = trans.app.model.FormDefinitionCurrent.query().filter_by(**filter)
else:
fdc_list = trans.app.model.FormDefinitionCurrent.query().all()
- return [ fdc.latest_form for fdc in fdc_list ]
+ return [ fdc.latest_form for fdc in fdc_list ]
def get_form_widgets( trans, form, contents={} ):
'''
Return the list of widgets that comprise a form definition,
diff -r c021935a25ac -r 228dd6f56b68 lib/galaxy/web/controllers/library.py
--- a/lib/galaxy/web/controllers/library.py Mon Aug 10 14:00:47 2009 -0400
+++ b/lib/galaxy/web/controllers/library.py Tue Aug 11 13:21:20 2009 -0400
@@ -1130,7 +1130,7 @@
library_id=library_id,
folder_id=folder_id,
ldda_id=ldda_id,
- forms=get_all_forms( trans ),
+ forms=get_all_forms( trans, filter=dict(deleted=False) ),
msg=msg,
messagetype=messagetype )
@web.expose
diff -r c021935a25ac -r 228dd6f56b68 lib/galaxy/web/controllers/requests.py
--- a/lib/galaxy/web/controllers/requests.py Mon Aug 10 14:00:47 2009 -0400
+++ b/lib/galaxy/web/controllers/requests.py Tue Aug 11 13:21:20 2009 -0400
@@ -7,6 +7,8 @@
import logging, tempfile, zipfile, tarfile, os, sys
from galaxy.web.form_builder import *
from datetime import datetime, timedelta
+from cgi import escape, FieldStorage
+
log = logging.getLogger( __name__ )
@@ -216,11 +218,10 @@
message="Invalid request ID",
**kwd) )
if params.get('import_samples_button', False) == 'Import samples':
- import traceback
try:
- fname = params.get('import_samples', '')
+ file_obj = params.get('file_data', '')
import csv
- reader = csv.reader(open(fname))
+ reader = csv.reader(file_obj.file)
for row in reader:
self.current_samples.append([row[0], row[1:]])
return trans.fill_template( '/requests/show_request.mako',
@@ -234,7 +235,7 @@
return trans.response.send_redirect( web.url_for( controller='requests',
action='list',
status='error',
- message='Error in importing <b>%s</b> samples file' % fname,
+ message='Error in importing <b>%s</b> samples file' % file_obj.file,
**kwd))
elif params.get('add_sample_button', False) == 'Add New':
# save the all (saved+unsaved) sample info in 'current_samples'
diff -r c021935a25ac -r 228dd6f56b68 templates/admin/forms/create_form.mako
--- a/templates/admin/forms/create_form.mako Mon Aug 10 14:00:47 2009 -0400
+++ b/templates/admin/forms/create_form.mako Tue Aug 11 13:21:20 2009 -0400
@@ -8,7 +8,7 @@
<div class="toolForm">
<div class="toolFormTitle">Create a new form definition</div>
<div class="toolFormBody">
- <form name="create_form" action="${h.url_for( controller='forms', action='new', create_form=True )}" method="post" >
+ <form name="create_form" action="${h.url_for( controller='forms', action='new', create_form=True )}" enctype="multipart/form-data" method="post" >
%for label, input in inputs:
<div class="form-row">
<label>${label}</label>
diff -r c021935a25ac -r 228dd6f56b68 templates/admin/requests/add_states.mako
--- a/templates/admin/requests/add_states.mako Mon Aug 10 14:00:47 2009 -0400
+++ b/templates/admin/requests/add_states.mako Tue Aug 11 13:21:20 2009 -0400
@@ -6,8 +6,8 @@
%endif
<div class="toolForm">
- <div class="toolFormTitle">Create ${num_states} states for the '${sample_type_name}' request type</div>
- <form name="new_form_fields" action="${h.url_for( controller='admin', action='request_type', save_new=True, create=False, edit=False, name=sample_type_name, description=desc, num_states=num_states, request_form_id=request_form_id, sample_form_id=sample_form_id)}" method="post" >
+ <div class="toolFormTitle">Create ${num_states} states for the '${request_type_name}' request type</div>
+ <form name="new_form_fields" action="${h.url_for( controller='admin', action='request_type', name=request_type_name, description=desc, num_states=num_states, request_form_id=request_form_id, sample_form_id=sample_form_id)}" method="post" >
<div class="toolFormBody">
%for element_count in range( num_states ):
<div class="form-row">
@@ -20,7 +20,7 @@
%endfor
</div>
<div class="form-row">
- <input type="submit" name="save_new_sample_type" value="Save"/>
+ <input type="submit" name="save_request_type" value="Save"/>
</div>
</form>
</div>
\ No newline at end of file
diff -r c021935a25ac -r 228dd6f56b68 templates/admin/requests/create_request_type.mako
--- a/templates/admin/requests/create_request_type.mako Mon Aug 10 14:00:47 2009 -0400
+++ b/templates/admin/requests/create_request_type.mako Tue Aug 11 13:21:20 2009 -0400
@@ -12,7 +12,7 @@
Create a form definition first to create a new request type.
%else:
<div class="toolFormBody">
- <form name="create_request_type" action="${h.url_for( controller='admin', action='request_type', add_states=True, create=False, edit=False )}" method="post" >
+ <form name="create_request_type" action="${h.url_for( controller='admin', action='request_type')}" method="post" >
<div class="form-row">
<label>Name:</label>
<div style="float: left; width: 250px; margin-right: 10px;">
@@ -60,7 +60,7 @@
<div style="clear: both"></div>
</div>
<div class="form-row">
- <input type="submit" name="create_request_type_button" value="Define states"/>
+ <input type="submit" name="define_states_button" value="Define states"/>
</div>
</form>
</div>
diff -r c021935a25ac -r 228dd6f56b68 templates/admin/requests/manage_request_types.mako
--- a/templates/admin/requests/manage_request_types.mako Mon Aug 10 14:00:47 2009 -0400
+++ b/templates/admin/requests/manage_request_types.mako Tue Aug 11 13:21:20 2009 -0400
@@ -46,7 +46,7 @@
%for request_type in request_types:
<tr>
<td>
- <a href="${h.url_for( controller='admin', action='request_type', edit='True', id=request_type.id)}">${request_type.name}</a>
+ <a href="${h.url_for( controller='admin', action='request_type', view='True', id=request_type.id)}">${request_type.name}</a>
<a id="request_type-${request_type.id}-popup" class="popup-arrow" style="display: none;">▼</a>
%if request_type.deleted:
<div popupmenu="request_type-${request_type.id}-popup">
diff -r c021935a25ac -r 228dd6f56b68 templates/requests/show_request.mako
--- a/templates/requests/show_request.mako Mon Aug 10 14:00:47 2009 -0400
+++ b/templates/requests/show_request.mako Tue Aug 11 13:21:20 2009 -0400
@@ -10,7 +10,6 @@
<div class="grid-header">
<h2>Sequencing Request "${request.name}"</h2>
</div>
-
<ul class="manage-table-actions">
%if request.unsubmitted() and request.samples:
@@ -109,7 +108,7 @@
<div class="toolForm">
##<div class="toolFormTitle">Samples (${len(request.samples)})</div>
- <form id="edit_form" name="edit_form" action="${h.url_for( controller='requests', action='show_request' )}" method="post" >
+ <form id="show_request" name="show_request" action="${h.url_for( controller='requests', action='show_request' )}" enctype="multipart/form-data" method="post" >
<div class="form-row">
%if current_samples:
<table class="grid">
@@ -186,7 +185,7 @@
<td>
##<div class="form-row">
<label>Import from csv file</label>
- <input type="text" name="import_samples" value="" size="20"/>
+ <input type="file" name="file_data" />
<input type="submit" name="import_samples_button" value="Import samples"/>
##</div>
</td>
1
0
Hello,
Here's a fix for the Galaxy-Reports + MySQL problem.
Note: this patch is incompatible with PostgreSQL, so DO NOT apply it unless you know what you're doing.
Bug description
---------------
The galaxy reports webapp shows jobs-per-month, errors-per-month and users-per-month reports.
All these controllers use Postgres's 'date_trunc()' function, which MySQL doesn't have.
The result is the exception shown in the email below.
Details
-------
jobs,py and users.py call date_trunc() in two ways:
1. In the SELECT clause to extract/format the result
2. In the GROUP BY clause to group results by day/month/year.
Solution
--------
1. In the SELECT part, the date_trunc() is removed. IMHO, it should not affect any functionality, because the SELECT returns a DATE object which is later formatted with strftime. So having the date value contain extra information (such as day/hour/minute/second etc.) is not a problem. This is also the reason similar MySQL functions can't be used - they return a string field and not a DATE field -> causes Python to complain about "object type string doesn't have a strftime method" (or something like that).
2. In the GROUP BY part, date_trunc() is replaced by three MySQL functions: day(), month(), year().
Tested with MySQL 5.0.45 and the latest galaxy version - and seems to work.
Comments are welcomed,
gordon.
Assaf Gordon wrote, On 06/26/2009 01:18 PM:
> Hello,
>
> I have a galaxy server running with a MySQL database.
> When I load the galaxy-reports server, several pages fail with missing
> SQL function 'DATE_TRUNC' error, like so:
> ------------------
> Module paste.exceptions.errormiddleware:144 in __call__
> Module paste.debug.prints:98 in __call__
> Module paste.wsgilib:539 in intercept_output
> Module beaker.session:103 in __call__
> Module paste.recursive:80 in __call__
> Module paste.httpexceptions:632 in __call__
> Module galaxy.web.framework.base:126 in __call__
> << kwargs.pop( '_', None )
> try:
> body = method( trans, **kwargs )
> except Exception, e:
> body = self.handle_controller_exception( e, trans,
> **kwargs )>> body = method( trans, **kwargs )
> Module galaxy.webapps.reports.controllers.jobs:311 in per_month_in_error
> << order_by = [ sa.desc( 'date' ) ] )
> jobs = []
> for row in q.execute():
> jobs.append( ( row.date.strftime( "%Y-%m" ),
> row.total_jobs,>> for row in q.execute():
> Module sqlalchemy.sql.expression:1087 in execute
> Module sqlalchemy.engine.base:1219 in execute_clauseelement
> Module sqlalchemy.engine.base:895 in execute_clauseelement
> Module sqlalchemy.engine.base:907 in _execute_compiled
> Module sqlalchemy.engine.base:916 in __execute_raw
> Module sqlalchemy.engine.base:960 in _cursor_execute
> Module sqlalchemy.engine.base:942 in _handle_dbapi_exception
> OperationalError: (OperationalError) (1305, 'FUNCTION
> publicgalaxy.date_trunc does not exist') u'SELECT date_trunc(%s,
> date(job.create_time)) AS date, count(job.id) AS total_jobs \nFROM job
> \nWHERE job.state = %s GROUP BY date_trunc(%s, date(job.create_time))
> ORDER BY date DESC' ['month', 'error', 'month']
> ---------------
>
> Googling for 'DATE_TRUNC' shows that this is a PostgreSQL function,
> which probably explains why it is missing in MySQL.
>
> Is there a quick workaround for this ?
> Maybe create a MySQL stored procedure named 'DATE_TRUNC' that acts the
> same as the PostgreSQL one ?
>
> Thanks,
> Gordon.
>
>
> _______________________________________________
> galaxy-bugs mailing list
> galaxy-bugs(a)bx.psu.edu
> http://mail.bx.psu.edu/cgi-bin/mailman/listinfo/galaxy-bugs
1
0
Hello,
A new version of FASTX-Toolkit is available for download at
http://cancan.cshl.edu/labmembers/gordon/fastx_toolkit/index.html
The small improvements are:
Fix 'bus error' bug on Mac OS X.
New tool: FASTX-Renamer (renames sequence identifiers in a FASTQ/A file).
Command line support for FASTQ ASCII quality scores with user-defined offsets.
Improved Barcode-Splitter tool: no need for external webserver anymore.
Uses libgtextutils-0.5 library (as a dynamic library)
You can test these tools on our demo Galaxy server:
http://cancan.cshl.edu/publicgalaxy/
Other tools (awk,sed,grep,sort,crosstab, etc) are also available.
Comments are welcomed,
gordon.
1
0
10 Aug '09
details: http://www.bx.psu.edu/hg/galaxy/rev/c021935a25ac
changeset: 2552:c021935a25ac
user: Dan Blankenberg <dan(a)bx.psu.edu>
date: Mon Aug 10 14:00:47 2009 -0400
description:
Have update_ucsc.sh.sample add 'chrom' dir to tool-data/shared/ucsc as needed. This empty directory was lost from the repository in the svn to hg conversion.
1 file(s) affected in this change:
cron/updateucsc.sh.sample
diffs (26 lines):
diff -r 5b405a43c406 -r c021935a25ac cron/updateucsc.sh.sample
--- a/cron/updateucsc.sh.sample Mon Aug 10 11:53:28 2009 -0400
+++ b/cron/updateucsc.sh.sample Mon Aug 10 14:00:47 2009 -0400
@@ -9,8 +9,20 @@
export PYTHONPATH=${GALAXY}/lib
# setup directories
-mkdir ${GALAXY}/tool-data/shared/ucsc/new
-mkdir ${GALAXY}/tool-data/shared/ucsc/chrom/new
+echo "Creating required directories."
+DIRS="
+${GALAXY}/tool-data/shared/ucsc/new
+${GALAXY}/tool-data/shared/ucsc/chrom
+${GALAXY}/tool-data/shared/ucsc/chrom/new
+"
+for dir in $DIRS; do
+ if [ ! -d $dir ]; then
+ echo "Creating $dir"
+ mkdir $dir
+ else
+ echo "$dir already exists, continuing."
+ fi
+done
date
echo "Updating UCSC shared data tables."
1
0
10 Aug '09
details: http://www.bx.psu.edu/hg/galaxy/rev/5b405a43c406
changeset: 2551:5b405a43c406
user: Dan Blankenberg <dan(a)bx.psu.edu>
date: Mon Aug 10 11:53:28 2009 -0400
description:
Enable the cleanup_datasets script to purge Dataset Instances (HDAs, LDDAs) and mark base datasets as deleted without requiring the container (history, library/folder) to be deleted.
3 file(s) affected in this change:
scripts/cleanup_datasets/cleanup_datasets.py
scripts/cleanup_datasets/delete_datasets.sh
scripts/cleanup_datasets/delete_datasets_main.sh
diffs (83 lines):
diff -r 50ce3d8388f8 -r 5b405a43c406 scripts/cleanup_datasets/cleanup_datasets.py
--- a/scripts/cleanup_datasets/cleanup_datasets.py Fri Aug 07 16:11:14 2009 -0400
+++ b/scripts/cleanup_datasets/cleanup_datasets.py Mon Aug 10 11:53:28 2009 -0400
@@ -36,13 +36,15 @@
parser.add_option( "-5", "--purge_folders", action="store_true", dest="purge_folders", default=False, help="purge deleted library folders" )
+ parser.add_option( "-6", "--delete_datasets", action="store_true", dest="delete_datasets", default=False, help="mark deletable datasets as deleted and purge associated dataset instances" )
+
( options, args ) = parser.parse_args()
ini_file = args[0]
if not ( options.purge_folders ^ options.delete_userless_histories ^ \
options.purge_libraries ^ options.purge_histories ^ \
- options.purge_datasets ):
+ options.purge_datasets ^ options.delete_datasets ):
parser.print_help()
sys.exit(0)
@@ -83,6 +85,8 @@
purge_libraries( app, cutoff_time, options.remove_from_disk, info_only = options.info_only, force_retry = options.force_retry )
elif options.purge_folders:
purge_folders( app, cutoff_time, options.remove_from_disk, info_only = options.info_only, force_retry = options.force_retry )
+ elif options.delete_datasets:
+ delete_datasets( app, cutoff_time, options.remove_from_disk, info_only = options.info_only, force_retry = options.force_retry )
sys.exit(0)
@@ -197,7 +201,36 @@
print '# Purged %d folders.' % ( folder_count ), '\n'
print "Elapsed time: ", stop - start, "\n"
-def purge_datasets( app, cutoff_time, remove_from_disk, info_only = False, repurge = False, force_retry = False ):
+def delete_datasets( app, cutoff_time, remove_from_disk, info_only = False, force_retry = False ):
+ # Marks datasets as deleted if associated items are all deleted.
+ print '# The following datasets have been marked deleted'
+ start = time.clock()
+ if force_retry:
+ datasets = app.model.Dataset.filter( app.model.LibraryDatasetDatasetAssociation.table.c.update_time < cutoff_time ).all() + app.model.Dataset.filter( app.model.HistoryDatasetAssociation.table.c.update_time < cutoff_time ).all()
+ else:
+ datasets = app.model.Dataset.filter( and_( app.model.HistoryDatasetAssociation.table.c.deleted==True,
+ app.model.Dataset.table.c.deleted == False,
+ app.model.HistoryDatasetAssociation.table.c.update_time < cutoff_time ) ).all()
+ datasets = datasets + app.model.Dataset.filter( and_( app.model.LibraryDatasetDatasetAssociation.table.c.deleted==True,
+ app.model.Dataset.table.c.deleted == False,
+ app.model.LibraryDatasetDatasetAssociation.table.c.update_time < cutoff_time ) ).all()
+ skip = []
+ deleted_dataset_count = 0
+ deleted_instance_count = 0
+ for dataset in datasets:
+ if dataset.id not in skip and _dataset_is_deletable( dataset ):
+ deleted_dataset_count += 1
+ print "Dataset:", dataset.id
+ for dataset_instance in dataset.history_associations + dataset.library_associations:
+ print "\tAssociated Dataset instance:", dataset_instance.__class__.__name__, dataset_instance.id
+ _purge_dataset_instance( dataset_instance, app, remove_from_disk, include_children = True, info_only = info_only )
+ deleted_instance_count += 1
+ skip.append( dataset.id )
+ print
+ print '# Examined %d datasets, marked %d as deleted and purged %d dataset instances\n' % ( len( skip ), deleted_dataset_count, deleted_instance_count )
+ print "Elapsed time: ", time.clock() - start, "\n"
+
+def purge_datasets( app, cutoff_time, remove_from_disk, info_only = False, force_retry = False ):
# Purges deleted datasets whose update_time is older than cutoff_time. Files may or may
# not be removed from disk.
dataset_count = 0
diff -r 50ce3d8388f8 -r 5b405a43c406 scripts/cleanup_datasets/delete_datasets.sh
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/cleanup_datasets/delete_datasets.sh Mon Aug 10 11:53:28 2009 -0400
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+cd `dirname $0`/../..
+python ./scripts/cleanup_datasets/cleanup_datasets.py ./universe_wsgi.ini -d 10 -6 -r $@ >> ./scripts/cleanup_datasets/delete_datasets.log
diff -r 50ce3d8388f8 -r 5b405a43c406 scripts/cleanup_datasets/delete_datasets_main.sh
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/cleanup_datasets/delete_datasets_main.sh Mon Aug 10 11:53:28 2009 -0400
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+cd `dirname $0`/../..
+python ./scripts/cleanup_datasets/cleanup_datasets.py ./universe_wsgi.ini -d 60 -6 -r $@ >> ./scripts/cleanup_datasets/delete_datasets.log
1
0