galaxy-commits
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
September 2011
- 1 participants
- 91 discussions
commit/galaxy-central: dan: Fix for maf_utilities.build_maf_index_species_chromosomes() return values when a bad MAF is provided.
by Bitbucket 23 Sep '11
by Bitbucket 23 Sep '11
23 Sep '11
1 new changeset in galaxy-central:
http://bitbucket.org/galaxy/galaxy-central/changeset/ef89e4d62007/
changeset: ef89e4d62007
user: dan
date: 2011-09-23 16:38:27
summary: Fix for maf_utilities.build_maf_index_species_chromosomes() return values when a bad MAF is provided.
affected #: 1 file (-1 bytes)
--- a/lib/galaxy/tools/util/maf_utilities.py Fri Sep 23 10:07:19 2011 -0400
+++ b/lib/galaxy/tools/util/maf_utilities.py Fri Sep 23 10:38:27 2011 -0400
@@ -227,7 +227,7 @@
except Exception, e:
#most likely a bad MAF
log.debug( 'Building MAF index on %s failed: %s' % ( filename, e ) )
- return ( None, [], {} )
+ return ( None, [], {}, 0 )
return ( indexes, species, species_chromosomes, blocks )
#builds and returns ( index, index_filename ) for specified maf_file
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
1 new changeset in galaxy-central:
http://bitbucket.org/galaxy/galaxy-central/changeset/651a627ec387/
changeset: 651a627ec387
user: greg
date: 2011-09-23 16:07:19
summary: Replace the online form approach to adding new entries into the tool_data_table_conf.xml file by requiring a tool shed user to upload a tool_data_table_conf.xml.sample file that includes all entries required by tools in the repository. Steamline the local Galaxy tool install process for tools that include dynamically generated select lists so that all of this occurs behind the scene.
affected #: 6 files (-1 bytes)
--- a/lib/galaxy/web/base/controller.py Fri Sep 23 10:01:18 2011 -0400
+++ b/lib/galaxy/web/base/controller.py Fri Sep 23 10:07:19 2011 -0400
@@ -2417,6 +2417,8 @@
if returncode == 0:
# The repository_tools_tups list contains tuples of ( relative_path_to_tool_config, tool ) pairs
repository_tools_tups = []
+ # The sample_files list contains all files whose name ends in .sample
+ sample_files = []
for root, dirs, files in os.walk( repo_files_dir ):
if not root.find( '.hg' ) >= 0 and not root.find( 'hgrc' ) >= 0:
if '.hg' in dirs:
@@ -2426,6 +2428,10 @@
if 'hgrc' in files:
# Don't include hgrc files in commit.
files.remove( 'hgrc' )
+ # Find all special .sample files first.
+ for name in files:
+ if name.endswith( '.sample' ):
+ sample_files.append( os.path.abspath( os.path.join( root, name ) ) )
for name in files:
# Find all tool configs.
if name.endswith( '.xml' ):
@@ -2434,6 +2440,30 @@
try:
repository_tool = trans.app.toolbox.load_tool( full_path )
if repository_tool:
+ # Check all of the tool's input parameters, looking for any that are dynamically
+ # generated using external data files to make sure the files exist.
+ for input_param in repository_tool.input_params:
+ if isinstance( input_param, tools.parameters.basic.SelectToolParameter ) and input_param.is_dynamic:
+ # If the tool refers to .loc files or requires an entry in the
+ # tool_data_table_conf.xml, make sure all requirements exist.
+ options = input_param.dynamic_options or input_param.options
+ if options.missing_tool_data_table_name:
+ # The repository must contain a tool_data_table_conf.xml.sample file.
+ for sample_file in sample_files:
+ head, tail = os.path.split( sample_file )
+ if tail == 'tool_data_table_conf.xml.sample':
+ error, correction_msg = handle_sample_tool_data_table_conf_file( trans, sample_file )
+ if error:
+ log.debug( exception_msg )
+ break
+ elif options.missing_index_file:
+ missing_head, missing_tail = os.path.split( options.missing_index_file )
+ # The repository must contain the required xxx.loc.sample file.
+ for sample_file in sample_files:
+ sample_head, sample_tail = os.path.split( sample_file )
+ if sample_tail == '%s.sample' % missing_tail:
+ copy_sample_loc_file( trans, sample_file )
+ break
# At this point, we need to lstrip tool_path from relative_path.
tup_path = relative_path.replace( tool_path, '' ).lstrip( '/' )
repository_tools_tups.append( ( tup_path, repository_tool ) )
@@ -2611,6 +2641,7 @@
"""
Write an in-memory tool panel section so we can load it into the tool panel and then
append it to the appropriate shed tool config.
+ TODO: re-write using ElementTree.
"""
tmp_url = self.__clean_repository_clone_url( repository_clone_url )
section_str = ''
@@ -2632,9 +2663,7 @@
## ---- Utility methods -------------------------------------------------------
def build_shed_tool_conf_select_field( trans ):
- """
- Build a SelectField whose options are the keys in trans.app.toolbox.shed_tool_confs.
- """
+ """Build a SelectField whose options are the keys in trans.app.toolbox.shed_tool_confs."""
options = []
for shed_tool_conf_filename, tool_path in trans.app.toolbox.shed_tool_confs.items():
options.append( ( shed_tool_conf_filename.lstrip( './' ), shed_tool_conf_filename ) )
@@ -2643,9 +2672,7 @@
select_field.add_option( option_tup[0], option_tup[1] )
return select_field
def build_tool_panel_section_select_field( trans ):
- """
- Build a SelectField whose options are the sections of the current in-memory toolbox.
- """
+ """Build a SelectField whose options are the sections of the current in-memory toolbox."""
options = []
for k, tool_section in trans.app.toolbox.tool_panel.items():
options.append( ( tool_section.name, tool_section.id ) )
@@ -2653,6 +2680,14 @@
for option_tup in options:
select_field.add_option( option_tup[0], option_tup[1] )
return select_field
+def copy_sample_loc_file( trans, filename ):
+ """Copy xxx.loc.sample to ~/tool-data/xxx.loc"""
+ head, sample_loc_file = os.path.split( filename )
+ loc_file = sample_loc_file.rstrip( '.sample' )
+ tool_data_path = os.path.abspath( trans.app.config.tool_data_path )
+ if not ( os.path.exists( os.path.join( tool_data_path, loc_file ) ) or os.path.exists( os.path.join( tool_data_path, sample_loc_file ) ) ):
+ shutil.copy( os.path.abspath( filename ), os.path.join( tool_data_path, sample_loc_file ) )
+ shutil.copy( os.path.abspath( filename ), os.path.join( tool_data_path, loc_file ) )
def get_user( trans, id ):
"""Get a User from the database by id."""
# Load user from database
@@ -2683,3 +2718,45 @@
id = trans.security.decode_id( id )
quota = trans.sa_session.query( trans.model.Quota ).get( id )
return quota
+def handle_sample_tool_data_table_conf_file( trans, filename ):
+ """
+ Parse the incoming filename and add new entries to the in-memory
+ trans.app.tool_data_tables dictionary as well as appending them
+ to the shed's tool_data_table_conf.xml file on disk.
+ """
+ # Parse the incoming file and add new entries to the in-memory
+ # trans.app.tool_data_tables dictionary.
+ error = False
+ message = ''
+ try:
+ new_table_elems = trans.app.tool_data_tables.add_new_entries_from_config_file( filename )
+ except Exception, e:
+ message = str( e )
+ error = True
+ if not error:
+ # Add an entry to the end of the tool_data_table_conf.xml file.
+ tdt_config = "%s/tool_data_table_conf.xml" % trans.app.config.root
+ if os.path.exists( tdt_config ):
+ # Make a backup of the file since we're going to be changing it.
+ today = date.today()
+ backup_date = today.strftime( "%Y_%m_%d" )
+ tdt_config_copy = '%s/tool_data_table_conf.xml_%s_backup' % ( trans.app.config.root, backup_date )
+ shutil.copy( os.path.abspath( tdt_config ), os.path.abspath( tdt_config_copy ) )
+ # Write each line of the tool_data_table_conf.xml file, except the last line to a temp file.
+ fh = tempfile.NamedTemporaryFile( 'wb' )
+ tmp_filename = fh.name
+ fh.close()
+ new_tdt_config = open( tmp_filename, 'wb' )
+ for i, line in enumerate( open( tdt_config, 'rb' ) ):
+ if line.find( '</tables>' ) >= 0:
+ for new_table_elem in new_table_elems:
+ new_tdt_config.write( ' %s\n' % util.xml_to_string( new_table_elem ).rstrip( '\n' ) )
+ new_tdt_config.write( '</tables>' )
+ else:
+ new_tdt_config.write( line )
+ new_tdt_config.close()
+ shutil.move( tmp_filename, os.path.abspath( tdt_config ) )
+ else:
+ message = "The required file named tool_data_table_conf.xml does not exist in the Galaxy install directory."
+ error = True
+ return error, message
--- a/lib/galaxy/webapps/community/controllers/common.py Fri Sep 23 10:01:18 2011 -0400
+++ b/lib/galaxy/webapps/community/controllers/common.py Fri Sep 23 10:07:19 2011 -0400
@@ -163,12 +163,59 @@
repository.name,
tool.id,
tool.version )
+def check_tool_input_params( trans, name, tool, sample_files, invalid_files ):
+ """
+ Check all of the tool's input parameters, looking for any that are dynamically generated
+ using external data files to make sure the files exist.
+ """
+ can_set_metadata = True
+ correction_msg = ''
+ for input_param in tool.input_params:
+ if isinstance( input_param, galaxy.tools.parameters.basic.SelectToolParameter ) and input_param.is_dynamic:
+ # If the tool refers to .loc files or requires an entry in the
+ # tool_data_table_conf.xml, make sure all requirements exist.
+ options = input_param.dynamic_options or input_param.options
+ if options.missing_tool_data_table_name:
+ # See if the repository contains a tool_data_table_conf.xml.sample file.
+ sample_found = False
+ for sample_file in sample_files:
+ head, tail = os.path.split( sample_file )
+ if tail == 'tool_data_table_conf.xml.sample':
+ sample_found = True
+ error, correction_msg = handle_sample_tool_data_table_conf_file( trans, sample_file )
+ if error:
+ can_set_metadata = False
+ invalid_files.append( ( tail, correction_msg ) )
+ break
+ if not sample_found:
+ can_set_metadata = False
+ correction_msg = "This file requires an entry in the tool_data_table_conf.xml file. "
+ correction_msg += "Upload a file named tool_data_table_conf.xml.sample to the repository "
+ correction_msg += "that includes the required entry to resolve this issue.<br/>"
+ invalid_files.append( ( name, correction_msg ) )
+ elif options.missing_index_file:
+ missing_head, missing_tail = os.path.split( options.missing_index_file )
+ # See if the repository contains the required xxx.loc.sample file.
+ sample_found = False
+ for sample_file in sample_files:
+ sample_head, sample_tail = os.path.split( sample_file )
+ if sample_tail == '%s.sample' % missing_tail:
+ copy_sample_loc_file( trans, sample_file )
+ sample_found = True
+ break
+ if not sample_found:
+ can_set_metadata = False
+ correction_msg = "This file refers to a missing file <b>%s</b>. " % str( options.missing_index_file )
+ correction_msg += "Upload a file named <b>%s.sample</b> to the repository to correct this error." % str( missing_tail )
+ invalid_files.append( ( name, correction_msg ) )
+ return can_set_metadata, invalid_files
def generate_tool_metadata( trans, id, changeset_revision, tool_config, tool, metadata_dict ):
"""
Update the received metadata_dict with changes that have been
applied to the received tool.
"""
repository = get_repository( trans, id )
+ # Handle tool.requirements.
tool_requirements = []
for tr in tool.requirements:
name=tr.name
@@ -187,6 +234,7 @@
fabfile=fabfile,
method=method )
tool_requirements.append( requirement_dict )
+ # Handle tool.tests.
tool_tests = []
if tool.tests:
for ttb in tool.tests:
@@ -258,6 +306,7 @@
repo_dir = repository.repo_path
repo = hg.repository( get_configured_ui(), repo_dir )
invalid_files = []
+ sample_files = []
ctx = get_changectx_for_changeset( trans, repo, changeset_revision )
if ctx is not None:
metadata_dict = {}
@@ -271,6 +320,10 @@
if 'hgrc' in files:
# Don't include hgrc files in commit.
files.remove( 'hgrc' )
+ # Find all special .sample files first.
+ for name in files:
+ if name.endswith( '.sample' ):
+ sample_files.append( os.path.abspath( os.path.join( root, name ) ) )
for name in files:
# Find all tool configs.
if name.endswith( '.xml' ):
@@ -278,9 +331,11 @@
full_path = os.path.abspath( os.path.join( root, name ) )
tool = load_tool( trans, full_path )
if tool is not None:
- # Update the list metadata dictionaries for tools in metadata_dict.
- tool_config = os.path.join( root, name )
- metadata_dict = generate_tool_metadata( trans, id, changeset_revision, tool_config, tool, metadata_dict )
+ can_set_metadata, invalid_files = check_tool_input_params( trans, name, tool, sample_files, invalid_files )
+ if can_set_metadata:
+ # Update the list of metadata dictionaries for tools in metadata_dict.
+ tool_config = os.path.join( root, name )
+ metadata_dict = generate_tool_metadata( trans, id, changeset_revision, tool_config, tool, metadata_dict )
except Exception, e:
invalid_files.append( ( name, str( e ) ) )
# Find all exported workflows
@@ -297,10 +352,14 @@
except Exception, e:
invalid_files.append( ( name, str( e ) ) )
else:
+ # Find all special .sample files first.
+ for filename in ctx:
+ if filename.endswith( '.sample' ):
+ sample_files.append( os.path.abspath( os.path.join( root, filename ) ) )
# Get all tool config file names from the hgweb url, something like:
# /repos/test/convert_chars1/file/e58dcf0026c7/convert_characters.xml
for filename in ctx:
- # Find all tool configs - should not have to update metadata for workflows.
+ # Find all tool configs - should not have to update metadata for workflows for now.
if filename.endswith( '.xml' ):
fctx = ctx[ filename ]
# Write the contents of the old tool config to a temporary file.
@@ -313,12 +372,14 @@
try:
tool = load_tool( trans, tmp_filename )
if tool is not None:
- # Update the list metadata dictionaries for tools in metadata_dict. Note that filename
- # here is the relative path to the config file within the change set context, something
- # like filtering.xml, but when the change set was the repository tip, the value was
- # something like database/community_files/000/repo_1/filtering.xml. This shouldn't break
- # anything, but may result in a bit of confusion when maintaining the code / data over time.
- metadata_dict = generate_tool_metadata( trans, id, changeset_revision, filename, tool, metadata_dict )
+ can_set_metadata, invalid_files = check_tool_input_params( trans, filename, tool, sample_files, invalid_files )
+ if can_set_metadata:
+ # Update the list of metadata dictionaries for tools in metadata_dict. Note that filename
+ # here is the relative path to the config file within the change set context, something
+ # like filtering.xml, but when the change set was the repository tip, the value was
+ # something like database/community_files/000/repo_1/filtering.xml. This shouldn't break
+ # anything, but may result in a bit of confusion when maintaining the code / data over time.
+ metadata_dict = generate_tool_metadata( trans, id, changeset_revision, filename, tool, metadata_dict )
except Exception, e:
invalid_files.append( ( name, str( e ) ) )
try:
@@ -346,49 +407,26 @@
trans.sa_session.add( repository_metadata )
trans.sa_session.flush()
else:
- message = "Changeset revision '%s' includes no tools or exported workflows for which metadata can be set." % str( changeset_revision )
+ message = "Change set revision '%s' includes no tools or exported workflows for which metadata can be set." % str( changeset_revision )
status = "error"
else:
# change_set is None
- message = "Repository does not include changeset revision '%s'." % str( changeset_revision )
+ message = "Repository does not include change set revision '%s'." % str( changeset_revision )
status = 'error'
if invalid_files:
if metadata_dict:
message = "Metadata was defined for some items in change set revision '%s'. " % str( changeset_revision )
- message += "If the following files are Galaxy tool configs or exported Galaxy workflows, correct the problems and reset metadata.<br/>"
+ message += "Correct the following problems if necessary and reset metadata.<br/>"
else:
message = "Metadata cannot be defined for change set revision '%s'. Correct the following problems and reset metadata.<br/>" % str( changeset_revision )
for itc_tup in invalid_files:
- tool_file = itc_tup[0]
- exception_msg = itc_tup[1]
+ tool_file, exception_msg = itc_tup
if exception_msg.find( 'No such file or directory' ) >= 0:
exception_items = exception_msg.split()
missing_file_items = exception_items[7].split( '/' )
missing_file = missing_file_items[-1].rstrip( '\'' )
correction_msg = "This file refers to a missing file <b>%s</b>. " % str( missing_file )
- if exception_msg.find( '.loc' ) >= 0:
- # Handle the special case where a tool depends on a missing xxx.loc file by telliing
- # the user to upload xxx.loc.sample to the repository so that it can be copied to
- # ~/tool-data/xxx.loc. In this case, exception_msg will look something like:
- # [Errno 2] No such file or directory: '/Users/gvk/central/tool-data/blast2go.loc'
- sample_loc_file = '%s.sample' % str( missing_file )
- correction_msg += "Upload a file named <b>%s</b> to the repository to correct this error." % sample_loc_file
- else:
- correction_msg += "Upload a file named <b>%s</b> to the repository to correct this error." % missing_file
- elif exception_msg.find( 'Data table named' ) >= 0:
- # Handle the special case where the tool requires an entry in the tool_data_table.conf file.
- # In this case, exception_msg will look something like:
- # Data table named 'tmap_indexes' is required by tool but not configured
- exception_items = exception_msg.split()
- name_attr = exception_items[3].lstrip( '\'' ).rstrip( '\'' )
- message += "<b>%s</b> - This tool requires an entry in the tool_data_table_conf.xml file. " % tool_file
- message += "Complete and <b>Save</b> the form below to resolve this issue.<br/>"
- return trans.response.send_redirect( web.url_for( controller='repository',
- action='add_tool_data_table_entry',
- name_attr=name_attr,
- repository_id=id,
- message=message,
- status='error' ) )
+ correction_msg += "Upload a file named <b>%s</b> to the repository to correct this error." % missing_file
else:
correction_msg = exception_msg
message += "<b>%s</b> - %s<br/>" % ( tool_file, correction_msg )
@@ -410,14 +448,6 @@
if repository_metadata:
return repository_metadata.malicious
return False
-def copy_sample_loc_file( trans, filename ):
- """Copy xxx.loc.sample to ~/tool-data/xxx.loc"""
- sample_loc_file = os.path.split( filename )[1]
- loc_file = os.path.split( filename )[1].rstrip( '.sample' )
- tool_data_path = os.path.abspath( trans.app.config.tool_data_path )
- if not ( os.path.exists( os.path.join( tool_data_path, loc_file ) ) or os.path.exists( os.path.join( tool_data_path, sample_loc_file ) ) ):
- shutil.copy( os.path.abspath( filename ), os.path.join( tool_data_path, sample_loc_file ) )
- shutil.copy( os.path.abspath( filename ), os.path.join( tool_data_path, loc_file ) )
def get_configured_ui():
# Configure any desired ui settings.
_ui = ui.ui()
--- a/lib/galaxy/webapps/community/controllers/repository.py Fri Sep 23 10:01:18 2011 -0400
+++ b/lib/galaxy/webapps/community/controllers/repository.py Fri Sep 23 10:07:19 2011 -0400
@@ -1049,6 +1049,7 @@
message=message,
status=status )
@web.expose
+ @web.require_login( "set email alerts" )
def set_email_alerts( self, trans, **kwd ):
# Set email alerts for selected repositories
params = util.Params( kwd )
@@ -1113,98 +1114,6 @@
message=message,
status=status ) )
@web.expose
- def add_tool_data_table_entry( self, trans, name_attr, repository_id, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- comment_char = util.restore_text( params.get( 'comment_char', '#' ) )
- loc_filename = util.restore_text( params.get( 'loc_filename', '' ) )
- repository = get_repository( trans, repository_id )
- repo = hg.repository( get_configured_ui(), repository.repo_path )
- column_fields = self.__get_column_fields( **kwd )
- if params.get( 'add_field_button', False ):
- # Add a field
- field_index = len( column_fields ) + 1
- field_tup = ( '%i_field_name' % field_index, '' )
- column_fields.append( field_tup )
- elif params.get( 'remove_button', False ):
- # Delete a field - find the index of the field to be removed from the remove button label
- index = int( kwd[ 'remove_button' ].split( ' ' )[2] ) - 1
- tup_to_remove = column_fields[ index ]
- column_fields.remove( tup_to_remove )
- # Re-number field tups
- new_column_fields = []
- for field_index, old_field_tup in enumerate( column_fields ):
- name = '%i_field_name' % ( field_index + 1 )
- value = old_field_tup[1]
- new_column_fields.append( ( name, value ) )
- column_fields = new_column_fields
- elif params.get( 'add_tool_data_table_entry_button', False ):
- # Add an entry to the end of the tool_data_table_conf.xml file
- tdt_config = "%s/tool_data_table_conf.xml" % trans.app.config.root
- if os.path.exists( tdt_config ):
- # Make a backup of the file since we're going to be changing it.
- today = date.today()
- backup_date = today.strftime( "%Y_%m_%d" )
- tdt_config_copy = '%s/tool_data_table_conf.xml_%s_backup' % ( trans.app.config.root, backup_date )
- shutil.copy( os.path.abspath( tdt_config ), os.path.abspath( tdt_config_copy ) )
- # Generate the string of column names
- column_names = ', '.join( [ column_tup[1] for column_tup in column_fields ] )
- # Write each line of the tool_data_table_conf.xml file, except the last line to a temp file.
- fh = tempfile.NamedTemporaryFile( 'wb' )
- tmp_filename = fh.name
- fh.close()
- new_tdt_config = open( tmp_filename, 'wb' )
- for i, line in enumerate( open( tdt_config, 'rb' ) ):
- if line.startswith( '</tables>' ):
- break
- new_tdt_config.write( line )
- new_tdt_config.write( ' <!-- Location of %s files -->\n' % name_attr )
- new_tdt_config.write( ' <table name="%s" comment_char="%s">\n' % ( name_attr, comment_char ) )
- new_tdt_config.write( ' <columns>%s</columns>\n' % column_names )
- new_tdt_config.write( ' <file path="tool-data/%s" />\n' % loc_filename )
- new_tdt_config.write( ' </table>\n' )
- # Now write the last line of the file
- new_tdt_config.write( '</tables>\n' )
- new_tdt_config.close()
- shutil.move( tmp_filename, os.path.abspath( tdt_config ) )
- # Reload the tool_data_table_conf entries
- trans.app.tool_data_tables = galaxy.tools.data.ToolDataTableManager( trans.app.config.tool_data_table_config_path )
- message = "The new entry has been added to the tool_data_table_conf.xml file, so click the <b>Reset metadata</b> button below."
- return trans.response.send_redirect( web.url_for( controller='repository',
- action='manage_repository',
- id=repository_id,
- message=message,
- status=status ) )
- is_malicious = change_set_is_malicious( trans, repository_id, repository.tip )
- return trans.fill_template( '/webapps/community/repository/add_tool_data_table_entry.mako',
- name_attr=name_attr,
- repository=repository,
- comment_char=comment_char,
- loc_filename=loc_filename,
- column_fields=column_fields,
- is_malicious=is_malicious,
- message=message,
- status=status )
- def __get_column_fields( self, **kwd ):
- '''
- Return a dictionary of the user-entered form fields representing columns
- in the location file.
- '''
- params = util.Params( kwd )
- column_fields = []
- index = 0
- while True:
- name = '%i_field_name' % ( index + 1 )
- if kwd.has_key( name ):
- value = util.restore_text( params.get( name, '' ) )
- field_tup = ( name, value )
- index += 1
- column_fields.append( field_tup )
- else:
- break
- return column_fields
- @web.expose
def display_tool( self, trans, repository_id, tool_config, changeset_revision, **kwd ):
params = util.Params( kwd )
message = util.restore_text( params.get( 'message', '' ) )
@@ -1284,6 +1193,7 @@
params = util.Params( kwd )
message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
+ display_for_install = util.string_as_bool( params.get( 'display_for_install', False ) )
repository = get_repository( trans, repository_id )
metadata = {}
tool = None
@@ -1312,6 +1222,7 @@
revision_label=revision_label,
changeset_revision_select_field=changeset_revision_select_field,
is_malicious=is_malicious,
+ display_for_install=display_for_install,
message=message,
status=status )
@web.expose
--- a/lib/galaxy/webapps/community/controllers/upload.py Fri Sep 23 10:01:18 2011 -0400
+++ b/lib/galaxy/webapps/community/controllers/upload.py Fri Sep 23 10:07:19 2011 -0400
@@ -95,6 +95,14 @@
# exception. If this happens, we'll try the following.
repo.dirstate.write()
repo.commit( user=trans.user.username, text=commit_message )
+ if full_path.endswith( 'tool_data_table_conf.xml.sample' ):
+ # Handle the special case where a tool_data_table_conf.xml.sample
+ # file is being uploaded by parsing the file and adding new entries
+ # to the in-memory trans.app.tool_data_tables dictionary as well as
+ # appending them to the shed's tool_data_table_conf.xml file on disk.
+ error, error_message = handle_sample_tool_data_table_conf_file( trans, full_path )
+ if error:
+ message = '%s<br/>%s' % ( message, error_message )
if full_path.endswith( '.loc.sample' ):
# Handle the special case where a xxx.loc.sample file is
# being uploaded by copying it to ~/tool-data/xxx.loc.
@@ -208,6 +216,14 @@
pass
for filename_in_archive in filenames_in_archive:
commands.add( repo.ui, repo, filename_in_archive )
+ if filename_in_archive.endswith( 'tool_data_table_conf.xml.sample' ):
+ # Handle the special case where a tool_data_table_conf.xml.sample
+ # file is being uploaded by parsing the file and adding new entries
+ # to the in-memory trans.app.tool_data_tables dictionary as well as
+ # appending them to the shed's tool_data_table_conf.xml file on disk.
+ error, message = handle_sample_tool_data_table_conf_file( trans, filename_in_archive )
+ if error:
+ return False, message, files_to_remove
if filename_in_archive.endswith( '.loc.sample' ):
# Handle the special case where a xxx.loc.sample file is
# being uploaded by copying it to ~/tool-data/xxx.loc.
--- a/templates/webapps/community/repository/add_tool_data_table_entry.mako Fri Sep 23 10:01:18 2011 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,124 +0,0 @@
-<%inherit file="/base.mako"/>
-<%namespace file="/message.mako" import="render_msg" />
-
-<%
- from galaxy.web.form_builder import TextField
- is_new = repository.is_new
- can_contact_owner = trans.user and trans.user != repository.user
- can_push = trans.app.security_agent.can_push( trans.user, repository )
- can_upload = can_push
- can_download = not is_new and ( not is_malicious or can_push )
- can_browse_contents = not is_new
- can_set_metadata = not is_new
- can_rate = not is_new and trans.user and repository.user != trans.user
- can_view_change_log = not is_new
- if can_push:
- browse_label = 'Browse or delete repository files'
- else:
- browse_label = 'Browse repository files'
-%>
-
-<%def name="javascripts()">
- ${parent.javascripts()}
- <script type="text/javascript">
- $(function(){
- $("input:text:first").focus();
- })
- </script>
-</%def>
-
-<br/><br/>
-<ul class="manage-table-actions">
- %if is_new and can_upload:
- <a class="action-button" href="${h.url_for( controller='upload', action='upload', repository_id=trans.security.encode_id( repository.id ), webapp='community' )}">Upload files to repository</a>
- %else:
- <li><a class="action-button" id="repository-${repository.id}-popup" class="menubutton">Repository Actions</a></li>
- <div popupmenu="repository-${repository.id}-popup">
- %if can_upload:
- <a class="action-button" href="${h.url_for( controller='upload', action='upload', repository_id=trans.security.encode_id( repository.id ), webapp='community' )}">Upload files to repository</a>
- %endif
- %if can_view_change_log:
- <a class="action-button" href="${h.url_for( controller='repository', action='view_changelog', id=trans.app.security.encode_id( repository.id ) )}">View change log</a>
- %endif
- %if can_rate:
- <a class="action-button" href="${h.url_for( controller='repository', action='rate_repository', id=trans.app.security.encode_id( repository.id ) )}">Rate repository</a>
- %endif
- %if can_browse_contents:
- <a class="action-button" href="${h.url_for( controller='repository', action='browse_repository', id=trans.app.security.encode_id( repository.id ) )}">${browse_label}</a>
- %endif
- %if can_contact_owner:
- <a class="action-button" href="${h.url_for( controller='repository', action='contact_owner', id=trans.security.encode_id( repository.id ), webapp='community' )}">Contact repository owner</a>
- %endif
- %if can_download:
- <a class="action-button" href="${h.url_for( controller='repository', action='download', repository_id=trans.app.security.encode_id( repository.id ), changeset_revision=repository.tip, file_type='gz' )}">Download as a .tar.gz file</a>
- <a class="action-button" href="${h.url_for( controller='repository', action='download', repository_id=trans.app.security.encode_id( repository.id ), changeset_revision=repository.tip, file_type='bz2' )}">Download as a .tar.bz2 file</a>
- <a class="action-button" href="${h.url_for( controller='repository', action='download', repository_id=trans.app.security.encode_id( repository.id ), changeset_revision=repository.tip, file_type='zip' )}">Download as a zip file</a>
- %endif
- </div>
- %endif
-</ul>
-
-%if message:
- ${render_msg( message, status )}
-%endif
-
-<%def name="render_field( index, field_tup )">
- <h4 class="msg_head">
- <div class="form-row">Column ${index + 1}</div>
- </h4>
- <div class="msg_body2">
- <div class="repeat-group-item">
- <div class="form-row">
- <% column_field = TextField( field_tup[0], 40, field_tup[1] ) %>
- ${column_field.get_html()}
- <div class="toolParamHelp" style="clear: both;">
- Enter the name of the location file column (e.g., value, dbkey, name, path, etc). See the tool_data_table_conf.xml file for examples.
- </div>
- </div>
- <div class="form-row">
- <input type="submit" name="remove_button" value="Remove field ${index + 1}"/>
- </div>
- </div>
- </div>
-</%def>
-
-<div class="toolForm">
- <div class="toolFormTitle">Add tool data table entry</div>
- <div class="toolFormBody">
- <form name="add_tool_data_table_entry" id="add_tool_data_table_entry" action="${h.url_for( controller='repository', action='add_tool_data_table_entry', name_attr=name_attr, repository_id=trans.security.encode_id( repository.id ) )}" method="post" >
- <div class="form-row">
- <label>Table name:</label>
- ${name_attr}
- <div style="clear: both"></div>
- </div>
- <div class="form-row">
- <label>Comment lines begin with:</label>
- <input name="comment_char" type="textfield" value="${comment_char}" size="8"/>
- <div class="toolParamHelp" style="clear: both;">
- Enter the character that designates comments lines in the location file (default is #).
- </div>
- <div style="clear: both"></div>
- </div>
- <div class="form-row">
- <label>Location file name:</label>
- <input name="loc_filename" type="textfield" value="${loc_filename}" size="80"/>
- <div class="toolParamHelp" style="clear: both;">
- Enter the name of the location file (e.g., bwa_index.loc).
- </div>
- <div style="clear: both"></div>
- </div>
- <div class="form-row">
- <label>Location file columns:</label>
- </div>
- %for ctr, field_tup in enumerate( column_fields ):
- ${render_field( ctr, field_tup )}
- %endfor
- <div class="form-row">
- <input type="submit" name="add_field_button" value="Add field"/>
- </div>
- <div class="form-row">
- <input type="submit" name="add_tool_data_table_entry_button" value="Save"/>
- </div>
- </form>
- </div>
-</div>
--- a/templates/webapps/community/repository/view_tool_metadata.mako Fri Sep 23 10:01:18 2011 -0400
+++ b/templates/webapps/community/repository/view_tool_metadata.mako Fri Sep 23 10:07:19 2011 -0400
@@ -33,34 +33,38 @@
<br/><br/><ul class="manage-table-actions">
- %if is_new:
- <a class="action-button" href="${h.url_for( controller='upload', action='upload', repository_id=trans.security.encode_id( repository.id ), webapp='community' )}">Upload files to repository</a>
+ %if display_for_install:
+ <a class="action-button" href="${h.url_for( controller='repository', action='install_repository_revision', repository_id=trans.security.encode_id( repository.id ), webapp='community', changeset_revision=changeset_revision )}">Install to local Galaxy</a>
%else:
- <li><a class="action-button" id="repository-${repository.id}-popup" class="menubutton">Repository Actions</a></li>
- <div popupmenu="repository-${repository.id}-popup">
- %if can_manage:
- <a class="action-button" href="${h.url_for( controller='repository', action='manage_repository', id=trans.app.security.encode_id( repository.id ), changeset_revision=changeset_revision )}">Manage repository</a>
- %else:
- <a class="action-button" href="${h.url_for( controller='repository', action='view_repository', id=trans.app.security.encode_id( repository.id ), changeset_revision=changeset_revision )}">View repository</a>
- %endif
- %if can_upload:
- <a class="action-button" href="${h.url_for( controller='upload', action='upload', repository_id=trans.security.encode_id( repository.id ), webapp='community' )}">Upload files to repository</a>
- %endif
- %if can_view_change_log:
- <a class="action-button" href="${h.url_for( controller='repository', action='view_changelog', id=trans.app.security.encode_id( repository.id ) )}">View change log</a>
- %endif
- %if can_browse_contents:
- <a class="action-button" href="${h.url_for( controller='repository', action='browse_repository', id=trans.app.security.encode_id( repository.id ) )}">${browse_label}</a>
- %endif
- %if can_contact_owner:
- <a class="action-button" href="${h.url_for( controller='repository', action='contact_owner', id=trans.security.encode_id( repository.id ), webapp='community' )}">Contact repository owner</a>
- %endif
- %if can_download:
- <a class="action-button" href="${h.url_for( controller='repository', action='download', repository_id=trans.app.security.encode_id( repository.id ), changeset_revision=changeset_revision, file_type='gz' )}">Download as a .tar.gz file</a>
- <a class="action-button" href="${h.url_for( controller='repository', action='download', repository_id=trans.app.security.encode_id( repository.id ), changeset_revision=changeset_revision, file_type='bz2' )}">Download as a .tar.bz2 file</a>
- <a class="action-button" href="${h.url_for( controller='repository', action='download', repository_id=trans.app.security.encode_id( repository.id ), changeset_revision=changeset_revision, file_type='zip' )}">Download as a zip file</a>
- %endif
- </div>
+ %if is_new:
+ <a class="action-button" href="${h.url_for( controller='upload', action='upload', repository_id=trans.security.encode_id( repository.id ), webapp='community' )}">Upload files to repository</a>
+ %else:
+ <li><a class="action-button" id="repository-${repository.id}-popup" class="menubutton">Repository Actions</a></li>
+ <div popupmenu="repository-${repository.id}-popup">
+ %if can_manage:
+ <a class="action-button" href="${h.url_for( controller='repository', action='manage_repository', id=trans.app.security.encode_id( repository.id ), changeset_revision=changeset_revision )}">Manage repository</a>
+ %else:
+ <a class="action-button" href="${h.url_for( controller='repository', action='view_repository', id=trans.app.security.encode_id( repository.id ), changeset_revision=changeset_revision )}">View repository</a>
+ %endif
+ %if can_upload:
+ <a class="action-button" href="${h.url_for( controller='upload', action='upload', repository_id=trans.security.encode_id( repository.id ), webapp='community' )}">Upload files to repository</a>
+ %endif
+ %if can_view_change_log:
+ <a class="action-button" href="${h.url_for( controller='repository', action='view_changelog', id=trans.app.security.encode_id( repository.id ) )}">View change log</a>
+ %endif
+ %if can_browse_contents:
+ <a class="action-button" href="${h.url_for( controller='repository', action='browse_repository', id=trans.app.security.encode_id( repository.id ) )}">${browse_label}</a>
+ %endif
+ %if can_contact_owner:
+ <a class="action-button" href="${h.url_for( controller='repository', action='contact_owner', id=trans.security.encode_id( repository.id ), webapp='community' )}">Contact repository owner</a>
+ %endif
+ %if can_download:
+ <a class="action-button" href="${h.url_for( controller='repository', action='download', repository_id=trans.app.security.encode_id( repository.id ), changeset_revision=changeset_revision, file_type='gz' )}">Download as a .tar.gz file</a>
+ <a class="action-button" href="${h.url_for( controller='repository', action='download', repository_id=trans.app.security.encode_id( repository.id ), changeset_revision=changeset_revision, file_type='bz2' )}">Download as a .tar.bz2 file</a>
+ <a class="action-button" href="${h.url_for( controller='repository', action='download', repository_id=trans.app.security.encode_id( repository.id ), changeset_revision=changeset_revision, file_type='zip' )}">Download as a zip file</a>
+ %endif
+ </div>
+ %endif
%endif
</ul>
@@ -72,7 +76,7 @@
<div class="toolFormTitle">Repository revision</div><div class="toolFormBody">
%if len( changeset_revision_select_field.options ) > 1:
- <form name="change_revision" id="change_revision" action="${h.url_for( controller='repository', action='view_tool_metadata', repository_id=trans.security.encode_id( repository.id ), tool_id=metadata[ 'id' ] )}" method="post" >
+ <form name="change_revision" id="change_revision" action="${h.url_for( controller='repository', action='view_tool_metadata', repository_id=trans.security.encode_id( repository.id ), tool_id=metadata[ 'id' ], display_for_install=display_for_install )}" method="post" ><div class="form-row"><%
if changeset_revision == repository.tip:
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
1 new changeset in galaxy-central:
http://bitbucket.org/galaxy/galaxy-central/changeset/898f4f57223d/
changeset: 898f4f57223d
user: greg
date: 2011-09-23 16:01:18
summary: Add 2 new attributes to dynamically generated select lists; "missing_tool_data_table_name" will include a value (e.g., tmap_indexes) if the select list requires a missing entry in the tool_data_table_conf.xml file, and "missing_index_file" will include a value (e.g., tmap_indexes.loc) if the select list requires a missing index file. Tools that used to not load in index files were missing will now load, although they will not properly execute. Work remaining is for a check to be performed against these new "missing" attributes and a message displayed on the tool form - not quite sure if this should be a validator or not...
Define a list in the Tool class to keep track of all input parameters (tool.input_params). These differ from the inputs dictionary (tool.inputs) in that inputs can be page elements like conditionals, but input_params are basic parameters like SelectField objects. This enables us to more easily ensure that parameter dependencies like index files or tool_data_table_conf.xml entries exist.
Add the ability to append new entries into the tool_data_table_conf.xml file in real time and add the same entries into the in-memory tool_data_tables dictionary.
affected #: 4 files (-1 bytes)
--- a/lib/galaxy/tools/__init__.py Fri Sep 23 09:36:57 2011 -0400
+++ b/lib/galaxy/tools/__init__.py Fri Sep 23 10:01:18 2011 -0400
@@ -360,13 +360,18 @@
tool_type = 'default'
def __init__( self, config_file, root, app, guid=None ):
- """
- Load a tool from the config named by `config_file`
- """
+ """Load a tool from the config named by `config_file`"""
# Determine the full path of the directory where the tool config is
self.config_file = config_file
self.tool_dir = os.path.dirname( config_file )
self.app = app
+ # Define a place to keep track of all input parameters. These
+ # differ from the inputs dictionary in that inputs can be page
+ # elements like conditionals, but input_params are basic form
+ # parameters like SelectField objects. This enables us to more
+ # easily ensure that parameter dependencies like index files or
+ # tool_data_table_conf.xml entries exist.
+ self.input_params = []
# Parse XML element containing configuration
self.parse( root, guid=guid )
@@ -698,7 +703,6 @@
name = attrib.pop( 'name', None )
if name is None:
raise Exception( "Test output does not have a 'name'" )
-
assert_elem = output_elem.find("assert_contents")
assert_list = None
# Trying to keep testing patch as localized as
@@ -713,13 +717,10 @@
for child_elem in child_elems:
converted_children.append( convert_elem(child_elem) )
return {"tag" : tag, "attributes" : attributes, "children" : converted_children}
-
if assert_elem is not None:
assert_list = []
for assert_child in list(assert_elem):
assert_list.append(convert_elem(assert_child))
-
-
file = attrib.pop( 'file', None )
# File no longer required if an list of assertions was present.
if assert_list is None and file is None:
@@ -734,8 +735,6 @@
attributes['sort'] = util.string_as_bool( attrib.pop( 'sort', False ) )
attributes['extra_files'] = []
attributes['assert_list'] = assert_list
-
-
if 'ftype' in attrib:
attributes['ftype'] = attrib['ftype']
for extra in output_elem.findall( 'extra_files' ):
@@ -862,6 +861,7 @@
rval[param.name] = param
if hasattr( param, 'data_ref' ):
param.ref_input = context[ param.data_ref ]
+ self.input_params.append( param )
return rval
def parse_param_elem( self, input_elem, enctypes, context ):
--- a/lib/galaxy/tools/data/__init__.py Fri Sep 23 09:36:57 2011 -0400
+++ b/lib/galaxy/tools/data/__init__.py Fri Sep 23 10:01:18 2011 -0400
@@ -12,34 +12,64 @@
log = logging.getLogger( __name__ )
class ToolDataTableManager( object ):
- """
- Manages a collection of tool data tables
- """
-
+ """Manages a collection of tool data tables"""
def __init__( self, config_filename=None ):
self.data_tables = {}
if config_filename:
- self.add_from_config_file( config_filename )
-
+ self.load_from_config_file( config_filename )
def __getitem__( self, key ):
return self.data_tables.__getitem__( key )
-
def __contains__( self, key ):
return self.data_tables.__contains__( key )
-
- def add_from_config_file( self, config_filename ):
+ def load_from_config_file( self, config_filename ):
tree = util.parse_xml( config_filename )
root = tree.getroot()
+ table_elems = []
for table_elem in root.findall( 'table' ):
type = table_elem.get( 'type', 'tabular' )
assert type in tool_data_table_types, "Unknown data table type '%s'" % type
+ table_elems.append( table_elem )
table = tool_data_table_types[ type ]( table_elem )
- self.data_tables[ table.name ] = table
- log.debug( "Loaded tool data table '%s", table.name )
+ if table.name not in self.data_tables:
+ self.data_tables[ table.name ] = table
+ log.debug( "Loaded tool data table '%s", table.name )
+ return table_elems
+ def add_new_entries_from_config_file( self, config_filename ):
+ """
+ We have 2 cases to handle, files whose root tag is <tables>, for example:
+ <tables>
+ <!-- Location of Tmap files -->
+ <table name="tmap_indexes" comment_char="#">
+ <columns>value, dbkey, name, path</columns>
+ <file path="tool-data/tmap_index.loc" />
+ </table>
+ </tables>
+ and files whose root tag is <table>, for example:
+ <!-- Location of Tmap files -->
+ <table name="tmap_indexes" comment_char="#">
+ <columns>value, dbkey, name, path</columns>
+ <file path="tool-data/tmap_index.loc" />
+ </table>
+ """
+ tree = util.parse_xml( config_filename )
+ root = tree.getroot()
+ if root.tag == 'tables':
+ table_elems = self.load_from_config_file( config_filename )
+ else:
+ table_elems = []
+ type = root.get( 'type', 'tabular' )
+ assert type in tool_data_table_types, "Unknown data table type '%s'" % type
+ table_elems.append( root )
+ table = tool_data_table_types[ type ]( root )
+ if table.name not in self.data_tables:
+ self.data_tables[ table.name ] = table
+ log.debug( "Loaded tool data table '%s", table.name )
+ return table_elems
class ToolDataTable( object ):
def __init__( self, config_element ):
self.name = config_element.get( 'name' )
+ self.missing_index_file = None
class TabularToolDataTable( ToolDataTable ):
"""
@@ -58,7 +88,6 @@
def __init__( self, config_element ):
super( TabularToolDataTable, self ).__init__( config_element )
self.configure_and_load( config_element )
-
def configure_and_load( self, config_element ):
"""
Configure and load table from an XML element.
@@ -71,15 +100,14 @@
all_rows = []
for file_element in config_element.findall( 'file' ):
filename = file_element.get( 'path' )
- if not os.path.exists( filename ):
+ if os.path.exists( filename ):
+ all_rows.extend( self.parse_file_fields( open( filename ) ) )
+ else:
+ self.missing_index_file = filename
log.warn( "Cannot find index file '%s' for tool data table '%s'" % ( filename, self.name ) )
- else:
- all_rows.extend( self.parse_file_fields( open( filename ) ) )
self.data = all_rows
-
def get_fields( self ):
return self.data
-
def parse_column_spec( self, config_element ):
"""
Parse column definitions, which can either be a set of 'column' elements
@@ -109,7 +137,6 @@
assert 'value' in self.columns, "Required 'value' column missing from column def"
if 'name' not in self.columns:
self.columns['name'] = self.columns['value']
-
def parse_file_fields( self, reader ):
"""
Parse separated lines from file and return a list of tuples.
--- a/lib/galaxy/tools/parameters/dynamic_options.py Fri Sep 23 09:36:57 2011 -0400
+++ b/lib/galaxy/tools/parameters/dynamic_options.py Fri Sep 23 10:01:18 2011 -0400
@@ -399,23 +399,29 @@
self.separator = elem.get( 'separator', '\t' )
self.line_startswith = elem.get( 'startswith', None )
data_file = elem.get( 'from_file', None )
+ self.missing_index_file = None
dataset_file = elem.get( 'from_dataset', None )
from_parameter = elem.get( 'from_parameter', None )
tool_data_table_name = elem.get( 'from_data_table', None )
-
# Options are defined from a data table loaded by the app
self.tool_data_table = None
+ self.missing_tool_data_table_name = None
if tool_data_table_name:
app = tool_param.tool.app
- assert tool_data_table_name in app.tool_data_tables, \
- "Data table named '%s' is required by tool but not configured" % tool_data_table_name
- self.tool_data_table = app.tool_data_tables[ tool_data_table_name ]
- # Column definitions are optional, but if provided override those from the table
- if elem.find( "column" ) is not None:
- self.parse_column_definitions( elem )
+ if tool_data_table_name in app.tool_data_tables:
+ self.tool_data_table = app.tool_data_tables[ tool_data_table_name ]
+ # Set self.missing_index_file if the index file to
+ # which the tool_data_table refers does not exist.
+ if self.tool_data_table.missing_index_file:
+ self.missing_index_file = self.tool_data_table.missing_index_file
+ # Column definitions are optional, but if provided override those from the table
+ if elem.find( "column" ) is not None:
+ self.parse_column_definitions( elem )
+ else:
+ self.columns = self.tool_data_table.columns
else:
- self.columns = self.tool_data_table.columns
-
+ self.missing_tool_data_table_name = tool_data_table_name
+ log.warn( "Data table named '%s' is required by tool but not configured" % tool_data_table_name )
# Options are defined by parsing tabular text data from an data file
# on disk, a dataset, or the value of another parameter
elif data_file is not None or dataset_file is not None or from_parameter is not None:
@@ -423,8 +429,11 @@
if data_file is not None:
data_file = data_file.strip()
if not os.path.isabs( data_file ):
- data_file = os.path.join( self.tool_param.tool.app.config.tool_data_path, data_file )
- self.file_fields = self.parse_file_fields( open( data_file ) )
+ full_path = os.path.join( self.tool_param.tool.app.config.tool_data_path, data_file )
+ if os.path.exists( full_path ):
+ self.file_fields = self.parse_file_fields( open( full_path ) )
+ else:
+ self.missing_index_file = data_file
elif dataset_file is not None:
self.dataset_ref_name = dataset_file
self.has_dataset_dependencies = True
--- a/lib/galaxy/tools/parameters/output.py Fri Sep 23 09:36:57 2011 -0400
+++ b/lib/galaxy/tools/parameters/output.py Fri Sep 23 10:01:18 2011 -0400
@@ -206,13 +206,16 @@
super( FromDataTableOutputActionOption, self ).__init__( parent, elem )
self.name = elem.get( 'name', None )
assert self.name is not None, "Required 'name' attribute missing from FromDataTableOutputActionOption"
- assert self.name in self.tool.app.tool_data_tables, "Data table named '%s' is required by tool but not configured" % self.name
- self.options = self.tool.app.tool_data_tables[ self.name ].get_fields()
- self.column = elem.get( 'column', None )
- assert self.column is not None, "Required 'column' attribute missing from FromDataTableOutputActionOption"
- self.column = int( self.column )
- self.offset = elem.get( 'offset', -1 )
- self.offset = int( self.offset )
+ self.missing_tool_data_table_name = None
+ if self.name in self.tool.app.tool_data_tables:
+ self.options = self.tool.app.tool_data_tables[ self.name ].get_fields()
+ self.column = elem.get( 'column', None )
+ assert self.column is not None, "Required 'column' attribute missing from FromDataTableOutputActionOption"
+ self.column = int( self.column )
+ self.offset = elem.get( 'offset', -1 )
+ self.offset = int( self.offset )
+ else:
+ self.missing_tool_data_table_name = self.name
def get_value( self, other_values ):
options = self.options
for filter in self.filters:
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: dan: MAF stats tool will now skip intervals with length less 1.
by Bitbucket 23 Sep '11
by Bitbucket 23 Sep '11
23 Sep '11
1 new changeset in galaxy-central:
http://bitbucket.org/galaxy/galaxy-central/changeset/c3680f58e97d/
changeset: c3680f58e97d
user: dan
date: 2011-09-23 15:36:57
summary: MAF stats tool will now skip intervals with length less 1.
affected #: 1 file (-1 bytes)
--- a/tools/maf/maf_stats.py Fri Sep 23 08:39:53 2011 -0400
+++ b/tools/maf/maf_stats.py Fri Sep 23 09:36:57 2011 -0400
@@ -55,12 +55,16 @@
out = open(output_filename, 'w')
num_region = None
+ num_bad_region = 0
species_summary = {}
total_length = 0
#loop through interval file
for num_region, region in enumerate( bx.intervals.io.NiceReaderWrapper( open( input_interval_filename, 'r' ), chrom_col = chr_col, start_col = start_col, end_col = end_col, fix_strand = True, return_header = False, return_comments = False ) ):
src = "%s.%s" % ( dbkey, region.chrom )
region_length = region.end - region.start
+ if region_length < 1:
+ num_bad_region += 1
+ continue
total_length += region_length
coverage = { dbkey: BitSet( region_length ) }
@@ -99,6 +103,8 @@
out.close()
if num_region is not None:
print "%i regions were processed with a total length of %i." % ( num_region + 1, total_length )
+ if num_bad_region:
+ print "%i regions were invalid." % ( num_bad_region )
maf_utilities.remove_temp_index_file( index_filename )
if __name__ == "__main__": __main__()
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: natefoo: This is what I get for making one "inconsequential" change after running the tests. =/
by Bitbucket 23 Sep '11
by Bitbucket 23 Sep '11
23 Sep '11
1 new changeset in galaxy-central:
http://bitbucket.org/galaxy/galaxy-central/changeset/070ac69b2283/
changeset: 070ac69b2283
user: natefoo
date: 2011-09-23 14:39:53
summary: This is what I get for making one "inconsequential" change after running the tests. =/
affected #: 1 file (-1 bytes)
--- a/lib/galaxy/web/base/controller.py Thu Sep 22 17:13:53 2011 -0400
+++ b/lib/galaxy/web/base/controller.py Fri Sep 23 08:39:53 2011 -0400
@@ -44,35 +44,35 @@
def get_class( self, class_name ):
""" Returns the class object that a string denotes. Without this method, we'd have to do eval(<class_name>). """
if class_name == 'History':
- item_class = trans.model.History
+ item_class = self.app.model.History
elif class_name == 'HistoryDatasetAssociation':
- item_class = trans.model.HistoryDatasetAssociation
+ item_class = self.app.model.HistoryDatasetAssociation
elif class_name == 'Page':
- item_class = trans.model.Page
+ item_class = self.app.model.Page
elif class_name == 'StoredWorkflow':
- item_class = trans.model.StoredWorkflow
+ item_class = self.app.model.StoredWorkflow
elif class_name == 'Visualization':
- item_class = trans.model.Visualization
+ item_class = self.app.model.Visualization
elif class_name == 'Tool':
- item_class = trans.model.Tool
+ item_class = self.app.model.Tool
elif class_name == 'Job':
- item_class = trans.model.Job
+ item_class = self.app.model.Job
elif class_name == 'User':
- item_class = trans.model.User
+ item_class = self.app.model.User
elif class_name == 'Group':
- item_class = trans.model.Group
+ item_class = self.app.model.Group
elif class_name == 'Role':
- item_class = trans.model.Role
+ item_class = self.app.model.Role
elif class_name == 'Quota':
- item_class = trans.model.Quota
+ item_class = self.app.model.Quota
elif class_name == 'Library':
- item_class = trans.model.Library
+ item_class = self.app.model.Library
elif class_name == 'LibraryFolder':
- item_class = trans.model.LibraryFolder
+ item_class = self.app.model.LibraryFolder
elif class_name == 'LibraryDatasetDatasetAssociation':
- item_class = trans.model.LibraryDatasetDatasetAssociation
+ item_class = self.app.model.LibraryDatasetDatasetAssociation
elif class_name == 'LibraryDataset':
- item_class = trans.model.LibraryDataset
+ item_class = self.app.model.LibraryDataset
else:
item_class = None
return item_class
@@ -90,7 +90,8 @@
item = trans.sa_session.query( item_class ).get( decoded_id )
assert item is not None
except:
- raise MessageException( "Invalid %s id ( %s ) specified" % ( class_name, display_id ), type="error" )
+ log.exception( "Invalid %s id ( %s ) specified" % ( class_name, id ) )
+ raise MessageException( "Invalid %s id ( %s ) specified" % ( class_name, id ), type="error" )
if check_ownership or check_accessible:
self.security_check( trans, item, check_ownership, check_accessible, encoded_id )
if deleted == True and not item.deleted:
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: natefoo: Abstraction for web methods, implemented for the Quota operations and the
by Bitbucket 22 Sep '11
by Bitbucket 22 Sep '11
22 Sep '11
1 new changeset in galaxy-central:
http://bitbucket.org/galaxy/galaxy-central/changeset/61cf27e60560/
changeset: 61cf27e60560
user: natefoo
date: 2011-09-22 23:13:53
summary: Abstraction for web methods, implemented for the Quota operations and the
framework is there for everything else now. Works like this:
* If your form/API params are the same across all forms for a model object
(e.g. all Quota operations), they should be placed in mixins in
galaxy.web.params and can be used universally by both the UI and API.
* Validating params and performing the requestion actions go in mixins in
galaxy.actions. These return a value upon success or raise a
MessageException upon failure.
* Fetching objects with checks relevant to the web methods can be done via
BaseController.get_object and its wrappers (see other helpers in the
Mixins in galaxy.web.base.controller as well).
* This leaves UI controller methods to just handle the stuff related to
filling in web forms.
* The API methods should be making use of paste.httpexceptions.* for
handling error conditions. Also, I think it'd be best to generally
return an item's API value from most methods and include the "message" as
an entry in the return dict rather than just by itself as a string.
None of this is required, but it would be great to convert the existing stuff
to this new model.
galaxy.exceptions is a collection point for all custom exceptions.
The User API now returns current disk usage with a User's detailed information.
Exceptions in the API are now logged by the expose_api decorator and hidden
from the client.
affected #: 56 files (-1 bytes)
--- a/lib/galaxy/model/__init__.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/model/__init__.py Thu Sep 22 17:13:53 2011 -0400
@@ -47,7 +47,7 @@
class User( object, APIItem ):
api_collection_visible_keys = ( 'id', 'email' )
- api_element_visible_keys = ( 'id', 'email', 'username' )
+ api_element_visible_keys = ( 'id', 'email', 'username', 'total_disk_usage', 'nice_total_disk_usage' )
def __init__( self, email=None, password=None ):
self.email = email
self.password = password
@@ -82,6 +82,9 @@
def set_disk_usage( self, bytes ):
self.disk_usage = bytes
total_disk_usage = property( get_disk_usage, set_disk_usage )
+ @property
+ def nice_total_disk_usage( self ):
+ return self.get_disk_usage( nice_size=True )
def calculate_disk_usage( self ):
dataset_ids = []
total = 0
--- a/lib/galaxy/quota/__init__.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/quota/__init__.py Thu Sep 22 17:13:53 2011 -0400
@@ -33,6 +33,8 @@
return usage
def get_percent( self, trans=None, user=False, history=False, usage=False, quota=False ):
return None
+ def get_user_quotas( self, user ):
+ return []
class QuotaAgent( NoQuotaAgent ):
"""Class that handles galaxy quotas"""
@@ -150,3 +152,20 @@
gqa = self.model.GroupQuotaAssociation( group, quota )
self.sa_session.add( gqa )
self.sa_session.flush()
+ def get_user_quotas( self, user ):
+ rval = []
+ if not user:
+ dqa = self.sa_session.query( self.model.DefaultQuotaAssociation ) \
+ .filter( self.model.DefaultQuotaAssociation.table.c.type==self.model.DefaultQuotaAssociation.types.UNREGISTERED ).first()
+ if dqa:
+ rval.append( dqa.quota )
+ else:
+ dqa = self.sa_session.query( self.model.DefaultQuotaAssociation ) \
+ .filter( self.model.DefaultQuotaAssociation.table.c.type==self.model.DefaultQuotaAssociation.types.REGISTERED ).first()
+ if dqa:
+ rval.append( dqa.quota )
+ for uqa in user.quotas:
+ rval.append( uqa.quota )
+ for gqa in [ uga.group for uga in user.groups ]:
+ rval.append( gqa.quota )
+ return rval
--- a/lib/galaxy/util/__init__.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/util/__init__.py Thu Sep 22 17:13:53 2011 -0400
@@ -179,7 +179,7 @@
return default
return out
-class Params:
+class Params( object ):
"""
Stores and 'sanitizes' parameters. Alphanumeric characters and the
non-alphanumeric ones that are deemed safe are let to pass through (see L{valid_chars}).
--- a/lib/galaxy/web/api/forms.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/api/forms.py Thu Sep 22 17:13:53 2011 -0400
@@ -2,14 +2,14 @@
API operations on FormDefinition objects.
"""
import logging
-from galaxy.web.base.controller import BaseController, url_for
+from galaxy.web.base.controller import BaseAPIController, url_for
from galaxy import web
from galaxy.forms.forms import form_factory
from elementtree.ElementTree import XML
log = logging.getLogger( __name__ )
-class FormDefinitionAPIController( BaseController ):
+class FormDefinitionAPIController( BaseAPIController ):
@web.expose_api
def index( self, trans, **kwd ):
--- a/lib/galaxy/web/api/histories.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/api/histories.py Thu Sep 22 17:13:53 2011 -0400
@@ -9,22 +9,23 @@
from galaxy.model.orm import *
import galaxy.datatypes
from galaxy.util.bunch import Bunch
-from galaxy.web.api.util import *
log = logging.getLogger( __name__ )
-class HistoriesController( BaseController ):
+class HistoriesController( BaseAPIController, UsesHistory ):
@web.expose_api
- def index( self, trans, **kwd ):
+ def index( self, trans, deleted='False', **kwd ):
"""
GET /api/histories
+ GET /api/histories/deleted
Displays a collection (list) of histories.
"""
rval = []
+ deleted = util.string_as_bool( deleted )
- try:
- query = trans.sa_session.query( trans.app.model.History ).filter_by( user=trans.user, deleted=False ).order_by(
+ try:
+ query = trans.sa_session.query( trans.app.model.History ).filter_by( user=trans.user, deleted=deleted ).order_by(
desc(trans.app.model.History.table.c.update_time)).all()
except Exception, e:
rval = "Error in history API"
@@ -44,13 +45,15 @@
return rval
@web.expose_api
- def show( self, trans, id, **kwd ):
+ def show( self, trans, id, deleted='False', **kwd ):
"""
GET /api/histories/{encoded_history_id}
+ GET /api/histories/deleted/{encoded_history_id}
Displays information about a history.
"""
history_id = id
params = util.Params( kwd )
+ deleted = util.string_as_bool( deleted )
def traverse( datasets ):
rval = {}
@@ -64,7 +67,7 @@
return rval
try:
- history = get_history_for_access( trans, history_id )
+ history = self.get_history( trans, history_id, check_ownership=True, check_accessible=True, deleted=deleted )
except Exception, e:
return str( e )
@@ -124,7 +127,7 @@
purge = util.string_as_bool( kwd['payload'].get( 'purge', False ) )
try:
- history = get_history_for_modification( trans, history_id )
+ history = self.get_history( trans, history_id, check_ownership=True, check_accessible=False, deleted=True )
except Exception, e:
return str( e )
@@ -146,3 +149,15 @@
trans.sa_session.flush()
return 'OK'
+
+ @web.expose_api
+ def undelete( self, trans, id, **kwd ):
+ """
+ POST /api/histories/deleted/{encoded_quota_id}/undelete
+ Undeletes a quota
+ """
+ history = self.get_history( trans, history_id, check_ownership=True, check_accessible=False, deleted=True )
+ history.deleted = False
+ trans.sa_session.add( history )
+ trans.sa_session.flush()
+ return 'OK'
--- a/lib/galaxy/web/api/history_contents.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/api/history_contents.py Thu Sep 22 17:13:53 2011 -0400
@@ -7,7 +7,6 @@
from galaxy.web.base.controller import *
from galaxy.util.sanitize_html import sanitize_html
from galaxy.model.orm import *
-from galaxy.web.api.util import *
import pkg_resources
pkg_resources.require( "Routes" )
@@ -15,7 +14,7 @@
log = logging.getLogger( __name__ )
-class HistoryContentsController( BaseController ):
+class HistoryContentsController( BaseAPIController, UsesHistoryDatasetAssociation, UsesHistory ):
@web.expose_api
def index( self, trans, history_id, **kwd ):
@@ -24,7 +23,7 @@
Displays a collection (list) of history contents
"""
try:
- history = get_history_for_access( trans, history_id )
+ history = self.get_history( trans, history_id, check_ownership=True, check_accessible=True )
except Exception, e:
return str( e )
@@ -51,7 +50,7 @@
"""
content_id = id
try:
- content = get_history_content_for_access( trans, content_id )
+ content = self.get_history_dataset_association( trans, content_id, check_ownership=True, check_accessible=True )
except Exception, e:
return str( e )
try:
@@ -63,7 +62,7 @@
# http://routes.groovie.org/generating.html
# url_for is being phased out, so new applications should use url
item['download_url'] = url(controller='dataset', action='display', dataset_id=trans.security.encode_id(content.id), to_ext=content.ext)
- item = encode_all_ids( trans, item )
+ item = self.encode_all_ids( trans, item )
except Exception, e:
item = "Error in history API at listing dataset"
log.error( item + ": %s" % str(e) )
@@ -80,7 +79,7 @@
from_ld_id = payload.get( 'from_ld_id', None )
try:
- history = get_history_for_modification( trans, history_id )
+ history = self.get_history( trans, history_id, check_ownership=True, check_accessible=False )
except Exception, e:
return str( e )
--- a/lib/galaxy/web/api/libraries.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/api/libraries.py Thu Sep 22 17:13:53 2011 -0400
@@ -10,7 +10,7 @@
log = logging.getLogger( __name__ )
-class LibrariesController( BaseController ):
+class LibrariesController( BaseAPIController ):
@web.expose_api
def index( self, trans, **kwd ):
--- a/lib/galaxy/web/api/library_contents.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/api/library_contents.py Thu Sep 22 17:13:53 2011 -0400
@@ -7,11 +7,10 @@
from galaxy.web.base.controller import *
from galaxy.util.sanitize_html import sanitize_html
from galaxy.model.orm import *
-from galaxy.web.api.util import *
log = logging.getLogger( __name__ )
-class LibraryContentsController( BaseController ):
+class LibraryContentsController( BaseAPIController, UsesLibrary, UsesLibraryItems ):
@web.expose_api
def index( self, trans, library_id, **kwd ):
@@ -74,12 +73,12 @@
GET /api/libraries/{encoded_library_id}/contents/{encoded_content_id}
Displays information about a library content (file or folder).
"""
- content_id = id
- try:
- content = get_library_content_for_access( trans, content_id )
- except Exception, e:
- return str( e )
- return encode_all_ids( trans, content.get_api_value( view='element' ) )
+ class_name, content_id = self.__decode_library_content_id( trans, id )
+ if class_name == 'LibraryFolder':
+ content = self.get_library_folder( trans, content_id, check_ownership=False, check_accessibility=True )
+ else:
+ content = self.get_library_dataset( trans, content_id, check_ownership=False, check_accessibility=True )
+ return self.encode_all_ids( trans, content.get_api_value( view='element' ) )
@web.expose_api
def create( self, trans, library_id, payload, **kwd ):
@@ -102,8 +101,8 @@
else:
folder_id = payload.pop( 'folder_id' )
try:
- # _for_modification is not necessary, that security happens in the library_common controller.
- parent = get_library_folder_for_access( trans, library_id, folder_id )
+ # security is checked in the downstream controller
+ parent = self.get_library_folder( trans, folder_id, check_ownership=False, check_accessibility=False )
except Exception, e:
return str( e )
# The rest of the security happens in the library_common controller.
@@ -128,3 +127,11 @@
name = v.name,
url = url_for( 'library_content', library_id=library_id, id=encoded_id ) ) )
return rval
+
+ def __decode_library_content_id( self, trans, content_id ):
+ if ( len( content_id ) % 16 == 0 ):
+ return 'LibraryDataset', content_id
+ elif ( content_id.startswith( 'F' ) ):
+ return 'LibraryFolder', content_id[1:]
+ else:
+ raise HTTPBadRequest( 'Malformed library content id ( %s ) specified, unable to decode.' % str( content_id ) )
--- a/lib/galaxy/web/api/permissions.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/api/permissions.py Thu Sep 22 17:13:53 2011 -0400
@@ -10,7 +10,7 @@
log = logging.getLogger( __name__ )
-class PermissionsController( BaseController ):
+class PermissionsController( BaseAPIController ):
# Method not ideally named
@web.expose_api
--- a/lib/galaxy/web/api/quotas.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/api/quotas.py Thu Sep 22 17:13:53 2011 -0400
@@ -2,24 +2,29 @@
API operations on Quota objects.
"""
import logging
-from galaxy.web.base.controller import BaseController, url_for
+from galaxy.web.base.controller import BaseAPIController, Admin, UsesQuota, url_for
from galaxy import web, util
from elementtree.ElementTree import XML
-from galaxy.web.api.util import *
+
+from galaxy.web.params import QuotaParamParser
+from galaxy.actions.admin import AdminActions
+
+from paste.httpexceptions import HTTPBadRequest
+from galaxy.exceptions import *
log = logging.getLogger( __name__ )
-class QuotaAPIController( BaseController ):
+class QuotaAPIController( BaseAPIController, Admin, AdminActions, UsesQuota, QuotaParamParser ):
@web.expose_api
@web.require_admin
- def index( self, trans, deleted=False, **kwd ):
+ def index( self, trans, deleted='False', **kwd ):
"""
GET /api/quotas
GET /api/quotas/deleted
Displays a collection (list) of quotas.
"""
- #return str( trans.webapp.api_mapper )
rval = []
+ deleted = util.string_as_bool( deleted )
query = trans.sa_session.query( trans.app.model.Quota )
if deleted:
route = 'deleted_quota'
@@ -36,16 +41,13 @@
@web.expose_api
@web.require_admin
- def show( self, trans, id, deleted=False, **kwd ):
+ def show( self, trans, id, deleted='False', **kwd ):
"""
GET /api/quotas/{encoded_quota_id}
GET /api/quotas/deleted/{encoded_quota_id}
Displays information about a quota.
"""
- try:
- quota = get_quota_for_access( trans, id, deleted=deleted )
- except BadRequestException, e:
- return str( e )
+ quota = self.get_quota( trans, id, deleted=util.string_as_bool( deleted ) )
return quota.get_api_value( view='element', value_mapper={ 'id': trans.security.encode_id } )
@web.expose_api
@@ -56,20 +58,18 @@
Creates a new quota.
"""
try:
- self._validate_in_users_and_groups( trans, payload )
+ self.validate_in_users_and_groups( trans, payload )
except Exception, e:
- trans.response.status = 400
- return str( e )
-
- status, result = trans.webapp.controllers['admin'].create_quota( trans, cntrller='api', **payload )
- if status != 200 or type( result ) != trans.app.model.Quota:
- trans.response.status = status
- return str( result )
- else:
- encoded_id = trans.security.encode_id( result.id )
- return dict( id = encoded_id,
- name = result.name,
- url = url_for( 'quotas', id=encoded_id ) )
+ raise HTTPBadRequest( detail=str( e ) )
+ params = self.get_quota_params( payload )
+ try:
+ quota, message = self._create_quota( params )
+ except ActionInputError, e:
+ raise HTTPBadRequest( detail=str( e ) )
+ item = quota.get_api_value( value_mapper={ 'id': trans.security.encode_id } )
+ item['url'] = url_for( 'quota', id=trans.security.encode_id( quota.id ) )
+ item['message'] = message
+ return item
@web.expose_api
@web.require_admin
@@ -79,32 +79,34 @@
Modifies a quota.
"""
try:
- self._validate_in_users_and_groups( trans, payload )
- quota = get_quota_for_access( trans, id, deleted=False ) # deleted quotas are not technically members of this collection
+ self.validate_in_users_and_groups( trans, payload )
except Exception, e:
- trans.response.status = 400
- return str( e )
- # TODO: Doing it this way makes the update non-atomic if a method fails after an earlier one has succeeded.
+ raise HTTPBadRequest( detail=str( e ) )
+
+ quota = self.get_quota( trans, id, deleted=False )
+
+ # FIXME: Doing it this way makes the update non-atomic if a method fails after an earlier one has succeeded.
payload['id'] = id
+ params = self.get_quota_params( payload )
methods = []
if payload.get( 'name', None ) or payload.get( 'description', None ):
- methods.append( trans.webapp.controllers['admin'].rename_quota )
+ methods.append( self._rename_quota )
if payload.get( 'amount', None ):
- methods.append( trans.webapp.controllers['admin'].edit_quota )
+ methods.append( self._edit_quota )
if payload.get( 'default', None ) == 'no':
- methods.append( trans.webapp.controllers['admin'].unset_quota_default )
+ methods.append( self._unset_quota_default )
elif payload.get( 'default', None ):
- methods.append( trans.webapp.controllers['admin'].set_quota_default )
+ methods.append( self._set_quota_default )
if payload.get( 'in_users', None ) or payload.get( 'in_groups', None ):
- methods.append( trans.webapp.controllers['admin'].manage_users_and_groups_for_quota )
+ methods.append( self._manage_users_and_groups_for_quota )
messages = []
for method in methods:
- status, result = method( trans, cntrller='api', **payload )
- if status != 200:
- trans.response.status = status
- return str( result )
- messages.append( result )
+ try:
+ message = method( quota, params )
+ except ActionInputError, e:
+ raise HTTPBadRequest( detail=str( e ) )
+ messages.append( message )
return '; '.join( messages )
@web.expose_api
@@ -114,27 +116,20 @@
DELETE /api/quotas/{encoded_quota_id}
Deletes a quota
"""
- try:
- get_quota_for_access( trans, id, deleted=False ) # deleted quotas are not technically members of this collection
- except BadRequestException, e:
- return str( e )
+ quota = self.get_quota( trans, id, deleted=False ) # deleted quotas are not technically members of this collection
+
# a request body is optional here
payload = kwd.get( 'payload', {} )
payload['id'] = id
+ params = self.get_quota_params( payload )
- status, result = trans.webapp.controllers['admin'].mark_quota_deleted( trans, cntrller='api', **payload )
- if status != 200:
- trans.response.status = status
- return str( result )
- rval = result
-
- if util.string_as_bool( payload.get( 'purge', False ) ):
- status, result = trans.webapp.controllers['admin'].purge_quota( trans, cntrller='api', **payload )
- if status != 200:
- trans.response.status = status
- return str( result )
- rval += '; %s' % result
- return rval
+ try:
+ message = self._mark_quota_deleted( quota, params )
+ if util.string_as_bool( payload.get( 'purge', False ) ):
+ message += self._purge_quota( quota, params )
+ except ActionInputError, e:
+ raise HTTPBadRequest( detail=str( e ) )
+ return message
@web.expose_api
@web.require_admin
@@ -143,39 +138,9 @@
POST /api/quotas/deleted/{encoded_quota_id}/undelete
Undeletes a quota
"""
- status, result = trans.webapp.controllers['admin'].undelete_quota( trans, cntrller='api', id=id )
- if status != 200:
- trans.response.status = status
- return str( result )
- return result
-
- def _validate_in_users_and_groups( self, trans, payload ):
- """
- For convenience, in_users and in_groups can be encoded IDs or emails/group names
- """
- def get_id( item, model_class, column ):
- try:
- return trans.security.decode_id( item )
- except:
- pass # maybe an email/group name
- # this will raise if the item is invalid
- return trans.sa_session.query( model_class ).filter( column == item ).first().id
- new_in_users = []
- new_in_groups = []
- invalid = []
- for item in util.listify( payload.get( 'in_users', [] ) ):
- try:
- new_in_users.append( get_id( item, trans.app.model.User, trans.app.model.User.table.c.email ) )
- except:
- invalid.append( item )
- for item in util.listify( payload.get( 'in_groups', [] ) ):
- try:
- new_in_groups.append( get_id( item, trans.app.model.Group, trans.app.model.Group.table.c.name ) )
- except:
- invalid.append( item )
- if invalid:
- msg = "The following value(s) for associated users and/or groups could not be parsed: %s." % ', '.join( invalid )
- msg += " Valid values are email addresses of users, names of groups, or IDs of both."
- raise Exception( msg )
- payload['in_users'] = map( str, new_in_users )
- payload['in_groups'] = map( str, new_in_groups )
+ quota = self.get_quota( trans, id, deleted=True )
+ params = self.get_quota_params( payload )
+ try:
+ return self._undelete_quota( quota, params )
+ except ActionInputError, e:
+ raise HTTPBadRequest( detail=str( e ) )
--- a/lib/galaxy/web/api/request_types.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/api/request_types.py Thu Sep 22 17:13:53 2011 -0400
@@ -2,7 +2,7 @@
API operations on RequestType objects.
"""
import logging
-from galaxy.web.base.controller import BaseController, url_for
+from galaxy.web.base.controller import BaseAPIController, url_for
from galaxy import web
from galaxy.sample_tracking.request_types import request_type_factory
from elementtree.ElementTree import XML
@@ -10,7 +10,7 @@
log = logging.getLogger( __name__ )
-class RequestTypeAPIController( BaseController ):
+class RequestTypeAPIController( BaseAPIController ):
@web.expose_api
def index( self, trans, **kwd ):
"""
--- a/lib/galaxy/web/api/requests.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/api/requests.py Thu Sep 22 17:13:53 2011 -0400
@@ -11,7 +11,7 @@
log = logging.getLogger( __name__ )
-class RequestsAPIController( BaseController ):
+class RequestsAPIController( BaseAPIController ):
update_types = Bunch( REQUEST = 'request_state' )
update_type_values = [v[1] for v in update_types.items()]
@web.expose_api
--- a/lib/galaxy/web/api/roles.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/api/roles.py Thu Sep 22 17:13:53 2011 -0400
@@ -2,13 +2,13 @@
API operations on Role objects.
"""
import logging
-from galaxy.web.base.controller import BaseController, url_for
+from galaxy.web.base.controller import BaseAPIController, url_for
from galaxy import web
from elementtree.ElementTree import XML
log = logging.getLogger( __name__ )
-class RoleAPIController( BaseController ):
+class RoleAPIController( BaseAPIController ):
@web.expose_api
def index( self, trans, **kwd ):
"""
--- a/lib/galaxy/web/api/samples.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/api/samples.py Thu Sep 22 17:13:53 2011 -0400
@@ -9,7 +9,7 @@
log = logging.getLogger( __name__ )
-class SamplesAPIController( BaseController ):
+class SamplesAPIController( BaseAPIController ):
update_types = Bunch( SAMPLE = [ 'sample_state', 'run_details' ],
SAMPLE_DATASET = [ 'sample_dataset_transfer_status' ] )
update_type_values = []
--- a/lib/galaxy/web/api/users.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/api/users.py Thu Sep 22 17:13:53 2011 -0400
@@ -2,59 +2,79 @@
API operations on User objects.
"""
import logging
-from galaxy.web.base.controller import BaseController, url_for
-from galaxy import web
+from galaxy.web.base.controller import BaseAPIController, url_for
+from galaxy import web, util
from elementtree.ElementTree import XML
+from paste.httpexceptions import *
log = logging.getLogger( __name__ )
-class UserAPIController( BaseController ):
+class UserAPIController( BaseAPIController ):
@web.expose_api
- def index( self, trans, **kwd ):
+ def index( self, trans, deleted='False', **kwd ):
"""
GET /api/users
+ GET /api/users/deleted
Displays a collection (list) of users.
"""
- if not trans.user_is_admin():
- trans.response.status = 403
- return "You are not authorized to view the list of users."
rval = []
- for user in trans.sa_session.query( trans.app.model.User ).filter( trans.app.model.User.table.c.deleted == False ):
+ query = trans.sa_session.query( trans.app.model.User )
+ deleted = util.string_as_bool( deleted )
+ if deleted:
+ route = 'deleted_user'
+ query = query.filter( trans.app.model.User.table.c.deleted == True )
+ # only admins can see deleted users
+ if not trans.user_is_admin():
+ return []
+ else:
+ route = 'user'
+ query = query.filter( trans.app.model.User.table.c.deleted == False )
+ # special case: user can see only their own user
+ if not trans.user_is_admin():
+ item = trans.user.get_api_value( value_mapper={ 'id': trans.security.encode_id } )
+ item['url'] = url_for( route, id=encoded_id )
+ return item
+ for user in query:
item = user.get_api_value( value_mapper={ 'id': trans.security.encode_id } )
encoded_id = trans.security.encode_id( user.id )
- item['url'] = url_for( 'user', id=encoded_id )
+ item['url'] = url_for( route, id=encoded_id )
rval.append( item )
return rval
@web.expose_api
- def show( self, trans, id, **kwd ):
+ def show( self, trans, id, deleted='False', **kwd ):
"""
GET /api/users/{encoded_user_id}
+ GET /api/users/deleted/{encoded_user_id}
Displays information about a user.
"""
- if not trans.user_is_admin():
- trans.response.status = 403
- return "You are not authorized to view user info."
- user_id = id
+ deleted = util.string_as_bool( deleted )
try:
- decoded_user_id = trans.security.decode_id( user_id )
- except TypeError:
- trans.response.status = 400
- return "Malformed user id ( %s ) specified, unable to decode." % str( user_id )
- try:
- user = trans.sa_session.query( trans.app.model.User ).get( decoded_user_id )
+ user = self.get_user( trans, id, deleted=deleted )
+ if not trans.user_is_admin():
+ assert trans.user == user
+ assert not user.deleted
except:
- trans.response.status = 400
- return "That user does not exist."
- item = user.get_api_value( view='element', value_mapper={ 'id': trans.security.encode_id } )
- item['url'] = url_for( 'user', id=user_id )
+ if trans.user_is_admin():
+ raise
+ else:
+ raise HTTPBadRequest( detail='Invalid user id ( %s ) specified' % id )
+ item = user.get_api_value( view='element', value_mapper={ 'id': trans.security.encode_id,
+ 'total_disk_usage': float } )
return item
- @web.expose_api
- def create( self, trans, payload, **kwd ):
- """
- POST /api/users
- Creates a new user.
- """
- trans.response.status = 403
- return "Not implemented."
+ @web.expose
+ def create( self, trans, **kwd ):
+ raise HTTPNotImplemented()
+
+ @web.expose
+ def update( self, trans, **kwd ):
+ raise HTTPNotImplemented()
+
+ @web.expose
+ def delete( self, trans, **kwd ):
+ raise HTTPNotImplemented()
+
+ @web.expose
+ def undelete( self, trans, **kwd ):
+ raise HTTPNotImplemented()
--- a/lib/galaxy/web/api/util.py Thu Sep 22 12:18:38 2011 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,116 +0,0 @@
-"""
-Utility methods for API controllers
-"""
-
-class BadRequestException( Exception ):
- pass
-
-def get_history_for_access( trans, history_id ):
- try:
- decoded_history_id = trans.security.decode_id( history_id )
- except TypeError:
- trans.response.status = 400
- raise BadRequestException( "Malformed history id ( %s ) specified, unable to decode." % str( history_id ) )
- try:
- history = trans.sa_session.query( trans.app.model.History ).get( decoded_history_id )
- assert history
- if history.user != trans.user and not trans.user_is_admin():
- assert trans.sa_session.query( trans.app.model.HistoryUserShareAssociation ).filter_by( user=trans.user, history=history ).count() != 0
- except:
- trans.response.status = 400
- raise BadRequestException( "Invalid history id ( %s ) specified." % str( history_id ) )
- return history
-
-def get_history_for_modification( trans, history_id ):
- history = get_history_for_access( trans, history_id )
- try:
- assert trans.user_is_admin() or history.user == trans.user
- except:
- trans.response.status = 400
- raise BadRequestException( "Invalid history id ( %s ) specified." % str( history_id ) )
- return history
-
-def get_history_content_for_access( trans, content_id ):
- # Note that we could check the history provided in the URL heirarchy here,
- # but it's irrelevant, we care about the history associated with the hda.
- try:
- decoded_content_id = trans.security.decode_id( content_id )
- model_class = trans.app.model.HistoryDatasetAssociation
- except:
- trans.response.status = 400
- raise BadRequestException( "Malformed history content id ( %s ) specified, unable to decode." % str( content_id ) )
- try:
- content = trans.sa_session.query( model_class ).get( decoded_content_id )
- assert content
- if content.history.user != trans.user and not trans.user_is_admin():
- assert trans.sa_session.query(trans.app.model.HistoryUserShareAssociation).filter_by(user=trans.user, history=content.history).count() != 0
- except:
- trans.response.status = 400
- raise BadRequestException( "Invalid history content id ( %s ) specified." % ( str( content_id ) ) )
- return content
-
-def get_library_folder_for_access( trans, library_id, folder_id ):
- """
- When we know we're looking for a folder, take either the 'F' + encoded_id or bare encoded_id.
- """
- if ( len( folder_id ) % 16 == 0 ):
- folder_id = 'F' + folder_id
- return get_library_content_for_access( trans, folder_id )
-
-def get_library_content_for_access( trans, content_id ):
- try:
- if ( len( content_id ) % 16 == 0 ):
- model_class = trans.app.model.LibraryDataset
- decoded_content_id = trans.security.decode_id( content_id )
- elif ( content_id.startswith( 'F' ) ):
- model_class = trans.app.model.LibraryFolder
- decoded_content_id = trans.security.decode_id( content_id[1:] )
- else:
- raise Exception( 'Bad id' )
- except:
- trans.response.status = 400
- raise BadRequestException( "Malformed library content id ( %s ) specified, unable to decode." % str( content_id ) )
- try:
- content = trans.sa_session.query( model_class ).get( decoded_content_id )
- assert content
- assert trans.user_is_admin() or trans.app.security_agent.can_access_library_item( trans.get_current_user_roles(), content, trans.user )
- except:
- trans.response.status = 400
- raise BadRequestException( "Invalid library content id ( %s ) specified." % str( content_id ) )
- return content
-
-def get_quota_for_access( trans, quota_id, deleted=False ):
- """
- No security - the only methods that use this do so with admin users. If
- quota modification becomes a role, this will need to be updated.
- """
- try:
- decoded_quota_id = trans.security.decode_id( quota_id )
- except TypeError:
- trans.response.status = 400
- raise BadRequestException( "Malformed quota id ( %s ) specified, unable to decode." % str( quota_id ) )
- try:
- quota = trans.sa_session.query( trans.app.model.Quota ).get( decoded_quota_id )
- assert quota is not None
- if deleted:
- assert quota.deleted
- else:
- assert not quota.deleted
- except:
- trans.response.status = 400
- raise BadRequestException( "Invalid quota id ( %s ) specified." % str( quota_id ) )
- return quota
-
-def encode_all_ids( trans, rval ):
- """
- encodes all integer values in the dict rval whose keys are 'id' or end with '_id'
- """
- if type( rval ) != dict:
- return rval
- for k, v in rval.items():
- if k == 'id' or k.endswith( '_id' ):
- try:
- rval[k] = trans.security.encode_id( v )
- except:
- pass # probably already encoded
- return rval
--- a/lib/galaxy/web/api/workflows.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/api/workflows.py Thu Sep 22 17:13:53 2011 -0400
@@ -7,13 +7,13 @@
from galaxy import util
from galaxy import web
from galaxy.tools.parameters import visit_input_values, DataToolParameter
-from galaxy.web.base.controller import BaseController, url_for
+from galaxy.web.base.controller import BaseAPIController, url_for
from galaxy.workflow.modules import module_factory
from galaxy.jobs.actions.post import ActionBox
log = logging.getLogger(__name__)
-class WorkflowsAPIController(BaseController):
+class WorkflowsAPIController(BaseAPIController):
@web.expose_api
def index(self, trans, **kwd):
"""
--- a/lib/galaxy/web/base/controller.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/base/controller.py Thu Sep 22 17:13:53 2011 -0400
@@ -13,6 +13,8 @@
from galaxy.visualization.tracks.data_providers import get_data_provider
from galaxy.visualization.tracks.visual_analytics import get_tool_def
from galaxy.security.validate_user_input import validate_username
+from paste.httpexceptions import *
+from galaxy.exceptions import *
from Cheetah.Template import Template
@@ -35,10 +37,11 @@
def __init__( self, app ):
"""Initialize an interface for application 'app'"""
self.app = app
+ self.sa_session = app.model.context
def get_toolbox(self):
"""Returns the application toolbox"""
return self.app.toolbox
- def get_class( self, trans, class_name ):
+ def get_class( self, class_name ):
""" Returns the class object that a string denotes. Without this method, we'd have to do eval(<class_name>). """
if class_name == 'History':
item_class = trans.model.History
@@ -53,27 +56,144 @@
elif class_name == 'Tool':
item_class = trans.model.Tool
elif class_name == 'Job':
- item_class == trans.model.Job
+ item_class = trans.model.Job
+ elif class_name == 'User':
+ item_class = trans.model.User
+ elif class_name == 'Group':
+ item_class = trans.model.Group
+ elif class_name == 'Role':
+ item_class = trans.model.Role
+ elif class_name == 'Quota':
+ item_class = trans.model.Quota
+ elif class_name == 'Library':
+ item_class = trans.model.Library
+ elif class_name == 'LibraryFolder':
+ item_class = trans.model.LibraryFolder
+ elif class_name == 'LibraryDatasetDatasetAssociation':
+ item_class = trans.model.LibraryDatasetDatasetAssociation
+ elif class_name == 'LibraryDataset':
+ item_class = trans.model.LibraryDataset
else:
item_class = None
return item_class
+ def get_object( self, trans, id, class_name, check_ownership=False, check_accessible=False, deleted=None ):
+ """
+ Convenience method to get a model object with the specified checks.
+ """
+ try:
+ decoded_id = trans.security.decode_id( id )
+ except:
+ raise MessageException( "Malformed %s id ( %s ) specified, unable to decode" % ( class_name, str( id ) ), type='error' )
+ try:
+ item_class = self.get_class( class_name )
+ assert item_class is not None
+ item = trans.sa_session.query( item_class ).get( decoded_id )
+ assert item is not None
+ except:
+ raise MessageException( "Invalid %s id ( %s ) specified" % ( class_name, display_id ), type="error" )
+ if check_ownership or check_accessible:
+ self.security_check( trans, item, check_ownership, check_accessible, encoded_id )
+ if deleted == True and not item.deleted:
+ raise ItemDeletionException( '%s "%s" is not deleted' % ( class_name, getattr( item, 'name', id ) ), type="warning" )
+ elif deleted == False and item.deleted:
+ raise ItemDeletionException( '%s "%s" is deleted' % ( class_name, getattr( item, 'name', id ) ), type="warning" )
+ return item
+ def get_user( self, trans, id, check_ownership=False, check_accessible=False, deleted=None ):
+ return self.get_object( trans, id, 'User', check_ownership=False, check_accessible=False, deleted=deleted )
+ def get_group( self, trans, id, check_ownership=False, check_accessible=False, deleted=None ):
+ return self.get_object( trans, id, 'Group', check_ownership=False, check_accessible=False, deleted=deleted )
+ def get_role( self, trans, id, check_ownership=False, check_accessible=False, deleted=None ):
+ return self.get_object( trans, id, 'Role', check_ownership=False, check_accessible=False, deleted=deleted )
+ def encode_all_ids( self, trans, rval ):
+ """
+ Encodes all integer values in the dict rval whose keys are 'id' or end with '_id'
+
+ It might be useful to turn this in to a decorator
+ """
+ if type( rval ) != dict:
+ return rval
+ for k, v in rval.items():
+ if k == 'id' or k.endswith( '_id' ):
+ try:
+ rval[k] = trans.security.encode_id( v )
+ except:
+ pass # probably already encoded
+ return rval
Root = BaseController
+class BaseUIController( BaseController ):
+ def get_object( self, trans, id, class_name, check_ownership=False, check_accessible=False, deleted=None ):
+ try:
+ return BaseController.get_object( self, trans, id, class_name, check_ownership=False, check_accessible=False, deleted=None )
+ except MessageException, e:
+ raise # handled in the caller
+ except:
+ log.exception( "Execption in get_object check for %s %s:" % ( class_name, str( id ) ) )
+ raise Exception( 'Server error retrieving %s id ( %s ).' % ( class_name, str( id ) ) )
+
+class BaseAPIController( BaseController ):
+ def get_object( self, trans, id, class_name, check_ownership=False, check_accessible=False, deleted=None ):
+ try:
+ return BaseController.get_object( self, trans, id, class_name, check_ownership=False, check_accessible=False, deleted=None )
+ except ItemDeletionException, e:
+ raise HTTPBadRequest( detail="Invalid %s id ( %s ) specified" % ( class_name, str( id ) ) )
+ except MessageException, e:
+ raise HTTPBadRequest( detail=e.err_msg )
+ except Exception, e:
+ log.exception( "Execption in get_object check for %s %s:" % ( class_name, str( id ) ) )
+ raise HTTPInternalServerError( comment=str( e ) )
+ def validate_in_users_and_groups( self, trans, payload ):
+ """
+ For convenience, in_users and in_groups can be encoded IDs or emails/group names in the API.
+ """
+ def get_id( item, model_class, column ):
+ try:
+ return trans.security.decode_id( item )
+ except:
+ pass # maybe an email/group name
+ # this will raise if the item is invalid
+ return trans.sa_session.query( model_class ).filter( column == item ).first().id
+ new_in_users = []
+ new_in_groups = []
+ invalid = []
+ for item in util.listify( payload.get( 'in_users', [] ) ):
+ try:
+ new_in_users.append( get_id( item, trans.app.model.User, trans.app.model.User.table.c.email ) )
+ except:
+ invalid.append( item )
+ for item in util.listify( payload.get( 'in_groups', [] ) ):
+ try:
+ new_in_groups.append( get_id( item, trans.app.model.Group, trans.app.model.Group.table.c.name ) )
+ except:
+ invalid.append( item )
+ if invalid:
+ msg = "The following value(s) for associated users and/or groups could not be parsed: %s." % ', '.join( invalid )
+ msg += " Valid values are email addresses of users, names of groups, or IDs of both."
+ raise Exception( msg )
+ payload['in_users'] = map( str, new_in_users )
+ payload['in_groups'] = map( str, new_in_groups )
+ def not_implemented( self, trans, **kwd ):
+ raise HTTPNotImplemented()
+
class SharableItemSecurity:
""" Mixin for handling security for sharable items. """
- def security_check( self, user, item, check_ownership=False, check_accessible=False ):
+ def security_check( self, trans, item, check_ownership=False, check_accessible=False ):
""" Security checks for an item: checks if (a) user owns item or (b) item is accessible to user. """
if check_ownership:
# Verify ownership.
- if not user:
- error( "Must be logged in to manage Galaxy items" )
- if item.user != user:
- error( "%s is not owned by current user" % item.__class__.__name__ )
+ if not trans.user:
+ raise ItemOwnershipException( "Must be logged in to manage Galaxy items", type='error' )
+ if item.user != trans.user:
+ raise ItemOwnershipException( "%s is not owned by the current user" % item.__class__.__name__, type='error' )
if check_accessible:
- # Verify accessible.
- if ( item.user != user ) and ( not item.importable ) and ( user not in item.users_shared_with_dot_users ):
- error( "%s is not accessible to current user" % item.__class__.__name__ )
+ if type( item ) in ( trans.app.model.LibraryFolder, trans.app.model.LibraryDatasetDatasetAssociation, trans.app.model.LibraryDataset ):
+ if not ( trans.user_is_admin() or trans.app.security_agent.can_access_library_i9tem( trans.get_current_user_roles(), item, trans.user ) ):
+ raise ItemAccessibilityException( "%s is not accessible to the current user" % item.__class__.__name__, type='error' )
+ else:
+ # Verify accessible.
+ if ( item.user != trans.user ) and ( not item.importable ) and ( trans.user not in item.users_shared_with_dot_users ):
+ raise ItemAccessibilityException( "%s is not accessible to the current user" % item.__class__.__name__, type='error' )
return item
#
@@ -95,7 +215,7 @@
except:
data = None
if not data:
- raise paste.httpexceptions.HTTPRequestRangeNotSatisfiable( "Invalid dataset id: %s." % str( dataset_id ) )
+ raise HTTPRequestRangeNotSatisfiable( "Invalid dataset id: %s." % str( dataset_id ) )
if check_ownership:
# Verify ownership.
user = trans.get_user()
@@ -111,6 +231,16 @@
else:
error( "You are not allowed to access this dataset" )
return data
+ def get_history_dataset_association( self, trans, dataset_id, check_ownership=True, check_accessible=False ):
+ """Get a HistoryDatasetAssociation from the database by id, verifying ownership."""
+ hda = self.get_object( trans, id, 'HistoryDatasetAssociation', check_ownership=check_ownership, check_accessible=check_accessible, deleted=deleted )
+ self.security_check( trans, history, check_ownership=check_ownership, check_accessible=False ) # check accessibility here
+ if check_accessible:
+ if trans.app.security_agent.can_access_dataset( trans.get_current_user_roles(), hda.dataset ):
+ if hda.state == trans.model.Dataset.states.UPLOAD:
+ error( "Please wait until this dataset finishes uploading before attempting to view it." )
+ else:
+ error( "You are not allowed to access this dataset" )
def get_data( self, dataset, preview=True ):
""" Gets a dataset's data. """
# Get data from file, truncating if necessary.
@@ -126,6 +256,21 @@
truncated = False
return truncated, dataset_data
+class UsesLibrary:
+ def get_library( self, trans, id, check_ownership=False, check_accessible=True ):
+ l = self.get_object( trans, id, 'Library' )
+ if check_accessible and not ( trans.user_is_admin() or trans.app.security_agent.can_access_library( trans.get_current_user_roles(), l ) ):
+ error( "LibraryFolder is not accessible to the current user" )
+ return l
+
+class UsesLibraryItems( SharableItemSecurity ):
+ def get_library_folder( self, trans, id, check_ownership=False, check_accessible=True ):
+ return self.get_object( trans, id, 'LibraryFolder', check_ownership=False, check_accessible=check_accessible )
+ def get_library_dataset_dataset_association( self, trans, id, check_ownership=False, check_accessible=True ):
+ return self.get_object( trans, id, 'LibraryDatasetDatasetAssociation', check_ownership=False, check_accessible=check_accessible )
+ def get_library_dataset( self, trans, id, check_ownership=False, check_accessible=True ):
+ return self.get_object( trans, id, 'LibraryDataset', check_ownership=False, check_accessible=check_accessible )
+
class UsesVisualization( SharableItemSecurity ):
""" Mixin for controllers that use Visualization objects. """
@@ -157,7 +302,7 @@
if not visualization:
error( "Visualization not found" )
else:
- return self.security_check( trans.get_user(), visualization, check_ownership, check_accessible )
+ return self.security_check( trans, visualization, check_ownership, check_accessible )
def get_visualization_config( self, trans, visualization ):
""" Returns a visualization's configuration. Only works for trackster visualizations right now. """
@@ -218,7 +363,7 @@
if not workflow:
error( "Workflow not found" )
else:
- return self.security_check( trans.get_user(), workflow, check_ownership, check_accessible )
+ return self.security_check( trans, workflow, check_ownership, check_accessible )
def get_stored_workflow_steps( self, trans, stored_workflow ):
""" Restores states for a stored workflow's steps. """
for step in stored_workflow.latest_workflow.steps:
@@ -246,17 +391,10 @@
class UsesHistory( SharableItemSecurity ):
""" Mixin for controllers that use History objects. """
- def get_history( self, trans, id, check_ownership=True, check_accessible=False ):
+ def get_history( self, trans, id, check_ownership=True, check_accessible=False, deleted=None ):
"""Get a History from the database by id, verifying ownership."""
- # Load history from database
- try:
- history = trans.sa_session.query( trans.model.History ).get( trans.security.decode_id( id ) )
- except TypeError:
- history = None
- if not history:
- error( "History not found" )
- else:
- return self.security_check( trans.get_user(), history, check_ownership, check_accessible )
+ history = self.get_object( trans, id, 'History', check_ownership=check_ownership, check_accessible=check_accessible, deleted=deleted )
+ return self.security_check( trans, history, check_ownership, check_accessible )
def get_history_datasets( self, trans, history, show_deleted=False, show_hidden=False, show_purged=False ):
""" Returns history's datasets. """
query = trans.sa_session.query( trans.model.HistoryDatasetAssociation ) \
@@ -1113,6 +1251,10 @@
""" Return item based on id. """
raise "Unimplemented Method"
+class UsesQuota( object ):
+ def get_quota( self, trans, id, check_ownership=False, check_accessible=False, deleted=None ):
+ return self.get_object( trans, id, 'Quota', check_ownership=False, check_accessible=False, deleted=deleted )
+
"""
Deprecated: `BaseController` used to be available under the name `Root`
"""
@@ -1524,538 +1666,6 @@
message=util.sanitize_text( message ),
status='done' ) )
- # Galaxy Quota Stuff
- @web.expose
- @web.require_admin
- def quotas( self, trans, **kwargs ):
- if 'operation' in kwargs:
- operation = kwargs['operation'].lower()
- if operation == "quotas":
- return self.quota( trans, **kwargs )
- if operation == "create":
- return self.create_quota( trans, **kwargs )
- if operation == "delete":
- return self.mark_quota_deleted( trans, **kwargs )
- if operation == "undelete":
- return self.undelete_quota( trans, **kwargs )
- if operation == "purge":
- return self.purge_quota( trans, **kwargs )
- if operation == "change amount":
- return self.edit_quota( trans, **kwargs )
- if operation == "manage users and groups":
- return self.manage_users_and_groups_for_quota( trans, **kwargs )
- if operation == "rename":
- return self.rename_quota( trans, **kwargs )
- if operation == "edit":
- return self.edit_quota( trans, **kwargs )
- # Render the list view
- return self.quota_list_grid( trans, **kwargs )
-
- @web.expose
- @web.require_admin
- def create_quota( self, trans, cntrller='admin', **kwd ):
- params = util.Params( kwd )
- webapp = params.get( 'webapp', 'galaxy' )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- name = util.restore_text( params.get( 'name', '' ) )
- description = util.restore_text( params.get( 'description', '' ) )
- amount = util.restore_text( params.get( 'amount', '' ).strip() )
- if amount.lower() in ( 'unlimited', 'none', 'no limit' ):
- create_amount = None
- else:
- try:
- create_amount = util.size_to_bytes( amount )
- except AssertionError:
- create_amount = False
- operation = params.get( 'operation', '' )
- default = params.get( 'default', 'no' )
- in_users = util.listify( params.get( 'in_users', [] ) )
- out_users = util.listify( params.get( 'out_users', [] ) )
- in_groups = util.listify( params.get( 'in_groups', [] ) )
- out_groups = util.listify( params.get( 'out_groups', [] ) )
- if params.get( 'create_quota_button', False ) or cntrller == 'api':
- if not name or not description:
- message = "Enter a valid name and a description."
- status = 'error'
- elif trans.sa_session.query( trans.app.model.Quota ).filter( trans.app.model.Quota.table.c.name==name ).first():
- message = "Quota names must be unique and a quota with that name already exists, so choose another name."
- status = 'error'
- elif not params.get( 'amount', None ):
- message = "Enter a valid quota amount."
- status = 'error'
- elif create_amount is False:
- message = "Unable to parse the provided amount."
- status = 'error'
- elif operation not in trans.app.model.Quota.valid_operations:
- message = "Enter a valid operation."
- status = 'error'
- elif default != 'no' and default not in trans.app.model.DefaultQuotaAssociation.types.__dict__.values():
- message = "Enter a valid default type."
- status = 'error'
- elif default != 'no' and operation != '=':
- message = "Operation for a default quota must be '='."
- status = 'error'
- operation = '='
- else:
- # Create the quota
- quota = trans.app.model.Quota( name=name, description=description, amount=create_amount, operation=operation )
- trans.sa_session.add( quota )
- # If this is a default quota, create the DefaultQuotaAssociation
- if default != 'no':
- trans.app.quota_agent.set_default_quota( default, quota )
- else:
- # Create the UserQuotaAssociations
- for user in [ trans.sa_session.query( trans.app.model.User ).get( x ) for x in in_users ]:
- uqa = trans.app.model.UserQuotaAssociation( user, quota )
- trans.sa_session.add( uqa )
- # Create the GroupQuotaAssociations
- for group in [ trans.sa_session.query( trans.app.model.Group ).get( x ) for x in in_groups ]:
- gqa = trans.app.model.GroupQuotaAssociation( group, quota )
- trans.sa_session.add( gqa )
- trans.sa_session.flush()
- message = "Quota '%s' has been created with %d associated users and %d associated groups." % ( quota.name, len( in_users ), len( in_groups ) )
- if cntrller == 'api':
- return 200, quota
- return trans.response.send_redirect( web.url_for( controller='admin',
- action='quotas',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='done' ) )
- in_users = map( int, in_users )
- in_groups = map( int, in_groups )
- new_in_users = []
- new_in_groups = []
- for user in trans.sa_session.query( trans.app.model.User ) \
- .filter( trans.app.model.User.table.c.deleted==False ) \
- .order_by( trans.app.model.User.table.c.email ):
- if user.id in in_users:
- new_in_users.append( ( user.id, user.email ) )
- else:
- out_users.append( ( user.id, user.email ) )
- for group in trans.sa_session.query( trans.app.model.Group ) \
- .filter( trans.app.model.Group.table.c.deleted==False ) \
- .order_by( trans.app.model.Group.table.c.name ):
- if group.id in in_groups:
- new_in_groups.append( ( group.id, group.name ) )
- else:
- out_groups.append( ( group.id, group.name ) )
- if cntrller == 'api':
- if status == 'error':
- return 400, message
- return 500, message # should never get here...
- return trans.fill_template( '/admin/quota/quota_create.mako',
- webapp=webapp,
- name=name,
- description=description,
- amount=amount,
- operation=operation,
- default=default,
- in_users=new_in_users,
- out_users=out_users,
- in_groups=new_in_groups,
- out_groups=out_groups,
- message=message,
- status=status )
-
- @web.expose
- @web.require_admin
- def rename_quota( self, trans, cntrller='admin', **kwd ):
- params = util.Params( kwd )
- webapp = params.get( 'webapp', 'galaxy' )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- id = params.get( 'id', None )
- error = True
- try:
- assert id, 'No quota ids received for renaming'
- quota = get_quota( trans, id )
- assert quota, 'Quota id (%s) is invalid' % id
- error = False
- except AssertionError, e:
- message = str( e )
- if error:
- if cntrller == 'api':
- return 400, message
- return trans.response.send_redirect( web.url_for( controller='admin',
- action='quotas',
- webapp=webapp,
- message=message,
- status='error' ) )
- if params.get( 'rename_quota_button', False ) or cntrller == 'api':
- new_name = util.restore_text( params.get( 'name', quota.name ) )
- new_description = util.restore_text( params.get( 'description', quota.description ) )
- if not new_name:
- message = 'Enter a valid name'
- status='error'
- elif new_name != quota.name and trans.sa_session.query( trans.app.model.Quota ).filter( trans.app.model.Quota.table.c.name==new_name ).first():
- message = 'A quota with that name already exists'
- status = 'error'
- else:
- old_name = quota.name
- quota.name = new_name
- quota.description = new_description
- trans.sa_session.add( quota )
- trans.sa_session.flush()
- message = "Quota '%s' has been renamed to '%s'" % ( old_name, new_name )
- if cntrller == 'api':
- return 200, message
- return trans.response.send_redirect( web.url_for( controller='admin',
- action='quotas',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='done' ) )
- if cntrller == 'api':
- if status == 'error':
- return 400, message
- return 500, message # should never get here...
- return trans.fill_template( '/admin/quota/quota_rename.mako',
- quota=quota,
- webapp=webapp,
- message=message,
- status=status )
-
- @web.expose
- @web.require_admin
- def manage_users_and_groups_for_quota( self, trans, cntrller='admin', **kwd ):
- params = util.Params( kwd )
- webapp = params.get( 'webapp', 'galaxy' )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- id = params.get( 'id', None )
- error = True
- try:
- assert id, 'No quota ids received for managing users and groups'
- quota = get_quota( trans, id )
- assert quota, 'Quota id (%s) is invalid' % id
- assert not quota.default, 'Default quotas cannot be associated with specific users and groups'
- error = False
- except AssertionError, e:
- message = str( e )
- if error:
- if cntrller == 'api':
- return 400, message
- return trans.response.send_redirect( web.url_for( controller='admin',
- action='quotas',
- webapp=webapp,
- message=message,
- status='error' ) )
- if params.get( 'quota_members_edit_button', False ) or cntrller == 'api':
- in_users = [ trans.sa_session.query( trans.app.model.User ).get( x ) for x in util.listify( params.in_users ) ]
- in_groups = [ trans.sa_session.query( trans.app.model.Group ).get( x ) for x in util.listify( params.in_groups ) ]
- trans.app.quota_agent.set_entity_quota_associations( quotas=[ quota ], users=in_users, groups=in_groups )
- trans.sa_session.refresh( quota )
- message = "Quota '%s' has been updated with %d associated users and %d associated groups" % ( quota.name, len( in_users ), len( in_groups ) )
- if cntrller == 'api':
- return 200, message
- return trans.response.send_redirect( web.url_for( controller='admin',
- action='quotas',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status=status ) )
- # api cannot get to here
- in_users = []
- out_users = []
- in_groups = []
- out_groups = []
- for user in trans.sa_session.query( trans.app.model.User ) \
- .filter( trans.app.model.User.table.c.deleted==False ) \
- .order_by( trans.app.model.User.table.c.email ):
- if user in [ x.user for x in quota.users ]:
- in_users.append( ( user.id, user.email ) )
- else:
- out_users.append( ( user.id, user.email ) )
- for group in trans.sa_session.query( trans.app.model.Group ) \
- .filter( trans.app.model.Group.table.c.deleted==False ) \
- .order_by( trans.app.model.Group.table.c.name ):
- if group in [ x.group for x in quota.groups ]:
- in_groups.append( ( group.id, group.name ) )
- else:
- out_groups.append( ( group.id, group.name ) )
- return trans.fill_template( '/admin/quota/quota.mako',
- quota=quota,
- in_users=in_users,
- out_users=out_users,
- in_groups=in_groups,
- out_groups=out_groups,
- webapp=webapp,
- message=message,
- status=status )
-
- @web.expose
- @web.require_admin
- def edit_quota( self, trans, cntrller='admin', **kwd ):
- params = util.Params( kwd )
- webapp = params.get( 'webapp', 'galaxy' )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- id = params.get( 'id', None )
- if not id:
- message = "No quota ids received for editing"
- if cntrller == 'api':
- return 400, message
- return trans.response.send_redirect( web.url_for( controller='admin',
- action='quotas',
- webapp=webapp,
- message=message,
- status='error' ) )
- quota = get_quota( trans, id )
- if params.get( 'edit_quota_button', False ) or cntrller == 'api':
- amount = util.restore_text( params.get( 'amount', '' ).strip() )
- if amount.lower() in ( 'unlimited', 'none', 'no limit' ):
- new_amount = None
- else:
- try:
- new_amount = util.size_to_bytes( amount )
- except AssertionError:
- new_amount = False
- operation = params.get( 'operation', None )
- if not params.get( 'amount', None ):
- message = 'Enter a valid amount'
- status='error'
- elif new_amount is False:
- message = 'Unable to parse the provided amount'
- status = 'error'
- elif operation not in trans.app.model.Quota.valid_operations:
- message = 'Enter a valid operation'
- status = 'error'
- else:
- quota.amount = new_amount
- quota.operation = operation
- trans.sa_session.add( quota )
- trans.sa_session.flush()
- message = "Quota '%s' is now '%s'" % ( quota.name, quota.operation + quota.display_amount )
- if cntrller == 'api':
- return 200, message
- return trans.response.send_redirect( web.url_for( controller='admin',
- action='quotas',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='done' ) )
- if cntrller == 'api':
- if status == 'error':
- return 400, message
- return 500, message # should never get here...
- return trans.fill_template( '/admin/quota/quota_edit.mako',
- quota=quota,
- webapp=webapp,
- message=message,
- status=status )
-
- @web.expose
- @web.require_admin
- def set_quota_default( self, trans, cntrller='admin', **kwd ):
- params = util.Params( kwd )
- webapp = params.get( 'webapp', 'galaxy' )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- default = params.get( 'default', '' )
- id = params.get( 'id', None )
- if not id:
- message = "No quota ids received for managing defaults"
- if cntrller == 'api':
- return 400, message
- return trans.response.send_redirect( web.url_for( controller='admin',
- action='quotas',
- webapp=webapp,
- message=message,
- status='error' ) )
- quota = get_quota( trans, id )
- if params.get( 'set_default_quota_button', False ) or cntrller == 'api':
- if default != 'no' and default not in trans.app.model.DefaultQuotaAssociation.types.__dict__.values():
- message = "Enter a valid default type."
- status = 'error'
- else:
- if default != 'no':
- trans.app.quota_agent.set_default_quota( default, quota )
- message = "Quota '%s' is now the default for %s users" % ( quota.name, default )
- if cntrller == 'api':
- return 200, message
- return trans.response.send_redirect( web.url_for( controller='admin',
- action='quotas',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='done' ) )
- if not default:
- default = 'no'
- if cntrller == 'api':
- if status == 'error':
- return 400, message
- return 500, message # should never get here...
- return trans.fill_template( '/admin/quota/quota_set_default.mako',
- quota=quota,
- webapp=webapp,
- default=default,
- message=message,
- status=status )
- @web.expose
- @web.require_admin
- def unset_quota_default( self, trans, cntrller='admin', **kwd ):
- params = util.Params( kwd )
- webapp = params.get( 'webapp', 'galaxy' )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- default = params.get( 'default', '' )
- id = params.get( 'id', None )
- if not id:
- message = "No quota ids received for managing defaults"
- if cntrller == 'api':
- return 400, message
- return trans.response.send_redirect( web.url_for( controller='admin',
- action='quotas',
- webapp=webapp,
- message=message,
- status='error' ) )
- quota = get_quota( trans, id )
- if not quota.default:
- message = "Quota '%s' is not a default." % quota.name
- status = 'error'
- else:
- message = "Quota '%s' is no longer the default for %s users." % ( quota.name, quota.default[0].type )
- status = 'done'
- for dqa in quota.default:
- trans.sa_session.delete( dqa )
- trans.sa_session.flush()
- if cntrller == 'api':
- if status == 'done':
- return 200, message
- elif status == 'error':
- return 400, message
- return 500, message # should never get here...
- return trans.response.send_redirect( web.url_for( controller='admin',
- action='quotas',
- webapp=webapp,
- message=message,
- status=status ) )
-
- @web.expose
- @web.require_admin
- def mark_quota_deleted( self, trans, cntrller='admin', **kwd ):
- params = util.Params( kwd )
- webapp = params.get( 'webapp', 'galaxy' )
- id = kwd.get( 'id', None )
- ids = util.listify( id )
- error = True
- quotas = []
- try:
- assert id, 'No quota ids received for deleting'
- for quota_id in ids:
- quota = get_quota( trans, quota_id )
- assert quota, 'Quota id (%s) is invalid' % id
- assert not quota.default, "Quota '%s' is a default, please unset it as a default before deleting it" % ( quota.name )
- quotas.append( quota )
- error = False
- except AssertionError, e:
- message = str( e )
- if error:
- if cntrller == 'api':
- return 400, message
- return trans.response.send_redirect( web.url_for( controller='admin',
- action='quotas',
- webapp=webapp,
- message=message,
- status='error' ) )
- message = "Deleted %d quotas: " % len( ids )
- for quota in quotas:
- quota.deleted = True
- trans.sa_session.add( quota )
- message += " %s " % quota.name
- trans.sa_session.flush()
- if cntrller == 'api':
- return 200, message
- trans.response.send_redirect( web.url_for( controller='admin',
- action='quotas',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='done' ) )
- @web.expose
- @web.require_admin
- def undelete_quota( self, trans, cntrller='admin', **kwd ):
- params = util.Params( kwd )
- webapp = params.get( 'webapp', 'galaxy' )
- id = kwd.get( 'id', None )
- ids = util.listify( id )
- error = True
- quotas = []
- try:
- assert id, 'No quota ids received for undeleting'
- for quota_id in ids:
- quota = get_quota( trans, quota_id )
- assert quota, 'Quota id (%s) is invalid' % id
- assert quota.deleted, "Quota '%s' has not been deleted, so it cannot be undeleted." % quota.name
- quotas.append( quota )
- error = False
- except AssertionError, e:
- message = str( e )
- if error:
- if cntrller == 'api':
- return 400, message
- return trans.response.send_redirect( web.url_for( controller='admin',
- action='quotas',
- webapp=webapp,
- message=message,
- status='error' ) )
- message = "Undeleted %d quotas: " % len( ids )
- for quota in quotas:
- quota.deleted = False
- trans.sa_session.add( quota )
- trans.sa_session.flush()
- message += " %s " % quota.name
- if cntrller == 'api':
- return 200, message
- trans.response.send_redirect( web.url_for( controller='admin',
- action='quotas',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='done' ) )
- @web.expose
- @web.require_admin
- def purge_quota( self, trans, cntrller='admin', **kwd ):
- # This method should only be called for a Quota that has previously been deleted.
- # Purging a deleted Quota deletes all of the following from the database:
- # - UserQuotaAssociations where quota_id == Quota.id
- # - GroupQuotaAssociations where quota_id == Quota.id
- params = util.Params( kwd )
- webapp = params.get( 'webapp', 'galaxy' )
- id = kwd.get( 'id', None )
- ids = util.listify( id )
- error = True
- quotas = []
- try:
- assert id, 'No quota ids received for undeleting'
- for quota_id in ids:
- quota = get_quota( trans, quota_id )
- assert quota, 'Quota id (%s) is invalid' % id
- assert quota.deleted, "Quota '%s' has not been deleted, so it cannot be purged." % quota.name
- quotas.append( quota )
- error = False
- except AssertionError, e:
- message = str( e )
- if error:
- if cntrller == 'api':
- return 400, message
- return trans.response.send_redirect( web.url_for( controller='admin',
- action='quotas',
- webapp=webapp,
- message=message,
- status='error' ) )
- message = "Purged %d quotas: " % len( ids )
- for quota in quotas:
- # Delete UserQuotaAssociations
- for uqa in quota.users:
- trans.sa_session.delete( uqa )
- # Delete GroupQuotaAssociations
- for gqa in quota.groups:
- trans.sa_session.delete( gqa )
- trans.sa_session.flush()
- message += " %s " % quota.name
- if cntrller == 'api':
- return 200, message
- trans.response.send_redirect( web.url_for( controller='admin',
- action='quotas',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='done' ) )
# Galaxy Group Stuff
@web.expose
@web.require_admin
--- a/lib/galaxy/web/buildapp.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/buildapp.py Thu Sep 22 17:13:53 2011 -0400
@@ -22,12 +22,12 @@
import galaxy.datatypes.registry
import galaxy.web.framework
-def add_controllers( webapp, app ):
+def add_ui_controllers( webapp, app ):
"""
Search for controllers in the 'galaxy.web.controllers' module and add
them to the webapp.
"""
- from galaxy.web.base.controller import BaseController
+ from galaxy.web.base.controller import BaseUIController
from galaxy.web.base.controller import ControllerUnavailable
import galaxy.web.controllers
controller_dir = galaxy.web.controllers.__path__[0]
@@ -45,11 +45,11 @@
# Look for a controller inside the modules
for key in dir( module ):
T = getattr( module, key )
- if isclass( T ) and T is not BaseController and issubclass( T, BaseController ):
- webapp.add_controller( name, T( app ) )
+ if isclass( T ) and T is not BaseUIController and issubclass( T, BaseUIController ):
+ webapp.add_ui_controller( name, T( app ) )
def add_api_controllers( webapp, app ):
- from galaxy.web.base.controller import BaseController
+ from galaxy.web.base.controller import BaseAPIController
from galaxy.web.base.controller import ControllerUnavailable
import galaxy.web.api
controller_dir = galaxy.web.api.__path__[0]
@@ -66,7 +66,7 @@
module = getattr( module, comp )
for key in dir( module ):
T = getattr( module, key )
- if isclass( T ) and T is not BaseController and issubclass( T, BaseController ):
+ if isclass( T ) and T is not BaseAPIController and issubclass( T, BaseAPIController ):
webapp.add_api_controller( name, T( app ) )
def app_factory( global_conf, **kwargs ):
@@ -87,7 +87,7 @@
atexit.register( app.shutdown )
# Create the universe WSGI application
webapp = galaxy.web.framework.WebApplication( app, session_cookie='galaxysession' )
- add_controllers( webapp, app )
+ add_ui_controllers( webapp, app )
# Force /history to go to /root/history -- needed since the tests assume this
webapp.add_route( '/history', controller='root', action='history' )
# These two routes handle our simple needs at the moment
@@ -129,9 +129,9 @@
webapp.api_mapper.resource( 'request_type', 'request_types', path_prefix='/api' )
webapp.api_mapper.resource( 'role', 'roles', path_prefix='/api' )
webapp.api_mapper.resource_with_deleted( 'quota', 'quotas', path_prefix='/api' )
- webapp.api_mapper.resource( 'user', 'users', path_prefix='/api' )
+ webapp.api_mapper.resource_with_deleted( 'user', 'users', path_prefix='/api' )
webapp.api_mapper.resource( 'workflow', 'workflows', path_prefix='/api' )
- webapp.api_mapper.resource( 'history', 'histories', path_prefix='/api' )
+ webapp.api_mapper.resource_with_deleted( 'history', 'histories', path_prefix='/api' )
#webapp.api_mapper.connect( 'run_workflow', '/api/workflow/{workflow_id}/library/{library_id}', controller='workflows', action='run', workflow_id=None, library_id=None, conditions=dict(method=["GET"]) )
webapp.finalize_config()
--- a/lib/galaxy/web/controllers/admin.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/controllers/admin.py Thu Sep 22 17:13:53 2011 -0400
@@ -5,6 +5,10 @@
import logging
log = logging.getLogger( __name__ )
+from galaxy.actions.admin import AdminActions
+from galaxy.web.params import QuotaParamParser
+from galaxy.exceptions import *
+
class UserListGrid( grids.Grid ):
class EmailColumn( grids.TextColumn ):
def get_value( self, trans, grid, user ):
@@ -344,7 +348,7 @@
allow_multiple=False,
url_args=dict( webapp="galaxy", action="edit_quota" ) ),
grids.GridOperation( "Manage users and groups",
- condition=( lambda item: not item.default ),
+ condition=( lambda item: not item.default and not item.deleted ),
allow_multiple=False,
url_args=dict( webapp="galaxy", action="manage_users_and_groups_for_quota" ) ),
grids.GridOperation( "Set as different type of default",
@@ -352,11 +356,11 @@
allow_multiple=False,
url_args=dict( webapp="galaxy", action="set_quota_default" ) ),
grids.GridOperation( "Set as default",
- condition=( lambda item: not item.default ),
+ condition=( lambda item: not item.default and not item.deleted ),
allow_multiple=False,
url_args=dict( webapp="galaxy", action="set_quota_default" ) ),
grids.GridOperation( "Unset as default",
- condition=( lambda item: item.default ),
+ condition=( lambda item: item.default and not item.deleted ),
allow_multiple=False,
url_args=dict( webapp="galaxy", action="unset_quota_default" ) ),
grids.GridOperation( "Delete",
@@ -380,9 +384,252 @@
preserve_state = False
use_paging = True
-class AdminGalaxy( BaseController, Admin ):
+class AdminGalaxy( BaseUIController, Admin, AdminActions, QuotaParamParser ):
user_list_grid = UserListGrid()
role_list_grid = RoleListGrid()
group_list_grid = GroupListGrid()
quota_list_grid = QuotaListGrid()
+
+ # Galaxy Quota Stuff
+ @web.expose
+ @web.require_admin
+ def quotas( self, trans, **kwargs ):
+ if 'operation' in kwargs:
+ operation = kwargs.pop('operation').lower()
+ if operation == "quotas":
+ return self.quota( trans, **kwargs )
+ if operation == "create":
+ return self.create_quota( trans, **kwargs )
+ if operation == "delete":
+ return self.mark_quota_deleted( trans, **kwargs )
+ if operation == "undelete":
+ return self.undelete_quota( trans, **kwargs )
+ if operation == "purge":
+ return self.purge_quota( trans, **kwargs )
+ if operation == "change amount":
+ return self.edit_quota( trans, **kwargs )
+ if operation == "manage users and groups":
+ return self.manage_users_and_groups_for_quota( trans, **kwargs )
+ if operation == "rename":
+ return self.rename_quota( trans, **kwargs )
+ if operation == "edit":
+ return self.edit_quota( trans, **kwargs )
+ # Render the list view
+ return self.quota_list_grid( trans, **kwargs )
+
+ @web.expose
+ @web.require_admin
+ def create_quota( self, trans, **kwd ):
+ params = self.get_quota_params( kwd )
+ if params.get( 'create_quota_button', False ):
+ try:
+ quota, message = self._create_quota( trans, params )
+ return trans.response.send_redirect( web.url_for( controller='admin',
+ action='quotas',
+ webapp=params.webapp,
+ message=util.sanitize_text( message ),
+ status='done' ) )
+ except MessageException, e:
+ params.message = str( e )
+ params.status = 'error'
+ in_users = map( int, params.in_users )
+ in_groups = map( int, params.in_groups )
+ new_in_users = []
+ new_in_groups = []
+ for user in trans.sa_session.query( trans.app.model.User ) \
+ .filter( trans.app.model.User.table.c.deleted==False ) \
+ .order_by( trans.app.model.User.table.c.email ):
+ if user.id in in_users:
+ new_in_users.append( ( user.id, user.email ) )
+ else:
+ params.out_users.append( ( user.id, user.email ) )
+ for group in trans.sa_session.query( trans.app.model.Group ) \
+ .filter( trans.app.model.Group.table.c.deleted==False ) \
+ .order_by( trans.app.model.Group.table.c.name ):
+ if group.id in in_groups:
+ new_in_groups.append( ( group.id, group.name ) )
+ else:
+ params.out_groups.append( ( group.id, group.name ) )
+ return trans.fill_template( '/admin/quota/quota_create.mako',
+ webapp=params.webapp,
+ name=params.name,
+ description=params.description,
+ amount=params.amount,
+ operation=params.operation,
+ default=params.default,
+ in_users=new_in_users,
+ out_users=params.out_users,
+ in_groups=new_in_groups,
+ out_groups=params.out_groups,
+ message=params.message,
+ status=params.status )
+
+ @web.expose
+ @web.require_admin
+ def rename_quota( self, trans, **kwd ):
+ quota, params = self._quota_op( trans, 'rename_quota_button', self._rename_quota, kwd )
+ if not quota:
+ return
+ return trans.fill_template( '/admin/quota/quota_rename.mako',
+ id=params.id,
+ name=params.name or quota.name,
+ description=params.description or quota.description,
+ webapp=params.webapp,
+ message=params.message,
+ status=params.status )
+
+ @web.expose
+ @web.require_admin
+ def manage_users_and_groups_for_quota( self, trans, **kwd ):
+ quota, params = self._quota_op( trans, 'quota_members_edit_button', self._manage_users_and_groups_for_quota, kwd )
+ if not quota:
+ return
+ in_users = []
+ out_users = []
+ in_groups = []
+ out_groups = []
+ for user in trans.sa_session.query( trans.app.model.User ) \
+ .filter( trans.app.model.User.table.c.deleted==False ) \
+ .order_by( trans.app.model.User.table.c.email ):
+ if user in [ x.user for x in quota.users ]:
+ in_users.append( ( user.id, user.email ) )
+ else:
+ out_users.append( ( user.id, user.email ) )
+ for group in trans.sa_session.query( trans.app.model.Group ) \
+ .filter( trans.app.model.Group.table.c.deleted==False ) \
+ .order_by( trans.app.model.Group.table.c.name ):
+ if group in [ x.group for x in quota.groups ]:
+ in_groups.append( ( group.id, group.name ) )
+ else:
+ out_groups.append( ( group.id, group.name ) )
+ return trans.fill_template( '/admin/quota/quota.mako',
+ id=params.id,
+ name=quota.name,
+ in_users=in_users,
+ out_users=out_users,
+ in_groups=in_groups,
+ out_groups=out_groups,
+ webapp=params.webapp,
+ message=params.message,
+ status=params.status )
+
+ @web.expose
+ @web.require_admin
+ def edit_quota( self, trans, **kwd ):
+ quota, params = self._quota_op( trans, 'edit_quota_button', self._edit_quota, kwd )
+ if not quota:
+ return
+ return trans.fill_template( '/admin/quota/quota_edit.mako',
+ id=params.id,
+ operation=params.operation or quota.operation,
+ display_amount=params.amount or quota.display_amount,
+ webapp=params.webapp,
+ message=params.message,
+ status=params.status )
+
+ @web.expose
+ @web.require_admin
+ def set_quota_default( self, trans, **kwd ):
+ quota, params = self._quota_op( trans, 'set_default_quota_button', self._set_quota_default, kwd )
+ if not quota:
+ return
+ if params.default:
+ default = params.default
+ elif quota.default:
+ default = quota.default[0].type
+ else:
+ default = "no"
+ return trans.fill_template( '/admin/quota/quota_set_default.mako',
+ id=params.id,
+ default=default,
+ webapp=params.webapp,
+ message=params.message,
+ status=params.status )
+
+ @web.expose
+ @web.require_admin
+ def unset_quota_default( self, trans, **kwd ):
+ quota, params = self._quota_op( trans, True, self._unset_quota_default, kwd )
+ if not quota:
+ return
+ return trans.response.send_redirect( web.url_for( controller='admin',
+ action='quotas',
+ webapp=params.webapp,
+ message=util.sanitize_text( params.message ),
+ status='error' ) )
+
+
+ @web.expose
+ @web.require_admin
+ def mark_quota_deleted( self, trans, **kwd ):
+ quota, params = self._quota_op( trans, True, self._mark_quota_deleted, kwd, listify=True )
+ if not quota:
+ return
+ return trans.response.send_redirect( web.url_for( controller='admin',
+ action='quotas',
+ webapp=params.webapp,
+ message=util.sanitize_text( params.message ),
+ status='error' ) )
+
+ @web.expose
+ @web.require_admin
+ def undelete_quota( self, trans, **kwd ):
+ quota, params = self._quota_op( trans, True, self._undelete_quota, kwd, listify=True )
+ if not quota:
+ return
+ return trans.response.send_redirect( web.url_for( controller='admin',
+ action='quotas',
+ webapp=params.webapp,
+ message=util.sanitize_text( params.message ),
+ status='error' ) )
+
+ @web.expose
+ @web.require_admin
+ def purge_quota( self, trans, **kwd ):
+ quota, params = self._quota_op( trans, True, self._purge_quota, kwd, listify=True )
+ if not quota:
+ return
+ return trans.response.send_redirect( web.url_for( controller='admin',
+ action='quotas',
+ webapp=params.webapp,
+ message=util.sanitize_text( params.message ),
+ status='error' ) )
+
+ def _quota_op( self, trans, do_op, op_method, kwd, listify=False ):
+ params = self.get_quota_params( kwd )
+ if listify:
+ quota = []
+ messages = []
+ for id in util.listify( params.id ):
+ try:
+ quota.append( self.get_quota( trans, id ) )
+ except MessageException, e:
+ messages.append( str( e ) )
+ if messages:
+ return None, trans.response.send_redirect( web.url_for( controller='admin',
+ action='quotas',
+ webapp=params.webapp,
+ message=util.sanitize_text( ', '.join( messages ) ),
+ status='error' ) )
+ else:
+ try:
+ quota = self.get_quota( trans, params.id, deleted=False )
+ except MessageException, e:
+ return None, trans.response.send_redirect( web.url_for( controller='admin',
+ action='quotas',
+ webapp=params.webapp,
+ message=util.sanitize_text( str( e ) ),
+ status='error' ) )
+ if do_op == True or ( do_op != False and params.get( do_op, False ) ):
+ try:
+ message = op_method( quota, params )
+ return None, trans.response.send_redirect( web.url_for( controller='admin',
+ action='quotas',
+ webapp=params.webapp,
+ message=util.sanitize_text( message ),
+ status='done' ) )
+ except MessageException, e:
+ params.message = e.err_msg
+ params.status = e.type
+ return quota, params
--- a/lib/galaxy/web/controllers/async.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/controllers/async.py Thu Sep 22 17:13:53 2011 -0400
@@ -11,7 +11,7 @@
log = logging.getLogger( __name__ )
-class ASync( BaseController ):
+class ASync( BaseUIController ):
@web.expose
def default(self, trans, tool_id=None, data_id=None, data_secret=None, **kwd):
--- a/lib/galaxy/web/controllers/dataset.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/controllers/dataset.py Thu Sep 22 17:13:53 2011 -0400
@@ -145,7 +145,7 @@
.filter( model.History.deleted==False ) \
.filter( self.model_class.visible==True )
-class DatasetInterface( BaseController, UsesAnnotations, UsesHistory, UsesHistoryDatasetAssociation, UsesItemRatings ):
+class DatasetInterface( BaseUIController, UsesAnnotations, UsesHistory, UsesHistoryDatasetAssociation, UsesItemRatings ):
stored_list_grid = HistoryDatasetAssociationListGrid()
--- a/lib/galaxy/web/controllers/error.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/controllers/error.py Thu Sep 22 17:13:53 2011 -0400
@@ -1,6 +1,6 @@
from galaxy.web.base.controller import *
-class Error( BaseController ):
+class Error( BaseUIController ):
@web.expose
def index( self, trans ):
raise Exception, "Fake error"
\ No newline at end of file
--- a/lib/galaxy/web/controllers/external_service.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/controllers/external_service.py Thu Sep 22 17:13:53 2011 -0400
@@ -63,7 +63,7 @@
grids.GridAction( "Create new external service", dict( controller='external_service', action='create_external_service' ) )
]
-class ExternalService( BaseController, UsesFormDefinitions ):
+class ExternalService( BaseUIController, UsesFormDefinitions ):
external_service_grid = ExternalServiceGrid()
@web.expose
--- a/lib/galaxy/web/controllers/external_services.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/controllers/external_services.py Thu Sep 22 17:13:53 2011 -0400
@@ -9,7 +9,7 @@
for model_class in [Sample]:
class_name_to_class[ model_class.__name__ ] = model_class
-class ExternalServiceController( BaseController ):
+class ExternalServiceController( BaseUIController ):
@web.expose
@web.require_admin
def access_action( self, trans, external_service_action, item, item_type, **kwd ):
--- a/lib/galaxy/web/controllers/forms.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/controllers/forms.py Thu Sep 22 17:13:53 2011 -0400
@@ -66,7 +66,7 @@
grids.GridAction( "Create new form", dict( controller='forms', action='create_form_definition' ) )
]
-class Forms( BaseController ):
+class Forms( BaseUIController ):
# Empty TextField
empty_field = { 'name': '',
'label': '',
--- a/lib/galaxy/web/controllers/history.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/controllers/history.py Thu Sep 22 17:13:53 2011 -0400
@@ -175,7 +175,7 @@
# A public history is published, has a slug, and is not deleted.
return query.filter( self.model_class.published == True ).filter( self.model_class.slug != None ).filter( self.model_class.deleted == False )
-class HistoryController( BaseController, Sharable, UsesAnnotations, UsesItemRatings, UsesHistory ):
+class HistoryController( BaseUIController, Sharable, UsesAnnotations, UsesItemRatings, UsesHistory ):
@web.expose
def index( self, trans ):
return ""
@@ -701,7 +701,7 @@
if not import_history:
return trans.show_error_message( "The specified history does not exist.<br>You can %s." % referer_message, use_panels=True )
# History is importable if user is admin or it's accessible. TODO: probably want to have app setting to enable admin access to histories.
- if not trans.user_is_admin() and not self.security_check( user, import_history, check_ownership=False, check_accessible=True ):
+ if not trans.user_is_admin() and not self.security_check( trans, import_history, check_ownership=False, check_accessible=True ):
return trans.show_error_message( "You cannot access this history.<br>You can %s." % referer_message, use_panels=True )
if user:
#dan: I can import my own history.
@@ -780,7 +780,7 @@
if history is None:
raise web.httpexceptions.HTTPNotFound()
# Security check raises error if user cannot access history.
- self.security_check( trans.get_user(), history, False, True)
+ self.security_check( trans, history, False, True)
# Get datasets.
datasets = self.get_history_datasets( trans, history )
--- a/lib/galaxy/web/controllers/library.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/controllers/library.py Thu Sep 22 17:13:53 2011 -0400
@@ -65,7 +65,7 @@
# public libraries and restricted libraries accessible by the current user.
return query.filter( or_( not_( trans.model.Library.table.c.id.in_( restricted_library_ids ) ),
trans.model.Library.table.c.id.in_( accessible_restricted_library_ids ) ) )
-class Library( BaseController ):
+class Library( BaseUIController ):
library_list_grid = LibraryListGrid()
--- a/lib/galaxy/web/controllers/library_admin.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/controllers/library_admin.py Thu Sep 22 17:13:53 2011 -0400
@@ -69,7 +69,7 @@
preserve_state = False
use_paging = True
-class LibraryAdmin( BaseController ):
+class LibraryAdmin( BaseUIController ):
library_list_grid = LibraryListGrid()
--- a/lib/galaxy/web/controllers/library_common.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/controllers/library_common.py Thu Sep 22 17:13:53 2011 -0400
@@ -68,7 +68,7 @@
pass
os.rmdir( tmpd )
-class LibraryCommon( BaseController, UsesFormDefinitions ):
+class LibraryCommon( BaseUIController, UsesFormDefinitions ):
@web.json
def library_item_updates( self, trans, ids=None, states=None ):
# Avoid caching
--- a/lib/galaxy/web/controllers/mobile.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/controllers/mobile.py Thu Sep 22 17:13:53 2011 -0400
@@ -1,6 +1,6 @@
from galaxy.web.base.controller import *
-class Mobile( BaseController ):
+class Mobile( BaseUIController ):
@web.expose
def index( self, trans, **kwargs ):
return trans.fill_template( "mobile/index.mako" )
--- a/lib/galaxy/web/controllers/page.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/controllers/page.py Thu Sep 22 17:13:53 2011 -0400
@@ -272,7 +272,7 @@
# Default behavior:
_BaseHTMLProcessor.unknown_endtag( self, tag )
-class PageController( BaseController, Sharable, UsesAnnotations, UsesHistory,
+class PageController( BaseUIController, Sharable, UsesAnnotations, UsesHistory,
UsesStoredWorkflow, UsesHistoryDatasetAssociation, UsesVisualization, UsesItemRatings ):
_page_list = PageListGrid()
@@ -533,7 +533,7 @@
annotations = from_json_string( annotations )
for annotation_dict in annotations:
item_id = trans.security.decode_id( annotation_dict[ 'item_id' ] )
- item_class = self.get_class( trans, annotation_dict[ 'item_class' ] )
+ item_class = self.get_class( annotation_dict[ 'item_class' ] )
item = trans.sa_session.query( item_class ).filter_by( id=item_id ).first()
if not item:
raise RuntimeError( "cannot find annotated item" )
@@ -582,7 +582,7 @@
if page is None:
raise web.httpexceptions.HTTPNotFound()
# Security check raises error if user cannot access page.
- self.security_check( trans.get_user(), page, False, True)
+ self.security_check( trans, page, False, True)
# Process page content.
processor = _PageContentProcessor( trans, 'utf-8', 'text/html', self._get_embed_html )
@@ -723,14 +723,14 @@
if not page:
err+msg( "Page not found" )
else:
- return self.security_check( trans.get_user(), page, check_ownership, check_accessible )
+ return self.security_check( trans, page, check_ownership, check_accessible )
def get_item( self, trans, id ):
return self.get_page( trans, id )
def _get_embed_html( self, trans, item_class, item_id ):
""" Returns HTML for embedding an item in a page. """
- item_class = self.get_class( trans, item_class )
+ item_class = self.get_class( item_class )
if item_class == model.History:
history = self.get_history( trans, item_id, False, True )
history.annotation = self.get_item_annotation_str( trans.sa_session, history.user, history )
--- a/lib/galaxy/web/controllers/request_type.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/controllers/request_type.py Thu Sep 22 17:13:53 2011 -0400
@@ -72,7 +72,7 @@
grids.GridAction( "Create new request type", dict( controller='request_type', action='create_request_type' ) )
]
-class RequestType( BaseController, UsesFormDefinitions ):
+class RequestType( BaseUIController, UsesFormDefinitions ):
request_type_grid = RequestTypeGrid()
@web.expose
--- a/lib/galaxy/web/controllers/requests.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/controllers/requests.py Thu Sep 22 17:13:53 2011 -0400
@@ -15,7 +15,7 @@
def apply_query_filter( self, trans, query, **kwd ):
return query.filter_by( user=trans.user )
-class Requests( BaseController ):
+class Requests( BaseUIController ):
request_grid = UserRequestsGrid()
@web.expose
--- a/lib/galaxy/web/controllers/requests_admin.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/controllers/requests_admin.py Thu Sep 22 17:13:53 2011 -0400
@@ -94,7 +94,7 @@
return query
return query.filter_by( sample_id=trans.security.decode_id( sample_id ) )
-class RequestsAdmin( BaseController, UsesFormDefinitions ):
+class RequestsAdmin( BaseUIController, UsesFormDefinitions ):
request_grid = AdminRequestsGrid()
datatx_grid = DataTransferGrid()
--- a/lib/galaxy/web/controllers/requests_common.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/controllers/requests_common.py Thu Sep 22 17:13:53 2011 -0400
@@ -93,7 +93,7 @@
confirm="Samples cannot be added to this request after it is submitted. Click OK to submit." )
]
-class RequestsCommon( BaseController, UsesFormDefinitions ):
+class RequestsCommon( BaseUIController, UsesFormDefinitions ):
@web.json
def sample_state_updates( self, trans, ids=None, states=None ):
# Avoid caching
--- a/lib/galaxy/web/controllers/root.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/controllers/root.py Thu Sep 22 17:13:53 2011 -0400
@@ -11,7 +11,7 @@
log = logging.getLogger( __name__ )
-class RootController( BaseController, UsesHistory, UsesAnnotations ):
+class RootController( BaseUIController, UsesHistory, UsesAnnotations ):
@web.expose
def default(self, trans, target1=None, target2=None, **kwd):
--- a/lib/galaxy/web/controllers/tag.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/controllers/tag.py Thu Sep 22 17:13:53 2011 -0400
@@ -8,9 +8,9 @@
log = logging.getLogger( __name__ )
-class TagsController ( BaseController ):
+class TagsController ( BaseUIController ):
def __init__( self, app ):
- BaseController.__init__( self, app )
+ BaseUIController.__init__( self, app )
self.tag_handler = app.tag_handler
@web.expose
@web.require_login( "edit item tags" )
@@ -72,7 +72,7 @@
if item_id is not None:
item = self._get_item( trans, item_class, trans.security.decode_id( item_id ) )
user = trans.user
- item_class = self.get_class( trans, item_class )
+ item_class = self.get_class( item_class )
q = q.encode( 'utf-8' )
if q.find( ":" ) == -1:
return self._get_tag_autocomplete_names( trans, q, limit, timestamp, user, item, item_class )
--- a/lib/galaxy/web/controllers/tool_runner.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/controllers/tool_runner.py Thu Sep 22 17:13:53 2011 -0400
@@ -17,7 +17,7 @@
self.debug = None
self.from_noframe = None
-class ToolRunner( BaseController ):
+class ToolRunner( BaseUIController ):
#Hack to get biomart to work, ideally, we could pass tool_id to biomart and receive it back
@web.expose
--- a/lib/galaxy/web/controllers/tracks.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/controllers/tracks.py Thu Sep 22 17:13:53 2011 -0400
@@ -162,7 +162,7 @@
def apply_query_filter( self, trans, query, **kwargs ):
return query.filter( self.model_class.user_id == trans.user.id )
-class TracksController( BaseController, UsesVisualization, UsesHistoryDatasetAssociation ):
+class TracksController( BaseUIController, UsesVisualization, UsesHistoryDatasetAssociation ):
"""
Controller for track browser interface. Handles building a new browser from
datasets in the current history, and display of the resulting browser.
--- a/lib/galaxy/web/controllers/ucsc_proxy.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/controllers/ucsc_proxy.py Thu Sep 22 17:13:53 2011 -0400
@@ -11,7 +11,7 @@
log = logging.getLogger( __name__ )
-class UCSCProxy( BaseController ):
+class UCSCProxy( BaseUIController ):
def create_display(self, store):
"""Creates a more meaningulf display name"""
--- a/lib/galaxy/web/controllers/user.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/controllers/user.py Thu Sep 22 17:13:53 2011 -0400
@@ -49,7 +49,7 @@
def build_initial_query( self, trans, **kwd ):
return trans.sa_session.query( self.model_class ).filter( self.model_class.user_id == trans.user.id )
-class User( BaseController, UsesFormDefinitions ):
+class User( BaseUIController, UsesFormDefinitions ):
user_openid_grid = UserOpenIDGrid()
installed_len_files = None
--- a/lib/galaxy/web/controllers/visualization.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/controllers/visualization.py Thu Sep 22 17:13:53 2011 -0400
@@ -68,7 +68,7 @@
return query.filter( self.model_class.deleted==False ).filter( self.model_class.published==True )
-class VisualizationController( BaseController, Sharable, UsesAnnotations,
+class VisualizationController( BaseUIController, Sharable, UsesAnnotations,
UsesHistoryDatasetAssociation, UsesVisualization,
UsesItemRatings ):
_user_list_grid = VisualizationListGrid()
@@ -299,7 +299,7 @@
raise web.httpexceptions.HTTPNotFound()
# Security check raises error if user cannot access visualization.
- self.security_check( trans.get_user(), visualization, False, True)
+ self.security_check( trans, visualization, False, True)
# Get rating data.
user_item_rating = 0
@@ -453,4 +453,4 @@
def get_item( self, trans, id ):
return self.get_visualization( trans, id )
-
\ No newline at end of file
+
--- a/lib/galaxy/web/controllers/workflow.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/controllers/workflow.py Thu Sep 22 17:13:53 2011 -0400
@@ -103,7 +103,7 @@
if self.cur_tag == self.target_tag:
self.tag_content += text
-class WorkflowController( BaseController, Sharable, UsesStoredWorkflow, UsesAnnotations, UsesItemRatings ):
+class WorkflowController( BaseUIController, Sharable, UsesStoredWorkflow, UsesAnnotations, UsesItemRatings ):
stored_list_grid = StoredWorkflowListGrid()
published_list_grid = StoredWorkflowAllPublishedGrid()
@@ -199,7 +199,7 @@
if stored_workflow is None:
raise web.httpexceptions.HTTPNotFound()
# Security check raises error if user cannot access workflow.
- self.security_check( trans.get_user(), stored_workflow, False, True)
+ self.security_check( trans, stored_workflow, False, True)
# Get data for workflow's steps.
self.get_stored_workflow_steps( trans, stored_workflow )
@@ -1016,7 +1016,7 @@
id = trans.security.decode_id( id )
trans.workflow_building_mode = True
stored = trans.sa_session.query( model.StoredWorkflow ).get( id )
- self.security_check( trans.get_user(), stored, False, True )
+ self.security_check( trans, stored, False, True )
# Convert workflow to dict.
workflow_dict = self._workflow_to_dict( trans, stored )
--- a/lib/galaxy/web/framework/__init__.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/framework/__init__.py Thu Sep 22 17:13:53 2011 -0400
@@ -10,6 +10,7 @@
import base
import pickle
from galaxy import util
+from galaxy.exceptions import MessageException
from galaxy.util.json import to_json_string, from_json_string
pkg_resources.require( "simplejson" )
@@ -19,6 +20,7 @@
pkg_resources.require( "PasteDeploy" )
from paste.deploy.converters import asbool
+import paste.httpexceptions
pkg_resources.require( "Mako" )
import mako.template
@@ -103,6 +105,9 @@
except NoResultFound:
error_message = 'Provided API key is not valid.'
return error
+ if provided_key.user.deleted:
+ error_message = 'User account is deactivated, please contact an administrator.'
+ return error
newest_key = provided_key.user.api_keys[0]
if newest_key.key != provided_key.key:
error_message = 'Provided API key has expired.'
@@ -135,10 +140,16 @@
trans.response.status = 400
return "That user does not exist."
- if trans.debug:
- return simplejson.dumps( func( self, trans, *args, **kwargs ), indent=4, sort_keys=True )
- else:
- return simplejson.dumps( func( self, trans, *args, **kwargs ) )
+ try:
+ if trans.debug:
+ return simplejson.dumps( func( self, trans, *args, **kwargs ), indent=4, sort_keys=True )
+ else:
+ return simplejson.dumps( func( self, trans, *args, **kwargs ) )
+ except paste.httpexceptions.HTTPException:
+ raise # handled
+ except:
+ log.exception( 'Uncaught exception in exposed API method:' )
+ raise paste.httpexceptions.HTTPServerError()
if not hasattr(func, '_orig'):
decorator._orig = func
decorator.exposed = True
@@ -164,14 +175,6 @@
NOT_SET = object()
-class MessageException( Exception ):
- """
- Exception to make throwing errors from deep in controllers easier
- """
- def __init__( self, err_msg, type="info" ):
- self.err_msg = err_msg
- self.type = type
-
def error( message ):
raise MessageException( message, type='error' )
--- a/lib/galaxy/web/framework/base.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/framework/base.py Thu Sep 22 17:13:53 2011 -0400
@@ -37,8 +37,8 @@
"""
collection_path = kwargs.get( 'path_prefix', '' ) + '/' + collection_name + '/deleted'
member_path = collection_path + '/:id'
- self.connect( 'deleted_' + collection_name, collection_path, controller=collection_name, action='index', deleted=True )
- self.connect( 'deleted_' + member_name, member_path, controller=collection_name, action='show', deleted=True )
+ self.connect( 'deleted_' + collection_name, collection_path, controller=collection_name, action='index', deleted=True, conditions=dict( method=['GET'] ) )
+ self.connect( 'deleted_' + member_name, member_path, controller=collection_name, action='show', deleted=True, conditions=dict( method=['GET'] ) )
self.connect( 'undelete_deleted_' + member_name, member_path + '/undelete', controller=collection_name, action='undelete',
conditions=dict( method=['POST'] ) )
self.resource( member_name, collection_name, **kwargs )
@@ -68,7 +68,7 @@
self.mapper.explicit = False
self.api_mapper = routes.Mapper()
self.transaction_factory = DefaultWebTransaction
- def add_controller( self, controller_name, controller ):
+ def add_ui_controller( self, controller_name, controller ):
"""
Add a controller class to this application. A controller class has
methods which handle web requests. To connect a URL to a controller's
--- a/lib/galaxy/web/security/__init__.py Thu Sep 22 12:18:38 2011 -0400
+++ b/lib/galaxy/web/security/__init__.py Thu Sep 22 17:13:53 2011 -0400
@@ -55,4 +55,3 @@
def get_new_guid( self ):
# Generate a unique, high entropy 128 bit random number
return get_random_bytes( 16 )
-
--- a/templates/admin/quota/quota.mako Thu Sep 22 12:18:38 2011 -0400
+++ b/templates/admin/quota/quota.mako Thu Sep 22 17:13:53 2011 -0400
@@ -32,7 +32,7 @@
$('#groups_remove_button').click(function() {
return !$('#in_groups option:selected').remove().appendTo('#out_groups');
});
- $('form#associate_role_user_group').submit(function() {
+ $('form#associate_quota_user_group').submit(function() {
$('#in_users option').each(function(i) {
$(this).attr("selected", "selected");
});
@@ -48,30 +48,30 @@
%endif
<div class="toolForm">
- <div class="toolFormTitle">Quota '${quota.name}'</div>
+ <div class="toolFormTitle">Quota '${name}'</div><div class="toolFormBody">
- <form name="associate_quota_user_group" id="associate_quota_user_group" action="${h.url_for( action='manage_users_and_groups_for_quota', id=trans.security.encode_id( quota.id ) )}" method="post" >
+ <form name="associate_quota_user_group" id="associate_quota_user_group" action="${h.url_for( action='manage_users_and_groups_for_quota', id=id )}" method="post" ><input name="webapp" type="hidden" value="${webapp}" size=40"/><div class="form-row"><div style="float: left; margin-right: 10px;">
- <label>Users associated with '${quota.name}'</label>
+ <label>Users associated with '${name}'</label>
${render_select( "in_users", in_users )}<br/><input type="submit" id="users_remove_button" value=">>"/></div><div>
- <label>Users not associated with '${quota.name}'</label>
+ <label>Users not associated with '${name}'</label>
${render_select( "out_users", out_users )}<br/><input type="submit" id="users_add_button" value="<<"/></div></div><div class="form-row"><div style="float: left; margin-right: 10px;">
- <label>Groups associated with '${quota.name}'</label>
+ <label>Groups associated with '${name}'</label>
${render_select( "in_groups", in_groups )}<br/><input type="submit" id="groups_remove_button" value=">>"/></div><div>
- <label>Groups not associated with '${quota.name}'</label>
+ <label>Groups not associated with '${name}'</label>
${render_select( "out_groups", out_groups )}<br/><input type="submit" id="groups_add_button" value="<<"/></div>
--- a/templates/admin/quota/quota_edit.mako Thu Sep 22 12:18:38 2011 -0400
+++ b/templates/admin/quota/quota_edit.mako Thu Sep 22 17:13:53 2011 -0400
@@ -14,7 +14,7 @@
from galaxy.web.form_builder import SelectField
operation_selectfield = SelectField( 'operation' )
for op in ( '=', '+', '-' ):
- selected = op == quota.operation
+ selected = op == operation
operation_selectfield.add_option( op, op, selected )
%>
@@ -27,10 +27,10 @@
<div class="toolFormBody"><form name="library" action="${h.url_for( controller='admin', action='edit_quota' )}" method="post" ><input name="webapp" type="hidden" value="${webapp}"/>
- <input name="id" type="hidden" value="${trans.security.encode_id( quota.id )}"/>
+ <input name="id" type="hidden" value="${id}"/><div class="form-row"><label>Amount</label>
- <input name="amount" type="textfield" value="${quota.display_amount}" size=40"/>
+ <input name="amount" type="textfield" value="${display_amount}" size=40"/><div class="toolParamHelp" style="clear: both;">
Examples: "10000MB", "99 gb", "0.2T", "unlimited"
</div>
--- a/templates/admin/quota/quota_rename.mako Thu Sep 22 12:18:38 2011 -0400
+++ b/templates/admin/quota/quota_rename.mako Thu Sep 22 17:13:53 2011 -0400
@@ -22,14 +22,14 @@
<input name="webapp" type="hidden" value="${webapp}" size=40"/><label>Name:</label><div style="float: left; width: 250px; margin-right: 10px;">
- <input type="text" name="name" value="${quota.name}" size="40"/>
+ <input type="text" name="name" value="${name}" size="40"/></div><div style="clear: both"></div></div><div class="form-row"><label>Description:</label><div style="float: left; width: 250px; margin-right: 10px;">
- <input name="description" type="textfield" value="${quota.description}" size=40"/>
+ <input name="description" type="textfield" value="${description}" size=40"/></div><div style="clear: both"></div></div>
@@ -41,7 +41,7 @@
</div><div class="form-row"><div style="float: left; width: 250px; margin-right: 10px;">
- <input type="hidden" name="id" value="${trans.security.encode_id( quota.id )}"/>
+ <input type="hidden" name="id" value="${id}"/></div><div style="clear: both"></div></div>
--- a/templates/admin/quota/quota_set_default.mako Thu Sep 22 12:18:38 2011 -0400
+++ b/templates/admin/quota/quota_set_default.mako Thu Sep 22 17:13:53 2011 -0400
@@ -25,11 +25,11 @@
%endif
<div class="toolForm">
- <div class="toolFormTitle">Create quota</div>
+ <div class="toolFormTitle">Set quota default</div><div class="toolFormBody"><form name="set_quota_default" id="set_quota_default" action="${h.url_for( action='set_quota_default' )}" method="post" ><input name="webapp" type="hidden" value="${webapp}" size=40"/>
- <input name="id" type="hidden" value="${trans.security.encode_id( quota.id )}"/>
+ <input name="id" type="hidden" value="${id}"/><div class="form-row"><label>Is this quota a default for a class of users (if yes, what type)?</label>
${default_selectfield.get_html()}
--- a/test/functional/test_history_functions.py Thu Sep 22 12:18:38 2011 -0400
+++ b/test/functional/test_history_functions.py Thu Sep 22 17:13:53 2011 -0400
@@ -203,7 +203,7 @@
self.login( email=regular_user2.email )
self.import_history_via_url( self.security.encode_id( history3.id ),
admin_user.email,
- strings_displayed_after_submit=[ 'History is not accessible to current user' ] )
+ strings_displayed_after_submit=[ 'History is not accessible to the current user' ] )
self.logout()
self.login( email=admin_user.email )
# Test sharing history3 with an invalid user
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: jgoecks: Improvements for dataset display on saved histories page: (1) performance improvements using a single query rather than iteration; and (2) do not count hidden datasets.
by Bitbucket 22 Sep '11
by Bitbucket 22 Sep '11
22 Sep '11
1 new changeset in galaxy-central:
http://bitbucket.org/galaxy/galaxy-central/changeset/7850b1df5d39/
changeset: 7850b1df5d39
user: jgoecks
date: 2011-09-22 18:18:38
summary: Improvements for dataset display on saved histories page: (1) performance improvements using a single query rather than iteration; and (2) do not count hidden datasets.
affected #: 1 file (-1 bytes)
--- a/lib/galaxy/web/controllers/history.py Thu Sep 22 11:36:11 2011 -0400
+++ b/lib/galaxy/web/controllers/history.py Thu Sep 22 12:18:38 2011 -0400
@@ -11,7 +11,8 @@
from galaxy.tools.parameters.basic import UnvalidatedValue
from galaxy.tools.actions import upload_common
from galaxy.tags.tag_handler import GalaxyTagHandler
-from sqlalchemy.sql.expression import ClauseElement
+from sqlalchemy.sql.expression import ClauseElement, func
+from sqlalchemy.sql import select
import webhelpers, logging, operator, os, tempfile, subprocess, shutil, tarfile
from datetime import datetime
from cgi import escape
@@ -26,11 +27,29 @@
# Custom column types
class DatasetsByStateColumn( grids.GridColumn ):
def get_value( self, trans, grid, history ):
+ # Build query to get (state, count) pairs.
+ cols_to_select = [ trans.app.model.Dataset.table.c.state, func.count( '*' ) ]
+ from_obj = trans.app.model.HistoryDatasetAssociation.table.join( trans.app.model.Dataset.table )
+ where_clause = and_( trans.app.model.HistoryDatasetAssociation.table.c.history_id == history.id,
+ trans.app.model.HistoryDatasetAssociation.table.c.deleted == False,
+ trans.app.model.HistoryDatasetAssociation.table.c.visible == True,
+ )
+ group_by = trans.app.model.Dataset.table.c.state
+ query = select( columns=cols_to_select,
+ from_obj=from_obj,
+ whereclause=where_clause,
+ group_by=group_by )
+
+ # Process results.
+ state_count_dict = {}
+ for row in trans.sa_session.execute( query ):
+ state, count = row
+ state_count_dict[ state ] = count
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 ) )
+ count = state_count_dict.get( state, 0 )
+ if count:
+ rval.append( '<div class="count-box state-color-%s">%s</div>' % ( state, count ) )
else:
rval.append( '' )
return rval
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: dannon: Fix use_tasked_jobs to inherit the parent job's user.
by Bitbucket 22 Sep '11
by Bitbucket 22 Sep '11
22 Sep '11
1 new changeset in galaxy-central:
http://bitbucket.org/galaxy/galaxy-central/changeset/3f926d934d98/
changeset: 3f926d934d98
user: dannon
date: 2011-09-22 17:36:11
summary: Fix use_tasked_jobs to inherit the parent job's user.
affected #: 1 file (-1 bytes)
--- a/lib/galaxy/jobs/__init__.py Wed Sep 21 16:50:18 2011 -0400
+++ b/lib/galaxy/jobs/__init__.py Thu Sep 22 11:36:11 2011 -0400
@@ -32,9 +32,9 @@
class JobManager( object ):
"""
Highest level interface to job management.
-
+
TODO: Currently the app accesses "job_queue" and "job_stop_queue" directly.
- This should be decoupled.
+ This should be decoupled.
"""
def __init__( self, app ):
self.app = app
@@ -71,7 +71,7 @@
class JobQueue( object ):
"""
- Job manager, waits for jobs to be runnable and then dispatches to
+ Job manager, waits for jobs to be runnable and then dispatches to
a JobRunner.
"""
STOP_SIGNAL = object()
@@ -95,7 +95,7 @@
self.running = True
self.dispatcher = dispatcher
self.monitor_thread = threading.Thread( target=self.__monitor )
- self.monitor_thread.start()
+ self.monitor_thread.start()
log.info( "job manager started" )
if app.config.get_bool( 'enable_job_recovery', True ):
self.__check_jobs_at_startup()
@@ -132,7 +132,7 @@
def __monitor( self ):
"""
- Continually iterate the waiting jobs, checking is each is ready to
+ Continually iterate the waiting jobs, checking is each is ready to
run and dispatching if so.
"""
# HACK: Delay until after forking, we need a way to do post fork notification!!!
@@ -180,12 +180,12 @@
jobs_to_check.append( self.sa_session.query( model.Job ).get( job_id ) )
except Empty:
pass
- # Iterate over new and waiting jobs and look for any that are
+ # Iterate over new and waiting jobs and look for any that are
# ready to run
new_waiting_jobs = []
for job in jobs_to_check:
try:
- # Check the job's dependencies, requeue if they're not done
+ # Check the job's dependencies, requeue if they're not done
job_state = self.__check_if_ready_to_run( job )
if job_state == JOB_WAIT:
if not self.track_jobs_in_database:
@@ -216,7 +216,7 @@
self.waiting_jobs = new_waiting_jobs
# Done with the session
self.sa_session.remove()
-
+
def __check_if_ready_to_run( self, job ):
"""
Check if a job is ready to run by verifying that each of its input
@@ -281,13 +281,13 @@
if len( user_jobs ) >= self.app.config.user_job_limit:
return JOB_WAIT
return JOB_READY
-
+
def put( self, job_id, tool ):
"""Add a job to the queue (by job identifier)"""
if not self.track_jobs_in_database:
self.queue.put( ( job_id, tool.id ) )
self.sleeper.wake()
-
+
def shutdown( self ):
"""Attempts to gracefully shut down the worker thread"""
if self.parent_pid != os.getpid():
@@ -304,7 +304,7 @@
class JobWrapper( object ):
"""
- Wraps a 'model.Job' with convenience methods for running processes and
+ Wraps a 'model.Job' with convenience methods for running processes and
state management.
"""
def __init__( self, job, queue ):
@@ -329,15 +329,15 @@
self.output_paths = None
self.tool_provided_job_metadata = None
# Wrapper holding the info required to restore and clean up from files used for setting metadata externally
- self.external_output_metadata = metadata.JobExternalOutputMetadataWrapper( job )
-
+ self.external_output_metadata = metadata.JobExternalOutputMetadataWrapper( job )
+
def get_job( self ):
return self.sa_session.query( model.Job ).get( self.job_id )
-
+
def get_id_tag(self):
# For compatability with drmaa, which uses job_id right now, and TaskWrapper
return str(self.job_id)
-
+
def get_param_dict( self ):
"""
Restore the dictionary of parameters from the database.
@@ -346,10 +346,10 @@
param_dict = dict( [ ( p.name, p.value ) for p in job.parameters ] )
param_dict = self.tool.params_from_strings( param_dict, self.app )
return param_dict
-
+
def get_version_string_path( self ):
return os.path.abspath(os.path.join(self.app.config.new_file_path, "GALAXY_VERSION_STRING_%s" % self.job_id))
-
+
def prepare( self ):
"""
Prepare the job to run by creating the working directory and the
@@ -371,9 +371,9 @@
out_data = dict( [ ( da.name, da.dataset ) for da in job.output_datasets ] )
inp_data.update( [ ( da.name, da.dataset ) for da in job.input_library_datasets ] )
out_data.update( [ ( da.name, da.dataset ) for da in job.output_library_datasets ] )
-
- # Set up output dataset association for export history jobs. Because job
- # uses a Dataset rather than an HDA or LDA, it's necessary to set up a
+
+ # Set up output dataset association for export history jobs. Because job
+ # uses a Dataset rather than an HDA or LDA, it's necessary to set up a
# fake dataset association that provides the needed attributes for
# preparing a job.
class FakeDatasetAssociation ( object ):
@@ -400,7 +400,7 @@
# ( this used to be performed in the "exec_before_job" hook, but hooks are deprecated ).
self.tool.exec_before_job( self.queue.app, inp_data, out_data, param_dict )
# Run the before queue ("exec_before_job") hook
- self.tool.call_hook( 'exec_before_job', self.queue.app, inp_data=inp_data,
+ self.tool.call_hook( 'exec_before_job', self.queue.app, inp_data=inp_data,
out_data=out_data, tool=self.tool, param_dict=incoming)
self.sa_session.flush()
# Build any required config files
@@ -433,7 +433,7 @@
def fail( self, message, exception=False ):
"""
- Indicate job failure by setting state and message on all output
+ Indicate job failure by setting state and message on all output
datasets.
"""
job = self.get_job()
@@ -479,7 +479,7 @@
if self.tool:
self.tool.job_failed( self, message, exception )
self.cleanup()
-
+
def change_state( self, state, info = False ):
job = self.get_job()
self.sa_session.refresh( job )
@@ -509,12 +509,12 @@
job.job_runner_external_id = external_id
self.sa_session.add( job )
self.sa_session.flush()
-
+
def finish( self, stdout, stderr ):
"""
- Called to indicate that the associated command has been run. Updates
+ Called to indicate that the associated command has been run. Updates
the output datasets based on stderr and stdout from the command, and
- the contents of the output files.
+ the contents of the output files.
"""
# default post job setup
self.sa_session.expunge_all()
@@ -536,7 +536,7 @@
if os.path.exists(version_filename):
self.version_string = open(version_filename).read()
os.unlink(version_filename)
-
+
if self.app.config.outputs_to_working_directory:
for dataset_path in self.get_output_fnames():
try:
@@ -584,7 +584,7 @@
else:
# Security violation.
log.exception( "from_work_dir specified a location not in the working directory: %s, %s" % ( source_file, self.working_directory ) )
-
+
dataset.blurb = 'done'
dataset.peek = 'no peek'
dataset.info = context['stdout'] + context['stderr']
@@ -599,7 +599,7 @@
dataset.init_meta( copy_from=dataset )
#if a dataset was copied, it won't appear in our dictionary:
#either use the metadata from originating output dataset, or call set_meta on the copies
- #it would be quicker to just copy the metadata from the originating output dataset,
+ #it would be quicker to just copy the metadata from the originating output dataset,
#but somewhat trickier (need to recurse up the copied_from tree), for now we'll call set_meta()
if not self.app.config.set_metadata_externally or \
( not self.external_output_metadata.external_metadata_set_successfully( dataset, self.sa_session ) \
@@ -611,7 +611,7 @@
#load metadata from file
#we need to no longer allow metadata to be edited while the job is still running,
#since if it is edited, the metadata changed on the running output will no longer match
- #the metadata that was stored to disk for use via the external process,
+ #the metadata that was stored to disk for use via the external process,
#and the changes made by the user will be lost, without warning or notice
dataset.metadata.from_JSON_dict( self.external_output_metadata.get_output_filenames_by_dataset( dataset, self.sa_session ).filename_out )
try:
@@ -652,13 +652,13 @@
# Flush all the dataset and job changes above. Dataset state changes
# will now be seen by the user.
self.sa_session.flush()
- # Save stdout and stderr
+ # Save stdout and stderr
if len( stdout ) > 32768:
log.error( "stdout for job %d is greater than 32K, only first part will be logged to database" % job.id )
job.stdout = stdout[:32768]
if len( stderr ) > 32768:
log.error( "stderr for job %d is greater than 32K, only first part will be logged to database" % job.id )
- job.stderr = stderr[:32768]
+ job.stderr = stderr[:32768]
# custom post process setup
inp_data = dict( [ ( da.name, da.dataset ) for da in job.input_datasets ] )
out_data = dict( [ ( da.name, da.dataset ) for da in job.output_datasets ] )
@@ -675,8 +675,8 @@
# ( this used to be performed in the "exec_after_process" hook, but hooks are deprecated ).
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,
+ self.tool.call_hook( 'exec_after_process', self.queue.app, inp_data=inp_data,
+ out_data=out_data, param_dict=param_dict,
tool=self.tool, stdout=stdout, stderr=stderr )
job.command_line = self.command_line
@@ -695,7 +695,7 @@
self.sa_session.flush()
log.debug( 'job %d ended' % self.job_id )
self.cleanup()
-
+
def cleanup( self ):
# remove temporary files
try:
@@ -709,10 +709,10 @@
galaxy.tools.imp_exp.JobImportHistoryArchiveWrapper( self.job_id ).cleanup_after_job( self.sa_session )
except:
log.exception( "Unable to cleanup job %d" % self.job_id )
-
+
def get_command_line( self ):
return self.command_line
-
+
def get_session_id( self ):
return self.session_id
@@ -865,7 +865,7 @@
Should be refactored into a generalized executable unit wrapper parent, then jobs and tasks.
"""
# Abstract this to be more useful for running tasks that *don't* necessarily compose a job.
-
+
def __init__(self, task, queue):
super(TaskWrapper, self).__init__(task.job, queue)
self.task_id = task.id
@@ -939,7 +939,7 @@
# ( this used to be performed in the "exec_before_job" hook, but hooks are deprecated ).
self.tool.exec_before_job( self.queue.app, inp_data, out_data, param_dict )
# Run the before queue ("exec_before_job") hook
- self.tool.call_hook( 'exec_before_job', self.queue.app, inp_data=inp_data,
+ self.tool.call_hook( 'exec_before_job', self.queue.app, inp_data=inp_data,
out_data=out_data, tool=self.tool, param_dict=incoming)
self.sa_session.flush()
# Build any required config files
@@ -986,12 +986,12 @@
task.state = state
self.sa_session.add( task )
self.sa_session.flush()
-
+
def get_state( self ):
task = self.get_task()
self.sa_session.refresh( task )
return task.state
-
+
def set_runner( self, runner_url, external_id ):
task = self.get_task()
self.sa_session.refresh( task )
@@ -1000,15 +1000,15 @@
# DBTODO Check task job_runner_stuff
self.sa_session.add( task )
self.sa_session.flush()
-
+
def finish( self, stdout, stderr ):
# DBTODO integrate previous finish logic.
# Simple finish for tasks. Just set the flag OK.
log.debug( 'task %s for job %d ended' % (self.task_id, self.job_id) )
"""
- Called to indicate that the associated command has been run. Updates
+ Called to indicate that the associated command has been run. Updates
the output datasets based on stderr and stdout from the command, and
- the contents of the output files.
+ the contents of the output files.
"""
# default post job setup_external_metadata
self.sa_session.expunge_all()
@@ -1025,7 +1025,7 @@
task.state = task.states.ERROR
else:
task.state = task.states.OK
- # Save stdout and stderr
+ # Save stdout and stderr
if len( stdout ) > 32768:
log.error( "stdout for task %d is greater than 32K, only first part will be logged to database" % task.id )
task.stdout = stdout[:32768]
@@ -1039,7 +1039,7 @@
def cleanup( self ):
# There is no task cleanup. The job cleans up for all tasks.
pass
-
+
def get_command_line( self ):
return self.command_line
@@ -1049,7 +1049,7 @@
def get_output_file_id( self, file ):
# There is no permanent output file for tasks.
return None
-
+
def get_tool_provided_job_metadata( self ):
# DBTODO Handle this as applicable for tasks.
return None
@@ -1071,10 +1071,6 @@
def setup_external_metadata( self, exec_dir = None, tmp_dir = None, dataset_files_path = None, config_root = None, datatypes_config = None, set_extension = True, **kwds ):
# There is no metadata setting for tasks. This is handled after the merge, at the job level.
return ""
-
- @property
- def user( self ):
- pass
class DefaultJobDispatcher( object ):
def __init__( self, app ):
@@ -1105,13 +1101,19 @@
runner = getattr( module, obj )
self.job_runners[name] = runner( self.app )
log.debug( 'Loaded job runner: %s' % display_name )
-
+
def put( self, job_wrapper ):
try:
- if self.app.config.use_tasked_jobs and job_wrapper.tool.parallelism is not None and not isinstance(job_wrapper, TaskWrapper):
- runner_name = "tasks"
- log.debug( "dispatching job %d to %s runner" %( job_wrapper.job_id, runner_name ) )
- self.job_runners[runner_name].put( job_wrapper )
+ if self.app.config.use_tasked_jobs and job_wrapper.tool.parallelism is not None:
+ if isinstance(job_wrapper, TaskWrapper):
+ #DBTODO Refactor
+ runner_name = ( job_wrapper.tool.job_runner.split(":", 1) )[0]
+ log.debug( "dispatching task %s, of job %d, to %s runner" %( job_wrapper.task_id, job_wrapper.job_id, runner_name ) )
+ self.job_runners[runner_name].put( job_wrapper )
+ else:
+ runner_name = "tasks"
+ log.debug( "dispatching job %d to %s runner" %( job_wrapper.job_id, runner_name ) )
+ self.job_runners[runner_name].put( job_wrapper )
else:
runner_name = ( job_wrapper.tool.job_runner.split(":", 1) )[0]
log.debug( "dispatching job %d to %s runner" %( job_wrapper.job_id, runner_name ) )
@@ -1167,7 +1169,7 @@
self.sleeper = Sleeper()
self.running = True
self.monitor_thread = threading.Thread( target=self.monitor )
- self.monitor_thread.start()
+ self.monitor_thread.start()
log.info( "job stopper started" )
def monitor( self ):
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: richard_burhans: aaChanges tool: Added an option to keep the columns from the SNP dataset
by Bitbucket 21 Sep '11
by Bitbucket 21 Sep '11
21 Sep '11
1 new changeset in galaxy-central:
http://bitbucket.org/galaxy/galaxy-central/changeset/5c0412d06db9/
changeset: 5c0412d06db9
user: richard_burhans
date: 2011-09-21 22:50:18
summary: aaChanges tool: Added an option to keep the columns from the SNP dataset
affected #: 1 file (-1 bytes)
--- a/tools/evolution/codingSnps.xml Wed Sep 21 12:20:07 2011 -0400
+++ b/tools/evolution/codingSnps.xml Wed Sep 21 16:50:18 2011 -0400
@@ -2,7 +2,7 @@
<description>amino-acid changes caused by a set of SNPs</description><command interpreter="perl">
- codingSnps.pl $input1 $input2 Galaxy build=${input1.metadata.dbkey} loc=${GALAXY_DATA_INDEX_DIR}/codingSnps.loc chr=${input1.metadata.chromCol} start=${input1.metadata.startCol} end=${input1.metadata.endCol} snp=$col1 > $out_file1
+ codingSnps.pl $input1 $input2 Galaxy build=${input1.metadata.dbkey} loc=${GALAXY_DATA_INDEX_DIR}/codingSnps.loc chr=${input1.metadata.chromCol} start=${input1.metadata.startCol} end=${input1.metadata.endCol} snp=$col1 keepColumns=$keep > $out_file1
</command><inputs>
@@ -13,6 +13,10 @@
<param format="interval" name="input2" type="data" label="Gene dataset"><validator type="dataset_metadata_in_file" filename="codingSnps.loc" metadata_name="dbkey" metadata_column="0" message="Sequences are not currently available for the specified build." split="\t" /></param>
+ <param name="keep" type="select" label="Keep columns from SNP dataset">
+ <option value="0" selected="true">No</option>
+ <option value="1">Yes</option>
+ </param></inputs><outputs>
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: jgoecks: Read and write summary tree in binary; this mitigates problems in reading summary tree file on some platforms.
by Bitbucket 21 Sep '11
by Bitbucket 21 Sep '11
21 Sep '11
1 new changeset in galaxy-central:
http://bitbucket.org/galaxy/galaxy-central/changeset/b2c68b980ffc/
changeset: b2c68b980ffc
user: jgoecks
date: 2011-09-21 18:20:07
summary: Read and write summary tree in binary; this mitigates problems in reading summary tree file on some platforms.
affected #: 1 file (-1 bytes)
--- a/lib/galaxy/visualization/tracks/summary.py Wed Sep 21 11:36:58 2011 -0400
+++ b/lib/galaxy/visualization/tracks/summary.py Wed Sep 21 12:20:07 2011 -0400
@@ -89,5 +89,5 @@
cPickle.dump(self, open(filename, 'wb'), 2)
def summary_tree_from_file(filename):
- return cPickle.load(open(filename, "r"))
+ return cPickle.load(open(filename, "rb"))
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0