Merge branch 'release_15.03' of https://github.com/galaxyproject/galaxy into release_15.03

This commit is contained in:
guerler
2015-03-05 22:16:54 -05:00
23 changed files with 123 additions and 78 deletions
+1
View File
@@ -85,6 +85,7 @@ tool-data/genome/*
# Test output
test-data-cache
run_functional_tests.html
run_api_tests.html
test/tool_shed/tmp/*
.coverage
htmlcov
@@ -439,7 +439,7 @@ var MultiPanelColumns = Backbone.View.extend( baseMVC.LoggableMixin ).extend({
// default
update : {
text: _l( 'most recent first' ),
fn: _comparator( function( h ){ return Date( h.get( 'update_time' ) ); }, { asc : false })
fn: _comparator( function( h ){ return new Date( h.get( 'update_time' ) ); }, { asc : false })
},
'name' : {
text: _l( 'name, a to z' ),
@@ -542,6 +542,7 @@ var MultiPanelColumns = Backbone.View.extend( baseMVC.LoggableMixin ).extend({
* (sorting the collection will re-render the panel)
*/
sortCollection : function( order, options ){
order = !_.isUndefined( order )? order : this.order;
if( !( order in this.sortOrders ) ){
order = 'update';
}
@@ -1043,7 +1044,7 @@ var MultiPanelColumns = Backbone.View.extend( baseMVC.LoggableMixin ).extend({
mainTemplate : _.template([
'<div class="header flex-column-container">',
'<div class="control-column control-column-left flex-column">',
'<button class="create-new btn btn-default" tabindex="4">', _l( 'Create new' ), '</button> ',
'<button class="done btn btn-default" tabindex="1">', _l( 'Done' ), '</button>',
'<div id="search-histories" class="search-control"></div>',
'<div id="search-datasets" class="search-control"></div>',
'<a class="open-more-options btn btn-default" tabindex="3">',
@@ -1055,7 +1056,7 @@ var MultiPanelColumns = Backbone.View.extend( baseMVC.LoggableMixin ).extend({
'<div class="header-info">', '</div>',
'</div>',
'<div class="control-column control-column-right flex-column">',
'<button class="done btn btn-default" tabindex="1">', _l( 'Done' ), '</button>',
'<button class="create-new btn btn-default" tabindex="4">', _l( 'Create new' ), '</button> ',
'</div>',
'</div>',
// middle - where the columns go
@@ -245,8 +245,11 @@ class DisplayApplication( object ):
if link:
self.links[ link.id ] = link
for dynamic_links in elem.findall( 'dynamic_links' ):
for link in DynamicDisplayApplicationBuilder( dynamic_links, self, self.app.datatypes_registry.build_sites ):
self.links[ link.id ] = link
try:
for link in DynamicDisplayApplicationBuilder( dynamic_links, self, self.app.datatypes_registry.build_sites ):
self.links[ link.id ] = link
except Exception, e:
log.error( "Error loading a set of Dynamic Display Application links: %s", e )
def get_link( self, link_name, data, dataset_hash, user_hash, trans, app_kwds ):
#returns a link object with data knowledge to generate links
self._check_and_reload()
+6
View File
@@ -182,6 +182,12 @@ class MutationList(MutationObj, list):
list.__delslice__(self, start, stop)
self.changed()
def __copy__(self):
return MutationList(MutationObj.coerce(self._key, self[:]))
def __deepcopy__(self, memo):
return MutationList(MutationObj.coerce(self._key, copy.deepcopy(self[:])))
def append(self, value):
list.append(self, MutationObj.coerce(self._key, value))
self.changed()
+2 -2
View File
@@ -263,7 +263,7 @@ class TabularToolDataTable( ToolDataTable, Dictifiable ):
self.data = []
self.configure_and_load( config_element, tool_data_path, from_shed_config)
def configure_and_load( self, config_element, tool_data_path, from_shed_config=False):
def configure_and_load( self, config_element, tool_data_path, from_shed_config=False, url_timeout=10 ):
"""
Configure and load table from an XML element.
"""
@@ -289,7 +289,7 @@ class TabularToolDataTable( ToolDataTable, Dictifiable ):
if filename:
tmp_file = NamedTemporaryFile( prefix='TTDT_URL_%s-' % self.name )
try:
tmp_file.write( urlopen( filename ).read() )
tmp_file.write( urlopen( filename, timeout=url_timeout ).read() )
except Exception, e:
log.error( 'Error loading Data Table URL "%s": %s', filename, e )
continue
+5 -5
View File
@@ -2,7 +2,7 @@
API operations allowing clients to determine datatype supported by Galaxy.
"""
from galaxy.web import _future_expose_api_anonymous as expose_api_anonymous
from galaxy.web import _future_expose_api_anonymous_and_sessionless as expose_api_anonymous_and_sessionless
from galaxy import exceptions
from galaxy.web.base.controller import BaseAPIController
from galaxy.util import asbool
@@ -14,7 +14,7 @@ log = logging.getLogger( __name__ )
class DatatypesController( BaseAPIController ):
@expose_api_anonymous
@expose_api_anonymous_and_sessionless
def index( self, trans, **kwd ):
"""
GET /api/datatypes
@@ -47,7 +47,7 @@ class DatatypesController( BaseAPIController ):
else:
raise
@expose_api_anonymous
@expose_api_anonymous_and_sessionless
def mapping( self, trans, **kwd ):
'''
GET /api/datatypes/mapping
@@ -81,7 +81,7 @@ class DatatypesController( BaseAPIController ):
else:
raise
@expose_api_anonymous
@expose_api_anonymous_and_sessionless
def sniffers( self, trans, **kwd ):
'''
GET /api/datatypes/sniffers
@@ -101,7 +101,7 @@ class DatatypesController( BaseAPIController ):
else:
raise
@expose_api_anonymous
@expose_api_anonymous_and_sessionless
def converters( self, trans, **kwd ):
converters = []
for (source_type, targets) in self._datatypes_registry.datatype_converters.iteritems():
+3 -2
View File
@@ -4,6 +4,7 @@ from galaxy import exceptions
from galaxy import web, util
from galaxy import managers
from galaxy.web import _future_expose_api_anonymous
from galaxy.web import _future_expose_api_anonymous_and_sessionless
from galaxy.web import _future_expose_api
from galaxy.web.base.controller import BaseAPIController
from galaxy.web.base.controller import UsesVisualizationMixin
@@ -59,7 +60,7 @@ class ToolsController( BaseAPIController, UsesVisualizationMixin ):
trans.response.status = 500
return { 'error': str( exc ) }
@_future_expose_api_anonymous
@_future_expose_api_anonymous_and_sessionless
def show( self, trans, id, **kwd ):
"""
GET /api/tools/{tool_id}
@@ -129,7 +130,7 @@ class ToolsController( BaseAPIController, UsesVisualizationMixin ):
"guid": tool.guid,
}
@_future_expose_api_anonymous
@_future_expose_api_anonymous_and_sessionless
def citations( self, trans, id, **kwds ):
tool = self._get_tool( id, user=trans.user )
rval = []
@@ -41,10 +41,14 @@ REQUIRE_LOGIN_TEMPLATE = """
PASSWORD_RESET_TEMPLATE = """
To reset your Galaxy password for the instance at %s, use the following link:
<a href="%s">%s</a>
<a href="%s">%s</a>
If you did not make this request, no action is necessary on your part, though
you may want to notify an administrator."""
you may want to notify an administrator.
If you're having trouble using the link when clicking it from email client, you
can also copy and paste it into your browser.
"""
class UserOpenIDGrid( grids.Grid ):
+1 -1
View File
@@ -25,7 +25,7 @@ class UniverseApplication( object ):
db_url,
self.config.database_engine_options,
create_tables=True )
self.targets_mysql = 'mysql' in self.config.database_connection
self.targets_mysql = self.config.database_connection and 'mysql' in self.config.database_connection
# Security helper
self.security = security.SecurityHelper( id_secret=self.config.id_secret )
# used for cachebusting -- refactor this into a *SINGLE* UniverseApplication base.
+4 -3
View File
@@ -439,7 +439,7 @@ var MultiPanelColumns = Backbone.View.extend( baseMVC.LoggableMixin ).extend({
// default
update : {
text: _l( 'most recent first' ),
fn: _comparator( function( h ){ return Date( h.get( 'update_time' ) ); }, { asc : false })
fn: _comparator( function( h ){ return new Date( h.get( 'update_time' ) ); }, { asc : false })
},
'name' : {
text: _l( 'name, a to z' ),
@@ -542,6 +542,7 @@ var MultiPanelColumns = Backbone.View.extend( baseMVC.LoggableMixin ).extend({
* (sorting the collection will re-render the panel)
*/
sortCollection : function( order, options ){
order = !_.isUndefined( order )? order : this.order;
if( !( order in this.sortOrders ) ){
order = 'update';
}
@@ -1043,7 +1044,7 @@ var MultiPanelColumns = Backbone.View.extend( baseMVC.LoggableMixin ).extend({
mainTemplate : _.template([
'<div class="header flex-column-container">',
'<div class="control-column control-column-left flex-column">',
'<button class="create-new btn btn-default" tabindex="4">', _l( 'Create new' ), '</button> ',
'<button class="done btn btn-default" tabindex="1">', _l( 'Done' ), '</button>',
'<div id="search-histories" class="search-control"></div>',
'<div id="search-datasets" class="search-control"></div>',
'<a class="open-more-options btn btn-default" tabindex="3">',
@@ -1055,7 +1056,7 @@ var MultiPanelColumns = Backbone.View.extend( baseMVC.LoggableMixin ).extend({
'<div class="header-info">', '</div>',
'</div>',
'<div class="control-column control-column-right flex-column">',
'<button class="done btn btn-default" tabindex="1">', _l( 'Done' ), '</button>',
'<button class="create-new btn btn-default" tabindex="4">', _l( 'Create new' ), '</button> ',
'</div>',
'</div>',
// middle - where the columns go
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -2119,7 +2119,7 @@ div.permissionContainer{padding-left:20px}
.annotated-history-panel table.list-items>tbody>tr>td>.list-item{border:0px}
.annotated-history-panel .empty-message{margin-top:8px}
.current-history-panel .list-item.history-content.current-content{border-left:5px solid #4E5777}
.multi-panel-history{display:flex;flex-direction:column;}.multi-panel-history .flex-row-container,.multi-panel-history .flex-column-container{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;min-width:0px;min-height:0px;-webkit-align-items:stretch;-ms-align-items:stretch;align-items:stretch;-webkit-align-content:stretch;-ms-align-content:stretch;align-content:stretch;-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start}
.multi-panel-history{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;}.multi-panel-history .flex-row-container,.multi-panel-history .flex-column-container{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;min-width:0px;min-height:0px;-webkit-align-items:stretch;-ms-align-items:stretch;align-items:stretch;-webkit-align-content:stretch;-ms-align-content:stretch;align-content:stretch;-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start}
.multi-panel-history .flex-row-container{-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}
.multi-panel-history .flex-column-container{-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}
.multi-panel-history .flex-row,.multi-panel-history .flex-column{-webkit-flex:1 1 auto;-ms-flex:1 1 auto;flex:1 1 auto;-webkit-align-self:auto;-ms-flex-item-align:auto;align-self:auto}
@@ -2142,7 +2142,7 @@ div.permissionContainer{padding-left:20px}
.multi-panel-history .history-column:nth-child(2){margin-left:320px}
.multi-panel-history .history-column:last-child{margin-right:0px}
.multi-panel-history .history-column .dropdown-menu{z-index:inherit}
.multi-panel-history .history-column .panel-controls{width:100%;height:24px;border-radius:3px;background-color:white;text-align:center;flex:0 0 auto;-webkit-align-self:auto;-ms-flex-item-align:auto;align-self:auto}.multi-panel-history .history-column .panel-controls .btn{height:20px;line-height:normal;font-size:90%;padding-top:0px;padding-bottom:0px}
.multi-panel-history .history-column .panel-controls{width:100%;height:24px;border-radius:3px;background-color:white;text-align:center;-webkit-flex:0 0 auto;-ms-flex:0 0 auto;flex:0 0 auto;-webkit-align-self:auto;-ms-flex-item-align:auto;align-self:auto}.multi-panel-history .history-column .panel-controls .btn{height:20px;line-height:normal;font-size:90%;padding-top:0px;padding-bottom:0px}
.multi-panel-history .history-column .panel-controls .pull-left .btn{margin-right:4px}
.multi-panel-history .history-column .panel-controls .pull-right .btn{margin-left:4px}
.multi-panel-history .history-column .panel-controls .panel-menu{z-index:1}.multi-panel-history .history-column .panel-controls .panel-menu .dropdown-menu a{text-align:left}
+8
View File
@@ -584,7 +584,13 @@
// for some reason, .flex-row-container below won't be applied to multi-panel-history since it's the enclosing node
// re-apply here
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column;
.flex-row-container,
@@ -770,6 +776,8 @@
background-color: white;
text-align: center;
-webkit-flex: 0 0 auto;
-ms-flex: 0 0 auto;
flex: 0 0 auto;
-webkit-align-self: auto;
@@ -78,18 +78,21 @@
field.add_option( param.name, param.name )
field_html = field.get_html()
elif isinstance( param, SelectToolParameter ) and hasattr( param, 'data_ref' ):
field = SelectField( param.name, display=param.display )
field = SelectField( param.name, display=param.display, multiple=param.multiple )
field.add_option( param.data_ref, param.data_ref )
field_html = field.get_html( prefix )
elif isinstance( param, SelectToolParameter ) and param.is_dynamic:
field = SelectField( param.name, display=param.display )
field = SelectField( param.name, display=param.display, multiple=param.multiple )
dynamic_options = param.options
if dynamic_options.index_file:
option_label = "Dynamically generated from entries in file %s" % str( dynamic_options.index_file )
field.add_option( option_label, "none" )
elif dynamic_options.missing_index_file:
option_label = "Dynamically generated from entries in missing file %s" % str( dynamic_options.missing_index_file )
field.add_option( option_label, "none" )
if dynamic_options is not None:
if dynamic_options.index_file:
option_label = "Dynamically generated from entries in file %s" % str( dynamic_options.index_file )
field.add_option( option_label, "none" )
elif dynamic_options.missing_index_file:
option_label = "Dynamically generated from entries in missing file %s" % str( dynamic_options.missing_index_file )
field.add_option( option_label, "none" )
else:
field.add_option( "Dynamically generated from old-style Dynamic Options.", "none" )
field_html = field.get_html( prefix )
else:
field = param.get_html_field( trans, None, other_values )
+12 -3
View File
@@ -29,14 +29,23 @@ class JobsApiTestCase( api.ApiTestCase, TestsDatasets ):
def test_index_state_filter( self ):
# Initial number of ok jobs
original_count = len( self.__uploads_with_state( "ok" ) )
# Run through dataset upload to ensure num uplaods at least greater
# by 1.
self.__history_with_ok_dataset()
# Verify number of ok jobs is actually greater.
new_count = len( self.__uploads_with_state( "ok" ) )
assert original_count < new_count
count_increased = False
for i in range(10):
new_count = len( self.__uploads_with_state( "ok" ) )
if original_count < new_count:
count_increased = True
break
time.sleep(.1)
if not count_increased:
template = "Jobs in ok state did not increase (was %d, now %d)"
message = template % (original_count, new_count)
raise AssertionError(message)
def test_index_date_filter( self ):
self.__history_with_new_dataset()
+28 -28
View File
@@ -1,11 +1,6 @@
""" Tests for the tool data API.
"""
import json
from base import api
from .helpers import DatasetPopulator
from requests import delete
import operator
@@ -40,7 +35,7 @@ class ToolDataApiTestCase( api.ApiTestCase ):
field = show_field_response.json()
self._assert_has_keys( field, "files", "name", "fields", "fingerprint", "base_dir" )
files = field[ "files" ]
assert len( files ) == 2
assert len( files ) == 2, "Length of files [%s] was not 2." % files
def test_download_field_file(self):
show_field_response = self._get( "tool_data/testalpha/fields/data1/files/entry.txt", admin=True )
@@ -48,27 +43,32 @@ class ToolDataApiTestCase( api.ApiTestCase ):
content = show_field_response.content
assert content == "This is data 1.", content
def test_delete_entry(self):
show_response = self._get( "tool_data/testbeta", admin=True )
original_count = len(show_response.json()["fields"])
# Following test case rendered invalid by the fix in
# https://github.com/galaxyproject/galaxy/commit/48f77dc742acf01ddbafafcc4634e69378f1f020#diff-bfb557a99c1f7d646d4968d8d680b885R154.
# TODO: Restore the test case when test framework allows actions from
# admin users.
dataset_populator = DatasetPopulator( self.galaxy_interactor )
history_id = dataset_populator.new_history()
payload = dataset_populator.run_tool_payload(
tool_id="data_manager",
inputs={"ignored_value": "moo"},
history_id=history_id,
)
create_response = self._post( "tools", data=payload )
self._assert_status_code_is( create_response, 200 )
dataset_populator.wait_for_history( history_id, assert_ok=True )
show_response = self._get( "tool_data/testbeta", admin=True )
updated_fields = show_response.json()["fields"]
assert len(updated_fields) == original_count + 1
field0 = updated_fields[0]
url = self._api_url( "tool_data/testbeta?key=%s" % self.galaxy_interactor.master_api_key )
delete( url, data=json.dumps({"values": "\t".join(field0)}) )
# def test_delete_entry(self):
# show_response = self._get( "tool_data/testbeta", admin=True )
# original_count = len(show_response.json()["fields"])
show_response = self._get( "tool_data/testbeta", admin=True )
updated_fields = show_response.json()["fields"]
assert len(updated_fields) == original_count
# dataset_populator = DatasetPopulator( self.galaxy_interactor )
# history_id = dataset_populator.new_history()
# payload = dataset_populator.run_tool_payload(
# tool_id="data_manager",
# inputs={"ignored_value": "moo"},
# history_id=history_id,
# )
# create_response = self._post( "tools", data=payload )
# self._assert_status_code_is( create_response, 200 )
# dataset_populator.wait_for_history( history_id, assert_ok=True )
# show_response = self._get( "tool_data/testbeta", admin=True )
# updated_fields = show_response.json()["fields"]
# assert len(updated_fields) == original_count + 1
# field0 = updated_fields[0]
# url = self._api_url( "tool_data/testbeta?key=%s" % self.galaxy_interactor.master_api_key )
# delete( url, data=json.dumps({"values": "\t".join(field0)}) )
# show_response = self._get( "tool_data/testbeta", admin=True )
# updated_fields = show_response.json()["fields"]
# assert len(updated_fields) == original_count
+3 -2
View File
@@ -219,8 +219,9 @@ class ToolsTestCase( api.ApiTestCase ):
'col': "' ; echo 'moo",
}
response = self._run( "column_param", history_id, inputs )
# TODO: make this test pass...
self._assert_status_code_is( response, 400 )
if response.status_code == 200:
message = "Known, high priority issue. Column parameters are sanitized but invalid values should prevent execution and doesn't."
raise AssertionError(message)
@skip_without_tool( "collection_paired_test" )
def test_collection_parameter( self ):
+17 -11
View File
@@ -168,6 +168,7 @@ class BaseWorkflowsApiTestCase( api.ApiTestCase ):
return RunJobsSummary(
history_id=history_id,
workflow_id=workflow_id,
invocation_id=invocation_id,
inputs=inputs,
jobs=jobs,
)
@@ -570,11 +571,13 @@ class WorkflowsApiTestCase( BaseWorkflowsApiTestCase ):
'0': self._ds_entry(hda1),
'1': self._ds_entry(hda2),
}
self.__invoke_workflow( history_id, workflow_id, inputs )
# TODO: wait on workflow invocations
time.sleep(10)
self.dataset_populator.wait_for_history( history_id, assert_ok=True )
self.assertEquals("10.0\n30.0\n20.0\n40.0\n", self.dataset_populator.get_history_dataset_content( history_id, hid=0 ) )
invocation_id = self.__invoke_workflow( history_id, workflow_id, inputs )
self.wait_for_invocation_and_jobs( history_id, workflow_id, invocation_id )
details = self.dataset_populator.get_history_dataset_details( history_id, hid=0 )
last_item_hid = details["hid"]
assert last_item_hid == 7, "Expected 7 history items, got %s" % last_item_hid
content = self.dataset_populator.get_history_dataset_content( history_id, hid=0 )
self.assertEquals("10.0\n30.0\n20.0\n40.0\n", content )
def test_workflow_request( self ):
workflow = self.workflow_populator.load_workflow( name="test_for_queue" )
@@ -731,7 +734,7 @@ test_data:
time.sleep( 2 )
history_id = run_summary.history_id
workflow_id = run_summary.workflow_id
invocation_id = run_summary.workflow_id
invocation_id = run_summary.invocation_id
self.dataset_populator.wait_for_history( history_id, assert_ok=True )
invocation = self._invocation_details( workflow_id, invocation_id )
assert invocation[ 'state' ] != 'scheduled'
@@ -740,12 +743,15 @@ test_data:
assert len( self._history_jobs( history_id ) ) == 2
self.__review_paused_steps( workflow_id, invocation_id, order_index=2, action=True )
self.wait_for_invocation( workflow_id, invocation_id )
time.sleep(1)
self.dataset_populator.wait_for_history( history_id, assert_ok=True )
time.sleep(1)
self.wait_for_invocation_and_jobs( history_id, workflow_id, invocation_id )
assert len( self._history_jobs( history_id ) ) == 4
def wait_for_invocation_and_jobs( self, history_id, workflow_id, invocation_id, assert_ok=True ):
self.wait_for_invocation( workflow_id, invocation_id )
time.sleep(.5)
self.dataset_populator.wait_for_history( history_id, assert_ok=True )
time.sleep(.5)
def test_cannot_run_inaccessible_workflow( self ):
workflow = self.workflow_populator.load_workflow( name="test_for_run_cannot_access" )
workflow_request, history_id = self._setup_workflow_run( workflow )
@@ -1169,4 +1175,4 @@ test_data:
'input_steps',
)
RunJobsSummary = namedtuple('RunJobsSummary', ['history_id', 'workflow_id', 'inputs', 'jobs'])
RunJobsSummary = namedtuple('RunJobsSummary', ['history_id', 'workflow_id', 'invocation_id', 'inputs', 'jobs'])
-3
View File
@@ -100,8 +100,6 @@ test_data:
$link: the_pause
""")
print self._get("workflows/%s/download" % workflow_id).json()
assert False
# TODO: fill out test...
def test_implicit_connections( self ):
workflow_id = self._upload_yaml_workflow("""
@@ -132,4 +130,3 @@ test_data:
""")
workflow = self._get("workflows/%s/download" % workflow_id).json()
print workflow
assert False
@@ -0,0 +1 @@
This is data 1.
@@ -0,0 +1 @@
fancy compressed map built with cool algorithms...
@@ -0,0 +1 @@
This is data 2.
@@ -0,0 +1 @@
fancy compressed map built with cool algorithms...2