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
February 2015
- 2 participants
- 305 discussions
commit/galaxy-central: carlfeberhard: Managers: re-implement url_for as overridable service, add tests for history contents
by commits-noreply@bitbucket.org 02 Feb '15
by commits-noreply@bitbucket.org 02 Feb '15
02 Feb '15
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/8d74daa932a0/
Changeset: 8d74daa932a0
User: carlfeberhard
Date: 2015-02-02 19:13:54+00:00
Summary: Managers: re-implement url_for as overridable service, add tests for history contents
Affected #: 4 files
diff -r d1b39530bd1ffa10c482e7f7c49b2b41c5a847a1 -r 8d74daa932a06f7bbd3e357f8216c8f58ae55965 lib/galaxy/managers/base.py
--- a/lib/galaxy/managers/base.py
+++ b/lib/galaxy/managers/base.py
@@ -503,7 +503,7 @@
item_dict = MySerializer.serialize( trans, my_item, keys_to_serialize )
"""
#: 'service' to use for getting urls - use class var to allow overriding when testing
- #url_service =
+ url_for = staticmethod( routes.url_for )
def __init__( self, app ):
"""
@@ -544,7 +544,7 @@
If `include_keys_from` is a proper view name, extend `key_list` by
the list in that view.
"""
- key_list += self.views.get( include_keys_from, [] )
+ key_list = list( set( key_list + self.views.get( include_keys_from, [] ) ) )
self.views[ view_name ] = key_list
self.serializable_keyset.update( key_list )
return key_list
@@ -590,10 +590,6 @@
id = getattr( item, key )
return self.app.security.encode_id( id ) if id is not None else None
- @staticmethod
- def url_for( *args, **kwargs ):
- return routes.url_for( *args, **kwargs )
-
# serializing to a view where a view is a predefied list of keys to serialize
def serialize_to_view( self, trans, item, view=None, keys=None, default_view=None ):
"""
diff -r d1b39530bd1ffa10c482e7f7c49b2b41c5a847a1 -r 8d74daa932a06f7bbd3e357f8216c8f58ae55965 test/unit/managers/mock.py
--- a/test/unit/managers/mock.py
+++ b/test/unit/managers/mock.py
@@ -21,6 +21,14 @@
class OpenObject( object ):
pass
+class MockApp( object ):
+ def __init__( self, **kwargs ):
+ self.config = MockAppConfig( **kwargs )
+ self.security = self.config.security
+ self.object_store = objectstore.build_object_store_from_config( self.config )
+ self.model = mapping.init( "/tmp", "sqlite:///:memory:", create_tables=True, object_store=self.object_store )
+ self.security_agent = self.model.security_agent
+ self.visualizations_registry = MockVisualizationsRegistry()
class MockAppConfig( Bunch ):
def __init__( self, **kwargs ):
@@ -40,15 +48,6 @@
self.expose_dataset_path = True
self.allow_user_dataset_purge = True
self.enable_old_display_applications = True
-
-
-class MockApp( object ):
- def __init__( self, **kwargs ):
- self.config = MockAppConfig( **kwargs )
- self.security = self.config.security
- self.object_store = objectstore.build_object_store_from_config( self.config )
- self.model = mapping.init( "/tmp", "sqlite:///:memory:", create_tables=True, object_store=self.object_store )
- self.security_agent = self.model.security_agent
class MockWebapp( object ):
def __init__( self, **kwargs ):
@@ -92,3 +91,7 @@
template = template_lookup.get_template( filename )
template.output_encoding = 'utf-8'
return template.render( **kwargs )
+
+class MockVisualizationsRegistry( object ):
+ def get_visualizations( self, trans, target ):
+ return []
diff -r d1b39530bd1ffa10c482e7f7c49b2b41c5a847a1 -r 8d74daa932a06f7bbd3e357f8216c8f58ae55965 test/unit/managers/test_HistoryManager.py
--- a/test/unit/managers/test_HistoryManager.py
+++ b/test/unit/managers/test_HistoryManager.py
@@ -312,15 +312,12 @@
# =============================================================================
# web.url_for doesn't work well in the framework
testable_url_for = lambda *a, **k: '(fake url): %s, %s' % ( a, k )
-HistorySerializer.url_for = testable_url_for
-hdas.HDASerializer.url_for = testable_url_for
+HistorySerializer.url_for = staticmethod( testable_url_for )
+hdas.HDASerializer.url_for = staticmethod( testable_url_for )
class HistorySerializerTestCase( BaseTestCase ):
- def assertHasKeys( self, obj, key_list ):
- self.assertEqual( sorted( obj.keys() ), sorted( key_list ) )
-
def set_up_managers( self ):
super( HistorySerializerTestCase, self ).set_up_managers()
self.history_mgr = HistoryManager( self.app )
@@ -333,15 +330,15 @@
self.log( 'should have a summary view' )
summary_view = self.history_serializer.serialize_to_view( self.trans, history1, view='summary' )
- self.assertHasKeys( summary_view, self.history_serializer.views[ 'summary' ] )
+ self.assertKeys( summary_view, self.history_serializer.views[ 'summary' ] )
self.log( 'should have a detailed view' )
detailed_view = self.history_serializer.serialize_to_view( self.trans, history1, view='detailed' )
- self.assertHasKeys( detailed_view, self.history_serializer.views[ 'detailed' ] )
+ self.assertKeys( detailed_view, self.history_serializer.views[ 'detailed' ] )
self.log( 'should have the summary view as default view' )
default_view = self.history_serializer.serialize_to_view( self.trans, history1, default_view='summary' )
- self.assertHasKeys( summary_view, self.history_serializer.views[ 'summary' ] )
+ self.assertKeys( summary_view, self.history_serializer.views[ 'summary' ] )
self.log( 'should have a serializer for all serializable keys' )
need_no_serializers = ( basestring, bool, type( None ) )
@@ -360,13 +357,13 @@
self.log( 'should be able to use keys with views' )
serialized = self.history_serializer.serialize_to_view( self.trans, history1,
view='summary', keys=[ 'state_ids', 'user_id' ] )
- self.assertHasKeys( serialized,
+ self.assertKeys( serialized,
self.history_serializer.views[ 'summary' ] + [ 'state_ids', 'user_id' ] )
self.log( 'should be able to use keys on their own' )
serialized = self.history_serializer.serialize_to_view( self.trans, history1,
keys=[ 'state_ids', 'user_id' ] )
- self.assertHasKeys( serialized, [ 'state_ids', 'user_id' ] )
+ self.assertKeys( serialized, [ 'state_ids', 'user_id' ] )
def test_sharable( self ):
user2 = self.user_mgr.create( self.trans, **user2_data )
@@ -375,7 +372,7 @@
self.log( 'should have a serializer for all SharableModel keys' )
sharable_attrs = [ 'user_id', 'username_and_slug', 'importable', 'published', 'slug' ]
serialized = self.history_serializer.serialize( self.trans, history1, sharable_attrs )
- self.assertHasKeys( serialized, sharable_attrs )
+ self.assertKeys( serialized, sharable_attrs )
def test_purgable( self ):
user2 = self.user_mgr.create( self.trans, **user2_data )
@@ -398,8 +395,6 @@
self.assertEqual( serialized[ 'deleted' ], True )
self.assertEqual( serialized[ 'purged' ], True )
- #pprint.pprint( self.history_serializer.serialize( self.trans, history1, [ 'contents' ] ) )
-
def test_history_serializers( self ):
user2 = self.user_mgr.create( self.trans, **user2_data )
history1 = self.history_mgr.create( self.trans, name='history1', user=user2 )
@@ -435,6 +430,8 @@
self.assertIsInstance( serialized[ 'hdas' ], list )
self.assertIsInstance( serialized[ 'hdas' ][0], basestring )
+ serialized = self.history_serializer.serialize( self.trans, history1, [ 'contents' ] )
+ self.assertHasKeys( serialized[ 'contents' ][0], [ 'id', 'name', 'peek', 'create_time' ])
# =============================================================================
class HistoryDeserializerTestCase( BaseTestCase ):
diff -r d1b39530bd1ffa10c482e7f7c49b2b41c5a847a1 -r 8d74daa932a06f7bbd3e357f8216c8f58ae55965 test/unit/managers/test_ModelManager.py
--- a/test/unit/managers/test_ModelManager.py
+++ b/test/unit/managers/test_ModelManager.py
@@ -75,6 +75,17 @@
def log( self, *args, **kwargs ):
print( *args, **kwargs )
+ # ---- additional test types
+ def assertKeys( self, obj, key_list ):
+ self.assertEqual( sorted( obj.keys() ), sorted( key_list ) )
+
+ def assertHasKeys( self, obj, key_list ):
+ for key in key_list:
+ if key not in obj:
+ self.fail( 'Missing key: ' + key )
+ else:
+ self.assertTrue( True, 'keys found in object' )
+
# =============================================================================
if __name__ == '__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: Update tag latest_2015.01.13 for changeset fd75aaee91cf
by commits-noreply@bitbucket.org 02 Feb '15
by commits-noreply@bitbucket.org 02 Feb '15
02 Feb '15
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/96aabc4e66f1/
Changeset: 96aabc4e66f1
Branch: stable
User: natefoo
Date: 2015-02-02 19:00:23+00:00
Summary: Update tag latest_2015.01.13 for changeset fd75aaee91cf
Affected #: 1 file
diff -r fd75aaee91cf3e8a0916689dfea72e0c752c447c -r 96aabc4e66f193d4bb3b7b37d8ad4dce671f1b04 .hgtags
--- a/.hgtags
+++ b/.hgtags
@@ -22,4 +22,4 @@
2092948937ac30ef82f71463a235c66d34987088 release_2014.10.06
782fa60fc65488aea0c618d723e9a63d42caf865 latest_2014.10.06
2e8dd2949dd3eee0f56f9a3a5ebf1b2baca24aee release_2015.01.13
-c5e7535b4d229dbbe52d48ea35a27ab601205b7b latest_2015.01.13
+fd75aaee91cf3e8a0916689dfea72e0c752c447c latest_2015.01.13
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: Remove GeneTrack tag from eggs.ini as well.
by commits-noreply@bitbucket.org 02 Feb '15
by commits-noreply@bitbucket.org 02 Feb '15
02 Feb '15
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/d1b39530bd1f/
Changeset: d1b39530bd1f
User: natefoo
Date: 2015-02-02 18:42:29+00:00
Summary: Remove GeneTrack tag from eggs.ini as well.
Affected #: 1 file
diff -r eca1b4b64574cf09b4cbe35c4a8b103c1b455a79 -r d1b39530bd1ffa10c482e7f7c49b2b41c5a847a1 eggs.ini
--- a/eggs.ini
+++ b/eggs.ini
@@ -82,7 +82,6 @@
psycopg2 = _9.2.4_static
pysqlite = _3.6.17_static
MySQL_python = _5.1.41_static
-GeneTrack = _dev_48da9e998f0caf01c5be731e926f4b0481f658f0
pysam = _kanwei_b10f6e722e9a
; dependency source urls, necessary for scrambling. for an explanation, see
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: carlfeberhard: Managers: add shorthand for adding a serializer view, back off of class-level url service, more testing
by commits-noreply@bitbucket.org 02 Feb '15
by commits-noreply@bitbucket.org 02 Feb '15
02 Feb '15
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/eca1b4b64574/
Changeset: eca1b4b64574
User: carlfeberhard
Date: 2015-02-02 18:33:06+00:00
Summary: Managers: add shorthand for adding a serializer view, back off of class-level url service, more testing
Affected #: 10 files
diff -r 620437dab02aa2bf8508292bc0df4b2e1066264e -r eca1b4b64574cf09b4cbe35c4a8b103c1b455a79 lib/galaxy/managers/base.py
--- a/lib/galaxy/managers/base.py
+++ b/lib/galaxy/managers/base.py
@@ -29,6 +29,9 @@
pkg_resources.require( "SQLAlchemy >= 0.4" )
import sqlalchemy
+pkg_resources.require("Routes")
+import routes
+
from galaxy import exceptions
from galaxy import model
from galaxy import web
@@ -490,15 +493,17 @@
that should be called for those keys.
E.g. { 'x' : lambda trans, item, key: item.x, ... }
+ Note: if a key to serialize is not listed in the Serializer.serializable_keyset
+ or serializers, it will not be returned.
+
To serialize call:
my_serializer = MySerializer( app )
...
keys_to_serialize = [ 'id', 'name', 'attr1', 'attr2', ... ]
item_dict = MySerializer.serialize( trans, my_item, keys_to_serialize )
- # if a key to serialize is not listed in the Serializer.serializable_keys or serializers, it will not be added
"""
#: 'service' to use for getting urls - use class var to allow overriding when testing
- url_for = web.url_for
+ #url_service =
def __init__( self, app ):
"""
@@ -506,14 +511,17 @@
"""
self.app = app
+ # a list of valid serializable keys that can use the default (string) serializer
+ # this allows us to: 'mention' the key without adding the default serializer
+ # TODO: we may want to eventually error if a key is requested
+ # that is in neither serializable_keyset or serializers
+ self.serializable_keyset = set([])
# a map of dictionary keys to the functions (often lambdas) that create the values for those keys
self.serializers = {}
- # a list of valid serializable keys that can use the default (string) serializer
- # this allows us to: 'mention' the key without adding the default serializer
- # NOTE: if a key is requested that is in neither serializable_keys or serializers, it is not returned
- self.serializable_keys = []
# add subclass serializers defined there
self.add_serializers()
+ # update the keyset by the serializers (removing the responsibility from subclasses)
+ self.serializable_keyset.update( self.serializers.keys() )
# views are collections of serializable attributes (a named array of keys)
# inspired by model.dict_{view}_visible_keys
@@ -528,6 +536,19 @@
# to be overridden in subclasses
pass
+ def add_view( self, view_name, key_list, include_keys_from=None ):
+ """
+ Add the list of serializable attributes `key_list` to the serializer's
+ view dictionary under the key `view_name`.
+
+ If `include_keys_from` is a proper view name, extend `key_list` by
+ the list in that view.
+ """
+ key_list += self.views.get( include_keys_from, [] )
+ self.views[ view_name ] = key_list
+ self.serializable_keyset.update( key_list )
+ return key_list
+
def serialize( self, trans, item, keys ):
"""
Serialize the model `item` to a dictionary.
@@ -542,7 +563,7 @@
# check both serializers and serializable keys
if key in self.serializers:
returned[ key ] = self.serializers[ key ]( trans, item, key )
- elif key in self.serializable_keys:
+ elif key in self.serializable_keyset:
returned[ key ] = self.default_serializer( trans, item, key )
# ignore bad/unreg keys
return returned
@@ -569,6 +590,10 @@
id = getattr( item, key )
return self.app.security.encode_id( id ) if id is not None else None
+ @staticmethod
+ def url_for( *args, **kwargs ):
+ return routes.url_for( *args, **kwargs )
+
# serializing to a view where a view is a predefied list of keys to serialize
def serialize_to_view( self, trans, item, view=None, keys=None, default_view=None ):
"""
@@ -628,7 +653,7 @@
self.app = app
self.deserializers = {}
- self.deserializable_keys = []
+ self.deserializable_keyset = set([])
self.add_deserializers()
# a sub object that can validate incoming values
self.validate = ModelValidator( self.app )
diff -r 620437dab02aa2bf8508292bc0df4b2e1066264e -r eca1b4b64574cf09b4cbe35c4a8b103c1b455a79 lib/galaxy/managers/datasets.py
--- a/lib/galaxy/managers/datasets.py
+++ b/lib/galaxy/managers/datasets.py
@@ -192,8 +192,8 @@
def __init__( self, app ):
super( DatasetSerializer, self ).__init__( app )
- # most of these views build/add to the previous view
- summary_view = [
+ self.default_view = 'summary'
+ self.add_view( 'summary', [
'id',
'create_time', 'update_time',
'state',
@@ -203,19 +203,13 @@
#'extra_files_path',
'file_size', 'total_size',
'uuid',
- ]
+ ])
# could do visualizations and/or display_apps
- self.serializable_keys = summary_view + [
- ]
- self.views = {
- 'summary' : summary_view,
- }
- self.default_view = 'summary'
-
def add_serializers( self ):
super( DatasetSerializer, self ).add_serializers()
deletable.PurgableSerializerMixin.add_serializers( self )
+
self.serializers.update({
'id' : self.serialize_id,
'create_time' : self.serialize_date,
diff -r 620437dab02aa2bf8508292bc0df4b2e1066264e -r eca1b4b64574cf09b4cbe35c4a8b103c1b455a79 lib/galaxy/managers/deletable.py
--- a/lib/galaxy/managers/deletable.py
+++ b/lib/galaxy/managers/deletable.py
@@ -35,7 +35,7 @@
class DeletableSerializerMixin( object ):
def add_serializers( self ):
- pass
+ self.serializable_keyset.add( 'deleted' )
# TODO: these are of questionable value if we don't want to enable users to delete/purge via update
@@ -87,6 +87,7 @@
def add_serializers( self ):
DeletableSerializerMixin.add_serializers( self )
+ self.serializable_keyset.add( 'purged' )
class PurgableDeserializerMixin( DeletableDeserializerMixin ):
diff -r 620437dab02aa2bf8508292bc0df4b2e1066264e -r eca1b4b64574cf09b4cbe35c4a8b103c1b455a79 lib/galaxy/managers/hdas.py
--- a/lib/galaxy/managers/hdas.py
+++ b/lib/galaxy/managers/hdas.py
@@ -315,8 +315,8 @@
super( HDASerializer, self ).__init__( app )
self.hda_manager = HDAManager( app )
- # most of these views build/add to the previous view
- summary_view = [
+ self.default_view = 'summary'
+ self.add_view( 'summary', [
'id', 'name',
'history_id', 'hid',
# why include if model_class is there?
@@ -325,12 +325,8 @@
'state', 'extension',
'deleted', 'purged', 'visible', 'resubmitted',
'type', 'url'
- ]
- inaccessible = [
- 'id', 'name', 'history_id', 'hid', 'history_content_type',
- 'state', 'deleted', 'visible'
- ]
- detailed_view = summary_view + [
+ ])
+ self.add_view( 'detailed', [
'model_class',
'history_id', 'hid',
# why include if model_class is there?
@@ -362,19 +358,17 @@
'annotation', 'tags',
'api_type'
- ]
- extended_view = detailed_view + [
+ ], include_keys_from='summary' )
+
+ self.add_view( 'extended', [
'tool_version', 'parent_id', 'designation',
- ]
+ ], include_keys_from='detailed' )
- self.serializable_keys = extended_view + [
- ]
- self.views = {
- 'summary' : summary_view,
- 'detailed' : detailed_view,
- 'extended' : extended_view,
- }
- self.default_view = 'summary'
+ # keyset returned to create show a dataset where the owner has no access
+ self.add_view( 'inaccessible', [
+ 'id', 'name', 'history_id', 'hid', 'history_content_type',
+ 'state', 'deleted', 'visible'
+ ])
def add_serializers( self ):
super( HDASerializer, self ).add_serializers()
@@ -597,4 +591,4 @@
'importable' : self.deserialize_bool,
})
- self.deserializable_keys.extend( self.deserializers.keys() )
+ self.deserializable_keyset.update( self.deserializers.keys() )
diff -r 620437dab02aa2bf8508292bc0df4b2e1066264e -r eca1b4b64574cf09b4cbe35c4a8b103c1b455a79 lib/galaxy/managers/histories.py
--- a/lib/galaxy/managers/histories.py
+++ b/lib/galaxy/managers/histories.py
@@ -249,12 +249,13 @@
def __init__( self, app ):
super( HistorySerializer, self ).__init__( app )
+
self.history_manager = HistoryManager( app )
-
self.hda_manager = hdas.HDAManager( app )
self.hda_serializer = hdas.HDASerializer( app )
- summary_view = [
+ self.default_view = 'summary'
+ self.add_view( 'summary', [
'id',
'model_class',
'name',
@@ -266,9 +267,8 @@
'published',
'annotation',
'tags',
- ]
- # in the Historys' case, each of these views includes the keys from the previous
- detailed_view = summary_view + [
+ ])
+ self.add_view( 'detailed', [
'contents_url',
#'hdas',
'empty',
@@ -281,16 +281,8 @@
'state',
'state_details',
'state_ids',
- ]
- extended_view = detailed_view + [
- ]
- self.serializable_keys = extended_view + []
- self.views = {
- 'summary' : summary_view,
- 'detailed' : detailed_view,
- 'extended' : extended_view,
- }
- self.default_view = 'summary'
+ # in the Historys' case, each of these views includes the keys from the previous
+ ], include_keys_from='summary' )
#assumes: outgoing to json.dumps and sanitized
def add_serializers( self ):
@@ -300,19 +292,19 @@
self.serializers.update({
'model_class' : lambda *a: 'History',
'id' : self.serialize_id,
- 'count' : lambda trans, item, key: len( item.datasets ),
'create_time' : self.serialize_date,
'update_time' : self.serialize_date,
'size' : lambda t, i, k: int( i.get_disk_size() ),
'nice_size' : lambda t, i, k: i.get_disk_size( nice_size=True ),
+ 'state' : lambda t, i, k: self.history_manager.get_history_state( t, i ),
'url' : lambda t, i, k: self.url_for( 'history', id=t.security.encode_id( i.id ) ),
'contents_url' : lambda t, i, k:
self.url_for( 'history_contents', history_id=t.security.encode_id( i.id ) ),
+
'empty' : lambda t, i, k: len( i.datasets ) <= 0,
-
+ 'count' : lambda trans, item, key: len( item.datasets ),
'hdas' : lambda t, i, k: [ t.security.encode_id( hda.id ) for hda in i.datasets ],
- 'state' : lambda t, i, k: self.history_manager.get_history_state( t, i ),
'state_details' : lambda t, i, k: self.history_manager.get_state_counts( t, i ),
'state_ids' : lambda t, i, k: self.history_manager.get_state_ids( t, i ),
'contents' : self.serialize_contents
diff -r 620437dab02aa2bf8508292bc0df4b2e1066264e -r eca1b4b64574cf09b4cbe35c4a8b103c1b455a79 lib/galaxy/managers/pages.py
--- a/lib/galaxy/managers/pages.py
+++ b/lib/galaxy/managers/pages.py
@@ -46,17 +46,9 @@
super( PageSerializer, self ).__init__( app )
self.page_manager = PageManager( app )
- summary_view = [
- ]
- # in the Pages' case, each of these views includes the keys from the previous
- detailed_view = summary_view + [
- ]
- self.serializable_keys = detailed_view + []
- self.views = {
- 'summary': summary_view,
- 'detailed': detailed_view
- }
self.default_view = 'summary'
+ self.add_view( 'summary', [] )
+ self.add_view( 'detailed', [] )
def add_serializers( self ):
super( PageSerializer, self ).add_serializers()
@@ -79,4 +71,4 @@
super( PageDeserializer, self ).add_deserializers()
self.deserializers.update({
})
- self.deserializable_keys = self.deserializers.keys()
+ self.deserializable_keyset.update( self.deserializers.keys() )
diff -r 620437dab02aa2bf8508292bc0df4b2e1066264e -r eca1b4b64574cf09b4cbe35c4a8b103c1b455a79 lib/galaxy/managers/sharable.py
--- a/lib/galaxy/managers/sharable.py
+++ b/lib/galaxy/managers/sharable.py
@@ -319,7 +319,7 @@
'user_id' : self.serialize_id,
'username_and_slug' : self.serialize_username_and_slug
})
- self.serializable_keys.extend([
+ self.serializable_keyset.update([
'importable', 'published', 'slug'
])
diff -r 620437dab02aa2bf8508292bc0df4b2e1066264e -r eca1b4b64574cf09b4cbe35c4a8b103c1b455a79 lib/galaxy/managers/users.py
--- a/lib/galaxy/managers/users.py
+++ b/lib/galaxy/managers/users.py
@@ -202,28 +202,26 @@
"""
super( UserSerializer, self ).__init__()
- summary_view = [
+ self.default_view = 'summary'
+ self.add_view( 'summary', [
'id', 'email', 'username'
- ]
- # in the Historys' case, each of these views includes the keys from the previous
- detailed_view = summary_view + [
- 'update_time', 'create_time',
- 'total_disk_usage', 'nice_total_disk_usage',
- 'deleted', 'purged',
+ ])
+ self.add_view( 'detailed', [
+ 'update_time',
+ 'create_time',
+ 'total_disk_usage',
+ 'nice_total_disk_usage',
+ 'deleted',
+ 'purged',
'active'
- ]
- extended_view = detailed_view + [
- #'preferences',
- #'tags', # all tags
- #'annotations' # all annotations
- ]
- self.serializable_keys = extended_view
- self.views = {
- 'summary' : summary_view,
- 'detailed' : detailed_view,
- 'extended' : extended_view,
- }
- self.default_view = 'summary'
+ ], include_keys_from='summary' )
+ #self.add_view( 'summary', [
+ # 'preferences',
+ # # all tags
+ # 'tags',
+ # # all annotations
+ # 'annotations'
+ #], include_keys_from='detailed' )
def add_serializers( self ):
self.serializers.update({
diff -r 620437dab02aa2bf8508292bc0df4b2e1066264e -r eca1b4b64574cf09b4cbe35c4a8b103c1b455a79 lib/galaxy/managers/visualizations.py
--- a/lib/galaxy/managers/visualizations.py
+++ b/lib/galaxy/managers/visualizations.py
@@ -48,17 +48,9 @@
super( VisualizationSerializer, self ).__init__( app )
self.visualizations_manager = VisualizationManager( app )
- summary_view = [
- ]
- # in the Visualizations' case, each of these views includes the keys from the previous
- detailed_view = summary_view + [
- ]
- self.serializable_keys = detailed_view + []
- self.views = {
- 'summary' : summary_view,
- 'detailed' : detailed_view
- }
self.default_view = 'summary'
+ self.add_view( 'summary', [] )
+ self.add_view( 'detailed', [] )
def add_serializers( self ):
super( VisualizationSerializer, self ).add_serializers()
@@ -81,4 +73,4 @@
super( VisualizationDeserializer, self ).add_deserializers()
self.deserializers.update({
})
- self.deserializable_keys = self.deserializers.keys()
+ self.deserializable_keyset.update( self.deserializers.keys() )
diff -r 620437dab02aa2bf8508292bc0df4b2e1066264e -r eca1b4b64574cf09b4cbe35c4a8b103c1b455a79 test/unit/managers/test_HistoryManager.py
--- a/test/unit/managers/test_HistoryManager.py
+++ b/test/unit/managers/test_HistoryManager.py
@@ -339,17 +339,13 @@
detailed_view = self.history_serializer.serialize_to_view( self.trans, history1, view='detailed' )
self.assertHasKeys( detailed_view, self.history_serializer.views[ 'detailed' ] )
- self.log( 'should have a extended view' )
- extended_view = self.history_serializer.serialize_to_view( self.trans, history1, view='extended' )
- self.assertHasKeys( extended_view, self.history_serializer.views[ 'extended' ] )
-
self.log( 'should have the summary view as default view' )
default_view = self.history_serializer.serialize_to_view( self.trans, history1, default_view='summary' )
self.assertHasKeys( summary_view, self.history_serializer.views[ 'summary' ] )
self.log( 'should have a serializer for all serializable keys' )
need_no_serializers = ( basestring, bool, type( None ) )
- for key in self.history_serializer.serializable_keys:
+ for key in self.history_serializer.serializable_keyset:
instantiated_attribute = getattr( history1, key, None )
if not ( ( key in self.history_serializer.serializers )
or ( isinstance( instantiated_attribute, need_no_serializers ) ) ):
@@ -372,24 +368,61 @@
keys=[ 'state_ids', 'user_id' ] )
self.assertHasKeys( serialized, [ 'state_ids', 'user_id' ] )
- def test_serializers( self ):
- # size
- # nice size
- pass
+ def test_sharable( self ):
+ user2 = self.user_mgr.create( self.trans, **user2_data )
+ history1 = self.history_mgr.create( self.trans, name='history1', user=user2 )
+
+ self.log( 'should have a serializer for all SharableModel keys' )
+ sharable_attrs = [ 'user_id', 'username_and_slug', 'importable', 'published', 'slug' ]
+ serialized = self.history_serializer.serialize( self.trans, history1, sharable_attrs )
+ self.assertHasKeys( serialized, sharable_attrs )
+
+ def test_purgable( self ):
+ user2 = self.user_mgr.create( self.trans, **user2_data )
+ history1 = self.history_mgr.create( self.trans, name='history1', user=user2 )
+
+ self.log( 'deleted and purged should be returned in their default states' )
+ keys = [ 'deleted', 'purged' ]
+ serialized = self.history_serializer.serialize( self.trans, history1, keys )
+ self.assertEqual( serialized[ 'deleted' ], False )
+ self.assertEqual( serialized[ 'purged' ], False )
+
+ self.log( 'deleted and purged should return their current state' )
+ self.history_mgr.delete( self.trans, history1 )
+ serialized = self.history_serializer.serialize( self.trans, history1, keys )
+ self.assertEqual( serialized[ 'deleted' ], True )
+ self.assertEqual( serialized[ 'purged' ], False )
+
+ self.history_mgr.purge( self.trans, history1 )
+ serialized = self.history_serializer.serialize( self.trans, history1, keys )
+ self.assertEqual( serialized[ 'deleted' ], True )
+ self.assertEqual( serialized[ 'purged' ], True )
+
+ #pprint.pprint( self.history_serializer.serialize( self.trans, history1, [ 'contents' ] ) )
+
+ def test_history_serializers( self ):
+ user2 = self.user_mgr.create( self.trans, **user2_data )
+ history1 = self.history_mgr.create( self.trans, name='history1', user=user2 )
+
+ serialized = self.history_serializer.serialize( self.trans, history1, [ 'size', 'nice_size' ])
+ self.assertIsInstance( serialized[ 'size' ], int )
+ self.assertIsInstance( serialized[ 'nice_size' ], basestring )
def test_contents( self ):
user2 = self.user_mgr.create( self.trans, **user2_data )
history1 = self.history_mgr.create( self.trans, name='history1', user=user2 )
self.log( 'a history with no contents should be properly reflected in empty, etc.' )
- keys = [ 'empty', 'count', 'state_ids', 'state_details', 'state' ]
+ keys = [ 'empty', 'count', 'state_ids', 'state_details', 'state', 'hdas' ]
serialized = self.history_serializer.serialize( self.trans, history1, keys )
self.assertEqual( serialized[ 'state' ], 'new' )
self.assertEqual( serialized[ 'empty' ], True )
self.assertEqual( serialized[ 'count' ], 0 )
self.assertEqual( sum( serialized[ 'state_details' ].values() ), 0 )
self.assertEqual( serialized[ 'state_ids' ][ 'ok' ], [] )
+ self.assertIsInstance( serialized[ 'hdas' ], list )
+ self.log( 'a history with contents should be properly reflected in empty, etc.' )
hda1 = self.hda_mgr.create( self.trans, history=history1, hid=1 )
self.hda_mgr.update( self.trans, hda1, dict( state='ok' ) )
@@ -399,8 +432,9 @@
self.assertEqual( serialized[ 'count' ], 1 )
self.assertEqual( serialized[ 'state_details' ][ 'ok' ], 1 )
self.assertIsInstance( serialized[ 'state_ids' ][ 'ok' ], list )
+ self.assertIsInstance( serialized[ 'hdas' ], list )
+ self.assertIsInstance( serialized[ 'hdas' ][0], basestring )
- #pprint.pprint( self.history_serializer.serialize( self.trans, history1, [ 'contents' ] ) )
# =============================================================================
class HistoryDeserializerTestCase( BaseTestCase ):
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: Merged in dan/galaxy-central-prs (pull request #643)
by commits-noreply@bitbucket.org 02 Feb '15
by commits-noreply@bitbucket.org 02 Feb '15
02 Feb '15
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/620437dab02a/
Changeset: 620437dab02a
User: dannon
Date: 2015-02-02 17:04:23+00:00
Summary: Merged in dan/galaxy-central-prs (pull request #643)
Add ZebrafishMine Data Source Tool.
Affected #: 3 files
diff -r 5aca28ab8cfbbc1769f68fcfacf3dfbf9352ccde -r 620437dab02aa2bf8508292bc0df4b2e1066264e config/tool_conf.xml.main
--- a/config/tool_conf.xml.main
+++ b/config/tool_conf.xml.main
@@ -15,6 +15,7 @@
<tool file="data_source/yeastmine.xml" /><tool file="data_source/worm_modencode.xml" /><tool file="data_source/wormbase.xml" />
+ <tool file="data_source/zebrafishmine.xml" /><tool file="data_source/eupathdb.xml" /><tool file="genomespace/genomespace_file_browser_prod.xml" /><tool file="genomespace/genomespace_importer.xml" />
diff -r 5aca28ab8cfbbc1769f68fcfacf3dfbf9352ccde -r 620437dab02aa2bf8508292bc0df4b2e1066264e config/tool_conf.xml.sample
--- a/config/tool_conf.xml.sample
+++ b/config/tool_conf.xml.sample
@@ -22,6 +22,7 @@
<tool file="data_source/worm_modencode.xml" /><tool file="data_source/wormbase.xml" /><tool file="data_source/wormbase_test.xml" />
+ <tool file="data_source/zebrafishmine.xml" /><tool file="data_source/eupathdb.xml" /><tool file="data_source/hbvar.xml" /><tool file="genomespace/genomespace_file_browser_prod.xml" />
diff -r 5aca28ab8cfbbc1769f68fcfacf3dfbf9352ccde -r 620437dab02aa2bf8508292bc0df4b2e1066264e tools/data_source/zebrafishmine.xml
--- /dev/null
+++ b/tools/data_source/zebrafishmine.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<tool name="ZebrafishMine" id="zebrafishmine" tool_type="data_source">
+ <description>server</description>
+ <command interpreter="python">data_source.py $output $__app__.config.output_size_limit</command>
+ <inputs action="http://zebrafishmine.org/begin.do" check_values="false" method="get">
+ <display>go to ZebrafishMine server $GALAXY_URL</display>
+ </inputs>
+ <request_param_translation>
+ <request_param galaxy_name="data_type" remote_name="data_type" missing="auto" >
+ <value_translation>
+ <value galaxy_value="auto" remote_value="txt" /><!-- make txt auto detect -->
+ </value_translation>
+ </request_param>
+ </request_param_translation>
+ <uihints minwidth="800"/>
+ <outputs>
+ <data name="output" format="txt" />
+ </outputs>
+ <options sanitize="False"/>
+</tool>
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
2 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/0377ecc3e49f/
Changeset: 0377ecc3e49f
User: dan
Date: 2015-01-21 19:56:08+00:00
Summary: Add ZebrafishMine Data Source Tool.
Affected #: 3 files
diff -r a09398a62b752f72c076fa98ccd149dc8f3050a7 -r 0377ecc3e49f13a85586b52f754118c923e54537 config/tool_conf.xml.main
--- a/config/tool_conf.xml.main
+++ b/config/tool_conf.xml.main
@@ -15,6 +15,7 @@
<tool file="data_source/yeastmine.xml" /><tool file="data_source/worm_modencode.xml" /><tool file="data_source/wormbase.xml" />
+ <tool file="data_source/zebrafishmine.xml" /><tool file="data_source/eupathdb.xml" /><tool file="genomespace/genomespace_file_browser_prod.xml" /><tool file="genomespace/genomespace_importer.xml" />
diff -r a09398a62b752f72c076fa98ccd149dc8f3050a7 -r 0377ecc3e49f13a85586b52f754118c923e54537 config/tool_conf.xml.sample
--- a/config/tool_conf.xml.sample
+++ b/config/tool_conf.xml.sample
@@ -22,6 +22,7 @@
<tool file="data_source/worm_modencode.xml" /><tool file="data_source/wormbase.xml" /><tool file="data_source/wormbase_test.xml" />
+ <tool file="data_source/zebrafishmine.xml" /><tool file="data_source/eupathdb.xml" /><tool file="data_source/hbvar.xml" /><tool file="genomespace/genomespace_file_browser_prod.xml" />
diff -r a09398a62b752f72c076fa98ccd149dc8f3050a7 -r 0377ecc3e49f13a85586b52f754118c923e54537 tools/data_source/zebrafishmine.xml
--- /dev/null
+++ b/tools/data_source/zebrafishmine.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<tool name="ZebrafishMine" id="zebrafishmine" tool_type="data_source">
+ <description>server</description>
+ <command interpreter="python">data_source.py $output $__app__.config.output_size_limit</command>
+ <inputs action="http://zebrafishmine.org/begin.do" check_values="false" method="get">
+ <display>go to ZebrafishMine server $GALAXY_URL</display>
+ </inputs>
+ <request_param_translation>
+ <request_param galaxy_name="data_type" remote_name="data_type" missing="auto" >
+ <value_translation>
+ <value galaxy_value="auto" remote_value="txt" /><!-- make txt auto detect -->
+ </value_translation>
+ </request_param>
+ </request_param_translation>
+ <uihints minwidth="800"/>
+ <outputs>
+ <data name="output" format="txt" />
+ </outputs>
+ <options sanitize="False"/>
+</tool>
https://bitbucket.org/galaxy/galaxy-central/commits/620437dab02a/
Changeset: 620437dab02a
User: dannon
Date: 2015-02-02 17:04:23+00:00
Summary: Merged in dan/galaxy-central-prs (pull request #643)
Add ZebrafishMine Data Source Tool.
Affected #: 3 files
diff -r 5aca28ab8cfbbc1769f68fcfacf3dfbf9352ccde -r 620437dab02aa2bf8508292bc0df4b2e1066264e config/tool_conf.xml.main
--- a/config/tool_conf.xml.main
+++ b/config/tool_conf.xml.main
@@ -15,6 +15,7 @@
<tool file="data_source/yeastmine.xml" /><tool file="data_source/worm_modencode.xml" /><tool file="data_source/wormbase.xml" />
+ <tool file="data_source/zebrafishmine.xml" /><tool file="data_source/eupathdb.xml" /><tool file="genomespace/genomespace_file_browser_prod.xml" /><tool file="genomespace/genomespace_importer.xml" />
diff -r 5aca28ab8cfbbc1769f68fcfacf3dfbf9352ccde -r 620437dab02aa2bf8508292bc0df4b2e1066264e config/tool_conf.xml.sample
--- a/config/tool_conf.xml.sample
+++ b/config/tool_conf.xml.sample
@@ -22,6 +22,7 @@
<tool file="data_source/worm_modencode.xml" /><tool file="data_source/wormbase.xml" /><tool file="data_source/wormbase_test.xml" />
+ <tool file="data_source/zebrafishmine.xml" /><tool file="data_source/eupathdb.xml" /><tool file="data_source/hbvar.xml" /><tool file="genomespace/genomespace_file_browser_prod.xml" />
diff -r 5aca28ab8cfbbc1769f68fcfacf3dfbf9352ccde -r 620437dab02aa2bf8508292bc0df4b2e1066264e tools/data_source/zebrafishmine.xml
--- /dev/null
+++ b/tools/data_source/zebrafishmine.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<tool name="ZebrafishMine" id="zebrafishmine" tool_type="data_source">
+ <description>server</description>
+ <command interpreter="python">data_source.py $output $__app__.config.output_size_limit</command>
+ <inputs action="http://zebrafishmine.org/begin.do" check_values="false" method="get">
+ <display>go to ZebrafishMine server $GALAXY_URL</display>
+ </inputs>
+ <request_param_translation>
+ <request_param galaxy_name="data_type" remote_name="data_type" missing="auto" >
+ <value_translation>
+ <value galaxy_value="auto" remote_value="txt" /><!-- make txt auto detect -->
+ </value_translation>
+ </request_param>
+ </request_param_translation>
+ <uihints minwidth="800"/>
+ <outputs>
+ <data name="output" format="txt" />
+ </outputs>
+ <options sanitize="False"/>
+</tool>
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: carlfeberhard: Managers: wire url_for in serializers under a class-level variable for overriding in tests, more test coverage (serializers)
by commits-noreply@bitbucket.org 02 Feb '15
by commits-noreply@bitbucket.org 02 Feb '15
02 Feb '15
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/5aca28ab8cfb/
Changeset: 5aca28ab8cfb
User: carlfeberhard
Date: 2015-02-02 16:07:17+00:00
Summary: Managers: wire url_for in serializers under a class-level variable for overriding in tests, more test coverage (serializers)
Affected #: 6 files
diff -r 2911cf250e75c953700e19f2e02b4f7f6f6c7f02 -r 5aca28ab8cfbbc1769f68fcfacf3dfbf9352ccde lib/galaxy/managers/base.py
--- a/lib/galaxy/managers/base.py
+++ b/lib/galaxy/managers/base.py
@@ -31,6 +31,7 @@
from galaxy import exceptions
from galaxy import model
+from galaxy import web
from galaxy.model import tool_shed_install
import logging
@@ -496,6 +497,8 @@
item_dict = MySerializer.serialize( trans, my_item, keys_to_serialize )
# if a key to serialize is not listed in the Serializer.serializable_keys or serializers, it will not be added
"""
+ #: 'service' to use for getting urls - use class var to allow overriding when testing
+ url_for = web.url_for
def __init__( self, app ):
"""
@@ -578,6 +581,7 @@
no `view` or `keys`: use the `default_view` if any
`view` and `keys`: combine both into one list of keys
"""
+#TODO: default view + view makes no sense outside the API.index context - move default view there
all_keys = []
keys = keys or []
# chose explicit over concise here
diff -r 2911cf250e75c953700e19f2e02b4f7f6f6c7f02 -r 5aca28ab8cfbbc1769f68fcfacf3dfbf9352ccde lib/galaxy/managers/hdas.py
--- a/lib/galaxy/managers/hdas.py
+++ b/lib/galaxy/managers/hdas.py
@@ -11,7 +11,6 @@
from galaxy import model
from galaxy import exceptions
from galaxy import datatypes
-import galaxy.web
import galaxy.datatypes.metadata
from galaxy import objectstore
@@ -436,11 +435,11 @@
#TODO: this intermittently causes a routes.GenerationException - temp use the legacy route to prevent this
# see also: https://trello.com/c/5d6j4X5y
# see also: https://sentry.galaxyproject.org/galaxy/galaxy-main/group/20769/events/9352…
- 'url' : lambda t, i, k: galaxy.web.url_for( 'history_content',
+ 'url' : lambda t, i, k: self.url_for( 'history_content',
history_id=t.security.encode_id( i.history_id ), id=t.security.encode_id( i.id ) ),
'urls' : self.serialize_urls,
- 'download_url' : lambda t, i, k: galaxy.web.url_for( 'history_contents_display',
+ 'download_url' : lambda t, i, k: self.url_for( 'history_contents_display',
history_id=t.security.encode_id( i.history.id ),
history_content_id=t.security.encode_id( i.id ) ),
@@ -548,7 +547,7 @@
"""
Return web controller urls useful for this HDA.
"""
- url_for = galaxy.web.url_for
+ url_for = self.url_for
encoded_id = self.security.encode_id( hda.id )
urls = {
'purge' : url_for( controller='dataset', action='purge_async', dataset_id=encoded_id ),
diff -r 2911cf250e75c953700e19f2e02b4f7f6f6c7f02 -r 5aca28ab8cfbbc1769f68fcfacf3dfbf9352ccde lib/galaxy/managers/histories.py
--- a/lib/galaxy/managers/histories.py
+++ b/lib/galaxy/managers/histories.py
@@ -5,7 +5,6 @@
created (or copied) by users over the course of an analysis.
"""
-import galaxy.web
from galaxy import model
from galaxy.managers import base
from galaxy.managers import sharable
@@ -307,9 +306,9 @@
'size' : lambda t, i, k: int( i.get_disk_size() ),
'nice_size' : lambda t, i, k: i.get_disk_size( nice_size=True ),
- 'url' : lambda t, i, k: galaxy.web.url_for( 'history', id=t.security.encode_id( i.id ) ),
+ 'url' : lambda t, i, k: self.url_for( 'history', id=t.security.encode_id( i.id ) ),
'contents_url' : lambda t, i, k:
- galaxy.web.url_for( 'history_contents', history_id=t.security.encode_id( i.id ) ),
+ self.url_for( 'history_contents', history_id=t.security.encode_id( i.id ) ),
'empty' : lambda t, i, k: len( i.datasets ) <= 0,
'hdas' : lambda t, i, k: [ t.security.encode_id( hda.id ) for hda in i.datasets ],
diff -r 2911cf250e75c953700e19f2e02b4f7f6f6c7f02 -r 5aca28ab8cfbbc1769f68fcfacf3dfbf9352ccde lib/galaxy/managers/sharable.py
--- a/lib/galaxy/managers/sharable.py
+++ b/lib/galaxy/managers/sharable.py
@@ -326,19 +326,20 @@
def serialize_username_and_slug( self, trans, item, key ):
if not ( item.user and item.slug and self.SINGLE_CHAR_ABBR ):
return None
+#TODO: self.url_for
return ( '/' ).join(( 'u', item.user.username, self.SINGLE_CHAR_ABBR, item.slug ) )
+ #def published_url( self, trans, item, key ):
+ # """
+ # """
+ # url = url_for(controller='history', action="display_by_username_and_slug",
+ # username=item.user.username, slug=item.slug )
+ # return url
+
# the only ones that needs any fns:
# user/user_id
# user_shares?
# username_and_slug?
- #def get_name_and_link_async( self, trans, id=None ):
- #def published_url( self, trans, item, key ):
- # """
- # """
- # url = url_for(controller='history', action="display_by_username_and_slug",
- # username=item.user.username, slug=item.slug )
- # return url
class SharableModelDeserializer( base.ModelDeserializer,
diff -r 2911cf250e75c953700e19f2e02b4f7f6f6c7f02 -r 5aca28ab8cfbbc1769f68fcfacf3dfbf9352ccde test/unit/managers/mock.py
--- a/test/unit/managers/mock.py
+++ b/test/unit/managers/mock.py
@@ -21,6 +21,7 @@
class OpenObject( object ):
pass
+
class MockAppConfig( Bunch ):
def __init__( self, **kwargs ):
Bunch.__init__( self, **kwargs )
@@ -36,7 +37,10 @@
self.user_activation_on = False
self.new_user_dataset_access_role_default_private = False
+ self.expose_dataset_path = True
self.allow_user_dataset_purge = True
+ self.enable_old_display_applications = True
+
class MockApp( object ):
def __init__( self, **kwargs ):
diff -r 2911cf250e75c953700e19f2e02b4f7f6f6c7f02 -r 5aca28ab8cfbbc1769f68fcfacf3dfbf9352ccde test/unit/managers/test_HistoryManager.py
--- a/test/unit/managers/test_HistoryManager.py
+++ b/test/unit/managers/test_HistoryManager.py
@@ -19,8 +19,12 @@
import mock
from test_ModelManager import BaseTestCase
+
from galaxy.managers.histories import HistoryManager
+from galaxy.managers.histories import HistorySerializer
+from galaxy.managers.histories import HistoryDeserializer
from galaxy.managers.histories import HistoryFilters
+from galaxy.managers import hdas
# =============================================================================
@@ -305,10 +309,122 @@
self.assertEqual( self.history_mgr.get_current( self.trans ), history1 )
+# =============================================================================
+# web.url_for doesn't work well in the framework
+testable_url_for = lambda *a, **k: '(fake url): %s, %s' % ( a, k )
+HistorySerializer.url_for = testable_url_for
+hdas.HDASerializer.url_for = testable_url_for
+
+
+class HistorySerializerTestCase( BaseTestCase ):
+
+ def assertHasKeys( self, obj, key_list ):
+ self.assertEqual( sorted( obj.keys() ), sorted( key_list ) )
+
+ def set_up_managers( self ):
+ super( HistorySerializerTestCase, self ).set_up_managers()
+ self.history_mgr = HistoryManager( self.app )
+ self.hda_mgr = hdas.HDAManager( self.app )
+ self.history_serializer = HistorySerializer( self.app )
+
+ def test_views( self ):
+ user2 = self.user_mgr.create( self.trans, **user2_data )
+ history1 = self.history_mgr.create( self.trans, name='history1', user=user2 )
+
+ self.log( 'should have a summary view' )
+ summary_view = self.history_serializer.serialize_to_view( self.trans, history1, view='summary' )
+ self.assertHasKeys( summary_view, self.history_serializer.views[ 'summary' ] )
+
+ self.log( 'should have a detailed view' )
+ detailed_view = self.history_serializer.serialize_to_view( self.trans, history1, view='detailed' )
+ self.assertHasKeys( detailed_view, self.history_serializer.views[ 'detailed' ] )
+
+ self.log( 'should have a extended view' )
+ extended_view = self.history_serializer.serialize_to_view( self.trans, history1, view='extended' )
+ self.assertHasKeys( extended_view, self.history_serializer.views[ 'extended' ] )
+
+ self.log( 'should have the summary view as default view' )
+ default_view = self.history_serializer.serialize_to_view( self.trans, history1, default_view='summary' )
+ self.assertHasKeys( summary_view, self.history_serializer.views[ 'summary' ] )
+
+ self.log( 'should have a serializer for all serializable keys' )
+ need_no_serializers = ( basestring, bool, type( None ) )
+ for key in self.history_serializer.serializable_keys:
+ instantiated_attribute = getattr( history1, key, None )
+ if not ( ( key in self.history_serializer.serializers )
+ or ( isinstance( instantiated_attribute, need_no_serializers ) ) ):
+ self.fail( 'no serializer for: %s (%s)' % ( key, instantiated_attribute ) )
+ else:
+ self.assertTrue( True, 'all serializable keys have a serializer' )
+
+ def test_views_and_keys( self ):
+ user2 = self.user_mgr.create( self.trans, **user2_data )
+ history1 = self.history_mgr.create( self.trans, name='history1', user=user2 )
+
+ self.log( 'should be able to use keys with views' )
+ serialized = self.history_serializer.serialize_to_view( self.trans, history1,
+ view='summary', keys=[ 'state_ids', 'user_id' ] )
+ self.assertHasKeys( serialized,
+ self.history_serializer.views[ 'summary' ] + [ 'state_ids', 'user_id' ] )
+
+ self.log( 'should be able to use keys on their own' )
+ serialized = self.history_serializer.serialize_to_view( self.trans, history1,
+ keys=[ 'state_ids', 'user_id' ] )
+ self.assertHasKeys( serialized, [ 'state_ids', 'user_id' ] )
+
+ def test_serializers( self ):
+ # size
+ # nice size
+ pass
+
+ def test_contents( self ):
+ user2 = self.user_mgr.create( self.trans, **user2_data )
+ history1 = self.history_mgr.create( self.trans, name='history1', user=user2 )
+
+ self.log( 'a history with no contents should be properly reflected in empty, etc.' )
+ keys = [ 'empty', 'count', 'state_ids', 'state_details', 'state' ]
+ serialized = self.history_serializer.serialize( self.trans, history1, keys )
+ self.assertEqual( serialized[ 'state' ], 'new' )
+ self.assertEqual( serialized[ 'empty' ], True )
+ self.assertEqual( serialized[ 'count' ], 0 )
+ self.assertEqual( sum( serialized[ 'state_details' ].values() ), 0 )
+ self.assertEqual( serialized[ 'state_ids' ][ 'ok' ], [] )
+
+ hda1 = self.hda_mgr.create( self.trans, history=history1, hid=1 )
+ self.hda_mgr.update( self.trans, hda1, dict( state='ok' ) )
+
+ serialized = self.history_serializer.serialize( self.trans, history1, keys )
+ self.assertEqual( serialized[ 'state' ], 'ok' )
+ self.assertEqual( serialized[ 'empty' ], False )
+ self.assertEqual( serialized[ 'count' ], 1 )
+ self.assertEqual( serialized[ 'state_details' ][ 'ok' ], 1 )
+ self.assertIsInstance( serialized[ 'state_ids' ][ 'ok' ], list )
+
+ #pprint.pprint( self.history_serializer.serialize( self.trans, history1, [ 'contents' ] ) )
+
+# =============================================================================
+class HistoryDeserializerTestCase( BaseTestCase ):
+
+ def set_up_managers( self ):
+ super( HistoryDeserializerTestCase, self ).set_up_managers()
+ self.history_mgr = HistoryManager( self.app )
+ self.history_deserializer = HistoryDeserializer( self.app )
+
+ def test_base( self ):
+ pass
+
+
+# =============================================================================
+class HistoryFiltersTestCase( BaseTestCase ):
+
+ def set_up_managers( self ):
+ super( HistoryFiltersTestCase, self ).set_up_managers()
+ self.history_mgr = HistoryManager( self.app )
+ self.filter_parser = HistoryFilters( self.app )
+
# ---- functional and orm filter splitting and resolution
def test_parse_filters( self ):
- filter_parser = HistoryFilters( self.app )
- filters = filter_parser.parse_filters([
+ filters = self.filter_parser.parse_filters([
( 'name', 'eq', 'wot' ),
( 'deleted', 'eq', 'True' ),
( 'annotation', 'has', 'hrrmm' )
@@ -320,36 +436,34 @@
self.assertEqual( filters[1].right.value, True )
def test_parse_filters_invalid_filters( self ):
- filter_parser = HistoryFilters( self.app )
self.log( 'should error on non-column attr')
- self.assertRaises( exceptions.RequestParameterInvalidException, filter_parser.parse_filters, [
+ self.assertRaises( exceptions.RequestParameterInvalidException, self.filter_parser.parse_filters, [
( 'merp', 'eq', 'wot' ),
])
self.log( 'should error on non-whitelisted attr')
- self.assertRaises( exceptions.RequestParameterInvalidException, filter_parser.parse_filters, [
+ self.assertRaises( exceptions.RequestParameterInvalidException, self.filter_parser.parse_filters, [
( 'user_id', 'eq', 'wot' ),
])
self.log( 'should error on non-whitelisted op')
- self.assertRaises( exceptions.RequestParameterInvalidException, filter_parser.parse_filters, [
+ self.assertRaises( exceptions.RequestParameterInvalidException, self.filter_parser.parse_filters, [
( 'name', 'lt', 'wot' ),
])
self.log( 'should error on non-listed fn op')
- self.assertRaises( exceptions.RequestParameterInvalidException, filter_parser.parse_filters, [
+ self.assertRaises( exceptions.RequestParameterInvalidException, self.filter_parser.parse_filters, [
( 'annotation', 'like', 'wot' ),
])
self.log( 'should error on val parsing error')
- self.assertRaises( exceptions.RequestParameterInvalidException, filter_parser.parse_filters, [
+ self.assertRaises( exceptions.RequestParameterInvalidException, self.filter_parser.parse_filters, [
( 'deleted', 'eq', 'true' ),
])
def test_orm_filter_parsing( self ):
- filter_parser = HistoryFilters( self.app )
user2 = self.user_mgr.create( self.trans, **user2_data )
history1 = self.history_mgr.create( self.trans, name='history1', user=user2 )
history2 = self.history_mgr.create( self.trans, name='history2', user=user2 )
history3 = self.history_mgr.create( self.trans, name='history3', user=user2 )
- filters = filter_parser.parse_filters([
+ filters = self.filter_parser.parse_filters([
( 'name', 'like', 'history%' ),
])
histories = self.history_mgr.list( self.trans, filters=filters )
@@ -357,23 +471,23 @@
# print h.name
self.assertEqual( histories, [ history1, history2, history3 ])
- filters = filter_parser.parse_filters([ ( 'name', 'like', '%2' ), ])
+ filters = self.filter_parser.parse_filters([ ( 'name', 'like', '%2' ), ])
self.assertEqual( self.history_mgr.list( self.trans, filters=filters ), [ history2 ])
- filters = filter_parser.parse_filters([ ( 'name', 'eq', 'history2' ), ])
+ filters = self.filter_parser.parse_filters([ ( 'name', 'eq', 'history2' ), ])
self.assertEqual( self.history_mgr.list( self.trans, filters=filters ), [ history2 ])
self.history_mgr.update( self.trans, history1, dict( deleted=True ) )
- filters = filter_parser.parse_filters([ ( 'deleted', 'eq', 'True' ), ])
+ filters = self.filter_parser.parse_filters([ ( 'deleted', 'eq', 'True' ), ])
self.assertEqual( self.history_mgr.list( self.trans, filters=filters ), [ history1 ])
- filters = filter_parser.parse_filters([ ( 'deleted', 'eq', 'False' ), ])
+ filters = self.filter_parser.parse_filters([ ( 'deleted', 'eq', 'False' ), ])
self.assertEqual( self.history_mgr.list( self.trans, filters=filters ), [ history2, history3 ])
self.assertEqual( self.history_mgr.list( self.trans ), [ history1, history2, history3 ])
self.history_mgr.update( self.trans, history3, dict( deleted=True ) )
self.history_mgr.update( self.trans, history1, dict( importable=True ) )
self.history_mgr.update( self.trans, history2, dict( importable=True ) )
- filters = filter_parser.parse_filters([
+ filters = self.filter_parser.parse_filters([
( 'deleted', 'eq', 'True' ),
( 'importable', 'eq', 'True' ),
])
@@ -381,13 +495,12 @@
self.assertEqual( self.history_mgr.list( self.trans ), [ history1, history2, history3 ])
def test_fn_filter_parsing( self ):
- filter_parser = HistoryFilters( self.app )
user2 = self.user_mgr.create( self.trans, **user2_data )
history1 = self.history_mgr.create( self.trans, name='history1', user=user2 )
history2 = self.history_mgr.create( self.trans, name='history2', user=user2 )
history3 = self.history_mgr.create( self.trans, name='history3', user=user2 )
- filters = filter_parser.parse_filters([ ( 'annotation', 'has', 'no play' ), ])
+ filters = self.filter_parser.parse_filters([ ( 'annotation', 'has', 'no play' ), ])
anno_filter = filters[0]
history3.add_item_annotation( self.trans.sa_session, user2, history3, "All work and no play" )
@@ -404,20 +517,19 @@
history1.add_item_annotation( self.trans.sa_session, user2, history1, "All work and no play" )
self.trans.sa_session.flush()
- shining_examples = self.history_mgr.list( self.trans, filters=filter_parser.parse_filters([
+ shining_examples = self.history_mgr.list( self.trans, filters=self.filter_parser.parse_filters([
( 'importable', 'eq', 'True' ),
( 'annotation', 'has', 'no play' ),
]))
self.assertEqual( shining_examples, [ history3 ])
def test_fn_filter_currying( self ):
- filter_parser = HistoryFilters( self.app )
- filter_parser.fn_filter_parsers = {
+ self.filter_parser.fn_filter_parsers = {
'name_len' : { 'op': { 'lt' : lambda i, v: len( i.name ) < v }, 'val': int }
}
self.log( 'should be 2 filters now' )
- self.assertEqual( len( filter_parser.fn_filter_parsers ), 1 )
- filters = filter_parser.parse_filters([
+ self.assertEqual( len( self.filter_parser.fn_filter_parsers ), 1 )
+ filters = self.filter_parser.parse_filters([
( 'name_len', 'lt', '4' )
])
self.log( 'should have parsed out a single filter' )
@@ -436,7 +548,6 @@
"""
Test limit and offset in conjunction with both orm and fn filtering.
"""
- filter_parser = HistoryFilters( self.app )
user2 = self.user_mgr.create( self.trans, **user2_data )
history1 = self.history_mgr.create( self.trans, name='history1', user=user2 )
history2 = self.history_mgr.create( self.trans, name='history2', user=user2 )
@@ -491,7 +602,7 @@
found = self.history_mgr.list( self.trans, filters=filters, offset=1, limit=1 )
self.assertEqual( found, [ history2 ] )
- filters = filter_parser.parse_filters([ ( 'annotation', 'has', test_annotation ) ])
+ filters = self.filter_parser.parse_filters([ ( 'annotation', 'has', test_annotation ) ])
self.log( "fn filtered, no offset, no limit should work" )
found = self.history_mgr.list( self.trans, filters=filters )
self.assertEqual( found, [ history2, history3, history4 ] )
@@ -505,7 +616,7 @@
found = self.history_mgr.list( self.trans, filters=filters, offset=1, limit=1 )
self.assertEqual( found, [ history3 ] )
- filters = filter_parser.parse_filters([
+ filters = self.filter_parser.parse_filters([
( 'deleted', 'eq', 'True' ),
( 'annotation', 'has', test_annotation )
])
@@ -536,8 +647,6 @@
self.assertEqual( found, deleted_and_annotated )
-
-
# =============================================================================
if __name__ == '__main__':
# or more generally, nosetests test_resourcemanagers.py -s -v
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: jmchilton: Temporary config option to isolate tool commands in their own shell.
by commits-noreply@bitbucket.org 02 Feb '15
by commits-noreply@bitbucket.org 02 Feb '15
02 Feb '15
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/2911cf250e75/
Changeset: 2911cf250e75
User: jmchilton
Date: 2015-02-02 14:27:19+00:00
Summary: Temporary config option to isolate tool commands in their own shell.
Like done for Docker to isolate metadata commands from the environment modifications required to resolve tool dependencies. Should allow for Python 3 dependencies (originally also allowed samtools - but Nate other commit resolved that problem also).
Just meant as a config option for now - it will become the default once tested more thoroughly. For now enable it by setting enable_beta_tool_command_isolation to True in galaxy.ini.
Affected #: 6 files
diff -r 0d92ab68b8b3764e98d338c7d594b8e77aaf5b99 -r 2911cf250e75c953700e19f2e02b4f7f6f6c7f02 config/galaxy.ini.sample
--- a/config/galaxy.ini.sample
+++ b/config/galaxy.ini.sample
@@ -790,6 +790,11 @@
# Enable Galaxy to communicate directly with a sequencer
#enable_sequencer_communication = False
+# Separate tool command from rest of job script so tool dependencies
+# don't interfer with metadata generation (this will be the default
+# in a future release).
+#enable_beta_tool_command_isolation = False
+
# Enable beta workflow modules that should not yet be considered part of Galaxy's
# stable API.
diff -r 0d92ab68b8b3764e98d338c7d594b8e77aaf5b99 -r 2911cf250e75c953700e19f2e02b4f7f6f6c7f02 lib/galaxy/config.py
--- a/lib/galaxy/config.py
+++ b/lib/galaxy/config.py
@@ -193,6 +193,7 @@
# Tasked job runner.
self.use_tasked_jobs = string_as_bool( kwargs.get( 'use_tasked_jobs', False ) )
self.local_task_queue_workers = int(kwargs.get("local_task_queue_workers", 2))
+ self.commands_in_new_shell = string_as_bool( kwargs.get( 'enable_beta_tool_command_isolation', "False" ) )
# The transfer manager and deferred job queue
self.enable_beta_job_managers = string_as_bool( kwargs.get( 'enable_beta_job_managers', 'False' ) )
# These workflow modules should not be considered part of Galaxy's
diff -r 0d92ab68b8b3764e98d338c7d594b8e77aaf5b99 -r 2911cf250e75c953700e19f2e02b4f7f6f6c7f02 lib/galaxy/jobs/__init__.py
--- a/lib/galaxy/jobs/__init__.py
+++ b/lib/galaxy/jobs/__init__.py
@@ -765,6 +765,10 @@
def get_parallelism(self):
return self.tool.parallelism
+ @property
+ def commands_in_new_shell(self):
+ return self.app.config.commands_in_new_shell
+
# legacy naming
get_job_runner = get_job_runner_url
diff -r 0d92ab68b8b3764e98d338c7d594b8e77aaf5b99 -r 2911cf250e75c953700e19f2e02b4f7f6f6c7f02 lib/galaxy/jobs/command_factory.py
--- a/lib/galaxy/jobs/command_factory.py
+++ b/lib/galaxy/jobs/command_factory.py
@@ -5,6 +5,7 @@
CAPTURE_RETURN_CODE = "return_code=$?"
YIELD_CAPTURED_CODE = 'sh -c "exit $return_code"'
+DEFAULT_SHELL = "/bin/sh"
from logging import getLogger
log = getLogger( __name__ )
@@ -45,28 +46,21 @@
if not container:
__handle_dependency_resolution(commands_builder, job_wrapper, remote_command_params)
- if container:
- # Stop now and build command before handling metadata and copying
- # working directory files back. These should always happen outside
- # of docker container - no security implications when generating
- # metadata and means no need for Galaxy to be available to container
- # and not copying workdir outputs back means on can be more restrictive
- # of where container can write to in some circumstances.
-
- local_container_script = join( job_wrapper.working_directory, "container.sh" )
- fh = file( local_container_script, "w" )
- fh.write( "#!/bin/sh\n%s" % commands_builder.build() )
- fh.close()
- chmod( local_container_script, 0755 )
-
- compute_container_script = local_container_script
- if 'working_directory' in remote_command_params:
- compute_container_script = "/bin/sh %s" % join(remote_command_params['working_directory'], "container.sh")
-
- run_in_container_command = container.containerize_command(
- compute_container_script
- )
- commands_builder = CommandsBuilder( run_in_container_command )
+ if container or job_wrapper.commands_in_new_shell:
+ externalized_commands = __externalize_commands(job_wrapper, commands_builder, remote_command_params)
+ if container:
+ # Stop now and build command before handling metadata and copying
+ # working directory files back. These should always happen outside
+ # of docker container - no security implications when generating
+ # metadata and means no need for Galaxy to be available to container
+ # and not copying workdir outputs back means on can be more restrictive
+ # of where container can write to in some circumstances.
+ run_in_container_command = container.containerize_command(
+ externalized_commands
+ )
+ commands_builder = CommandsBuilder( run_in_container_command )
+ else:
+ commands_builder = CommandsBuilder( externalized_commands )
if include_work_dir_outputs:
__handle_work_dir_outputs(commands_builder, job_wrapper, runner, remote_command_params)
@@ -77,6 +71,20 @@
return commands_builder.build()
+def __externalize_commands(job_wrapper, commands_builder, remote_command_params, script_name="tool_script.sh"):
+ local_container_script = join( job_wrapper.working_directory, script_name )
+ fh = file( local_container_script, "w" )
+ fh.write( "#!%s\n%s" % (DEFAULT_SHELL, commands_builder.build()))
+ fh.close()
+ chmod( local_container_script, 0755 )
+
+ commands = local_container_script
+ if 'working_directory' in remote_command_params:
+ commands = "%s %s" % (DEFAULT_SHELL, join(remote_command_params['working_directory'], script_name))
+ log.info("Built script [%s] for tool command[%s]" % (local_container_script, commands))
+ return commands
+
+
def __handle_version_command(commands_builder, job_wrapper):
# Prepend version string
write_version_cmd = job_wrapper.write_version_cmd
diff -r 0d92ab68b8b3764e98d338c7d594b8e77aaf5b99 -r 2911cf250e75c953700e19f2e02b4f7f6f6c7f02 test/unit/jobs/test_command_factory.py
--- a/test/unit/jobs/test_command_factory.py
+++ b/test/unit/jobs/test_command_factory.py
@@ -140,6 +140,7 @@
self.configured_external_metadata_kwds = None
self.working_directory = "job1"
self.prepare_input_files_cmds = None
+ self.commands_in_new_shell = False
def get_command_line(self):
return self.command_line
diff -r 0d92ab68b8b3764e98d338c7d594b8e77aaf5b99 -r 2911cf250e75c953700e19f2e02b4f7f6f6c7f02 test/unit/jobs/test_runner_local.py
--- a/test/unit/jobs/test_runner_local.py
+++ b/test/unit/jobs/test_runner_local.py
@@ -106,6 +106,7 @@
self.tool = tool
self.state = model.Job.states.QUEUED
self.command_line = "echo HelloWorld"
+ self.commands_in_new_shell = False
self.prepare_called = False
self.write_version_cmd = None
self.dependency_shell_commands = None
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
5 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/2f208d50dfe4/
Changeset: 2f208d50dfe4
User: carlfeberhard
Date: 2015-01-31 17:12:30+00:00
Summary: Managers: move Deletable and Purgable mixins into deletable.py, move Accesible and Owned mixins to secured.py
Affected #: 7 files
diff -r c8da6ac751e7ba5c7eddd5dd3231bd914ecd5ba2 -r 2f208d50dfe43fbfba876a172a3ae3c7524102eb lib/galaxy/managers/base.py
--- a/lib/galaxy/managers/base.py
+++ b/lib/galaxy/managers/base.py
@@ -993,237 +993,3 @@
#TODO: move id decoding out
id_list = [ self.app.security.decode_id( id_ ) for id_ in id_list_string.split( sep ) ]
return id_list
-
-
-# ==== Security Mixins
-class AccessibleManagerMixin( object ):
- """
- A security interface to check if a User can read/view an item's.
-
- This can also be thought of as 'read but not modify' privileges.
- """
-
- # don't want to override by_id since consumers will also want to fetch w/o any security checks
- def is_accessible( self, trans, item, user ):
- """
- Return True if the item accessible to user.
- """
- # override in subclasses
- raise exceptions.NotImplemented( "Abstract interface Method" )
-
- def get_accessible( self, trans, id, user, **kwargs ):
- """
- Return the item with the given id if it's accessible to user,
- otherwise raise an error.
-
- :raises exceptions.ItemAccessibilityException:
- """
- item = ModelManager.by_id( self, trans, id )
- return self.error_unless_accessible( trans, item, user )
-
- def error_unless_accessible( self, trans, item, user ):
- """
- Raise an error if the item is NOT accessible to user, otherwise return the item.
-
- :raises exceptions.ItemAccessibilityException:
- """
- if self.is_accessible( trans, item, user ):
- return item
- raise exceptions.ItemAccessibilityException( "%s is not accessible by user" % ( self.model_class.__name__ ) )
-
- # TODO:?? are these even useful?
- def list_accessible( self, trans, user, **kwargs ):
- """
- Return a list of items accessible to the user, raising an error if ANY
- are inaccessible.
-
- :raises exceptions.ItemAccessibilityException:
- """
- raise exceptions.NotImplemented( "Abstract interface Method" )
- # NOTE: this will be a large, inefficient list if filters are not passed in kwargs
- # items = ModelManager.list( self, trans, **kwargs )
- # return [ self.error_unless_accessible( trans, item, user ) for item in items ]
-
- def filter_accessible( self, trans, user, **kwargs ):
- """
- Return a list of items accessible to the user.
- """
- raise exceptions.NotImplemented( "Abstract interface Method" )
- # NOTE: this will be a large, inefficient list if filters are not passed in kwargs
- # items = ModelManager.list( self, trans, **kwargs )
- # return filter( lambda item: self.is_accessible( trans, item, user ), items )
-
-
-class OwnableManagerMixin( object ):
- """
- A security interface to check if a User is an item's owner.
-
- Some resources are associated with the User that created or imported them
- and these Users can be considered the models' owner.
-
- This can also be thought of as write/edit privileges.
- """
-
- def is_owner( self, trans, item, user ):
- """
- Return True if user owns the item.
- """
- # override in subclasses
- raise exceptions.NotImplemented( "Abstract interface Method" )
-
- def get_owned( self, trans, id, user, **kwargs ):
- """
- Return the item with the given id if owned by the user,
- otherwise raise an error.
-
- :raises exceptions.ItemOwnershipException:
- """
- item = ModelManager.by_id( self, trans, id )
- return self.error_unless_owner( trans, item, user )
-
- def error_unless_owner( self, trans, item, user ):
- """
- Raise an error if the item is NOT owned by user, otherwise return the item.
-
- :raises exceptions.ItemAccessibilityException:
- """
- if self.is_owner( trans, item, user ):
- return item
- raise exceptions.ItemOwnershipException( "%s is not owned by user" % ( self.model_class.__name__ ) )
-
- def list_owned( self, trans, user, **kwargs ):
- """
- Return a list of items owned by the user, raising an error if ANY
- are not.
-
- :raises exceptions.ItemAccessibilityException:
- """
- raise exceptions.NotImplemented( "Abstract interface Method" )
- # just alias to by_user (easier/same thing)
- #return self.by_user( trans, user, **kwargs )
-
- def filter_owned( self, trans, user, **kwargs ):
- """
- Return a list of items owned by the user.
- """
- # just alias to list_owned
- return self.list_owned( trans, user, **kwargs )
-
-
-# ---- Deletable and Purgable models
-class DeletableManagerMixin( object ):
- """
- A mixin/interface for a model that is deletable (i.e. has a 'deleted' attr).
-
- Many resources in Galaxy can be marked as deleted - meaning (in most cases)
- that they are no longer needed, should not be displayed, or may be actually
- removed by an admin/script.
- """
-
- def delete( self, trans, item, flush=True, **kwargs ):
- """
- Mark as deleted and return.
- """
- trans.sa_session.add( item )
- item.deleted = True
- if flush:
- trans.sa_session.flush()
- return item
-
- def undelete( self, trans, item, flush=True, **kwargs ):
- """
- Mark as not deleted and return.
- """
- trans.sa_session.add( item )
- item.deleted = False
- if flush:
- trans.sa_session.flush()
- return item
-
-
-class DeletableSerializerMixin( object ):
-
- def add_serializers( self ):
- pass
-
-
-# TODO: these are of questionable value if we don't want to enable users to delete/purge via update
-class DeletableDeserializerMixin( object ):
-
- def add_deserializers( self ):
- self.deserializers[ 'deleted' ] = self.deserialize_deleted
-
- def deserialize_deleted( self, trans, item, key, val ):
- """
- Delete or undelete `item` based on `val` then return `item.deleted`.
- """
- new_deleted = self.validate.bool( key, val )
- if new_deleted == item.deleted:
- return item.deleted
- # TODO:?? flush=False?
- if new_deleted:
- self.manager.delete( trans, item, flush=False )
- else:
- self.manager.undelete( trans, item, flush=False )
- return item.deleted
-
-
-class DeletableFiltersMixin( object ):
-
- def _add_parsers( self ):
- self.orm_filter_parsers.update({
- 'deleted' : { 'op': ( 'eq' ), 'val': self.parse_bool }
- })
-
-
-class PurgableManagerMixin( DeletableManagerMixin ):
- """
- A manager interface/mixin for a resource that allows deleting and purging where
- purging is often removal of some additional, non-db resource (e.g. a dataset's
- file).
- """
-
- def purge( self, trans, item, flush=True, **kwargs ):
- """
- Mark as purged and return.
-
- Override this in subclasses to do the additional resource removal.
- """
- trans.sa_session.add( item )
- item.purged = True
- if flush:
- trans.sa_session.flush()
- return item
-
-
-class PurgableSerializerMixin( DeletableSerializerMixin ):
-
- def add_serializers( self ):
- DeletableSerializerMixin.add_serializers( self )
-
-
-class PurgableDeserializerMixin( DeletableDeserializerMixin ):
-
- def add_deserializers( self ):
- DeletableDeserializerMixin.add_deserializers( self )
- self.deserializers[ 'purged' ] = self.deserialize_purged
-
- def deserialize_purged( self, trans, item, key, val ):
- """
- If `val` is True, purge `item` and return `item.purged`.
- """
- new_purged = self.validate.bool( key, val )
- if new_purged == item.purged:
- return item.purged
- if new_purged:
- self.manager.purge( trans, item, flush=False )
- return self.purged
-
-
-class PurgableFiltersMixin( DeletableFiltersMixin ):
-
- def _add_parsers( self ):
- DeletableFiltersMixin._add_parsers( self )
- self.orm_filter_parsers.update({
- 'purged' : { 'op': ( 'eq' ), 'val': self.parse_bool }
- })
diff -r c8da6ac751e7ba5c7eddd5dd3231bd914ecd5ba2 -r 2f208d50dfe43fbfba876a172a3ae3c7524102eb lib/galaxy/managers/datasets.py
--- a/lib/galaxy/managers/datasets.py
+++ b/lib/galaxy/managers/datasets.py
@@ -5,13 +5,15 @@
from galaxy import exceptions
from galaxy.managers import base
+from galaxy.managers import secured
+from galaxy.managers import deletable
from galaxy.managers import users
import logging
log = logging.getLogger( __name__ )
-class DatasetManager( base.ModelManager, base.AccessibleManagerMixin, base.PurgableManagerMixin ):
+class DatasetManager( base.ModelManager, secured.AccessibleManagerMixin, deletable.PurgableManagerMixin ):
"""
Manipulate datasets: the components contained in DatasetAssociations/DatasetInstances/HDAs/LDDAs
"""
@@ -185,7 +187,7 @@
# .... data, object_store
-class DatasetSerializer( base.ModelSerializer, base.PurgableSerializerMixin ):
+class DatasetSerializer( base.ModelSerializer, deletable.PurgableSerializerMixin ):
def __init__( self, app ):
super( DatasetSerializer, self ).__init__( app )
@@ -213,7 +215,7 @@
def add_serializers( self ):
super( DatasetSerializer, self ).add_serializers()
- base.PurgableSerializerMixin.add_serializers( self )
+ deletable.PurgableSerializerMixin.add_serializers( self )
self.serializers.update({
'id' : self.serialize_id,
'create_time' : self.serialize_date,
@@ -222,16 +224,15 @@
})
-class DatasetDeserializer( base.ModelDeserializer, base.PurgableDeserializerMixin ):
+class DatasetDeserializer( base.ModelDeserializer, deletable.PurgableDeserializerMixin ):
model_manager_class = DatasetManager
def add_deserializers( self ):
super( DatasetDeserializer, self ).add_deserializers()
- base.PurgableDeserializerMixin.add_deserializers( self )
+ deletable.PurgableDeserializerMixin.add_deserializers( self )
-
-class DatasetAssociationManager( base.ModelManager, base.AccessibleManagerMixin, base.PurgableManagerMixin ):
+class DatasetAssociationManager( base.ModelManager, secured.AccessibleManagerMixin, deletable.PurgableManagerMixin ):
"""
DatasetAssociation/DatasetInstances are intended to be working
proxies to a Dataset, associated with either a library or a
@@ -266,15 +267,15 @@
# pass
-class DatasetAssociationSerializer( base.ModelSerializer, base.PurgableSerializerMixin ):
+class DatasetAssociationSerializer( base.ModelSerializer, deletable.PurgableSerializerMixin ):
def add_serializers( self ):
super( DatasetAssociationSerializer, self ).add_serializers()
- base.PurgableSerializerMixin.add_serializers( self )
+ deletable.PurgableSerializerMixin.add_serializers( self )
-class DatasetAssociationDeserializer( base.ModelDeserializer, base.PurgableDeserializerMixin ):
+class DatasetAssociationDeserializer( base.ModelDeserializer, deletable.PurgableDeserializerMixin ):
def add_deserializers( self ):
super( DatasetAssociationDeserializer, self ).add_deserializers()
- base.PurgableDeserializerMixin.add_deserializers( self )
+ deletable.PurgableDeserializerMixin.add_deserializers( self )
diff -r c8da6ac751e7ba5c7eddd5dd3231bd914ecd5ba2 -r 2f208d50dfe43fbfba876a172a3ae3c7524102eb lib/galaxy/managers/deletable.py
--- /dev/null
+++ b/lib/galaxy/managers/deletable.py
@@ -0,0 +1,128 @@
+"""
+Many models in Galaxy are not meant to be removed from the database but only
+marked as deleted. These models have the boolean attribute 'deleted'.
+
+Other models are deletable and also may be purged. Most often these are
+models have some backing/supporting resources that can be removed as well
+(e.g. Datasets have data files on a drive). Purging these models removes
+the supporting resources as well. These models also have the boolean
+attribute 'purged'.
+"""
+
+# ---- Deletable and Purgable models
+class DeletableManagerMixin( object ):
+ """
+ A mixin/interface for a model that is deletable (i.e. has a 'deleted' attr).
+
+ Many resources in Galaxy can be marked as deleted - meaning (in most cases)
+ that they are no longer needed, should not be displayed, or may be actually
+ removed by an admin/script.
+ """
+
+ def delete( self, trans, item, flush=True, **kwargs ):
+ """
+ Mark as deleted and return.
+ """
+ trans.sa_session.add( item )
+ item.deleted = True
+ if flush:
+ trans.sa_session.flush()
+ return item
+
+ def undelete( self, trans, item, flush=True, **kwargs ):
+ """
+ Mark as not deleted and return.
+ """
+ trans.sa_session.add( item )
+ item.deleted = False
+ if flush:
+ trans.sa_session.flush()
+ return item
+
+
+class DeletableSerializerMixin( object ):
+
+ def add_serializers( self ):
+ pass
+
+
+# TODO: these are of questionable value if we don't want to enable users to delete/purge via update
+class DeletableDeserializerMixin( object ):
+
+ def add_deserializers( self ):
+ self.deserializers[ 'deleted' ] = self.deserialize_deleted
+
+ def deserialize_deleted( self, trans, item, key, val ):
+ """
+ Delete or undelete `item` based on `val` then return `item.deleted`.
+ """
+ new_deleted = self.validate.bool( key, val )
+ if new_deleted == item.deleted:
+ return item.deleted
+ # TODO:?? flush=False?
+ if new_deleted:
+ self.manager.delete( trans, item, flush=False )
+ else:
+ self.manager.undelete( trans, item, flush=False )
+ return item.deleted
+
+
+class DeletableFiltersMixin( object ):
+
+ def _add_parsers( self ):
+ self.orm_filter_parsers.update({
+ 'deleted' : { 'op': ( 'eq' ), 'val': self.parse_bool }
+ })
+
+
+class PurgableManagerMixin( DeletableManagerMixin ):
+ """
+ A manager interface/mixin for a resource that allows deleting and purging where
+ purging is often removal of some additional, non-db resource (e.g. a dataset's
+ file).
+ """
+
+ def purge( self, trans, item, flush=True, **kwargs ):
+ """
+ Mark as purged and return.
+
+ Override this in subclasses to do the additional resource removal.
+ """
+ trans.sa_session.add( item )
+ item.purged = True
+ if flush:
+ trans.sa_session.flush()
+ return item
+
+
+class PurgableSerializerMixin( DeletableSerializerMixin ):
+
+ def add_serializers( self ):
+ DeletableSerializerMixin.add_serializers( self )
+
+
+class PurgableDeserializerMixin( DeletableDeserializerMixin ):
+
+ def add_deserializers( self ):
+ DeletableDeserializerMixin.add_deserializers( self )
+ self.deserializers[ 'purged' ] = self.deserialize_purged
+
+ def deserialize_purged( self, trans, item, key, val ):
+ """
+ If `val` is True, purge `item` and return `item.purged`.
+ """
+ new_purged = self.validate.bool( key, val )
+ if new_purged == item.purged:
+ return item.purged
+ if new_purged:
+ self.manager.purge( trans, item, flush=False )
+ return self.purged
+
+
+class PurgableFiltersMixin( DeletableFiltersMixin ):
+
+ def _add_parsers( self ):
+ DeletableFiltersMixin._add_parsers( self )
+ self.orm_filter_parsers.update({
+ 'purged' : { 'op': ( 'eq' ), 'val': self.parse_bool }
+ })
diff -r c8da6ac751e7ba5c7eddd5dd3231bd914ecd5ba2 -r 2f208d50dfe43fbfba876a172a3ae3c7524102eb lib/galaxy/managers/hdas.py
--- a/lib/galaxy/managers/hdas.py
+++ b/lib/galaxy/managers/hdas.py
@@ -16,7 +16,7 @@
from galaxy import objectstore
from galaxy.managers import datasets
-from galaxy.managers import base
+from galaxy.managers import secured
from galaxy.managers import taggable
from galaxy.managers import annotatable
from galaxy.managers import users
@@ -25,7 +25,7 @@
log = logging.getLogger( __name__ )
-class HDAManager( datasets.DatasetAssociationManager, base.OwnableManagerMixin,
+class HDAManager( datasets.DatasetAssociationManager, secured.OwnableManagerMixin,
taggable.TaggableManagerMixin, annotatable.AnnotatableManagerMixin ):
"""
Interface/service object for interacting with HDAs.
diff -r c8da6ac751e7ba5c7eddd5dd3231bd914ecd5ba2 -r 2f208d50dfe43fbfba876a172a3ae3c7524102eb lib/galaxy/managers/histories.py
--- a/lib/galaxy/managers/histories.py
+++ b/lib/galaxy/managers/histories.py
@@ -8,15 +8,16 @@
import galaxy.web
from galaxy import model
from galaxy.managers import base
+from galaxy.managers import sharable
+from galaxy.managers import deletable
from galaxy.managers import hdas
-from galaxy.managers import sharable
from galaxy.managers.collections_util import dictify_dataset_collection_instance
import logging
log = logging.getLogger( __name__ )
-class HistoryManager( sharable.SharableModelManager, base.PurgableManagerMixin ):
+class HistoryManager( sharable.SharableModelManager, deletable.PurgableManagerMixin ):
model_class = model.History
foreign_key_name = 'history'
@@ -234,7 +235,7 @@
return state
-class HistorySerializer( sharable.SharableModelSerializer, base.PurgableSerializerMixin ):
+class HistorySerializer( sharable.SharableModelSerializer, deletable.PurgableSerializerMixin ):
"""
Interface/service object for serializing histories into dictionaries.
"""
@@ -288,7 +289,7 @@
#assumes: outgoing to json.dumps and sanitized
def add_serializers( self ):
super( HistorySerializer, self ).add_serializers()
- base.PurgableSerializerMixin.add_serializers( self )
+ deletable.PurgableSerializerMixin.add_serializers( self )
self.serializers.update({
'model_class' : lambda *a: 'History',
@@ -333,7 +334,7 @@
security=self.app.security, parent=dataset_collection_instance.history, view="element" )
-class HistoryDeserializer( sharable.SharableModelDeserializer, base.PurgableDeserializerMixin ):
+class HistoryDeserializer( sharable.SharableModelDeserializer, deletable.PurgableDeserializerMixin ):
"""
Interface/service object for validating and deserializing dictionaries into histories.
"""
@@ -345,7 +346,7 @@
def add_deserializers( self ):
super( HistoryDeserializer, self ).add_deserializers()
- base.PurgableDeserializerMixin.add_deserializers( self )
+ deletable.PurgableDeserializerMixin.add_deserializers( self )
self.deserializers.update({
'name' : self.deserialize_basestring,
@@ -353,12 +354,12 @@
})
-class HistoryFilters( sharable.SharableModelFilters, base.PurgableFiltersMixin ):
+class HistoryFilters( sharable.SharableModelFilters, deletable.PurgableFiltersMixin ):
model_class = model.History
def _add_parsers( self ):
super( HistoryFilters, self )._add_parsers()
- base.PurgableFiltersMixin._add_parsers( self )
+ deletable.PurgableFiltersMixin._add_parsers( self )
self.orm_filter_parsers.update({
# history specific
diff -r c8da6ac751e7ba5c7eddd5dd3231bd914ecd5ba2 -r 2f208d50dfe43fbfba876a172a3ae3c7524102eb lib/galaxy/managers/secured.py
--- /dev/null
+++ b/lib/galaxy/managers/secured.py
@@ -0,0 +1,122 @@
+"""
+Accessible models can be read and copied but not modified or deleted.
+
+Owned models can be modified and deleted.
+"""
+
+from galaxy import exceptions
+from galaxy.managers import base
+
+
+class AccessibleManagerMixin( object ):
+ """
+ A security interface to check if a User can read/view an item's.
+
+ This can also be thought of as 'read but not modify' privileges.
+ """
+
+ # don't want to override by_id since consumers will also want to fetch w/o any security checks
+ def is_accessible( self, trans, item, user ):
+ """
+ Return True if the item accessible to user.
+ """
+ # override in subclasses
+ raise exceptions.NotImplemented( "Abstract interface Method" )
+
+ def get_accessible( self, trans, id, user, **kwargs ):
+ """
+ Return the item with the given id if it's accessible to user,
+ otherwise raise an error.
+
+ :raises exceptions.ItemAccessibilityException:
+ """
+ item = self.by_id( trans, id )
+ return self.error_unless_accessible( trans, item, user )
+
+ def error_unless_accessible( self, trans, item, user ):
+ """
+ Raise an error if the item is NOT accessible to user, otherwise return the item.
+
+ :raises exceptions.ItemAccessibilityException:
+ """
+ if self.is_accessible( trans, item, user ):
+ return item
+ raise exceptions.ItemAccessibilityException( "%s is not accessible by user" % ( self.model_class.__name__ ) )
+
+ # TODO:?? are these even useful?
+ def list_accessible( self, trans, user, **kwargs ):
+ """
+ Return a list of items accessible to the user, raising an error if ANY
+ are inaccessible.
+
+ :raises exceptions.ItemAccessibilityException:
+ """
+ raise exceptions.NotImplemented( "Abstract interface Method" )
+ # NOTE: this will be a large, inefficient list if filters are not passed in kwargs
+ # items = ModelManager.list( self, trans, **kwargs )
+ # return [ self.error_unless_accessible( trans, item, user ) for item in items ]
+
+ def filter_accessible( self, trans, user, **kwargs ):
+ """
+ Return a list of items accessible to the user.
+ """
+ raise exceptions.NotImplemented( "Abstract interface Method" )
+ # NOTE: this will be a large, inefficient list if filters are not passed in kwargs
+ # items = ModelManager.list( self, trans, **kwargs )
+ # return filter( lambda item: self.is_accessible( trans, item, user ), items )
+
+
+class OwnableManagerMixin( object ):
+ """
+ A security interface to check if a User is an item's owner.
+
+ Some resources are associated with the User that created or imported them
+ and these Users can be considered the models' owner.
+
+ This can also be thought of as write/edit privileges.
+ """
+
+ def is_owner( self, trans, item, user ):
+ """
+ Return True if user owns the item.
+ """
+ # override in subclasses
+ raise exceptions.NotImplemented( "Abstract interface Method" )
+
+ def get_owned( self, trans, id, user, **kwargs ):
+ """
+ Return the item with the given id if owned by the user,
+ otherwise raise an error.
+
+ :raises exceptions.ItemOwnershipException:
+ """
+ item = self.by_id( trans, id )
+ return self.error_unless_owner( trans, item, user )
+
+ def error_unless_owner( self, trans, item, user ):
+ """
+ Raise an error if the item is NOT owned by user, otherwise return the item.
+
+ :raises exceptions.ItemAccessibilityException:
+ """
+ if self.is_owner( trans, item, user ):
+ return item
+ raise exceptions.ItemOwnershipException( "%s is not owned by user" % ( self.model_class.__name__ ) )
+
+ def list_owned( self, trans, user, **kwargs ):
+ """
+ Return a list of items owned by the user, raising an error if ANY
+ are not.
+
+ :raises exceptions.ItemAccessibilityException:
+ """
+ raise exceptions.NotImplemented( "Abstract interface Method" )
+ # just alias to by_user (easier/same thing)
+ #return self.by_user( trans, user, **kwargs )
+
+ def filter_owned( self, trans, user, **kwargs ):
+ """
+ Return a list of items owned by the user.
+ """
+ # just alias to list_owned
+ return self.list_owned( trans, user, **kwargs )
diff -r c8da6ac751e7ba5c7eddd5dd3231bd914ecd5ba2 -r 2f208d50dfe43fbfba876a172a3ae3c7524102eb lib/galaxy/managers/sharable.py
--- a/lib/galaxy/managers/sharable.py
+++ b/lib/galaxy/managers/sharable.py
@@ -15,6 +15,7 @@
from galaxy import exceptions
from galaxy.managers import base
+from galaxy.managers import secured
from galaxy.managers import taggable
from galaxy.managers import annotatable
from galaxy.managers import ratable
@@ -24,7 +25,7 @@
log = logging.getLogger( __name__ )
-class SharableModelManager( base.ModelManager, base.OwnableManagerMixin, base.AccessibleManagerMixin,
+class SharableModelManager( base.ModelManager, secured.OwnableManagerMixin, secured.AccessibleManagerMixin,
taggable.TaggableManagerMixin, annotatable.AnnotatableManagerMixin, ratable.RatableManagerMixin ):
# e.g. histories, pages, stored workflows, visualizations
# base.DeleteableModelMixin? (all four are deletable)
https://bitbucket.org/galaxy/galaxy-central/commits/f6fb30a976ab/
Changeset: f6fb30a976ab
User: carlfeberhard
Date: 2015-02-01 13:40:09+00:00
Summary: Managers: work towards a removing trans from managers
Affected #: 7 files
diff -r 2f208d50dfe43fbfba876a172a3ae3c7524102eb -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c lib/galaxy/managers/annotatable.py
--- a/lib/galaxy/managers/annotatable.py
+++ b/lib/galaxy/managers/annotatable.py
@@ -42,8 +42,8 @@
"""
Get and serialize an `item`'s annotation.
"""
- # TODO: have to assume trans.user here...
# user = item.user
+ #TODO: trans
user = trans.user
sa_session = self.app.model.context
returned = item.get_item_annotation_str( sa_session, user, item )
@@ -63,7 +63,7 @@
val = self.validate.nullable_basestring( key, val )
sa_session = self.app.model.context
- # TODO: have to assume trans.user here...
+ #TODO: trans
user = trans.user
if val is None:
item.delete_item_annotation( sa_session, user, item )
diff -r 2f208d50dfe43fbfba876a172a3ae3c7524102eb -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c lib/galaxy/managers/base.py
--- a/lib/galaxy/managers/base.py
+++ b/lib/galaxy/managers/base.py
@@ -159,6 +159,20 @@
def __init__( self, app ):
self.app = app
+ def session( self ):
+ return self.app.model.context
+
+ def _session_setattr( self, item, attr, val, fn=None, flush=True ):
+ if fn:
+ fn( item, attr, val )
+ else:
+ setattr( item, attr, val )
+
+ self.session().add( item )
+ if flush:
+ self.session().flush()
+ return item
+
# .... query foundation wrapper
def query( self, trans, eagerloads=True, filters=None, order_by=None, limit=None, offset=None, **kwargs ):
"""
@@ -166,7 +180,7 @@
Set eagerloads to False to disable them for this query.
"""
- query = trans.sa_session.query( self.model_class )
+ query = self.session().query( self.model_class )
# joined table loading
if eagerloads is False:
query = query.enable_eagerloads( False )
@@ -371,7 +385,7 @@
# TODO: this does not order by the original 'ids' array
# ...could use get (supposedly since found are in the session, the db won't be hit twice)
- # return map( trans.sa_session.query( self.model_class ).get, ids )
+ # return map( self.session().query( self.model_class ).get, ids )
# ...could implement own version here - slow?
return self._order_items_by_id( ids, found )
@@ -407,9 +421,9 @@
"""
# override in subclasses
item = self.model_class( *args, **kwargs )
- trans.sa_session.add( item )
+ self.session().add( item )
if flush:
- trans.sa_session.flush()
+ self.session().flush()
return item
def copy( self, trans, item, **kwargs ):
@@ -424,12 +438,12 @@
..note: NO validation or deserialization occurs here.
"""
- trans.sa_session.add( item )
+ self.session().add( item )
for key, value in new_values.items():
if hasattr( item, key ):
setattr( item, key, value )
if flush:
- trans.sa_session.flush()
+ self.session().flush()
return item
# TODO: yagni?
@@ -447,7 +461,7 @@
"""
foreign_key_name = foreign_key_name or self.foreign_key_name
foreign_key = getattr( associated_model_class, foreign_key_name )
- return trans.sa_session.query( associated_model_class ).filter( foreign_key == item )
+ return self.session().query( associated_model_class ).filter( foreign_key == item )
# a rename of sql DELETE to differentiate from the Galaxy notion of mark_as_deleted
# def destroy( self, trans, item, **kwargs ):
diff -r 2f208d50dfe43fbfba876a172a3ae3c7524102eb -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c lib/galaxy/managers/deletable.py
--- a/lib/galaxy/managers/deletable.py
+++ b/lib/galaxy/managers/deletable.py
@@ -23,21 +23,13 @@
"""
Mark as deleted and return.
"""
- trans.sa_session.add( item )
- item.deleted = True
- if flush:
- trans.sa_session.flush()
- return item
+ return self._session_setattr( item, 'deleted', True, flush=flush )
def undelete( self, trans, item, flush=True, **kwargs ):
"""
Mark as not deleted and return.
"""
- trans.sa_session.add( item )
- item.deleted = False
- if flush:
- trans.sa_session.flush()
- return item
+ return self._session_setattr( item, 'deleted', False, flush=flush )
class DeletableSerializerMixin( object ):
@@ -88,11 +80,7 @@
Override this in subclasses to do the additional resource removal.
"""
- trans.sa_session.add( item )
- item.purged = True
- if flush:
- trans.sa_session.flush()
- return item
+ return self._session_setattr( item, 'purged', True, flush=flush )
class PurgableSerializerMixin( DeletableSerializerMixin ):
diff -r 2f208d50dfe43fbfba876a172a3ae3c7524102eb -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c lib/galaxy/managers/histories.py
--- a/lib/galaxy/managers/histories.py
+++ b/lib/galaxy/managers/histories.py
@@ -49,6 +49,7 @@
"""
# handle default and/or anonymous user (which still may not have a history yet)
if self.user_manager.is_anonymous( user ):
+ #TODO: trans
current_history = trans.get_history()
return [ current_history ] if current_history else []
@@ -59,6 +60,7 @@
True if the current user is the owner of the given history.
"""
# anon users are only allowed to view their current history
+ #TODO: trans
if self.user_manager.is_anonymous( user ) and history == trans.get_history():
return True
return super( HistoryManager, self ).is_owner( trans, history, user )
@@ -68,11 +70,12 @@
"""
Return the most recently update history for the user.
"""
- #TODO: normalize this return value
+ #TODO: trans
if not user:
return None if trans.history.deleted else trans.history
desc_update_time = self.model_class.table.c.update_time
filters = self._munge_filters( filters, self.model_class.user_id == user.id )
+ #TODO: normalize this return value
return self.query( trans, filters=filters, order_by=desc_update_time, limit=1, **kwargs ).first()
# .... purgable
@@ -95,12 +98,14 @@
"""
Return the current history.
"""
+ #TODO: trans
return trans.get_history()
def set_current( self, trans, history ):
"""
Set the current history.
"""
+ #TODO: trans
trans.set_history( history )
return history
diff -r 2f208d50dfe43fbfba876a172a3ae3c7524102eb -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c lib/galaxy/managers/sharable.py
--- a/lib/galaxy/managers/sharable.py
+++ b/lib/galaxy/managers/sharable.py
@@ -88,12 +88,8 @@
Does not flush/commit changes, however. Item must have name, user,
importable, and slug attributes.
"""
- self.app.model.context.add( item )
- item.importable = True
self.create_unique_slug( trans, item, flush=False )
- if flush:
- self.app.model.context.flush()
- return item
+ return self._session_setattr( item, 'importable', True, flush=flush )
def make_non_importable( self, trans, item, flush=True ):
"""
@@ -102,26 +98,9 @@
importable, and slug attributes.
"""
# item must be unpublished if non-importable
- self.app.model.context.add( item )
if item.published:
self.unpublish( trans, item, flush=False )
- item.importable = False
- if flush:
- self.app.model.context.flush()
- return item
-
- #def _query_importable( self, trans, filters=None, **kwargs ):
- # """
- # """
- # importable_filter = self.model_class.importable == True
- # filters = self._munge_filters( importable_filter, filters )
- # return self.list( trans, filters=filters, **kwargs )
- #
- #def list_importable( self, trans, **kwargs ):
- # """
- # """
- # query = self._query_importable( trans, user, **kwargs )
- # return self.list( trans, query=query, **kwargs )
+ return self._session_setattr( item, 'importable', False, flush=flush )
# .... published
def publish( self, trans, item, flush=True ):
@@ -131,21 +110,13 @@
# item must be importable to be published
if not item.importable:
self.make_importable( trans, item, flush=False )
- self.app.model.context.add( item )
- item.published = True
- if flush:
- self.app.model.context.flush()
- return item
+ return self._session_setattr( item, 'published', True, flush=flush )
def unpublish( self, trans, item, flush=True ):
"""
Set the published flag on `item` to False.
"""
- self.app.model.context.add( item )
- item.published = False
- if flush:
- self.app.model.context.flush()
- return item
+ return self._session_setattr( item, 'published', False, flush=flush )
#def _query_published( self, trans, filters=None, **kwargs ):
# """
diff -r 2f208d50dfe43fbfba876a172a3ae3c7524102eb -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c lib/galaxy/managers/taggable.py
--- a/lib/galaxy/managers/taggable.py
+++ b/lib/galaxy/managers/taggable.py
@@ -53,13 +53,13 @@
"""
new_tags_list = self.validate.basestring_list( key, val )
#TODO: have to assume trans.user here...
+ #TODO: trans
user = trans.user
#TODO: duped from tags manager - de-dupe when moved to taggable mixin
- tag_handler = trans.app.tag_handler
+ tag_handler = self.app.tag_handler
tag_handler.delete_item_tags( user, item )
new_tags_str = ','.join( new_tags_list )
tag_handler.apply_item_tags( user, item, unicode( new_tags_str.encode( 'utf-8' ), 'utf-8' ) )
- #trans.sa_session.flush()
#TODO:!! does the creation of new_tags_list mean there are now more and more unused tag rows in the db?
return item.tags
diff -r 2f208d50dfe43fbfba876a172a3ae3c7524102eb -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c lib/galaxy/managers/users.py
--- a/lib/galaxy/managers/users.py
+++ b/lib/galaxy/managers/users.py
@@ -58,7 +58,7 @@
# can throw an sqlalx.IntegrityError if username not unique
self.app.security_agent.create_private_user_role( user )
-#TODO: any other route to webapp?
+ #TODO: trans
if trans.webapp.name == 'galaxy':
# We set default user permissions, before we log in and set the default history permissions
permissions = self.app.config.new_user_dataset_access_role_default_private
https://bitbucket.org/galaxy/galaxy-central/commits/e7560410c0f2/
Changeset: e7560410c0f2
User: carlfeberhard
Date: 2015-02-01 13:46:21+00:00
Summary: Branch merge
Affected #: 56 files
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef client/galaxy/scripts/galaxy.workflow_editor.canvas.js
--- a/client/galaxy/scripts/galaxy.workflow_editor.canvas.js
+++ b/client/galaxy/scripts/galaxy.workflow_editor.canvas.js
@@ -1870,6 +1870,9 @@
left: x,
top: y
});
+ self.cv.css( { "background-position-x": x,
+ "background-position-y": y
+ });
self.update_viewport_overlay();
};
// Dragging within canvas background
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef client/galaxy/scripts/mvc/history/history-panel-edit.js
--- a/client/galaxy/scripts/mvc/history/history-panel-edit.js
+++ b/client/galaxy/scripts/mvc/history/history-panel-edit.js
@@ -75,13 +75,17 @@
/** Override to handle history as drag-drop target */
_setUpListeners : function(){
- _super.prototype._setUpListeners.call( this );
+ var panel = this;
+ _super.prototype._setUpListeners.call( panel );
- this.on( 'drop', function( ev, data ){
- this.dataDropped( data );
+ panel.on( 'drop', function( ev, data ){
+ panel.dataDropped( data );
// remove the drop target
- this.dropTargetOff();
+ panel.dropTargetOff();
});
+ panel.on( 'view:attached view:removed', function(){
+ panel._renderCounts();
+ }, panel );
},
// ------------------------------------------------------------------------ listeners
@@ -332,7 +336,10 @@
// override to control where the view is added, how/whether it's rendered
panel.views.unshift( view );
panel.$list().prepend( view.render( 0 ).$el.hide() );
- view.$el.slideDown( panel.fxSpeed );
+ panel.trigger( 'view:attached', view );
+ view.$el.slideDown( panel.fxSpeed, function(){
+ panel.trigger( 'view:attached:rendered' );
+ });
},
/** In this override, add purgeAllowed and whether tags/annotation editors should be shown */
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef client/galaxy/scripts/mvc/list/list-panel.js
--- a/client/galaxy/scripts/mvc/list/list-panel.js
+++ b/client/galaxy/scripts/mvc/list/list-panel.js
@@ -477,7 +477,10 @@
// override to control where the view is added, how/whether it's rendered
panel.views.push( view );
panel.$list().append( view.render( 0 ).$el.hide() );
- view.$el.slideDown( panel.fxSpeed );
+ panel.trigger( 'view:attached', view );
+ view.$el.slideDown( panel.fxSpeed, function(){
+ panel.trigger( 'view:attached:rendered' );
+ });
},
/** Remove a view from the panel (if found) */
@@ -487,6 +490,7 @@
view = panel.viewFromModel( model );
if( !view ){ return undefined; }
panel.views = _.without( panel.views, view );
+ panel.trigger( 'view:removed', view );
// potentially show the empty message if no views left
// use anonymous queue here - since remove can happen multiple times
@@ -494,6 +498,7 @@
function( next ){ view.$el.fadeOut( panel.fxSpeed, next ); },
function( next ){
view.remove();
+ panel.trigger( 'view:removed:rendered' );
if( !panel.views.length ){
panel._renderEmptyMessage().fadeIn( panel.fxSpeed, next );
} else {
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef client/galaxy/scripts/mvc/upload/upload-row.js
--- a/client/galaxy/scripts/mvc/upload/upload-row.js
+++ b/client/galaxy/scripts/mvc/upload/upload-row.js
@@ -69,7 +69,7 @@
css: 'genome',
onchange : function(genome) {
self.model.set('genome', genome);
- self.app.updateGenome(genome);
+ self.app.updateGenome(genome, true);
},
data: self.app.list_genomes,
container: it.find('#genome'),
@@ -87,7 +87,7 @@
css: 'extension',
onchange : function(extension) {
self.model.set('extension', extension);
- self.app.updateExtension(extension);
+ self.app.updateExtension(extension, true);
},
data: self.app.list_extensions,
container: it.find('#extension'),
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef client/galaxy/scripts/mvc/upload/upload-view.js
--- a/client/galaxy/scripts/mvc/upload/upload-view.js
+++ b/client/galaxy/scripts/mvc/upload/upload-view.js
@@ -26,6 +26,10 @@
nginx_upload_path : ''
},
+ // default for selection fields
+ default_extension : 'auto',
+ default_genome : '?',
+
// own modal
modal : null,
@@ -130,16 +134,13 @@
// sort
self.list_extensions.sort(function(a, b) {
- return a.id > b.id ? 1 : a.id < b.id ? -1 : 0;
+ return a.text > b.text ? 1 : a.text < b.text ? -1 : 0;
});
// add auto field
if (!self.options.datatypes_disable_auto) {
self.list_extensions.unshift(self.auto);
}
-
- // set default extension
- self.default_extension = self.list_extensions[0] && self.list_extensions[0].id;
}
});
@@ -156,11 +157,10 @@
// sort
self.list_genomes.sort(function(a, b) {
- return a.id > b.id ? 1 : a.id < b.id ? -1 : 0;
+ if (a.id == self.default_genome) { return -1; }
+ if (b.id == self.default_genome) { return 1; }
+ return a.text > b.text ? 1 : a.text < b.text ? -1 : 0;
});
-
- // set default genome
- self.default_genome = self.list_genomes[0] && self.list_genomes[0].id;
}
});
@@ -585,20 +585,20 @@
},
// update extension for all models
- updateExtension: function(extension) {
+ updateExtension: function(extension, defaults_only) {
var self = this;
this.collection.each(function(item) {
- if (item.get('status') == 'init' && item.get('extension') == self.default_extension) {
+ if (item.get('status') == 'init' && (item.get('extension') == self.default_extension || !defaults_only)) {
item.set('extension', extension);
}
});
},
// update genome for all models
- updateGenome: function(genome) {
+ updateGenome: function(genome, defaults_only) {
var self = this;
this.collection.each(function(item) {
- if (item.get('status') == 'init' && item.get('genome') == self.default_genome) {
+ if (item.get('status') == 'init' && (item.get('genome') == self.default_genome || !defaults_only)) {
item.set('genome', genome);
}
});
@@ -724,10 +724,10 @@
'</table>' +
'</div>' +
'<div id="upload-header" class="upload-header">' +
- '<span class="header-title">Type (default):</span>' +
+ '<span class="header-title">Type (set all):</span>' +
'<span id="header-extension"/>' +
'<span id="header-extension-info" class="upload-icon-button fa fa-search"/> ' +
- '<span class="header-title">Genome (default):</span>' +
+ '<span class="header-title">Genome (set all):</span>' +
'<span id="header-genome"/>' +
'</div>';
}
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef config/reports_wsgi.ini.sample
--- a/config/reports_wsgi.ini.sample
+++ b/config/reports_wsgi.ini.sample
@@ -22,9 +22,9 @@
# database_connection = postgres:///galaxy_test?user=postgres&password=postgres
# Where dataset files are saved
-file_path = database/files
+#file_path = database/files
# Temporary storage for additional datasets, this should be shared through the cluster
-new_file_path = database/tmp
+#new_file_path = database/tmp
# Mako templates are compiled as needed and cached for reuse, this directory is
# used for the cache
@@ -64,10 +64,10 @@
use_new_layout = true
# Serving static files (needed if running standalone)
-static_enabled = True
-static_cache_time = 360
-static_dir = %(here)s/static/
-static_images_dir = %(here)s/static/images
-static_favicon_dir = %(here)s/static/favicon.ico
-static_scripts_dir = %(here)s/static/scripts/
-static_style_dir = %(here)s/static/june_2007_style/blue
+# static_enabled = True
+# static_cache_time = 360
+# static_dir = %(here)s/static/
+# static_images_dir = %(here)s/static/images
+# static_favicon_dir = %(here)s/static/favicon.ico
+# static_scripts_dir = %(here)s/static/scripts/
+# static_style_dir = %(here)s/static/june_2007_style/blue
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef config/tool_shed.ini.sample
--- a/config/tool_shed.ini.sample
+++ b/config/tool_shed.ini.sample
@@ -125,3 +125,6 @@
#static_favicon_dir = static/favicon.ico
#static_scripts_dir = static/scripts/
#static_style_dir = static/style/blue
+
+# Sentry (getsentry.com) DSN for catching bugs.
+#sentry_dsn = None
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef cron/parse_builds.py
--- a/cron/parse_builds.py
+++ b/cron/parse_builds.py
@@ -8,12 +8,7 @@
import sys
import urllib
-if sys.version_info[:2] >= ( 2, 5 ):
- import xml.etree.ElementTree as ElementTree
-else:
- from galaxy import eggs
- import pkg_resources; pkg_resources.require( "elementtree" )
- from elementtree import ElementTree
+import xml.etree.ElementTree as ElementTree
URL = "http://genome.cse.ucsc.edu/cgi-bin/das/dsn"
@@ -38,7 +33,7 @@
for dsn in tree:
build = dsn.find("SOURCE").attrib['id']
description = dsn.find("DESCRIPTION").text.replace(" - Genome at UCSC","").replace(" Genome at UCSC","")
-
+
fields = description.split(" ")
temp = fields[0]
for i in range(len(fields)-1):
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef cron/parse_builds_3_sites.py
--- a/cron/parse_builds_3_sites.py
+++ b/cron/parse_builds_3_sites.py
@@ -5,12 +5,7 @@
import sys
import urllib
-if sys.version_info[:2] >= ( 2, 5 ):
- import xml.etree.ElementTree as ElementTree
-else:
- from galaxy import eggs
- import pkg_resources; pkg_resources.require( "elementtree" )
- from elementtree import ElementTree
+import xml.etree.ElementTree as ElementTree
sites = ['http://genome.ucsc.edu/cgi-bin/',
'http://archaea.ucsc.edu/cgi-bin/',
@@ -38,7 +33,7 @@
print "#Invalid xml passed back from " + site
continue
print "#Harvested from",site
-
+
for dsn in tree:
build = dsn.find("SOURCE").attrib['id']
builds.append(build)
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef eggs.ini
--- a/eggs.ini
+++ b/eggs.ini
@@ -38,7 +38,6 @@
decorator = 3.1.2
docutils = 0.7
drmaa = 0.7.6
-elementtree = 1.2.6_20050316
Fabric = 1.7.0
importlib = 1.0.3
kombu = 3.0.13
@@ -74,7 +73,7 @@
wsgiref = 0.1.2
Babel = 1.3
wchartype = 0.1
-Whoosh = 2.5.7
+Whoosh = 2.4.1
; fluent_logger = 0.3.3
raven = 3.1.8
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/datatypes/dataproviders/hierarchy.py
--- a/lib/galaxy/datatypes/dataproviders/hierarchy.py
+++ b/lib/galaxy/datatypes/dataproviders/hierarchy.py
@@ -3,7 +3,7 @@
"""
import line
-import xml.etree.ElementTree as elementtree
+from xml.etree.ElementTree import Element, iterparse
_TODO = """
"""
@@ -30,7 +30,7 @@
"""
Data provider that converts selected XML elements to dictionaries.
"""
- # using elementtree's iterparse method to keep mem down
+ # using xml.etree's iterparse method to keep mem down
#TODO: this, however (AFAIK), prevents the use of xpath
settings = {
'selector' : 'str', #urlencoded
@@ -54,7 +54,7 @@
"""
Returns true if the ``element`` matches the ``selector``.
- :param element: an XML ``ElementTree.Element``
+ :param element: an XML ``Element``
:param selector: some partial string in the desired tags to return
Change point for more sophisticated selectors.
@@ -64,13 +64,13 @@
#TODO: fails with '#' - browser thinks it's an anchor - use urlencode
#TODO: need removal/replacement of etree namespacing here - then move to string match
return bool( ( selector == None )
- or ( isinstance( element, elementtree.Element ) and selector in element.tag ) )
+ or ( isinstance( element, Element ) and selector in element.tag ) )
def element_as_dict( self, element ):
"""
Converts an XML element (its text, tag, and attributes) to dictionary form.
- :param element: an XML ``ElementTree.Element``
+ :param element: an XML ``Element``
"""
#TODO: Key collision is unlikely here, but still should be better handled
return {
@@ -84,7 +84,7 @@
"""
Yield all children of element (and their children - recursively)
in dictionary form.
- :param element: an XML ``ElementTree.Element``
+ :param element: an XML ``Element``
:param max_depth: the number of generations of descendents to return
"""
if not isinstance( max_depth, int ) or max_depth >= 1:
@@ -99,7 +99,7 @@
yield child_data
def __iter__( self ):
- context = elementtree.iterparse( self.source, events=self.ITERPARSE_ALL_EVENTS )
+ context = iterparse( self.source, events=self.ITERPARSE_ALL_EVENTS )
context = iter( context )
selected_element = None
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/datatypes/registry.py
--- a/lib/galaxy/datatypes/registry.py
+++ b/lib/galaxy/datatypes/registry.py
@@ -346,7 +346,7 @@
"""
sniffer_elem_classes = [ e.attrib[ 'type' ] for e in self.sniffer_elems ]
sniffers = root.find( 'sniffers' )
- if sniffers:
+ if sniffers is not None:
for elem in sniffers.findall( 'sniffer' ):
# Keep a status of the process steps to enable stopping the process of handling the sniffer if necessary.
ok = True
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/sample_tracking/external_service_types.py
--- a/lib/galaxy/sample_tracking/external_service_types.py
+++ b/lib/galaxy/sample_tracking/external_service_types.py
@@ -4,7 +4,6 @@
from galaxy import util, jobs, model
from galaxy.forms.forms import form_factory
from galaxy.external_services.service import ExternalServiceActionsGroup
-from elementtree.ElementTree import XML
from galaxy.sample_tracking.data_transfer import data_transfer_factories
log = logging.getLogger( __name__ )
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/tools/__init__.py
--- a/lib/galaxy/tools/__init__.py
+++ b/lib/galaxy/tools/__init__.py
@@ -18,12 +18,11 @@
eggs.require( "MarkupSafe" ) # MarkupSafe must load before mako
eggs.require( "Mako" )
-eggs.require( "elementtree" )
eggs.require( "Paste" )
eggs.require( "SQLAlchemy >= 0.4" )
from cgi import FieldStorage
-from elementtree import ElementTree
+from xml.etree import ElementTree
from mako.template import Template
from paste import httpexceptions
@@ -2280,7 +2279,7 @@
trans.response.status = 500
log.error('Failed to get job information.')
return { 'error': 'Failed to get job information.' }
-
+
# load job parameters into incoming
tool_message = ''
if job:
@@ -2295,7 +2294,7 @@
# create parameter object
params = galaxy.util.Params( kwd, sanitize = False )
-
+
# convert value to jsonifiable value
def jsonify(v):
# check if value is numeric
@@ -2344,16 +2343,16 @@
# update and return
dict[key] = value
-
+
# check the current state of a value and update it if necessary
def check_state(trans, input, default_value, context):
value = default_value
error = 'State validation failed.'
-
+
# skip dynamic fields if deactivated
if not is_dynamic and input.is_dynamic:
return [value, None]
-
+
# validate value content
try:
# resolves the inconsistent definition of boolean parameters (see base.py) without modifying shared code
@@ -2365,7 +2364,7 @@
log.error('Checking parameter failed. %s', str(err))
pass
return [value, error]
-
+
# populates state with incoming url parameters
def populate_state(trans, inputs, state, errors, incoming, prefix="", context=None ):
context = ExpressionContext(state, context)
@@ -2410,7 +2409,7 @@
if error:
errors[key] = error
state[input.name] = value
-
+
# builds tool model including all attributes
def iterate(group_inputs, inputs, state_inputs, other_values=None):
other_values = ExpressionContext( state_inputs, other_values )
@@ -2442,13 +2441,13 @@
tool_dict = input.to_dict(trans, other_values=other_values)
except Exception:
pass
-
+
# identify name
input_name = tool_dict.get('name')
if input_name:
# backup default value
tool_dict['default_value'] = input.get_initial_value(trans, other_values)
-
+
# update input value from tool state
if input_name in state_inputs:
tool_dict['value'] = state_inputs[input_name]
@@ -2481,7 +2480,7 @@
# do param translation here, used by datasource tools
if self.input_translator:
self.input_translator.translate( params )
-
+
# initialize and populate tool state
state_inputs = {}
state_errors = {}
@@ -2490,7 +2489,7 @@
# create basic tool model
tool_model = self.to_dict(trans)
tool_model['inputs'] = {}
-
+
# build tool model and tool state
iterate(tool_model['inputs'], self.inputs, state_inputs, '')
@@ -2504,18 +2503,18 @@
tool_help = tool_help.render( static_path=url_for( '/static' ), host_url=url_for('/', qualified=True) )
if type( tool_help ) is not unicode:
tool_help = unicode( tool_help, 'utf-8')
-
+
# check if citations exist
tool_citations = False
if self.citations:
tool_citations = True
-
+
# get tool versions
tool_versions = []
tools = self.app.toolbox.get_loaded_tools_by_lineage(self.id)
for t in tools:
tool_versions.append(t.version)
-
+
## add information with underlying requirements and their versions
tool_requirements = []
if self.requirements:
@@ -2577,13 +2576,13 @@
except Exception, error:
trans.response.status = 500
return { 'error': str (error) }
-
+
# can't rerun upload, external data sources, et cetera. workflow compatible will proxy this for now
#if not self.is_workflow_compatible:
# trans.response.status = 500
# return { 'error': 'The \'%s\' tool does currently not support re-running.' % self.name }
return message
-
+
def get_default_history_by_trans( self, trans, create=False ):
return trans.get_history( create=create )
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/tools/loader.py
--- a/lib/galaxy/tools/loader.py
+++ b/lib/galaxy/tools/loader.py
@@ -120,6 +120,7 @@
# HACK for elementtree, newer implementations (etree/lxml) won't
# require this parent_map data structure but elementtree does not
# track parents or recongnize .find('..').
+ # TODO fix this now that we're not using elementtree
parent_map = dict((c, p) for p in element.getiterator() for c in p)
_xml_replace(expand_el, macro_def, parent_map)
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/tools/parameters/basic.py
--- a/lib/galaxy/tools/parameters/basic.py
+++ b/lib/galaxy/tools/parameters/basic.py
@@ -8,7 +8,7 @@
import os
import os.path
import urllib
-from elementtree.ElementTree import XML, Element
+from xml.etree.ElementTree import XML
from galaxy import config, datatypes, util
from galaxy.web import form_builder
from galaxy.util.bunch import Bunch
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/tools/parameters/input_translation.py
--- a/lib/galaxy/tools/parameters/input_translation.py
+++ b/lib/galaxy/tools/parameters/input_translation.py
@@ -13,7 +13,7 @@
This is used for data source tools
>>> from galaxy.util import Params
- >>> from elementtree.ElementTree import XML
+ >>> from xml.etree.ElementTree import XML
>>> translator = ToolInputTranslator.from_element( XML(
... '''
... <request_param_translation>
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/tools/parameters/sanitize.py
--- a/lib/galaxy/tools/parameters/sanitize.py
+++ b/lib/galaxy/tools/parameters/sanitize.py
@@ -12,7 +12,7 @@
"""
Handles tool parameter specific sanitizing.
- >>> from elementtree.ElementTree import XML
+ >>> from xml.etree.ElementTree import XML
>>> sanitizer = ToolParameterSanitizer.from_element( XML(
... '''
... <sanitizer invalid_char="">
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/tools/parameters/validation.py
--- a/lib/galaxy/tools/parameters/validation.py
+++ b/lib/galaxy/tools/parameters/validation.py
@@ -3,7 +3,7 @@
"""
import os, re, logging
-from elementtree.ElementTree import XML
+from xml.etree.ElementTree import XML
from galaxy import model
log = logging.getLogger( __name__ )
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/util/__init__.py
--- a/lib/galaxy/util/__init__.py
+++ b/lib/galaxy/util/__init__.py
@@ -40,8 +40,7 @@
import docutils.core
import docutils.writers.html4css1
-eggs.require( 'elementtree' )
-from elementtree import ElementTree, ElementInclude
+from xml.etree import ElementTree, ElementInclude
eggs.require( "wchartype" )
import wchartype
@@ -372,7 +371,7 @@
if second_diff < 86400:
return str(second_diff / 3600) + " hours ago"
if day_diff == 1:
- return "Yesterday"
+ return "yesterday"
if day_diff < 7:
return str( day_diff ) + " days ago"
if day_diff < 31:
@@ -382,16 +381,16 @@
return str( day_diff / 365 ) + " years ago"
else:
if day_diff == 0:
- return "Today"
+ return "today"
if day_diff == 1:
- return "Yesterday"
+ return "yesterday"
if day_diff < 7:
- return "this week"
+ return "less than a week"
if day_diff < 31:
- return "this month"
+ return "less than a month"
if day_diff < 365:
- return "this year"
- return str( day_diff / 365 ) + " years ago"
+ return "less than a year"
+ return "a few years ago"
def pretty_print_json(json_data, is_json_string=False):
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/web/base/controller.py
--- a/lib/galaxy/web/base/controller.py
+++ b/lib/galaxy/web/base/controller.py
@@ -40,6 +40,7 @@
from galaxy.managers import tags
from galaxy.managers import workflows
from galaxy.managers import base as managers_base
+from galaxy.managers import users
from galaxy.datatypes.metadata import FileParameter
from galaxy.tools.parameters import visit_input_values
from galaxy.tools.parameters.basic import DataToolParameter
@@ -69,6 +70,7 @@
"""Initialize an interface for application 'app'"""
self.app = app
self.sa_session = app.model.context
+ self.user_manager = users.UserManager( app )
def get_toolbox(self):
"""Returns the application toolbox"""
@@ -103,9 +105,11 @@
# ---- parsing query params
def decode_id( self, id ):
try:
- return self.app.security.decode_id( id )
- except:
- msg = "Malformed History id ( %s ) specified, unable to decode" % ( str( id ) )
+ # note: use str - occasionally a fully numeric id will be placed in post body and parsed as int via JSON
+ # resulting in error for valid id
+ return self.app.security.decode_id( str( id ) )
+ except ( ValueError, TypeError ):
+ msg = "Malformed id ( %s ) specified, unable to decode" % ( str( id ) )
raise exceptions.MalformedId( msg, id=str( id ) )
def encode_all_ids( self, trans, rval, recursive=False ):
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/web/framework/webapp.py
--- a/lib/galaxy/web/framework/webapp.py
+++ b/lib/galaxy/web/framework/webapp.py
@@ -814,3 +814,26 @@
template = Template( source=template_string,
searchList=[context or kwargs, dict(caller=self)] )
return str(template)
+
+
+def build_url_map( app, global_conf, local_conf ):
+ from paste.urlmap import URLMap
+ from galaxy.web.framework.middleware.static import CacheableStaticURLParser as Static
+ urlmap = URLMap()
+ # Merge the global and local configurations
+ conf = global_conf.copy()
+ conf.update(local_conf)
+ # Get cache time in seconds
+ cache_time = conf.get( "static_cache_time", None )
+ if cache_time is not None:
+ cache_time = int( cache_time )
+ # Send to dynamic app by default
+ urlmap["/"] = app
+ # Define static mappings from config
+ urlmap["/static"] = Static( conf.get( "static_dir", "./static/" ), cache_time )
+ urlmap["/images"] = Static( conf.get( "static_images_dir", "./static/images" ), cache_time )
+ urlmap["/static/scripts"] = Static( conf.get( "static_scripts_dir", "./static/scripts/" ), cache_time )
+ urlmap["/static/style"] = Static( conf.get( "static_style_dir", "./static/style/blue" ), cache_time )
+ urlmap["/favicon.ico"] = Static( conf.get( "static_favicon_dir", "./static/favicon.ico" ), cache_time )
+ urlmap["/robots.txt"] = Static( conf.get( "static_robots_txt", "./static/robots.txt" ), cache_time )
+ return urlmap, cache_time
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/webapps/galaxy/api/forms.py
--- a/lib/galaxy/webapps/galaxy/api/forms.py
+++ b/lib/galaxy/webapps/galaxy/api/forms.py
@@ -5,7 +5,7 @@
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
+from xml.etree.ElementTree import XML
log = logging.getLogger( __name__ )
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/webapps/galaxy/api/provenance.py
--- a/lib/galaxy/webapps/galaxy/api/provenance.py
+++ b/lib/galaxy/webapps/galaxy/api/provenance.py
@@ -52,15 +52,21 @@
if item.copied_from_library_dataset_dataset_association:
item = item.copied_from_library_dataset_dataset_association
job = item.creating_job
- return {
- "id": trans.security.encode_id(item.id),
- "uuid": ( lambda uuid: str( uuid ) if uuid else None )( item.dataset.uuid),
- "job_id": trans.security.encode_id( job.id ),
- "tool_id": job.tool_id,
- "parameters": self._get_job_record(trans, job, follow),
- "stderr": job.stderr,
- "stdout": job.stdout,
- }
+ if job is not None:
+ return {
+ "id": trans.security.encode_id(item.id),
+ "uuid": ( lambda uuid: str( uuid ) if uuid else None )( item.dataset.uuid),
+ "job_id": trans.security.encode_id( job.id ),
+ "tool_id": job.tool_id,
+ "parameters": self._get_job_record(trans, job, follow),
+ "stderr": job.stderr,
+ "stdout": job.stdout,
+ }
+ else:
+ return {
+ "id": trans.security.encode_id(item.id),
+ "uuid": ( lambda uuid: str( uuid ) if uuid else None )( item.dataset.uuid)
+ }
return None
def _get_job_record(self, trans, job, follow):
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/webapps/galaxy/api/request_types.py
--- a/lib/galaxy/webapps/galaxy/api/request_types.py
+++ b/lib/galaxy/webapps/galaxy/api/request_types.py
@@ -5,7 +5,7 @@
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
+from xml.etree.ElementTree import XML
log = logging.getLogger( __name__ )
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/webapps/galaxy/buildapp.py
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -610,26 +610,8 @@
return app
def wrap_in_static( app, global_conf, plugin_frameworks=None, **local_conf ):
- from paste.urlmap import URLMap
from galaxy.web.framework.middleware.static import CacheableStaticURLParser as Static
- urlmap = URLMap()
- # Merge the global and local configurations
- conf = global_conf.copy()
- conf.update(local_conf)
- # Get cache time in seconds
- cache_time = conf.get( "static_cache_time", None )
- if cache_time is not None:
- cache_time = int( cache_time )
- # Send to dynamic app by default
- urlmap["/"] = app
- # Define static mappings from config
- urlmap["/static"] = Static( conf.get( "static_dir", "./static/" ), cache_time )
- urlmap["/images"] = Static( conf.get( "static_images_dir", "./static/images" ), cache_time )
- urlmap["/static/scripts"] = Static( conf.get( "static_scripts_dir", "./static/scripts/" ), cache_time )
- urlmap["/static/style"] = Static( conf.get( "static_style_dir", "./static/style/blue" ), cache_time )
- urlmap["/favicon.ico"] = Static( conf.get( "static_favicon_dir", "./static/favicon.ico" ), cache_time )
- urlmap["/robots.txt"] = Static( conf.get( "static_robots_txt", "./static/robots.txt" ), cache_time )
-
+ urlmap, cache_time = galaxy.web.framework.webapp.build_url_map( app, global_conf, local_conf )
# wrap any static dirs for plugins
plugin_frameworks = plugin_frameworks or []
for framework in plugin_frameworks:
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/webapps/galaxy/controllers/forms.py
--- a/lib/galaxy/webapps/galaxy/controllers/forms.py
+++ b/lib/galaxy/webapps/galaxy/controllers/forms.py
@@ -5,7 +5,7 @@
import logging, os, sys
from galaxy.web.form_builder import *
from galaxy.tools.parameters.basic import parameter_types
-from elementtree.ElementTree import XML, Element
+from xml.etree.ElementTree import XML, Element
from galaxy.util.odict import odict
import copy
from galaxy.web.framework.helpers import time_ago, iff, grids
@@ -65,7 +65,7 @@
global_actions = [
grids.GridAction( "Create new form", dict( controller='forms', action='create_form_definition' ) )
]
-
+
def build_initial_query( self, trans, **kwargs ):
return trans.sa_session.query( self.model_class ).join (model.FormDefinition, self.model_class.latest_form_id == model.FormDefinition.id)
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/webapps/galaxy/controllers/history.py
--- a/lib/galaxy/webapps/galaxy/controllers/history.py
+++ b/lib/galaxy/webapps/galaxy/controllers/history.py
@@ -761,7 +761,9 @@
histories=histories,
email=email,
send_to_err=send_to_err )
- histories, send_to_users, send_to_err = self._get_histories_and_users( trans, user, id, email )
+
+ histories = self._get_histories( trans, id )
+ send_to_users, send_to_err = self._get_users( trans, user, email )
if not send_to_users:
if not send_to_err:
send_to_err += "%s is not a valid Galaxy user. %s" % ( email, err_msg )
@@ -769,14 +771,21 @@
histories=histories,
email=email,
send_to_err=send_to_err )
+
if params.get( 'share_button', False ):
+
# The user has not yet made a choice about how to share, so dictionaries will be built for display
can_change, cannot_change, no_change_needed, unique_no_change_needed, send_to_err = \
self._populate_restricted( trans, user, histories, send_to_users, None, send_to_err, unique=True )
+
send_to_err += err_msg
if cannot_change and not no_change_needed and not can_change:
send_to_err = "The histories you are sharing do not contain any datasets that can be accessed by the users with which you are sharing."
- return trans.fill_template( "/history/share.mako", histories=histories, email=email, send_to_err=send_to_err )
+ return trans.fill_template( "/history/share.mako",
+ histories=histories,
+ email=email,
+ send_to_err=send_to_err )
+
if can_change or cannot_change:
return trans.fill_template( "/history/share.mako",
histories=histories,
@@ -785,12 +794,18 @@
can_change=can_change,
cannot_change=cannot_change,
no_change_needed=unique_no_change_needed )
+
if no_change_needed:
return self._share_histories( trans, user, send_to_err, histories=no_change_needed )
+
elif not send_to_err:
# User seems to be sharing an empty history
send_to_err = "You cannot share an empty history. "
- return trans.fill_template( "/history/share.mako", histories=histories, email=email, send_to_err=send_to_err )
+
+ return trans.fill_template( "/history/share.mako",
+ histories=histories,
+ email=email,
+ send_to_err=send_to_err )
@web.expose
@web.require_login( "share restricted histories with other users" )
@@ -807,7 +822,8 @@
share_button=True ) )
user = trans.get_user()
user_roles = user.all_roles()
- histories, send_to_users, send_to_err = self._get_histories_and_users( trans, user, id, email )
+ histories = self._get_histories( trans, id )
+ send_to_users, send_to_err = self._get_users( trans, user, email )
send_to_err = ''
# The user has made a choice, so dictionaries will be built for sharing
can_change, cannot_change, no_change_needed, unique_no_change_needed, send_to_err = \
@@ -856,31 +872,49 @@
histories_for_sharing[ send_to_user ].append( history )
return self._share_histories( trans, user, send_to_err, histories=histories_for_sharing )
- def _get_histories_and_users( self, trans, user, id, email ):
- if not id:
+ def _get_histories( self, trans, ids ):
+ if not ids:
# Default to the current history
- id = trans.security.encode_id( trans.history.id )
- id = galaxy.util.listify( id )
+ ids = trans.security.encode_id( trans.history.id )
+ ids = galaxy.util.listify( ids )
+ histories = []
+ for history_id in ids:
+ histories.append( self.history_manager.get_owned( trans, self.decode_id( history_id ), trans.user ) )
+ return histories
+
+ def _get_users( self, trans, user, emails_or_ids ):
+ send_to_users = []
send_to_err = ""
- histories = []
- for history_id in id:
- histories.append( self.history_manager.get_owned( trans, self.decode_id( history_id ), trans.user ) )
- send_to_users = []
- for email_address in galaxy.util.listify( email ):
- email_address = email_address.strip()
- if email_address:
- if email_address == user.email:
- send_to_err += "You cannot send histories to yourself. "
- else:
- send_to_user = trans.sa_session.query( trans.app.model.User ) \
- .filter( and_( trans.app.model.User.table.c.email==email_address,
- trans.app.model.User.table.c.deleted==False ) ) \
- .first()
- if send_to_user:
- send_to_users.append( send_to_user )
- else:
- send_to_err += "%s is not a valid Galaxy user. " % email_address
- return histories, send_to_users, send_to_err
+ for string in galaxy.util.listify( emails_or_ids ):
+ string = string.strip()
+ if not string:
+ continue
+
+ send_to_user = None
+ if '@' in string:
+ email_address = string
+ send_to_user = self.user_manager.by_email( trans, email_address,
+ filters=[ trans.app.model.User.table.c.deleted == False ] )
+
+ else:
+ user_id = string
+ try:
+ decoded_user_id = self.decode_id( string )
+ send_to_user = self.user_manager.by_id( trans, decoded_user_id )
+ if send_to_user.deleted:
+ send_to_user = None
+ #TODO: in an ideal world, we would let this bubble up to web.expose which would handle it
+ except exceptions.MalformedId:
+ send_to_user = None
+
+ if not send_to_user:
+ send_to_err += "%s is not a valid Galaxy user. " % string
+ elif send_to_user == user:
+ send_to_err += "You cannot send histories to yourself. "
+ else:
+ send_to_users.append( send_to_user )
+
+ return send_to_users, send_to_err
def _populate( self, trans, histories_for_sharing, other, send_to_err ):
# This method will populate the histories_for_sharing dictionary with the users and
@@ -1466,7 +1500,6 @@
trans.set_history( history )
return self.history_serializer.serialize_to_view( trans, history, view='detailed' )
except exceptions.MessageException, msg_exc:
- print type( msg_exc )
trans.response.status = msg_exc.err_code.code
return { 'err_msg': msg_exc.err_msg, 'err_code': msg_exc.err_code.code }
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/webapps/reports/buildapp.py
--- a/lib/galaxy/webapps/reports/buildapp.py
+++ b/lib/galaxy/webapps/reports/buildapp.py
@@ -16,6 +16,7 @@
import galaxy.model
import galaxy.model.mapping
import galaxy.web.framework.webapp
+from galaxy.util.properties import load_app_properties
log = logging.getLogger( __name__ )
@@ -46,6 +47,9 @@
def app_factory( global_conf, **kwargs ):
"""Return a wsgi application serving the root object"""
# Create the Galaxy application unless passed in
+ kwargs = load_app_properties(
+ kwds=kwargs
+ )
if 'app' in kwargs:
app = kwargs.pop( 'app' )
else:
@@ -62,7 +66,7 @@
# Wrap the webapp in some useful middleware
if kwargs.get( 'middleware', True ):
webapp = wrap_in_middleware( webapp, global_conf, **kwargs )
- if kwargs.get( 'static_enabled', True ):
+ if asbool( kwargs.get( 'static_enabled', True ) ):
webapp = wrap_in_static( webapp, global_conf, **kwargs )
# Close any pooled database connections before forking
try:
@@ -135,25 +139,7 @@
return app
def wrap_in_static( app, global_conf, **local_conf ):
- from paste.urlmap import URLMap
- from galaxy.web.framework.middleware.static import CacheableStaticURLParser as Static
- urlmap = URLMap()
- # Merge the global and local configurations
- conf = global_conf.copy()
- conf.update(local_conf)
- # Get cache time in seconds
- cache_time = conf.get( "static_cache_time", None )
- if cache_time is not None:
- cache_time = int( cache_time )
- # Send to dynamic app by default
- urlmap["/"] = app
- # Define static mappings from config
- urlmap["/static"] = Static( conf.get( "static_dir" ), cache_time )
- urlmap["/images"] = Static( conf.get( "static_images_dir" ), cache_time )
- urlmap["/static/scripts"] = Static( conf.get( "static_scripts_dir" ), cache_time )
- urlmap["/static/style"] = Static( conf.get( "static_style_dir" ), cache_time )
- urlmap["/favicon.ico"] = Static( conf.get( "static_favicon_dir" ), cache_time )
- # URL mapper becomes the root webapp
+ urlmap, _ = galaxy.web.framework.webapp.build_url_map( app, global_conf, local_conf )
return urlmap
def build_template_error_formatters():
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/webapps/tool_shed/buildapp.py
--- a/lib/galaxy/webapps/tool_shed/buildapp.py
+++ b/lib/galaxy/webapps/tool_shed/buildapp.py
@@ -134,7 +134,7 @@
# Wrap the webapp in some useful middleware
if kwargs.get( 'middleware', True ):
webapp = wrap_in_middleware( webapp, global_conf, **kwargs )
- if kwargs.get( 'static_enabled', True ):
+ if asbool( kwargs.get( 'static_enabled', True ) ):
webapp = wrap_in_static( webapp, global_conf, **kwargs )
# Close any pooled database connections before forking
try:
@@ -171,6 +171,14 @@
from paste import recursive
app = recursive.RecursiveMiddleware( app, conf )
log.debug( "Enabling 'recursive' middleware" )
+ # If sentry logging is enabled, log here before propogating up to
+ # the error middleware
+ # TODO sentry config is duplicated between tool_shed/galaxy, refactor this.
+ sentry_dsn = conf.get( 'sentry_dsn', None )
+ if sentry_dsn:
+ from galaxy.web.framework.middleware.sentry import Sentry
+ log.debug( "Enabling 'sentry' middleware" )
+ app = Sentry( app, sentry_dsn )
# Various debug middleware that can only be turned on if the debug
# flag is set, either because they are insecure or greatly hurt
# performance
@@ -219,26 +227,7 @@
return app
def wrap_in_static( app, global_conf, **local_conf ):
- from paste.urlmap import URLMap
- from galaxy.web.framework.middleware.static import CacheableStaticURLParser as Static
- urlmap = URLMap()
- # Merge the global and local configurations
- conf = global_conf.copy()
- conf.update(local_conf)
- # Get cache time in seconds
- cache_time = conf.get( "static_cache_time", None )
- if cache_time is not None:
- cache_time = int( cache_time )
- # Send to dynamic app by default
- urlmap["/"] = app
- # Define static mappings from config
- urlmap["/static"] = Static( conf.get( "static_dir", "./static/" ), cache_time )
- urlmap["/images"] = Static( conf.get( "static_images_dir", "./static/images" ), cache_time )
- urlmap["/static/scripts"] = Static( conf.get( "static_scripts_dir", "./static/scripts/" ), cache_time )
- urlmap["/static/style"] = Static( conf.get( "static_style_dir", "./static/style/blue" ), cache_time )
- urlmap["/favicon.ico"] = Static( conf.get( "static_favicon_dir", "./static/favicon.ico" ), cache_time )
- urlmap["/robots.txt"] = Static( conf.get( "static_robots_txt", "./static/robots.txt" ), cache_time )
- # URL mapper becomes the root webapp
+ urlmap, _ = galaxy.web.framework.webapp.build_url_map( app, global_conf, local_conf )
return urlmap
def build_template_error_formatters():
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/webapps/tool_shed/config.py
--- a/lib/galaxy/webapps/tool_shed/config.py
+++ b/lib/galaxy/webapps/tool_shed/config.py
@@ -2,11 +2,13 @@
Universe configuration builder.
"""
import os
+import re
import sys
import logging
import logging.config
from optparse import OptionParser
import ConfigParser
+from galaxy import eggs
from galaxy.util import string_as_bool, listify
log = logging.getLogger( __name__ )
@@ -133,6 +135,19 @@
self.citation_cache_data_dir = resolve_path( kwargs.get( "citation_cache_data_dir", "database/tool_shed_citations/data" ), self.root )
self.citation_cache_lock_dir = resolve_path( kwargs.get( "citation_cache_lock_dir", "database/tool_shed_citations/locks" ), self.root )
+ @property
+ def sentry_dsn_public( self ):
+ """
+ Sentry URL with private key removed for use in client side scripts,
+ sentry server will need to be configured to accept events
+ """
+ # TODO refactor this to a common place between toolshed/galaxy config, along
+ # with other duplicated methods.
+ if self.sentry_dsn:
+ return re.sub( r"^([^:/?#]+:)?//(\w+):(\w+)", r"\1//\2", self.sentry_dsn )
+ else:
+ return None
+
def __parse_config_file_options( self, kwargs ):
defaults = dict(
datatypes_config_file = [ 'config/datatypes_conf.xml', 'datatypes_conf.xml', 'config/datatypes_conf.xml.sample' ],
@@ -274,3 +289,11 @@
# Hook everything up
handler.setFormatter( formatter )
root.addHandler( handler )
+ # If sentry is configured, also log to it
+ if config.sentry_dsn:
+ eggs.require( "raven" )
+ from raven.handlers.logging import SentryHandler
+ sentry_handler = SentryHandler( config.sentry_dsn )
+ sentry_handler.setLevel( logging.WARN )
+ root.addHandler( sentry_handler )
+
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/webapps/tool_shed/search/repo_search.py
--- a/lib/galaxy/webapps/tool_shed/search/repo_search.py
+++ b/lib/galaxy/webapps/tool_shed/search/repo_search.py
@@ -45,7 +45,7 @@
use_final = True
def final( self, searcher, docnum, score ):
- log.debug('score before: ' + str(score) )
+ # log.debug('score before: ' + str(score) )
# Arbitrary for now
reasonable_hits = 100.0
@@ -59,15 +59,15 @@
if times_downloaded == 0:
times_downloaded = 1
popularity_modifier = ( times_downloaded / reasonable_hits )
- log.debug('popularity_modifier: ' + str(popularity_modifier) )
+ # log.debug('popularity_modifier: ' + str(popularity_modifier) )
cert_modifier = 2 if searcher.stored_fields( docnum )[ "approved" ] == 'yes' else 1
- log.debug('cert_modifier: ' + str(cert_modifier) )
+ # log.debug('cert_modifier: ' + str(cert_modifier) )
# Adjust the computed score for this document by the popularity
# and by the certification level.
final_score = score * popularity_modifier * cert_modifier
- log.debug('score after: ' + str( final_score ) )
+ # log.debug('score after: ' + str( final_score ) )
return final_score
@@ -108,16 +108,14 @@
'remote_repository_url',
'repo_owner_username' ], schema = schema )
- # user_query = parser.parse( search_term )
user_query = parser.parse( '*' + search_term + '*' )
+ hits = searcher.search_page( user_query, page, pagelen = 10, terms = True )
- # hits = searcher.search( user_query, terms = True )
- hits = searcher.search_page( user_query, page, pagelen = 10, terms = True )
log.debug( 'searching for: #' + str( search_term ) )
log.debug( 'total hits: ' + str( len( hits ) ) )
log.debug( 'scored hits: ' + str( hits.scored_length() ) )
results = {}
- results[ 'total_results'] = str( hits.scored_length() )
+ results[ 'total_results'] = str( len( hits ) )
results[ 'hits' ] = []
for hit in hits:
hit_dict = {}
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/workflow/modules.py
--- a/lib/galaxy/workflow/modules.py
+++ b/lib/galaxy/workflow/modules.py
@@ -6,9 +6,8 @@
import re
from galaxy import eggs
-eggs.require( "elementtree" )
-from elementtree.ElementTree import Element
+from xml.etree.ElementTree import Element
import galaxy.tools
from galaxy import exceptions
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef scripts/loc_files/create_all_fasta_loc.py
--- a/scripts/loc_files/create_all_fasta_loc.py
+++ b/scripts/loc_files/create_all_fasta_loc.py
@@ -1,5 +1,5 @@
import optparse, os, sys
-import elementtree.ElementTree as etree
+from xml.etree.ElementTree import parse
"""
Generates a loc file containing names of all the fasta files that match the
@@ -212,7 +212,7 @@
# get column names
col_values = []
loc_path = None
- tree = etree.parse( options.data_table_xml )
+ tree = parse( options.data_table_xml )
tables = tree.getroot()
for table in tables.getiterator():
name = table.attrib.get( 'name' )
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef scripts/microbes/ncbi_to_ucsc.py
--- a/scripts/microbes/ncbi_to_ucsc.py
+++ b/scripts/microbes/ncbi_to_ucsc.py
@@ -7,7 +7,7 @@
import sys, os
import urllib
-from elementtree import ElementTree
+from xml.etree import ElementTree
from BeautifulSoup import BeautifulSoup
from shutil import move
@@ -17,7 +17,7 @@
base_dir = sys.argv[1]
except:
print "using default base_dir:", base_dir
-
+
organisms = {}
for result in os.walk(base_dir):
this_base_dir,sub_dirs,files = result
@@ -76,7 +76,7 @@
org_page.pop(0)
if org_page[-1]=="":
org_page.pop(-1)
-
+
for row in org_page:
chr = row.split("</a>")[0].split(">")[-1]
refseq = row.split("</a>")[-2].split(">")[-1]
@@ -87,20 +87,20 @@
builds[org]={'chrs':{},'build':build}
builds[org]['chrs'][refseq]=chr
#print build,org,chr,refseq
-
+
print
ext_to_edit = ['bed', 'info', ]
for org in builds:
print org,"changed to",builds[org]['build']
-
+
#org info file
info_file_old = os.path.join(base_dir+org,org+".info")
info_file_new = os.path.join(base_dir+org,builds[org]['build']+".info")
-
-
+
+
old_dir = base_dir+org
new_dir = base_dir+builds[org]['build']
-
+
#open and edit org info file
info_file_contents = open(info_file_old).read()
info_file_contents = info_file_contents+"build="+builds[org]['build']+"\n"
@@ -114,31 +114,31 @@
old_name = os.path.join(this_base_dir,file)
new_name = os.path.join(this_base_dir,builds[org]['chrs'][chrom]+file[len(chrom):])
move(old_name,new_name)
-
+
#edit contents of file, skiping those in list
if file.split(".")[-1] not in ext_to_edit: continue
-
+
file_contents = open(new_name).read()
file_contents = file_contents.replace(chrom,builds[org]['chrs'][chrom])
-
+
#special case fixes...
if file[-5:] == ".info":
file_contents = file_contents.replace("organism="+org,"organism="+builds[org]['build'])
file_contents = file_contents.replace("refseq="+builds[org]['chrs'][chrom],"refseq="+chrom)
-
+
#write out new file
file_out = open(new_name,'w')
file_out.write(file_contents)
file_out.close()
-
-
-
+
+
+
#write out org info file and remove old file
org_info_out = open(info_file_new,'w')
org_info_out.write(info_file_contents)
org_info_out.close()
os.unlink(info_file_old)
-
+
#change org directory name
move(old_dir,new_dir)
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef scripts/update_shed_config_path.py
--- /dev/null
+++ b/scripts/update_shed_config_path.py
@@ -0,0 +1,80 @@
+import os
+import argparse
+import ConfigParser
+import sys
+new_path = [ os.path.join( os.getcwd(), "lib" ) ]
+new_path.extend( sys.path[1:] )
+sys.path = new_path
+
+import logging
+import galaxy.model.tool_shed_install
+import galaxy.model.tool_shed_install.mapping as mapping
+from galaxy.model.orm import *
+from galaxy import eggs
+eggs.require('sqlalchemy')
+import sqlalchemy
+
+def main( opts, session, model ):
+ '''
+ Find all tool shed repositories with the bad path and update with the correct path.
+ '''
+ for row in session.query( model.ToolShedRepository ).all():
+ if 'shed_config_filename' in row.metadata:
+ if row.metadata['shed_config_filename'] == opts.bad_filename:
+ row.metadata['shed_config_filename'] = opts.good_filename
+ session.add( row )
+ session.flush()
+ return 0
+
+def create_database( config_file ):
+ parser = ConfigParser.SafeConfigParser()
+ parser.read( config_file )
+ # Determine which database connection to use.
+ database_connection = parser.get( 'app:main', 'install_database_connection' )
+ if database_connection is None:
+ database_connection = parser.get( 'app:main', 'database_connection' )
+ if database_connection is None:
+ database_connection = 'sqlite:///%s' % parser.get( 'app:main', 'database_file' )
+ if database_connection is None:
+ print 'Unable to determine correct database connection.'
+ exit(1)
+
+ '''Initialize the database file.'''
+ dialect_to_egg = {
+ "sqlite" : "pysqlite>=2",
+ "postgres" : "psycopg2",
+ "postgresql" : "psycopg2",
+ "mysql" : "MySQL_python"
+ }
+ dialect = ( database_connection.split( ':', 1 ) )[0]
+ try:
+ egg = dialect_to_egg[ dialect ]
+ try:
+ eggs.require( egg )
+ print( "%s egg successfully loaded for %s dialect" % ( egg, dialect ) )
+ except:
+ # If the module is in the path elsewhere (i.e. non-egg), it'll still load.
+ print( "%s egg not found, but an attempt will be made to use %s anyway" % ( egg, dialect ) )
+ except KeyError:
+ # Let this go, it could possibly work with db's we don't support.
+ print( "database_connection contains an unknown SQLAlchemy database dialect: %s" % dialect )
+
+ # Initialize the database connection.
+ engine = create_engine( database_connection )
+ meta = MetaData( bind=engine )
+ install_session = Session = scoped_session( sessionmaker( bind=engine, autoflush=False, autocommit=True ) )
+ model = mapping.init( database_connection )
+ return install_session, model
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser()
+ parser.add_argument( '--config_file', dest='config_file', required=True, help="The path to your Galaxy configuration .ini file." )
+ parser.add_argument( '--from', dest='bad_filename', required=True, help="The old, invalid path to the shed_tool_conf.xml or migrated_tools_conf.xml file." )
+ parser.add_argument( '--to', dest='good_filename', required=True, help="The updated path to the shed_tool_conf.xml or migrated_tools_conf.xml file." )
+ parser.add_argument( '--force', dest='force', action='store_true', help="Use this flag to set the new path even if the file does not (yet) exist there." )
+ opts = parser.parse_args()
+ if not os.path.exists( opts.good_filename ) and not opts.force:
+ print 'The file %s does not exist, use the --force option to proceed.' % opts.good_filename
+ exit(1)
+ session, model = create_database( opts.config_file )
+ exit( main( opts, session, model ) )
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef static/scripts/galaxy.workflow_editor.canvas.js
--- a/static/scripts/galaxy.workflow_editor.canvas.js
+++ b/static/scripts/galaxy.workflow_editor.canvas.js
@@ -1870,6 +1870,9 @@
left: x,
top: y
});
+ self.cv.css( { "background-position-x": x,
+ "background-position-y": y
+ });
self.update_viewport_overlay();
};
// Dragging within canvas background
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef static/scripts/mvc/history/history-panel-edit.js
--- a/static/scripts/mvc/history/history-panel-edit.js
+++ b/static/scripts/mvc/history/history-panel-edit.js
@@ -75,13 +75,17 @@
/** Override to handle history as drag-drop target */
_setUpListeners : function(){
- _super.prototype._setUpListeners.call( this );
+ var panel = this;
+ _super.prototype._setUpListeners.call( panel );
- this.on( 'drop', function( ev, data ){
- this.dataDropped( data );
+ panel.on( 'drop', function( ev, data ){
+ panel.dataDropped( data );
// remove the drop target
- this.dropTargetOff();
+ panel.dropTargetOff();
});
+ panel.on( 'view:attached view:removed', function(){
+ panel._renderCounts();
+ }, panel );
},
// ------------------------------------------------------------------------ listeners
@@ -332,7 +336,10 @@
// override to control where the view is added, how/whether it's rendered
panel.views.unshift( view );
panel.$list().prepend( view.render( 0 ).$el.hide() );
- view.$el.slideDown( panel.fxSpeed );
+ panel.trigger( 'view:attached', view );
+ view.$el.slideDown( panel.fxSpeed, function(){
+ panel.trigger( 'view:attached:rendered' );
+ });
},
/** In this override, add purgeAllowed and whether tags/annotation editors should be shown */
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef static/scripts/mvc/list/list-panel.js
--- a/static/scripts/mvc/list/list-panel.js
+++ b/static/scripts/mvc/list/list-panel.js
@@ -477,7 +477,10 @@
// override to control where the view is added, how/whether it's rendered
panel.views.push( view );
panel.$list().append( view.render( 0 ).$el.hide() );
- view.$el.slideDown( panel.fxSpeed );
+ panel.trigger( 'view:attached', view );
+ view.$el.slideDown( panel.fxSpeed, function(){
+ panel.trigger( 'view:attached:rendered' );
+ });
},
/** Remove a view from the panel (if found) */
@@ -487,6 +490,7 @@
view = panel.viewFromModel( model );
if( !view ){ return undefined; }
panel.views = _.without( panel.views, view );
+ panel.trigger( 'view:removed', view );
// potentially show the empty message if no views left
// use anonymous queue here - since remove can happen multiple times
@@ -494,6 +498,7 @@
function( next ){ view.$el.fadeOut( panel.fxSpeed, next ); },
function( next ){
view.remove();
+ panel.trigger( 'view:removed:rendered' );
if( !panel.views.length ){
panel._renderEmptyMessage().fadeIn( panel.fxSpeed, next );
} else {
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef static/scripts/mvc/upload/upload-row.js
--- a/static/scripts/mvc/upload/upload-row.js
+++ b/static/scripts/mvc/upload/upload-row.js
@@ -69,7 +69,7 @@
css: 'genome',
onchange : function(genome) {
self.model.set('genome', genome);
- self.app.updateGenome(genome);
+ self.app.updateGenome(genome, true);
},
data: self.app.list_genomes,
container: it.find('#genome'),
@@ -87,7 +87,7 @@
css: 'extension',
onchange : function(extension) {
self.model.set('extension', extension);
- self.app.updateExtension(extension);
+ self.app.updateExtension(extension, true);
},
data: self.app.list_extensions,
container: it.find('#extension'),
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef static/scripts/mvc/upload/upload-view.js
--- a/static/scripts/mvc/upload/upload-view.js
+++ b/static/scripts/mvc/upload/upload-view.js
@@ -26,6 +26,10 @@
nginx_upload_path : ''
},
+ // default for selection fields
+ default_extension : 'auto',
+ default_genome : '?',
+
// own modal
modal : null,
@@ -130,16 +134,13 @@
// sort
self.list_extensions.sort(function(a, b) {
- return a.id > b.id ? 1 : a.id < b.id ? -1 : 0;
+ return a.text > b.text ? 1 : a.text < b.text ? -1 : 0;
});
// add auto field
if (!self.options.datatypes_disable_auto) {
self.list_extensions.unshift(self.auto);
}
-
- // set default extension
- self.default_extension = self.list_extensions[0] && self.list_extensions[0].id;
}
});
@@ -156,11 +157,10 @@
// sort
self.list_genomes.sort(function(a, b) {
- return a.id > b.id ? 1 : a.id < b.id ? -1 : 0;
+ if (a.id == self.default_genome) { return -1; }
+ if (b.id == self.default_genome) { return 1; }
+ return a.text > b.text ? 1 : a.text < b.text ? -1 : 0;
});
-
- // set default genome
- self.default_genome = self.list_genomes[0] && self.list_genomes[0].id;
}
});
@@ -585,20 +585,20 @@
},
// update extension for all models
- updateExtension: function(extension) {
+ updateExtension: function(extension, defaults_only) {
var self = this;
this.collection.each(function(item) {
- if (item.get('status') == 'init' && item.get('extension') == self.default_extension) {
+ if (item.get('status') == 'init' && (item.get('extension') == self.default_extension || !defaults_only)) {
item.set('extension', extension);
}
});
},
// update genome for all models
- updateGenome: function(genome) {
+ updateGenome: function(genome, defaults_only) {
var self = this;
this.collection.each(function(item) {
- if (item.get('status') == 'init' && item.get('genome') == self.default_genome) {
+ if (item.get('status') == 'init' && (item.get('genome') == self.default_genome || !defaults_only)) {
item.set('genome', genome);
}
});
@@ -724,10 +724,10 @@
'</table>' +
'</div>' +
'<div id="upload-header" class="upload-header">' +
- '<span class="header-title">Type (default):</span>' +
+ '<span class="header-title">Type (set all):</span>' +
'<span id="header-extension"/>' +
'<span id="header-extension-info" class="upload-icon-button fa fa-search"/> ' +
- '<span class="header-title">Genome (default):</span>' +
+ '<span class="header-title">Genome (set all):</span>' +
'<span id="header-genome"/>' +
'</div>';
}
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef static/scripts/packed/galaxy.workflow_editor.canvas.js
--- a/static/scripts/packed/galaxy.workflow_editor.canvas.js
+++ b/static/scripts/packed/galaxy.workflow_editor.canvas.js
@@ -1,1 +1,1 @@
-function CollectionTypeDescription(a){this.collectionType=a;this.isCollection=true;this.rank=a.split(":").length}$.extend(CollectionTypeDescription.prototype,{append:function(a){if(a===NULL_COLLECTION_TYPE_DESCRIPTION){return this}if(a===ANY_COLLECTION_TYPE_DESCRIPTION){return otherCollectionType}return new CollectionTypeDescription(this.collectionType+":"+a.collectionType)},canMatch:function(a){if(a===NULL_COLLECTION_TYPE_DESCRIPTION){return false}if(a===ANY_COLLECTION_TYPE_DESCRIPTION){return true}return a.collectionType==this.collectionType},canMapOver:function(b){if(b===NULL_COLLECTION_TYPE_DESCRIPTION){return false}if(b===ANY_COLLECTION_TYPE_DESCRIPTION){return false}if(this.rank<=b.rank){return false}var a=b.collectionType;return this._endsWith(this.collectionType,a)},effectiveMapOver:function(a){var c=a.collectionType;var b=this.collectionType.substring(0,this.collectionType.length-c.length-1);return new CollectionTypeDescription(b)},equal:function(a){return a.collectionType==this.collectionType},toString:function(){return"CollectionType["+this.collectionType+"]"},_endsWith:function(b,a){return b.indexOf(a,b.length-a.length)!==-1}});NULL_COLLECTION_TYPE_DESCRIPTION={isCollection:false,canMatch:function(a){return false},canMapOver:function(a){return false},toString:function(){return"NullCollectionType[]"},append:function(a){return a},equal:function(a){return a===this}};ANY_COLLECTION_TYPE_DESCRIPTION={isCollection:true,canMatch:function(a){return NULL_COLLECTION_TYPE_DESCRIPTION!==a},canMapOver:function(a){return false},toString:function(){return"AnyCollectionType[]"},append:function(a){throw"Cannot append to ANY_COLLECTION_TYPE_DESCRIPTION"},equal:function(a){return a===this}};var TerminalMapping=Backbone.Model.extend({initialize:function(a){this.mapOver=a.mapOver||NULL_COLLECTION_TYPE_DESCRIPTION;this.terminal=a.terminal;this.terminal.terminalMapping=this},disableMapOver:function(){this.setMapOver(NULL_COLLECTION_TYPE_DESCRIPTION)},setMapOver:function(a){this.mapOver=a;this.trigger("change")}});var TerminalMappingView=Backbone.View.extend({tagName:"div",className:"fa-icon-button fa fa-folder-o",initialize:function(b){var a="Run tool in parallel over collection";this.$el.tooltip({delay:500,title:a});this.model.bind("change",_.bind(this.render,this))},render:function(){if(this.model.mapOver.isCollection){this.$el.show()}else{this.$el.hide()}},});var InputTerminalMappingView=TerminalMappingView.extend({events:{click:"onClick",mouseenter:"onMouseEnter",mouseleave:"onMouseLeave",},onMouseEnter:function(b){var a=this.model;if(!a.terminal.connected()&&a.mapOver.isCollection){this.$el.css("color","red")}},onMouseLeave:function(a){this.$el.css("color","black")},onClick:function(b){var a=this.model;if(!a.terminal.connected()&&a.mapOver.isCollection){a.terminal.resetMapping()}},});var InputTerminalMapping=TerminalMapping;var InputCollectionTerminalMapping=TerminalMapping;var OutputTerminalMapping=TerminalMapping;var OutputTerminalMappingView=TerminalMappingView;var InputCollectionTerminalMappingView=InputTerminalMappingView;var OutputCollectionTerminalMapping=TerminalMapping;var OutputCollectionTerminalMappingView=TerminalMappingView;var Terminal=Backbone.Model.extend({initialize:function(a){this.element=a.element;this.connectors=[]},connect:function(a){this.connectors.push(a);if(this.node){this.node.markChanged()}},disconnect:function(a){this.connectors.splice($.inArray(a,this.connectors),1);if(this.node){this.node.markChanged();this.resetMappingIfNeeded()}},redraw:function(){$.each(this.connectors,function(a,b){b.redraw()})},destroy:function(){$.each(this.connectors.slice(),function(a,b){b.destroy()})},destroyInvalidConnections:function(){_.each(this.connectors,function(a){a.destroyIfInvalid()})},setMapOver:function(a){if(this.multiple){return}if(!this.mapOver().equal(a)){this.terminalMapping.setMapOver(a);_.each(this.node.output_terminals,function(b){b.setMapOver(a)})}},mapOver:function(){if(!this.terminalMapping){return NULL_COLLECTION_TYPE_DESCRIPTION}else{return this.terminalMapping.mapOver}},isMappedOver:function(){return this.terminalMapping&&this.terminalMapping.mapOver.isCollection},resetMapping:function(){this.terminalMapping.disableMapOver()},resetMappingIfNeeded:function(){},});var OutputTerminal=Terminal.extend({initialize:function(a){Terminal.prototype.initialize.call(this,a);this.datatypes=a.datatypes},resetMappingIfNeeded:function(){if(!this.node.hasConnectedOutputTerminals()&&!this.node.hasConnectedMappedInputTerminals()){_.each(this.node.mappedInputTerminals(),function(b){b.resetMappingIfNeeded()})}var a=!this.node.hasMappedOverInputTerminals();if(a){this.resetMapping()}},resetMapping:function(){this.terminalMapping.disableMapOver();_.each(this.connectors,function(a){var b=a.handle2;if(b){b.resetMappingIfNeeded();a.destroyIfInvalid()}})}});var BaseInputTerminal=Terminal.extend({initialize:function(a){Terminal.prototype.initialize.call(this,a);this.update(a.input)},canAccept:function(a){if(this._inputFilled()){return false}else{return this.attachable(a)}},resetMappingIfNeeded:function(){var b=this.mapOver();if(!b.isCollection){return}var a=this.node.hasConnectedMappedInputTerminals()||(!this.node.hasConnectedOutputTerminals());if(a){this.resetMapping()}},resetMapping:function(){this.terminalMapping.disableMapOver();if(!this.node.hasMappedOverInputTerminals()){_.each(this.node.output_terminals,function(a){a.resetMapping()})}},connected:function(){return this.connectors.length!==0},_inputFilled:function(){var a;if(!this.connected()){a=false}else{if(this.multiple){if(this._collectionAttached()){inputsFilled=true}else{a=false}}else{a=true}}return a},_collectionAttached:function(){if(!this.connected()){return false}else{var a=this.connectors[0].handle1;if(!a){return false}else{if(a.isDataCollectionInput||a.isMappedOver()||a.datatypes.indexOf("input_collection")>0){return true}else{return false}}}},_mappingConstraints:function(){if(!this.node){return[]}var b=this.mapOver();if(b.isCollection){return[b]}var a=[];if(!this.node.hasConnectedOutputTerminals()){_.each(this.node.connectedMappedInputTerminals(),function(c){a.push(c.mapOver())})}else{a.push(_.first(_.values(this.node.output_terminals)).mapOver())}return a},_producesAcceptableDatatype:function(a){for(var c in this.datatypes){var f=new Array();f=f.concat(a.datatypes);if(a.node.post_job_actions){for(var d in a.node.post_job_actions){var g=a.node.post_job_actions[d];if(g.action_type=="ChangeDatatypeAction"&&(g.output_name==""||g.output_name==a.name)&&g.action_arguments){f.push(g.action_arguments.newtype)}}}for(var b in f){var h=f[b];if(h=="input"||h=="input_collection"||issubtype(f[b],this.datatypes[c])){return true}}}return false},_otherCollectionType:function(a){var c=NULL_COLLECTION_TYPE_DESCRIPTION;if(a.isDataCollectionInput){c=a.collectionType}else{var b=a.mapOver();if(b.isCollection){c=b}}return c},});var InputTerminal=BaseInputTerminal.extend({update:function(a){this.datatypes=a.extensions;this.multiple=a.multiple;this.collection=false},connect:function(a){BaseInputTerminal.prototype.connect.call(this,a);var b=a.handle1;if(!b){return}var c=this._otherCollectionType(b);if(c.isCollection){this.setMapOver(c)}},attachable:function(b){var d=this._otherCollectionType(b);var a=this.mapOver();if(d.isCollection){if(this.multiple){if(this.connected()&&!this._collectionAttached()){return false}if(d.rank==1){return this._producesAcceptableDatatype(b)}else{return false}}if(a.isCollection&&a.canMatch(d)){return this._producesAcceptableDatatype(b)}else{var c=this._mappingConstraints();if(c.every(_.bind(d.canMatch,d))){return this._producesAcceptableDatatype(b)}else{return false}}}else{if(a.isCollection){return false}}return this._producesAcceptableDatatype(b)}});var InputCollectionTerminal=BaseInputTerminal.extend({update:function(a){this.multiple=false;this.collection=true;this.datatypes=a.extensions;if(a.collection_type){this.collectionType=new CollectionTypeDescription(a.collection_type)}else{this.collectionType=ANY_COLLECTION_TYPE_DESCRIPTION}},connect:function(b){BaseInputTerminal.prototype.connect.call(this,b);var a=b.handle1;if(!a){return}var c=this._effectiveMapOver(a);this.setMapOver(c)},_effectiveMapOver:function(a){var b=this.collectionType;var c=this._otherCollectionType(a);if(!b.canMatch(c)){return c.effectiveMapOver(b)}else{return NULL_COLLECTION_TYPE_DESCRIPTION}},_effectiveCollectionType:function(){var b=this.collectionType;var a=this.mapOver();return a.append(b)},attachable:function(b){var g=this._otherCollectionType(b);if(g.isCollection){var f=this._effectiveCollectionType();var a=this.mapOver();if(f.canMatch(g)){return this._producesAcceptableDatatype(b)}else{if(a.isCollection){return false}else{if(g.canMapOver(this.collectionType)){var d=this._effectiveMapOver(b);if(!d.isCollection){return false}var c=this._mappingConstraints();if(c.every(d.canMatch)){return this._producesAcceptableDatatype(b)}}}}}return false}});var OutputCollectionTerminal=Terminal.extend({initialize:function(a){Terminal.prototype.initialize.call(this,a);this.datatypes=a.datatypes;this.collectionType=new CollectionTypeDescription(a.collection_type);this.isDataCollectionInput=true},update:function(a){var b=new CollectionTypeDescription(a.collection_type);if(b.collectionType!=this.collectionType.collectionType){_.each(this.connectors,function(c){c.destroy()})}this.collectionType=b}});function Connector(b,a){this.canvas=null;this.dragging=false;this.inner_color="#FFFFFF";this.outer_color="#D8B365";if(b&&a){this.connect(b,a)}}$.extend(Connector.prototype,{connect:function(b,a){this.handle1=b;if(this.handle1){this.handle1.connect(this)}this.handle2=a;if(this.handle2){this.handle2.connect(this)}},destroy:function(){if(this.handle1){this.handle1.disconnect(this)}if(this.handle2){this.handle2.disconnect(this)}$(this.canvas).remove()},destroyIfInvalid:function(){if(this.handle1&&this.handle2&&!this.handle2.attachable(this.handle1)){this.destroy()}},redraw:function(){var f=$("#canvas-container");if(!this.canvas){this.canvas=document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(this.canvas)}f.append($(this.canvas));if(this.dragging){this.canvas.style.zIndex="300"}}var v=function(c){return $(c).offset().left-f.offset().left};var p=function(c){return $(c).offset().top-f.offset().top};if(!this.handle1||!this.handle2){return}var o=v(this.handle1.element)+5;var n=p(this.handle1.element)+5;var x=v(this.handle2.element)+5;var u=p(this.handle2.element)+5;var k=100;var r=Math.min(o,x);var a=Math.max(o,x);var q=Math.min(n,u);var B=Math.max(n,u);var d=Math.min(Math.max(Math.abs(B-q)/2,100),300);var w=r-k;var A=q-k;var y=a-r+2*k;var s=B-q+2*k;this.canvas.style.left=w+"px";this.canvas.style.top=A+"px";this.canvas.setAttribute("width",y);this.canvas.setAttribute("height",s);o-=w;n-=A;x-=w;u-=A;var z=this.canvas.getContext("2d"),h=null,l=null;var g=1;if(this.handle1&&this.handle1.isMappedOver()){var h=[-6,-3,0,3,6];g=5}else{var h=[0]}if(this.handle2&&this.handle2.isMappedOver()){var l=[-6,-3,0,3,6];g=5}else{var l=[0]}var b=this;for(var t=0;t<g;t++){var m=5,j=7;if(h.length>1||l.length>1){m=1;j=3}b.draw_outlined_curve(o,n,x,u,d,m,j,h[t%h.length],l[t%l.length])}},draw_outlined_curve:function(j,i,l,k,a,b,f,g,d){var g=g||0;var d=d||0;var h=this.canvas.getContext("2d");h.lineCap="round";h.strokeStyle=this.outer_color;h.lineWidth=f;h.beginPath();h.moveTo(j,i+g);h.bezierCurveTo(j+a,i+g,l-a,k+d,l,k+d);h.stroke();h.strokeStyle=this.inner_color;h.lineWidth=b;h.beginPath();h.moveTo(j,i+g);h.bezierCurveTo(j+a,i+g,l-a,k+d,l,k+d);h.stroke()}});var Node=Backbone.Model.extend({initialize:function(a){this.element=a.element;this.input_terminals={};this.output_terminals={};this.tool_errors={}},connectedOutputTerminals:function(){return this._connectedTerminals(this.output_terminals)},_connectedTerminals:function(b){var a=[];$.each(b,function(c,d){if(d.connectors.length>0){a.push(d)}});return a},hasConnectedOutputTerminals:function(){var a=this.output_terminals;for(var b in a){if(a[b].connectors.length>0){return true}}return false},connectedMappedInputTerminals:function(){return this._connectedMappedTerminals(this.input_terminals)},hasConnectedMappedInputTerminals:function(){var c=this.input_terminals;for(var b in c){var a=c[b];if(a.connectors.length>0&&a.isMappedOver()){return true}}return false},_connectedMappedTerminals:function(b){var a=[];$.each(b,function(c,d){var f=d.mapOver();if(f.isCollection){if(d.connectors.length>0){a.push(d)}}});return a},mappedInputTerminals:function(){return this._mappedTerminals(this.input_terminals)},_mappedTerminals:function(b){var a=[];$.each(b,function(c,d){var f=d.mapOver();if(f.isCollection){a.push(d)}});return a},hasMappedOverInputTerminals:function(){var a=false;_.each(this.input_terminals,function(b){var c=b.mapOver();if(c.isCollection){a=true}});return a},redraw:function(){$.each(this.input_terminals,function(a,b){b.redraw()});$.each(this.output_terminals,function(a,b){b.redraw()})},destroy:function(){$.each(this.input_terminals,function(a,b){b.destroy()});$.each(this.output_terminals,function(a,b){b.destroy()});workflow.remove_node(this);$(this.element).remove()},make_active:function(){$(this.element).addClass("toolForm-active")},make_inactive:function(){var a=this.element.get(0);(function(b){b.removeChild(a);b.appendChild(a)})(a.parentNode);$(a).removeClass("toolForm-active")},init_field_data:function(b){if(b.type){this.type=b.type}this.name=b.name;this.form_html=b.form_html;this.tool_state=b.tool_state;this.tool_errors=b.tool_errors;this.tooltip=b.tooltip?b.tooltip:"";this.annotation=b.annotation;this.post_job_actions=b.post_job_actions?b.post_job_actions:{};this.label=b.label;this.uuid=b.uuid;this.workflow_outputs=b.workflow_outputs?b.workflow_outputs:[];var a=this;var c=new NodeView({el:this.element[0],node:a,});a.nodeView=c;$.each(b.data_inputs,function(f,d){c.addDataInput(d)});if((b.data_inputs.length>0)&&(b.data_outputs.length>0)){c.addRule()}$.each(b.data_outputs,function(f,d){c.addDataOutput(d)});c.render();workflow.node_changed(this,true)},update_field_data:function(d){var c=this;nodeView=c.nodeView;this.tool_state=d.tool_state;this.form_html=d.form_html;this.tool_errors=d.tool_errors;this.annotation=d.annotation;if("post_job_actions" in d){var f=$.parseJSON(d.post_job_actions);this.post_job_actions=f?f:{}}c.nodeView.renderToolErrors();var g=nodeView.$("div.inputs");var a=nodeView.newInputsDiv();var b={};_.each(d.data_inputs,function(h){var i=c.nodeView.addDataInput(h,a);b[h.name]=i});_.each(_.difference(_.values(nodeView.terminalViews),_.values(b)),function(h){h.el.terminal.destroy()});nodeView.terminalViews=b;if(d.data_outputs.length==1&&"collection_type" in d.data_outputs[0]){nodeView.updateDataOutput(d.data_outputs[0])}g.replaceWith(a);this.markChanged();this.redraw()},error:function(d){var a=$(this.element).find(".toolFormBody");a.find("div").remove();var c="<div style='color: red; text-style: italic;'>"+d+"</div>";this.form_html=c;a.html(c);workflow.node_changed(this)},markChanged:function(){workflow.node_changed(this)}});function Workflow(a){this.canvas_container=a;this.id_counter=0;this.nodes={};this.name=null;this.has_changes=false;this.active_form_has_changes=false}$.extend(Workflow.prototype,{add_node:function(a){a.id=this.id_counter;a.element.attr("id","wf-node-step-"+a.id);this.id_counter++;this.nodes[a.id]=a;this.has_changes=true;a.workflow=this},remove_node:function(a){if(this.active_node==a){this.clear_active_node()}delete this.nodes[a.id];this.has_changes=true},remove_all:function(){wf=this;$.each(this.nodes,function(b,a){a.destroy();wf.remove_node(a)})},rectify_workflow_outputs:function(){var b=false;var a=false;$.each(this.nodes,function(c,d){if(d.workflow_outputs&&d.workflow_outputs.length>0){b=true}$.each(d.post_job_actions,function(g,f){if(f.action_type==="HideDatasetAction"){a=true}})});if(b!==false||a!==false){$.each(this.nodes,function(c,g){if(g.type==="tool"){var f=false;if(g.post_job_actions==null){g.post_job_actions={};f=true}var d=[];$.each(g.post_job_actions,function(i,h){if(h.action_type=="HideDatasetAction"){d.push(i)}});if(d.length>0){$.each(d,function(h,j){f=true;delete g.post_job_actions[j]})}if(b){$.each(g.output_terminals,function(i,j){var h=true;$.each(g.workflow_outputs,function(l,m){if(j.name===m){h=false}});if(h===true){f=true;var k={action_type:"HideDatasetAction",output_name:j.name,action_arguments:{}};g.post_job_actions["HideDatasetAction"+j.name]=null;g.post_job_actions["HideDatasetAction"+j.name]=k}})}if(workflow.active_node==g&&f===true){workflow.reload_active_node()}}})}},to_simple:function(){var a={};$.each(this.nodes,function(c,f){var g={};$.each(f.input_terminals,function(i,j){g[j.name]=null;var h=[];$.each(j.connectors,function(k,l){h[k]={id:l.handle1.node.id,output_name:l.handle1.name};g[j.name]=h})});var b={};if(f.post_job_actions){$.each(f.post_job_actions,function(j,h){var k={action_type:h.action_type,output_name:h.output_name,action_arguments:h.action_arguments};b[h.action_type+h.output_name]=null;b[h.action_type+h.output_name]=k})}if(!f.workflow_outputs){f.workflow_outputs=[]}var d={id:f.id,type:f.type,tool_id:f.tool_id,tool_state:f.tool_state,tool_errors:f.tool_errors,input_connections:g,position:$(f.element).position(),annotation:f.annotation,post_job_actions:f.post_job_actions,uuid:f.uuid,label:f.label,workflow_outputs:f.workflow_outputs};a[f.id]=d});return{steps:a}},from_simple:function(b){wf=this;var c=0;wf.name=b.name;var a=false;$.each(b.steps,function(g,f){var d=prebuild_node(f.type,f.name,f.tool_id);d.init_field_data(f);if(f.position){d.element.css({top:f.position.top,left:f.position.left})}d.id=f.id;wf.nodes[d.id]=d;c=Math.max(c,parseInt(g));if(!a&&d.type==="tool"){if(d.workflow_outputs.length>0){a=true}else{$.each(d.post_job_actions,function(i,h){if(h.action_type==="HideDatasetAction"){a=true}})}}});wf.id_counter=c+1;$.each(b.steps,function(g,f){var d=wf.nodes[g];$.each(f.input_connections,function(i,h){if(h){if(!$.isArray(h)){h=[h]}$.each(h,function(k,j){var m=wf.nodes[j.id];var n=new Connector();n.connect(m.output_terminals[j.output_name],d.input_terminals[i]);n.redraw()})}});if(a&&d.type==="tool"){$.each(d.output_terminals,function(h,i){if(d.post_job_actions["HideDatasetAction"+i.name]===undefined){d.workflow_outputs.push(i.name);callout=$(d.element).find(".callout."+i.name);callout.find("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small.png");workflow.has_changes=true}})}})},check_changes_in_active_form:function(){if(this.active_form_has_changes){this.has_changes=true;$("#right-content").find("form").submit();this.active_form_has_changes=false}},reload_active_node:function(){if(this.active_node){var a=this.active_node;this.clear_active_node();this.activate_node(a)}},clear_active_node:function(){if(this.active_node){this.active_node.make_inactive();this.active_node=null}parent.show_form_for_tool("<div>No node selected</div>")},activate_node:function(a){if(this.active_node!=a){this.check_changes_in_active_form();this.clear_active_node();if(parent.__NEWTOOLFORM__){parent.show_form_for_tool(a.form_html,a)}else{parent.show_form_for_tool(a.form_html+a.tooltip,a)}a.make_active();this.active_node=a}},node_changed:function(a,b){this.has_changes=true;if(this.active_node==a&&(!parent.__NEWTOOLFORM__||b)){this.check_changes_in_active_form();if(parent.__NEWTOOLFORM__){parent.show_form_for_tool(a.form_html,a)}else{parent.show_form_for_tool(a.form_html+a.tooltip,a)}}},layout:function(){this.check_changes_in_active_form();this.has_changes=true;var i={};var b={};$.each(this.nodes,function(l,k){if(i[l]===undefined){i[l]=0}if(b[l]===undefined){b[l]=[]}});$.each(this.nodes,function(l,k){$.each(k.input_terminals,function(m,n){$.each(n.connectors,function(p,q){var o=q.handle1.node;i[k.id]+=1;b[o.id].push(k.id)})})});node_ids_by_level=[];while(true){level_parents=[];for(var a in i){if(i[a]==0){level_parents.push(a)}}if(level_parents.length==0){break}node_ids_by_level.push(level_parents);for(var f in level_parents){var j=level_parents[f];delete i[j];for(var g in b[j]){i[b[j][g]]-=1}}}if(i.length){return}var d=this.nodes;var h=80;v_pad=30;var c=h;$.each(node_ids_by_level,function(k,l){l.sort(function(p,o){return $(d[p].element).position().top-$(d[o].element).position().top});var m=0;var n=v_pad;$.each(l,function(o,r){var q=d[r];var p=$(q.element);$(p).css({top:n,left:c});m=Math.max(m,$(p).width());n+=$(p).height()+v_pad});c+=m+h});$.each(d,function(k,l){l.redraw()})},bounds_for_all_nodes:function(){var d=Infinity,b=-Infinity,c=Infinity,a=-Infinity,f;$.each(this.nodes,function(h,g){e=$(g.element);f=e.position();d=Math.min(d,f.left);b=Math.max(b,f.left+e.width());c=Math.min(c,f.top);a=Math.max(a,f.top+e.width())});return{xmin:d,xmax:b,ymin:c,ymax:a}},fit_canvas_to_nodes:function(){var a=this.bounds_for_all_nodes();var f=this.canvas_container.position();var i=this.canvas_container.parent();var d=fix_delta(a.xmin,100);var h=fix_delta(a.ymin,100);d=Math.max(d,f.left);h=Math.max(h,f.top);var c=f.left-d;var g=f.top-h;var b=round_up(a.xmax+100,100)+d;var j=round_up(a.ymax+100,100)+h;b=Math.max(b,-c+i.width());j=Math.max(j,-g+i.height());this.canvas_container.css({left:c,top:g,width:b,height:j});this.canvas_container.children().each(function(){var k=$(this).position();$(this).css("left",k.left+d);$(this).css("top",k.top+h)})}});function fix_delta(a,b){if(a<b||a>3*b){new_pos=(Math.ceil(((a%b))/b)+1)*b;return(-(a-new_pos))}return 0}function round_up(a,b){return Math.ceil(a/b)*b}function prebuild_node(l,j,r){var i=$("<div class='toolForm toolFormInCanvas'></div>");var g=new Node({element:i});g.type=l;if(l=="tool"){g.tool_id=r}var n=$("<div class='toolFormTitle unselectable'>"+j+"</div>");i.append(n);i.css("left",$(window).scrollLeft()+20);i.css("top",$(window).scrollTop()+20);var m=$("<div class='toolFormBody'></div>");var h="<div><img height='16' align='middle' src='"+galaxy_config.root+"static/images/loading_small_white_bg.gif'/> loading tool info...</div>";m.append(h);g.form_html=h;i.append(m);var k=$("<div class='buttons' style='float: right;'></div>");k.append($("<div>").addClass("fa-icon-button fa fa-times").click(function(b){g.destroy()}));i.appendTo("#canvas-container");var d=$("#canvas-container").position();var c=$("#canvas-container").parent();var a=i.width();var q=i.height();i.css({left:(-d.left)+(c.width()/2)-(a/2),top:(-d.top)+(c.height()/2)-(q/2)});k.prependTo(n);a+=(k.width()+10);i.css("width",a);$(i).bind("dragstart",function(){workflow.activate_node(g)}).bind("dragend",function(){workflow.node_changed(this);workflow.fit_canvas_to_nodes();canvas_manager.draw_overview()}).bind("dragclickonly",function(){workflow.activate_node(g)}).bind("drag",function(o,p){var f=$(this).offsetParent().offset(),b=p.offsetX-f.left,s=p.offsetY-f.top;$(this).css({left:b,top:s});$(this).find(".terminal").each(function(){this.terminal.redraw()})});return g}function add_node(b,d,a){var c=prebuild_node(b,d,a);workflow.add_node(c);workflow.fit_canvas_to_nodes();canvas_manager.draw_overview();workflow.activate_node(c);return c}var ext_to_type=null;var type_to_type=null;function issubtype(b,a){b=ext_to_type[b];a=ext_to_type[a];return(type_to_type[b])&&(a in type_to_type[b])}function populate_datatype_info(a){ext_to_type=a.ext_to_class_name;type_to_type=a.class_to_classes}var NodeView=Backbone.View.extend({initialize:function(a){this.node=a.node;this.output_width=Math.max(150,this.$el.width());this.tool_body=this.$el.find(".toolFormBody");this.tool_body.find("div").remove();this.newInputsDiv().appendTo(this.tool_body);this.terminalViews={};this.outputTerminlViews={}},render:function(){this.renderToolErrors();this.$el.css("width",Math.min(250,Math.max(this.$el.width(),this.output_width)))},renderToolErrors:function(){if(this.node.tool_errors){this.$el.addClass("tool-node-error")}else{this.$el.removeClass("tool-node-error")}},newInputsDiv:function(){return $("<div class='inputs'></div>")},updateMaxWidth:function(a){this.output_width=Math.max(this.output_width,a)},addRule:function(){this.tool_body.append($("<div class='rule'></div>"))},addDataInput:function(i,d){var j=true;if(!d){d=this.$(".inputs");j=false}var f=this.terminalViews[i.name];var h=(i.input_type=="dataset_collection")?InputCollectionTerminalView:InputTerminalView;if(f&&!(f instanceof h)){f.el.terminal.destroy();f=null}if(!f){f=new h({node:this.node,input:i})}else{var g=f.el.terminal;g.update(i);g.destroyInvalidConnections()}this.terminalViews[i.name]=f;var c=f.el;var b=new DataInputView({terminalElement:c,input:i,nodeView:this,skipResize:j});var a=b.$el;d.append(a.prepend(f.terminalElements()));return f},addDataOutput:function(a){var d=(a.collection_type)?OutputCollectionTerminalView:OutputTerminalView;var c=new d({node:this.node,output:a});var b=new DataOutputView({output:a,terminalElement:c.el,nodeView:this,});this.tool_body.append(b.$el.append(c.terminalElements()))},updateDataOutput:function(b){var a=this.node.output_terminals[b.name];a.update(b)}});var DataInputView=Backbone.View.extend({className:"form-row dataRow input-data-row",initialize:function(a){this.input=a.input;this.nodeView=a.nodeView;this.terminalElement=a.terminalElement;this.$el.attr("name",this.input.name).html(this.input.label);if(!a.skipResize){this.$el.css({position:"absolute",left:-1000,top:-1000,display:"none"});$("body").append(this.el);this.nodeView.updateMaxWidth(this.$el.outerWidth());this.$el.css({position:"",left:"",top:"",display:""});this.$el.remove()}},});var OutputCalloutView=Backbone.View.extend({tagName:"div",initialize:function(b){this.label=b.label;this.node=b.node;this.output=b.output;var a=this;this.$el.attr("class","callout "+this.label).css({display:"none"}).append($("<div class='buttons'></div>").append($("<img/>").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-outline.png").click(function(){if($.inArray(a.output.name,a.node.workflow_outputs)!=-1){a.node.workflow_outputs.splice($.inArray(a.output.name,a.node.workflow_outputs),1);a.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-outline.png")}else{a.node.workflow_outputs.push(a.output.name);a.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small.png")}workflow.has_changes=true;canvas_manager.draw_overview()}))).tooltip({delay:500,title:"Mark dataset as a workflow output. All unmarked datasets will be hidden."});this.$el.css({top:"50%",margin:"-8px 0px 0px 0px",right:8});this.$el.show();this.resetImage()},resetImage:function(){if($.inArray(this.output.name,this.node.workflow_outputs)===-1){this.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-outline.png")}else{this.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small.png")}},hoverImage:function(){this.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-yellow.png")}});var DataOutputView=Backbone.View.extend({className:"form-row dataRow",initialize:function(c){this.output=c.output;this.terminalElement=c.terminalElement;this.nodeView=c.nodeView;var a=this.output;var b=a.name;var f=this.nodeView.node;var d=a.extensions.indexOf("input")>=0||a.extensions.indexOf("input_collection")>=0;if(!d){b=b+" ("+a.extensions.join(", ")+")"}this.$el.html(b);if(f.type=="tool"){var g=new OutputCalloutView({label:b,output:a,node:f,});this.$el.append(g.el);this.$el.hover(function(){g.hoverImage()},function(){g.resetImage()})}this.$el.css({position:"absolute",left:-1000,top:-1000,display:"none"});$("body").append(this.el);this.nodeView.updateMaxWidth(this.$el.outerWidth()+17);this.$el.css({position:"",left:"",top:"",display:""}).detach()}});var TerminalView=Backbone.View.extend({setupMappingView:function(b){var c=new this.terminalMappingClass({terminal:b});var a=new this.terminalMappingViewClass({model:c});a.render();b.terminalMappingView=a;this.terminalMappingView=a},terminalElements:function(){if(this.terminalMappingView){return[this.terminalMappingView.el,this.el]}else{return[this.el]}}});var BaseInputTerminalView=TerminalView.extend({className:"terminal input-terminal",initialize:function(c){var f=c.node;var a=c.input;var b=a.name;var d=this.terminalForInput(a);if(!d.multiple){this.setupMappingView(d)}this.el.terminal=d;d.node=f;d.name=b;f.input_terminals[b]=d},events:{dropinit:"onDropInit",dropstart:"onDropStart",dropend:"onDropEnd",drop:"onDrop",hover:"onHover",},onDropInit:function(b,c){var a=this.el.terminal;return $(c.drag).hasClass("output-terminal")&&a.canAccept(c.drag.terminal)},onDropStart:function(a,b){if(b.proxy.terminal){b.proxy.terminal.connectors[0].inner_color="#BBFFBB"}},onDropEnd:function(a,b){if(b.proxy.terminal){b.proxy.terminal.connectors[0].inner_color="#FFFFFF"}},onDrop:function(b,c){var a=this.el.terminal;new Connector(c.drag.terminal,a).redraw()},onHover:function(){var c=this.el;var b=c.terminal;if(b.connectors.length>0){var a=$("<div class='callout'></div>").css({display:"none"}).appendTo("body").append($("<div class='button'></div>").append($("<div/>").addClass("fa-icon-button fa fa-times").click(function(){$.each(b.connectors,function(f,d){if(d){d.destroy()}});a.remove()}))).bind("mouseleave",function(){$(this).remove()});a.css({top:$(c).offset().top-2,left:$(c).offset().left-a.width(),"padding-right":$(c).width()}).show()}},});var InputTerminalView=BaseInputTerminalView.extend({terminalMappingClass:InputTerminalMapping,terminalMappingViewClass:InputTerminalMappingView,terminalForInput:function(a){return new InputTerminal({element:this.el,input:a})},});var InputCollectionTerminalView=BaseInputTerminalView.extend({terminalMappingClass:InputCollectionTerminalMapping,terminalMappingViewClass:InputCollectionTerminalMappingView,terminalForInput:function(a){return new InputCollectionTerminal({element:this.el,input:a})},});var BaseOutputTerminalView=TerminalView.extend({className:"terminal output-terminal",initialize:function(c){var f=c.node;var a=c.output;var b=a.name;var d=this.terminalForOutput(a);this.setupMappingView(d);this.el.terminal=d;d.node=f;d.name=b;f.output_terminals[b]=d},events:{drag:"onDrag",dragstart:"onDragStart",dragend:"onDragEnd",},onDrag:function(b,c){var a=function(){var f=$(c.proxy).offsetParent().offset(),d=c.offsetX-f.left,g=c.offsetY-f.top;$(c.proxy).css({left:d,top:g});c.proxy.terminal.redraw();canvas_manager.update_viewport_overlay()};a();$("#canvas-container").get(0).scroll_panel.test(b,a)},onDragStart:function(b,f){$(f.available).addClass("input-terminal-active");workflow.check_changes_in_active_form();var a=$('<div class="drag-terminal" style="position: absolute;"></div>').appendTo("#canvas-container").get(0);a.terminal=new OutputTerminal({element:a});var g=new Connector();g.dragging=true;g.connect(this.el.terminal,a.terminal);return a},onDragEnd:function(b,c){var a=c.proxy.terminal.connectors[0];if(a){a.destroy()}$(c.proxy).remove();$(c.available).removeClass("input-terminal-active");$("#canvas-container").get(0).scroll_panel.stop()}});var OutputTerminalView=BaseOutputTerminalView.extend({terminalMappingClass:OutputTerminalMapping,terminalMappingViewClass:OutputTerminalMappingView,terminalForOutput:function(a){var c=a.extensions;var b=new OutputTerminal({element:this.el,datatypes:c});return b},});var OutputCollectionTerminalView=BaseOutputTerminalView.extend({terminalMappingClass:OutputCollectionTerminalMapping,terminalMappingViewClass:OutputCollectionTerminalMappingView,terminalForOutput:function(a){var c=a.collection_type;var b=new OutputCollectionTerminal({element:this.el,collection_type:c,datatypes:a.extensions});return b},});function ScrollPanel(a){this.panel=a}$.extend(ScrollPanel.prototype,{test:function(v,d){clearTimeout(this.timeout);var k=v.pageX,j=v.pageY,l=$(this.panel),c=l.position(),b=l.width(),i=l.height(),w=l.parent(),s=w.width(),a=w.height(),r=w.offset(),p=r.left,m=r.top,A=p+w.width(),u=m+w.height(),B=-(b-(s/2)),z=-(i-(a/2)),g=(s/2),f=(a/2),h=false,q=5,o=23;if(k-q<p){if(c.left<g){var n=Math.min(o,g-c.left);l.css("left",c.left+n);h=true}}else{if(k+q>A){if(c.left>B){var n=Math.min(o,c.left-B);l.css("left",c.left-n);h=true}}else{if(j-q<m){if(c.top<f){var n=Math.min(o,f-c.top);l.css("top",c.top+n);h=true}}else{if(j+q>u){if(c.top>z){var n=Math.min(o,c.top-B);l.css("top",(c.top-n)+"px");h=true}}}}}if(h){d();var l=this;this.timeout=setTimeout(function(){l.test(v,d)},50)}},stop:function(b,a){clearTimeout(this.timeout)}});function CanvasManager(b,a){this.cv=b;this.cc=this.cv.find("#canvas-container");this.oc=a.find("#overview-canvas");this.ov=a.find("#overview-viewport");this.init_drag()}$.extend(CanvasManager.prototype,{init_drag:function(){var b=this;var a=function(f,g){f=Math.min(f,b.cv.width()/2);f=Math.max(f,-b.cc.width()+b.cv.width()/2);g=Math.min(g,b.cv.height()/2);g=Math.max(g,-b.cc.height()+b.cv.height()/2);b.cc.css({left:f,top:g});b.update_viewport_overlay()};this.cc.each(function(){this.scroll_panel=new ScrollPanel(this)});var d,c;this.cv.bind("dragstart",function(){var g=$(this).offset();var f=b.cc.position();c=f.top-g.top;d=f.left-g.left}).bind("drag",function(f,g){a(g.offsetX+d,g.offsetY+c)}).bind("dragend",function(){workflow.fit_canvas_to_nodes();b.draw_overview()});this.ov.bind("drag",function(k,l){var h=b.cc.width(),n=b.cc.height(),m=b.oc.width(),j=b.oc.height(),f=$(this).offsetParent().offset(),i=l.offsetX-f.left,g=l.offsetY-f.top;a(-(i/m*h),-(g/j*n))}).bind("dragend",function(){workflow.fit_canvas_to_nodes();b.draw_overview()});$("#overview-border").bind("drag",function(g,i){var j=$(this).offsetParent();var h=j.offset();var f=Math.max(j.width()-(i.offsetX-h.left),j.height()-(i.offsetY-h.top));$(this).css({width:f,height:f});b.draw_overview()});$("#overview-border div").bind("drag",function(){})},update_viewport_overlay:function(){var b=this.cc,f=this.cv,a=this.oc,c=this.ov,d=b.width(),j=b.height(),i=a.width(),g=a.height(),h=b.position();c.css({left:-(h.left/d*i),top:-(h.top/j*g),width:(f.width()/d*i)-2,height:(f.height()/j*g)-2})},draw_overview:function(){var j=$("#overview-canvas"),m=j.parent().parent().width(),i=j.get(0).getContext("2d"),d=$("#canvas-container").width(),l=$("#canvas-container").height();var g,a,k,f;var h=this.cv.width();var b=this.cv.height();if(d<h&&l<b){k=d/h*m;f=(m-k)/2;g=l/b*m;a=(m-g)/2}else{if(d<l){a=0;g=m;k=Math.ceil(g*d/l);f=(m-k)/2}else{k=m;f=0;g=Math.ceil(k*l/d);a=(m-g)/2}}j.parent().css({left:f,top:a,width:k,height:g});j.attr("width",k);j.attr("height",g);$.each(workflow.nodes,function(t,q){i.fillStyle="#D2C099";i.strokeStyle="#D8B365";i.lineWidth=1;var s=$(q.element),n=s.position(),c=n.left/d*k,r=n.top/l*g,o=s.width()/d*k,p=s.height()/l*g;if(q.tool_errors){i.fillStyle="#FFCCCC";i.strokeStyle="#AA6666"}else{if(q.workflow_outputs!=undefined&&q.workflow_outputs.length>0){i.fillStyle="#E8A92D";i.strokeStyle="#E8A92D"}}i.fillRect(c,r,o,p);i.strokeRect(c,r,o,p)});this.update_viewport_overlay()}});
\ No newline at end of file
+function CollectionTypeDescription(a){this.collectionType=a;this.isCollection=true;this.rank=a.split(":").length}$.extend(CollectionTypeDescription.prototype,{append:function(a){if(a===NULL_COLLECTION_TYPE_DESCRIPTION){return this}if(a===ANY_COLLECTION_TYPE_DESCRIPTION){return otherCollectionType}return new CollectionTypeDescription(this.collectionType+":"+a.collectionType)},canMatch:function(a){if(a===NULL_COLLECTION_TYPE_DESCRIPTION){return false}if(a===ANY_COLLECTION_TYPE_DESCRIPTION){return true}return a.collectionType==this.collectionType},canMapOver:function(b){if(b===NULL_COLLECTION_TYPE_DESCRIPTION){return false}if(b===ANY_COLLECTION_TYPE_DESCRIPTION){return false}if(this.rank<=b.rank){return false}var a=b.collectionType;return this._endsWith(this.collectionType,a)},effectiveMapOver:function(a){var c=a.collectionType;var b=this.collectionType.substring(0,this.collectionType.length-c.length-1);return new CollectionTypeDescription(b)},equal:function(a){return a.collectionType==this.collectionType},toString:function(){return"CollectionType["+this.collectionType+"]"},_endsWith:function(b,a){return b.indexOf(a,b.length-a.length)!==-1}});NULL_COLLECTION_TYPE_DESCRIPTION={isCollection:false,canMatch:function(a){return false},canMapOver:function(a){return false},toString:function(){return"NullCollectionType[]"},append:function(a){return a},equal:function(a){return a===this}};ANY_COLLECTION_TYPE_DESCRIPTION={isCollection:true,canMatch:function(a){return NULL_COLLECTION_TYPE_DESCRIPTION!==a},canMapOver:function(a){return false},toString:function(){return"AnyCollectionType[]"},append:function(a){throw"Cannot append to ANY_COLLECTION_TYPE_DESCRIPTION"},equal:function(a){return a===this}};var TerminalMapping=Backbone.Model.extend({initialize:function(a){this.mapOver=a.mapOver||NULL_COLLECTION_TYPE_DESCRIPTION;this.terminal=a.terminal;this.terminal.terminalMapping=this},disableMapOver:function(){this.setMapOver(NULL_COLLECTION_TYPE_DESCRIPTION)},setMapOver:function(a){this.mapOver=a;this.trigger("change")}});var TerminalMappingView=Backbone.View.extend({tagName:"div",className:"fa-icon-button fa fa-folder-o",initialize:function(b){var a="Run tool in parallel over collection";this.$el.tooltip({delay:500,title:a});this.model.bind("change",_.bind(this.render,this))},render:function(){if(this.model.mapOver.isCollection){this.$el.show()}else{this.$el.hide()}},});var InputTerminalMappingView=TerminalMappingView.extend({events:{click:"onClick",mouseenter:"onMouseEnter",mouseleave:"onMouseLeave",},onMouseEnter:function(b){var a=this.model;if(!a.terminal.connected()&&a.mapOver.isCollection){this.$el.css("color","red")}},onMouseLeave:function(a){this.$el.css("color","black")},onClick:function(b){var a=this.model;if(!a.terminal.connected()&&a.mapOver.isCollection){a.terminal.resetMapping()}},});var InputTerminalMapping=TerminalMapping;var InputCollectionTerminalMapping=TerminalMapping;var OutputTerminalMapping=TerminalMapping;var OutputTerminalMappingView=TerminalMappingView;var InputCollectionTerminalMappingView=InputTerminalMappingView;var OutputCollectionTerminalMapping=TerminalMapping;var OutputCollectionTerminalMappingView=TerminalMappingView;var Terminal=Backbone.Model.extend({initialize:function(a){this.element=a.element;this.connectors=[]},connect:function(a){this.connectors.push(a);if(this.node){this.node.markChanged()}},disconnect:function(a){this.connectors.splice($.inArray(a,this.connectors),1);if(this.node){this.node.markChanged();this.resetMappingIfNeeded()}},redraw:function(){$.each(this.connectors,function(a,b){b.redraw()})},destroy:function(){$.each(this.connectors.slice(),function(a,b){b.destroy()})},destroyInvalidConnections:function(){_.each(this.connectors,function(a){a.destroyIfInvalid()})},setMapOver:function(a){if(this.multiple){return}if(!this.mapOver().equal(a)){this.terminalMapping.setMapOver(a);_.each(this.node.output_terminals,function(b){b.setMapOver(a)})}},mapOver:function(){if(!this.terminalMapping){return NULL_COLLECTION_TYPE_DESCRIPTION}else{return this.terminalMapping.mapOver}},isMappedOver:function(){return this.terminalMapping&&this.terminalMapping.mapOver.isCollection},resetMapping:function(){this.terminalMapping.disableMapOver()},resetMappingIfNeeded:function(){},});var OutputTerminal=Terminal.extend({initialize:function(a){Terminal.prototype.initialize.call(this,a);this.datatypes=a.datatypes},resetMappingIfNeeded:function(){if(!this.node.hasConnectedOutputTerminals()&&!this.node.hasConnectedMappedInputTerminals()){_.each(this.node.mappedInputTerminals(),function(b){b.resetMappingIfNeeded()})}var a=!this.node.hasMappedOverInputTerminals();if(a){this.resetMapping()}},resetMapping:function(){this.terminalMapping.disableMapOver();_.each(this.connectors,function(a){var b=a.handle2;if(b){b.resetMappingIfNeeded();a.destroyIfInvalid()}})}});var BaseInputTerminal=Terminal.extend({initialize:function(a){Terminal.prototype.initialize.call(this,a);this.update(a.input)},canAccept:function(a){if(this._inputFilled()){return false}else{return this.attachable(a)}},resetMappingIfNeeded:function(){var b=this.mapOver();if(!b.isCollection){return}var a=this.node.hasConnectedMappedInputTerminals()||(!this.node.hasConnectedOutputTerminals());if(a){this.resetMapping()}},resetMapping:function(){this.terminalMapping.disableMapOver();if(!this.node.hasMappedOverInputTerminals()){_.each(this.node.output_terminals,function(a){a.resetMapping()})}},connected:function(){return this.connectors.length!==0},_inputFilled:function(){var a;if(!this.connected()){a=false}else{if(this.multiple){if(this._collectionAttached()){inputsFilled=true}else{a=false}}else{a=true}}return a},_collectionAttached:function(){if(!this.connected()){return false}else{var a=this.connectors[0].handle1;if(!a){return false}else{if(a.isDataCollectionInput||a.isMappedOver()||a.datatypes.indexOf("input_collection")>0){return true}else{return false}}}},_mappingConstraints:function(){if(!this.node){return[]}var b=this.mapOver();if(b.isCollection){return[b]}var a=[];if(!this.node.hasConnectedOutputTerminals()){_.each(this.node.connectedMappedInputTerminals(),function(c){a.push(c.mapOver())})}else{a.push(_.first(_.values(this.node.output_terminals)).mapOver())}return a},_producesAcceptableDatatype:function(a){for(var c in this.datatypes){var f=new Array();f=f.concat(a.datatypes);if(a.node.post_job_actions){for(var d in a.node.post_job_actions){var g=a.node.post_job_actions[d];if(g.action_type=="ChangeDatatypeAction"&&(g.output_name==""||g.output_name==a.name)&&g.action_arguments){f.push(g.action_arguments.newtype)}}}for(var b in f){var h=f[b];if(h=="input"||h=="input_collection"||issubtype(f[b],this.datatypes[c])){return true}}}return false},_otherCollectionType:function(a){var c=NULL_COLLECTION_TYPE_DESCRIPTION;if(a.isDataCollectionInput){c=a.collectionType}else{var b=a.mapOver();if(b.isCollection){c=b}}return c},});var InputTerminal=BaseInputTerminal.extend({update:function(a){this.datatypes=a.extensions;this.multiple=a.multiple;this.collection=false},connect:function(a){BaseInputTerminal.prototype.connect.call(this,a);var b=a.handle1;if(!b){return}var c=this._otherCollectionType(b);if(c.isCollection){this.setMapOver(c)}},attachable:function(b){var d=this._otherCollectionType(b);var a=this.mapOver();if(d.isCollection){if(this.multiple){if(this.connected()&&!this._collectionAttached()){return false}if(d.rank==1){return this._producesAcceptableDatatype(b)}else{return false}}if(a.isCollection&&a.canMatch(d)){return this._producesAcceptableDatatype(b)}else{var c=this._mappingConstraints();if(c.every(_.bind(d.canMatch,d))){return this._producesAcceptableDatatype(b)}else{return false}}}else{if(a.isCollection){return false}}return this._producesAcceptableDatatype(b)}});var InputCollectionTerminal=BaseInputTerminal.extend({update:function(a){this.multiple=false;this.collection=true;this.datatypes=a.extensions;if(a.collection_type){this.collectionType=new CollectionTypeDescription(a.collection_type)}else{this.collectionType=ANY_COLLECTION_TYPE_DESCRIPTION}},connect:function(b){BaseInputTerminal.prototype.connect.call(this,b);var a=b.handle1;if(!a){return}var c=this._effectiveMapOver(a);this.setMapOver(c)},_effectiveMapOver:function(a){var b=this.collectionType;var c=this._otherCollectionType(a);if(!b.canMatch(c)){return c.effectiveMapOver(b)}else{return NULL_COLLECTION_TYPE_DESCRIPTION}},_effectiveCollectionType:function(){var b=this.collectionType;var a=this.mapOver();return a.append(b)},attachable:function(b){var g=this._otherCollectionType(b);if(g.isCollection){var f=this._effectiveCollectionType();var a=this.mapOver();if(f.canMatch(g)){return this._producesAcceptableDatatype(b)}else{if(a.isCollection){return false}else{if(g.canMapOver(this.collectionType)){var d=this._effectiveMapOver(b);if(!d.isCollection){return false}var c=this._mappingConstraints();if(c.every(d.canMatch)){return this._producesAcceptableDatatype(b)}}}}}return false}});var OutputCollectionTerminal=Terminal.extend({initialize:function(a){Terminal.prototype.initialize.call(this,a);this.datatypes=a.datatypes;this.collectionType=new CollectionTypeDescription(a.collection_type);this.isDataCollectionInput=true},update:function(a){var b=new CollectionTypeDescription(a.collection_type);if(b.collectionType!=this.collectionType.collectionType){_.each(this.connectors,function(c){c.destroy()})}this.collectionType=b}});function Connector(b,a){this.canvas=null;this.dragging=false;this.inner_color="#FFFFFF";this.outer_color="#D8B365";if(b&&a){this.connect(b,a)}}$.extend(Connector.prototype,{connect:function(b,a){this.handle1=b;if(this.handle1){this.handle1.connect(this)}this.handle2=a;if(this.handle2){this.handle2.connect(this)}},destroy:function(){if(this.handle1){this.handle1.disconnect(this)}if(this.handle2){this.handle2.disconnect(this)}$(this.canvas).remove()},destroyIfInvalid:function(){if(this.handle1&&this.handle2&&!this.handle2.attachable(this.handle1)){this.destroy()}},redraw:function(){var f=$("#canvas-container");if(!this.canvas){this.canvas=document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(this.canvas)}f.append($(this.canvas));if(this.dragging){this.canvas.style.zIndex="300"}}var v=function(c){return $(c).offset().left-f.offset().left};var p=function(c){return $(c).offset().top-f.offset().top};if(!this.handle1||!this.handle2){return}var o=v(this.handle1.element)+5;var n=p(this.handle1.element)+5;var x=v(this.handle2.element)+5;var u=p(this.handle2.element)+5;var k=100;var r=Math.min(o,x);var a=Math.max(o,x);var q=Math.min(n,u);var B=Math.max(n,u);var d=Math.min(Math.max(Math.abs(B-q)/2,100),300);var w=r-k;var A=q-k;var y=a-r+2*k;var s=B-q+2*k;this.canvas.style.left=w+"px";this.canvas.style.top=A+"px";this.canvas.setAttribute("width",y);this.canvas.setAttribute("height",s);o-=w;n-=A;x-=w;u-=A;var z=this.canvas.getContext("2d"),h=null,l=null;var g=1;if(this.handle1&&this.handle1.isMappedOver()){var h=[-6,-3,0,3,6];g=5}else{var h=[0]}if(this.handle2&&this.handle2.isMappedOver()){var l=[-6,-3,0,3,6];g=5}else{var l=[0]}var b=this;for(var t=0;t<g;t++){var m=5,j=7;if(h.length>1||l.length>1){m=1;j=3}b.draw_outlined_curve(o,n,x,u,d,m,j,h[t%h.length],l[t%l.length])}},draw_outlined_curve:function(j,i,l,k,a,b,f,g,d){var g=g||0;var d=d||0;var h=this.canvas.getContext("2d");h.lineCap="round";h.strokeStyle=this.outer_color;h.lineWidth=f;h.beginPath();h.moveTo(j,i+g);h.bezierCurveTo(j+a,i+g,l-a,k+d,l,k+d);h.stroke();h.strokeStyle=this.inner_color;h.lineWidth=b;h.beginPath();h.moveTo(j,i+g);h.bezierCurveTo(j+a,i+g,l-a,k+d,l,k+d);h.stroke()}});var Node=Backbone.Model.extend({initialize:function(a){this.element=a.element;this.input_terminals={};this.output_terminals={};this.tool_errors={}},connectedOutputTerminals:function(){return this._connectedTerminals(this.output_terminals)},_connectedTerminals:function(b){var a=[];$.each(b,function(c,d){if(d.connectors.length>0){a.push(d)}});return a},hasConnectedOutputTerminals:function(){var a=this.output_terminals;for(var b in a){if(a[b].connectors.length>0){return true}}return false},connectedMappedInputTerminals:function(){return this._connectedMappedTerminals(this.input_terminals)},hasConnectedMappedInputTerminals:function(){var c=this.input_terminals;for(var b in c){var a=c[b];if(a.connectors.length>0&&a.isMappedOver()){return true}}return false},_connectedMappedTerminals:function(b){var a=[];$.each(b,function(c,d){var f=d.mapOver();if(f.isCollection){if(d.connectors.length>0){a.push(d)}}});return a},mappedInputTerminals:function(){return this._mappedTerminals(this.input_terminals)},_mappedTerminals:function(b){var a=[];$.each(b,function(c,d){var f=d.mapOver();if(f.isCollection){a.push(d)}});return a},hasMappedOverInputTerminals:function(){var a=false;_.each(this.input_terminals,function(b){var c=b.mapOver();if(c.isCollection){a=true}});return a},redraw:function(){$.each(this.input_terminals,function(a,b){b.redraw()});$.each(this.output_terminals,function(a,b){b.redraw()})},destroy:function(){$.each(this.input_terminals,function(a,b){b.destroy()});$.each(this.output_terminals,function(a,b){b.destroy()});workflow.remove_node(this);$(this.element).remove()},make_active:function(){$(this.element).addClass("toolForm-active")},make_inactive:function(){var a=this.element.get(0);(function(b){b.removeChild(a);b.appendChild(a)})(a.parentNode);$(a).removeClass("toolForm-active")},init_field_data:function(b){if(b.type){this.type=b.type}this.name=b.name;this.form_html=b.form_html;this.tool_state=b.tool_state;this.tool_errors=b.tool_errors;this.tooltip=b.tooltip?b.tooltip:"";this.annotation=b.annotation;this.post_job_actions=b.post_job_actions?b.post_job_actions:{};this.label=b.label;this.uuid=b.uuid;this.workflow_outputs=b.workflow_outputs?b.workflow_outputs:[];var a=this;var c=new NodeView({el:this.element[0],node:a,});a.nodeView=c;$.each(b.data_inputs,function(f,d){c.addDataInput(d)});if((b.data_inputs.length>0)&&(b.data_outputs.length>0)){c.addRule()}$.each(b.data_outputs,function(f,d){c.addDataOutput(d)});c.render();workflow.node_changed(this,true)},update_field_data:function(d){var c=this;nodeView=c.nodeView;this.tool_state=d.tool_state;this.form_html=d.form_html;this.tool_errors=d.tool_errors;this.annotation=d.annotation;if("post_job_actions" in d){var f=$.parseJSON(d.post_job_actions);this.post_job_actions=f?f:{}}c.nodeView.renderToolErrors();var g=nodeView.$("div.inputs");var a=nodeView.newInputsDiv();var b={};_.each(d.data_inputs,function(h){var i=c.nodeView.addDataInput(h,a);b[h.name]=i});_.each(_.difference(_.values(nodeView.terminalViews),_.values(b)),function(h){h.el.terminal.destroy()});nodeView.terminalViews=b;if(d.data_outputs.length==1&&"collection_type" in d.data_outputs[0]){nodeView.updateDataOutput(d.data_outputs[0])}g.replaceWith(a);this.markChanged();this.redraw()},error:function(d){var a=$(this.element).find(".toolFormBody");a.find("div").remove();var c="<div style='color: red; text-style: italic;'>"+d+"</div>";this.form_html=c;a.html(c);workflow.node_changed(this)},markChanged:function(){workflow.node_changed(this)}});function Workflow(a){this.canvas_container=a;this.id_counter=0;this.nodes={};this.name=null;this.has_changes=false;this.active_form_has_changes=false}$.extend(Workflow.prototype,{add_node:function(a){a.id=this.id_counter;a.element.attr("id","wf-node-step-"+a.id);this.id_counter++;this.nodes[a.id]=a;this.has_changes=true;a.workflow=this},remove_node:function(a){if(this.active_node==a){this.clear_active_node()}delete this.nodes[a.id];this.has_changes=true},remove_all:function(){wf=this;$.each(this.nodes,function(b,a){a.destroy();wf.remove_node(a)})},rectify_workflow_outputs:function(){var b=false;var a=false;$.each(this.nodes,function(c,d){if(d.workflow_outputs&&d.workflow_outputs.length>0){b=true}$.each(d.post_job_actions,function(g,f){if(f.action_type==="HideDatasetAction"){a=true}})});if(b!==false||a!==false){$.each(this.nodes,function(c,g){if(g.type==="tool"){var f=false;if(g.post_job_actions==null){g.post_job_actions={};f=true}var d=[];$.each(g.post_job_actions,function(i,h){if(h.action_type=="HideDatasetAction"){d.push(i)}});if(d.length>0){$.each(d,function(h,j){f=true;delete g.post_job_actions[j]})}if(b){$.each(g.output_terminals,function(i,j){var h=true;$.each(g.workflow_outputs,function(l,m){if(j.name===m){h=false}});if(h===true){f=true;var k={action_type:"HideDatasetAction",output_name:j.name,action_arguments:{}};g.post_job_actions["HideDatasetAction"+j.name]=null;g.post_job_actions["HideDatasetAction"+j.name]=k}})}if(workflow.active_node==g&&f===true){workflow.reload_active_node()}}})}},to_simple:function(){var a={};$.each(this.nodes,function(c,f){var g={};$.each(f.input_terminals,function(i,j){g[j.name]=null;var h=[];$.each(j.connectors,function(k,l){h[k]={id:l.handle1.node.id,output_name:l.handle1.name};g[j.name]=h})});var b={};if(f.post_job_actions){$.each(f.post_job_actions,function(j,h){var k={action_type:h.action_type,output_name:h.output_name,action_arguments:h.action_arguments};b[h.action_type+h.output_name]=null;b[h.action_type+h.output_name]=k})}if(!f.workflow_outputs){f.workflow_outputs=[]}var d={id:f.id,type:f.type,tool_id:f.tool_id,tool_state:f.tool_state,tool_errors:f.tool_errors,input_connections:g,position:$(f.element).position(),annotation:f.annotation,post_job_actions:f.post_job_actions,uuid:f.uuid,label:f.label,workflow_outputs:f.workflow_outputs};a[f.id]=d});return{steps:a}},from_simple:function(b){wf=this;var c=0;wf.name=b.name;var a=false;$.each(b.steps,function(g,f){var d=prebuild_node(f.type,f.name,f.tool_id);d.init_field_data(f);if(f.position){d.element.css({top:f.position.top,left:f.position.left})}d.id=f.id;wf.nodes[d.id]=d;c=Math.max(c,parseInt(g));if(!a&&d.type==="tool"){if(d.workflow_outputs.length>0){a=true}else{$.each(d.post_job_actions,function(i,h){if(h.action_type==="HideDatasetAction"){a=true}})}}});wf.id_counter=c+1;$.each(b.steps,function(g,f){var d=wf.nodes[g];$.each(f.input_connections,function(i,h){if(h){if(!$.isArray(h)){h=[h]}$.each(h,function(k,j){var m=wf.nodes[j.id];var n=new Connector();n.connect(m.output_terminals[j.output_name],d.input_terminals[i]);n.redraw()})}});if(a&&d.type==="tool"){$.each(d.output_terminals,function(h,i){if(d.post_job_actions["HideDatasetAction"+i.name]===undefined){d.workflow_outputs.push(i.name);callout=$(d.element).find(".callout."+i.name);callout.find("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small.png");workflow.has_changes=true}})}})},check_changes_in_active_form:function(){if(this.active_form_has_changes){this.has_changes=true;$("#right-content").find("form").submit();this.active_form_has_changes=false}},reload_active_node:function(){if(this.active_node){var a=this.active_node;this.clear_active_node();this.activate_node(a)}},clear_active_node:function(){if(this.active_node){this.active_node.make_inactive();this.active_node=null}parent.show_form_for_tool("<div>No node selected</div>")},activate_node:function(a){if(this.active_node!=a){this.check_changes_in_active_form();this.clear_active_node();if(parent.__NEWTOOLFORM__){parent.show_form_for_tool(a.form_html,a)}else{parent.show_form_for_tool(a.form_html+a.tooltip,a)}a.make_active();this.active_node=a}},node_changed:function(a,b){this.has_changes=true;if(this.active_node==a&&(!parent.__NEWTOOLFORM__||b)){this.check_changes_in_active_form();if(parent.__NEWTOOLFORM__){parent.show_form_for_tool(a.form_html,a)}else{parent.show_form_for_tool(a.form_html+a.tooltip,a)}}},layout:function(){this.check_changes_in_active_form();this.has_changes=true;var i={};var b={};$.each(this.nodes,function(l,k){if(i[l]===undefined){i[l]=0}if(b[l]===undefined){b[l]=[]}});$.each(this.nodes,function(l,k){$.each(k.input_terminals,function(m,n){$.each(n.connectors,function(p,q){var o=q.handle1.node;i[k.id]+=1;b[o.id].push(k.id)})})});node_ids_by_level=[];while(true){level_parents=[];for(var a in i){if(i[a]==0){level_parents.push(a)}}if(level_parents.length==0){break}node_ids_by_level.push(level_parents);for(var f in level_parents){var j=level_parents[f];delete i[j];for(var g in b[j]){i[b[j][g]]-=1}}}if(i.length){return}var d=this.nodes;var h=80;v_pad=30;var c=h;$.each(node_ids_by_level,function(k,l){l.sort(function(p,o){return $(d[p].element).position().top-$(d[o].element).position().top});var m=0;var n=v_pad;$.each(l,function(o,r){var q=d[r];var p=$(q.element);$(p).css({top:n,left:c});m=Math.max(m,$(p).width());n+=$(p).height()+v_pad});c+=m+h});$.each(d,function(k,l){l.redraw()})},bounds_for_all_nodes:function(){var d=Infinity,b=-Infinity,c=Infinity,a=-Infinity,f;$.each(this.nodes,function(h,g){e=$(g.element);f=e.position();d=Math.min(d,f.left);b=Math.max(b,f.left+e.width());c=Math.min(c,f.top);a=Math.max(a,f.top+e.width())});return{xmin:d,xmax:b,ymin:c,ymax:a}},fit_canvas_to_nodes:function(){var a=this.bounds_for_all_nodes();var f=this.canvas_container.position();var i=this.canvas_container.parent();var d=fix_delta(a.xmin,100);var h=fix_delta(a.ymin,100);d=Math.max(d,f.left);h=Math.max(h,f.top);var c=f.left-d;var g=f.top-h;var b=round_up(a.xmax+100,100)+d;var j=round_up(a.ymax+100,100)+h;b=Math.max(b,-c+i.width());j=Math.max(j,-g+i.height());this.canvas_container.css({left:c,top:g,width:b,height:j});this.canvas_container.children().each(function(){var k=$(this).position();$(this).css("left",k.left+d);$(this).css("top",k.top+h)})}});function fix_delta(a,b){if(a<b||a>3*b){new_pos=(Math.ceil(((a%b))/b)+1)*b;return(-(a-new_pos))}return 0}function round_up(a,b){return Math.ceil(a/b)*b}function prebuild_node(l,j,r){var i=$("<div class='toolForm toolFormInCanvas'></div>");var g=new Node({element:i});g.type=l;if(l=="tool"){g.tool_id=r}var n=$("<div class='toolFormTitle unselectable'>"+j+"</div>");i.append(n);i.css("left",$(window).scrollLeft()+20);i.css("top",$(window).scrollTop()+20);var m=$("<div class='toolFormBody'></div>");var h="<div><img height='16' align='middle' src='"+galaxy_config.root+"static/images/loading_small_white_bg.gif'/> loading tool info...</div>";m.append(h);g.form_html=h;i.append(m);var k=$("<div class='buttons' style='float: right;'></div>");k.append($("<div>").addClass("fa-icon-button fa fa-times").click(function(b){g.destroy()}));i.appendTo("#canvas-container");var d=$("#canvas-container").position();var c=$("#canvas-container").parent();var a=i.width();var q=i.height();i.css({left:(-d.left)+(c.width()/2)-(a/2),top:(-d.top)+(c.height()/2)-(q/2)});k.prependTo(n);a+=(k.width()+10);i.css("width",a);$(i).bind("dragstart",function(){workflow.activate_node(g)}).bind("dragend",function(){workflow.node_changed(this);workflow.fit_canvas_to_nodes();canvas_manager.draw_overview()}).bind("dragclickonly",function(){workflow.activate_node(g)}).bind("drag",function(o,p){var f=$(this).offsetParent().offset(),b=p.offsetX-f.left,s=p.offsetY-f.top;$(this).css({left:b,top:s});$(this).find(".terminal").each(function(){this.terminal.redraw()})});return g}function add_node(b,d,a){var c=prebuild_node(b,d,a);workflow.add_node(c);workflow.fit_canvas_to_nodes();canvas_manager.draw_overview();workflow.activate_node(c);return c}var ext_to_type=null;var type_to_type=null;function issubtype(b,a){b=ext_to_type[b];a=ext_to_type[a];return(type_to_type[b])&&(a in type_to_type[b])}function populate_datatype_info(a){ext_to_type=a.ext_to_class_name;type_to_type=a.class_to_classes}var NodeView=Backbone.View.extend({initialize:function(a){this.node=a.node;this.output_width=Math.max(150,this.$el.width());this.tool_body=this.$el.find(".toolFormBody");this.tool_body.find("div").remove();this.newInputsDiv().appendTo(this.tool_body);this.terminalViews={};this.outputTerminlViews={}},render:function(){this.renderToolErrors();this.$el.css("width",Math.min(250,Math.max(this.$el.width(),this.output_width)))},renderToolErrors:function(){if(this.node.tool_errors){this.$el.addClass("tool-node-error")}else{this.$el.removeClass("tool-node-error")}},newInputsDiv:function(){return $("<div class='inputs'></div>")},updateMaxWidth:function(a){this.output_width=Math.max(this.output_width,a)},addRule:function(){this.tool_body.append($("<div class='rule'></div>"))},addDataInput:function(i,d){var j=true;if(!d){d=this.$(".inputs");j=false}var f=this.terminalViews[i.name];var h=(i.input_type=="dataset_collection")?InputCollectionTerminalView:InputTerminalView;if(f&&!(f instanceof h)){f.el.terminal.destroy();f=null}if(!f){f=new h({node:this.node,input:i})}else{var g=f.el.terminal;g.update(i);g.destroyInvalidConnections()}this.terminalViews[i.name]=f;var c=f.el;var b=new DataInputView({terminalElement:c,input:i,nodeView:this,skipResize:j});var a=b.$el;d.append(a.prepend(f.terminalElements()));return f},addDataOutput:function(a){var d=(a.collection_type)?OutputCollectionTerminalView:OutputTerminalView;var c=new d({node:this.node,output:a});var b=new DataOutputView({output:a,terminalElement:c.el,nodeView:this,});this.tool_body.append(b.$el.append(c.terminalElements()))},updateDataOutput:function(b){var a=this.node.output_terminals[b.name];a.update(b)}});var DataInputView=Backbone.View.extend({className:"form-row dataRow input-data-row",initialize:function(a){this.input=a.input;this.nodeView=a.nodeView;this.terminalElement=a.terminalElement;this.$el.attr("name",this.input.name).html(this.input.label);if(!a.skipResize){this.$el.css({position:"absolute",left:-1000,top:-1000,display:"none"});$("body").append(this.el);this.nodeView.updateMaxWidth(this.$el.outerWidth());this.$el.css({position:"",left:"",top:"",display:""});this.$el.remove()}},});var OutputCalloutView=Backbone.View.extend({tagName:"div",initialize:function(b){this.label=b.label;this.node=b.node;this.output=b.output;var a=this;this.$el.attr("class","callout "+this.label).css({display:"none"}).append($("<div class='buttons'></div>").append($("<img/>").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-outline.png").click(function(){if($.inArray(a.output.name,a.node.workflow_outputs)!=-1){a.node.workflow_outputs.splice($.inArray(a.output.name,a.node.workflow_outputs),1);a.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-outline.png")}else{a.node.workflow_outputs.push(a.output.name);a.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small.png")}workflow.has_changes=true;canvas_manager.draw_overview()}))).tooltip({delay:500,title:"Mark dataset as a workflow output. All unmarked datasets will be hidden."});this.$el.css({top:"50%",margin:"-8px 0px 0px 0px",right:8});this.$el.show();this.resetImage()},resetImage:function(){if($.inArray(this.output.name,this.node.workflow_outputs)===-1){this.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-outline.png")}else{this.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small.png")}},hoverImage:function(){this.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-yellow.png")}});var DataOutputView=Backbone.View.extend({className:"form-row dataRow",initialize:function(c){this.output=c.output;this.terminalElement=c.terminalElement;this.nodeView=c.nodeView;var a=this.output;var b=a.name;var f=this.nodeView.node;var d=a.extensions.indexOf("input")>=0||a.extensions.indexOf("input_collection")>=0;if(!d){b=b+" ("+a.extensions.join(", ")+")"}this.$el.html(b);if(f.type=="tool"){var g=new OutputCalloutView({label:b,output:a,node:f,});this.$el.append(g.el);this.$el.hover(function(){g.hoverImage()},function(){g.resetImage()})}this.$el.css({position:"absolute",left:-1000,top:-1000,display:"none"});$("body").append(this.el);this.nodeView.updateMaxWidth(this.$el.outerWidth()+17);this.$el.css({position:"",left:"",top:"",display:""}).detach()}});var TerminalView=Backbone.View.extend({setupMappingView:function(b){var c=new this.terminalMappingClass({terminal:b});var a=new this.terminalMappingViewClass({model:c});a.render();b.terminalMappingView=a;this.terminalMappingView=a},terminalElements:function(){if(this.terminalMappingView){return[this.terminalMappingView.el,this.el]}else{return[this.el]}}});var BaseInputTerminalView=TerminalView.extend({className:"terminal input-terminal",initialize:function(c){var f=c.node;var a=c.input;var b=a.name;var d=this.terminalForInput(a);if(!d.multiple){this.setupMappingView(d)}this.el.terminal=d;d.node=f;d.name=b;f.input_terminals[b]=d},events:{dropinit:"onDropInit",dropstart:"onDropStart",dropend:"onDropEnd",drop:"onDrop",hover:"onHover",},onDropInit:function(b,c){var a=this.el.terminal;return $(c.drag).hasClass("output-terminal")&&a.canAccept(c.drag.terminal)},onDropStart:function(a,b){if(b.proxy.terminal){b.proxy.terminal.connectors[0].inner_color="#BBFFBB"}},onDropEnd:function(a,b){if(b.proxy.terminal){b.proxy.terminal.connectors[0].inner_color="#FFFFFF"}},onDrop:function(b,c){var a=this.el.terminal;new Connector(c.drag.terminal,a).redraw()},onHover:function(){var c=this.el;var b=c.terminal;if(b.connectors.length>0){var a=$("<div class='callout'></div>").css({display:"none"}).appendTo("body").append($("<div class='button'></div>").append($("<div/>").addClass("fa-icon-button fa fa-times").click(function(){$.each(b.connectors,function(f,d){if(d){d.destroy()}});a.remove()}))).bind("mouseleave",function(){$(this).remove()});a.css({top:$(c).offset().top-2,left:$(c).offset().left-a.width(),"padding-right":$(c).width()}).show()}},});var InputTerminalView=BaseInputTerminalView.extend({terminalMappingClass:InputTerminalMapping,terminalMappingViewClass:InputTerminalMappingView,terminalForInput:function(a){return new InputTerminal({element:this.el,input:a})},});var InputCollectionTerminalView=BaseInputTerminalView.extend({terminalMappingClass:InputCollectionTerminalMapping,terminalMappingViewClass:InputCollectionTerminalMappingView,terminalForInput:function(a){return new InputCollectionTerminal({element:this.el,input:a})},});var BaseOutputTerminalView=TerminalView.extend({className:"terminal output-terminal",initialize:function(c){var f=c.node;var a=c.output;var b=a.name;var d=this.terminalForOutput(a);this.setupMappingView(d);this.el.terminal=d;d.node=f;d.name=b;f.output_terminals[b]=d},events:{drag:"onDrag",dragstart:"onDragStart",dragend:"onDragEnd",},onDrag:function(b,c){var a=function(){var f=$(c.proxy).offsetParent().offset(),d=c.offsetX-f.left,g=c.offsetY-f.top;$(c.proxy).css({left:d,top:g});c.proxy.terminal.redraw();canvas_manager.update_viewport_overlay()};a();$("#canvas-container").get(0).scroll_panel.test(b,a)},onDragStart:function(b,f){$(f.available).addClass("input-terminal-active");workflow.check_changes_in_active_form();var a=$('<div class="drag-terminal" style="position: absolute;"></div>').appendTo("#canvas-container").get(0);a.terminal=new OutputTerminal({element:a});var g=new Connector();g.dragging=true;g.connect(this.el.terminal,a.terminal);return a},onDragEnd:function(b,c){var a=c.proxy.terminal.connectors[0];if(a){a.destroy()}$(c.proxy).remove();$(c.available).removeClass("input-terminal-active");$("#canvas-container").get(0).scroll_panel.stop()}});var OutputTerminalView=BaseOutputTerminalView.extend({terminalMappingClass:OutputTerminalMapping,terminalMappingViewClass:OutputTerminalMappingView,terminalForOutput:function(a){var c=a.extensions;var b=new OutputTerminal({element:this.el,datatypes:c});return b},});var OutputCollectionTerminalView=BaseOutputTerminalView.extend({terminalMappingClass:OutputCollectionTerminalMapping,terminalMappingViewClass:OutputCollectionTerminalMappingView,terminalForOutput:function(a){var c=a.collection_type;var b=new OutputCollectionTerminal({element:this.el,collection_type:c,datatypes:a.extensions});return b},});function ScrollPanel(a){this.panel=a}$.extend(ScrollPanel.prototype,{test:function(v,d){clearTimeout(this.timeout);var k=v.pageX,j=v.pageY,l=$(this.panel),c=l.position(),b=l.width(),i=l.height(),w=l.parent(),s=w.width(),a=w.height(),r=w.offset(),p=r.left,m=r.top,A=p+w.width(),u=m+w.height(),B=-(b-(s/2)),z=-(i-(a/2)),g=(s/2),f=(a/2),h=false,q=5,o=23;if(k-q<p){if(c.left<g){var n=Math.min(o,g-c.left);l.css("left",c.left+n);h=true}}else{if(k+q>A){if(c.left>B){var n=Math.min(o,c.left-B);l.css("left",c.left-n);h=true}}else{if(j-q<m){if(c.top<f){var n=Math.min(o,f-c.top);l.css("top",c.top+n);h=true}}else{if(j+q>u){if(c.top>z){var n=Math.min(o,c.top-B);l.css("top",(c.top-n)+"px");h=true}}}}}if(h){d();var l=this;this.timeout=setTimeout(function(){l.test(v,d)},50)}},stop:function(b,a){clearTimeout(this.timeout)}});function CanvasManager(b,a){this.cv=b;this.cc=this.cv.find("#canvas-container");this.oc=a.find("#overview-canvas");this.ov=a.find("#overview-viewport");this.init_drag()}$.extend(CanvasManager.prototype,{init_drag:function(){var b=this;var a=function(f,g){f=Math.min(f,b.cv.width()/2);f=Math.max(f,-b.cc.width()+b.cv.width()/2);g=Math.min(g,b.cv.height()/2);g=Math.max(g,-b.cc.height()+b.cv.height()/2);b.cc.css({left:f,top:g});b.cv.css({"background-position-x":f,"background-position-y":g});b.update_viewport_overlay()};this.cc.each(function(){this.scroll_panel=new ScrollPanel(this)});var d,c;this.cv.bind("dragstart",function(){var g=$(this).offset();var f=b.cc.position();c=f.top-g.top;d=f.left-g.left}).bind("drag",function(f,g){a(g.offsetX+d,g.offsetY+c)}).bind("dragend",function(){workflow.fit_canvas_to_nodes();b.draw_overview()});this.ov.bind("drag",function(k,l){var h=b.cc.width(),n=b.cc.height(),m=b.oc.width(),j=b.oc.height(),f=$(this).offsetParent().offset(),i=l.offsetX-f.left,g=l.offsetY-f.top;a(-(i/m*h),-(g/j*n))}).bind("dragend",function(){workflow.fit_canvas_to_nodes();b.draw_overview()});$("#overview-border").bind("drag",function(g,i){var j=$(this).offsetParent();var h=j.offset();var f=Math.max(j.width()-(i.offsetX-h.left),j.height()-(i.offsetY-h.top));$(this).css({width:f,height:f});b.draw_overview()});$("#overview-border div").bind("drag",function(){})},update_viewport_overlay:function(){var b=this.cc,f=this.cv,a=this.oc,c=this.ov,d=b.width(),j=b.height(),i=a.width(),g=a.height(),h=b.position();c.css({left:-(h.left/d*i),top:-(h.top/j*g),width:(f.width()/d*i)-2,height:(f.height()/j*g)-2})},draw_overview:function(){var j=$("#overview-canvas"),m=j.parent().parent().width(),i=j.get(0).getContext("2d"),d=$("#canvas-container").width(),l=$("#canvas-container").height();var g,a,k,f;var h=this.cv.width();var b=this.cv.height();if(d<h&&l<b){k=d/h*m;f=(m-k)/2;g=l/b*m;a=(m-g)/2}else{if(d<l){a=0;g=m;k=Math.ceil(g*d/l);f=(m-k)/2}else{k=m;f=0;g=Math.ceil(k*l/d);a=(m-g)/2}}j.parent().css({left:f,top:a,width:k,height:g});j.attr("width",k);j.attr("height",g);$.each(workflow.nodes,function(t,q){i.fillStyle="#D2C099";i.strokeStyle="#D8B365";i.lineWidth=1;var s=$(q.element),n=s.position(),c=n.left/d*k,r=n.top/l*g,o=s.width()/d*k,p=s.height()/l*g;if(q.tool_errors){i.fillStyle="#FFCCCC";i.strokeStyle="#AA6666"}else{if(q.workflow_outputs!=undefined&&q.workflow_outputs.length>0){i.fillStyle="#E8A92D";i.strokeStyle="#E8A92D"}}i.fillRect(c,r,o,p);i.strokeRect(c,r,o,p)});this.update_viewport_overlay()}});
\ No newline at end of file
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef static/scripts/packed/mvc/history/history-panel-edit.js
--- a/static/scripts/packed/mvc/history/history-panel-edit.js
+++ b/static/scripts/packed/mvc/history/history-panel-edit.js
@@ -1,1 +1,1 @@
-define(["mvc/history/history-panel","mvc/history/history-contents","mvc/dataset/states","mvc/history/hda-model","mvc/history/hda-li-edit","mvc/history/hdca-li-edit","mvc/tags","mvc/annotations","ui/fa-icon-button","mvc/ui/popup-menu","utils/localization"],function(h,j,m,f,e,i,l,c,b,a,d){var k=h.HistoryPanel;var g=k.extend({HDAViewClass:e.HDAListItemEdit,HDCAViewClass:i.HDCAListItemEdit,initialize:function(n){n=n||{};k.prototype.initialize.call(this,n);this.tagsEditor=null;this.annotationEditor=null;this.purgeAllowed=n.purgeAllowed||false;this.annotationEditorShown=n.annotationEditorShown||false;this.tagsEditorShown=n.tagsEditorShown||false;this.multiselectActions=n.multiselectActions||this._getActions()},_setUpListeners:function(){k.prototype._setUpListeners.call(this);this.on("drop",function(n,o){this.dataDropped(o);this.dropTargetOff()})},_setUpCollectionListeners:function(){k.prototype._setUpCollectionListeners.call(this);this.collection.on("change:deleted",this._handleHdaDeletionChange,this);this.collection.on("change:visible",this._handleHdaVisibleChange,this);this.collection.on("change:purged",function(n){this.model.fetch()},this);return this},_setUpModelListeners:function(){k.prototype._setUpModelListeners.call(this);this.model.on("change:nice_size",this.updateHistoryDiskSize,this);return this},_buildNewRender:function(){var n=k.prototype._buildNewRender.call(this);if(!this.model){return n}if(Galaxy&&Galaxy.currUser&&Galaxy.currUser.id&&Galaxy.currUser.id===this.model.get("user_id")){this._renderTags(n);this._renderAnnotation(n)}return n},renderItems:function(o){var n=k.prototype.renderItems.call(this,o);this._renderCounts(o);return n},_renderCounts:function(p){function o(s,t){return['<a class="',s,'" href="javascript:void(0);">',t,"</a>"].join("")}p=p||this.$el;var n=this.collection.where({deleted:true}),r=this.collection.where({visible:false}),q=[];if(this.views.length){q.push([this.views.length,d("shown")].join(" "))}if(n.length){q.push((!this.showDeleted)?([n.length,o("toggle-deleted-link",d("deleted"))].join(" ")):(o("toggle-deleted-link",d("hide deleted"))))}if(r.length){q.push((!this.showHidden)?([r.length,o("toggle-hidden-link",d("hidden"))].join(" ")):(o("toggle-hidden-link",d("hide hidden"))))}return p.find("> .controls .subtitle").html(q.join(", "))},_renderTags:function(n){var o=this;this.tagsEditor=new l.TagsEditor({model:this.model,el:n.find(".controls .tags-display"),onshowFirstTime:function(){this.render()},onshow:function(){o.toggleHDATagEditors(true,o.fxSpeed)},onhide:function(){o.toggleHDATagEditors(false,o.fxSpeed)},$activator:b({title:d("Edit history tags"),classes:"history-tag-btn",faIcon:"fa-tags"}).appendTo(n.find(".controls .actions"))})},_renderAnnotation:function(n){var o=this;this.annotationEditor=new c.AnnotationEditor({model:this.model,el:n.find(".controls .annotation-display"),onshowFirstTime:function(){this.render()},onshow:function(){o.toggleHDAAnnotationEditors(true,o.fxSpeed)},onhide:function(){o.toggleHDAAnnotationEditors(false,o.fxSpeed)},$activator:b({title:d("Edit history annotation"),classes:"history-annotate-btn",faIcon:"fa-comment"}).appendTo(n.find(".controls .actions"))})},_setUpBehaviors:function(n){n=n||this.$el;k.prototype._setUpBehaviors.call(this,n);if(!this.model){return}if(this.multiselectActions.length){this.actionsPopup=new a(n.find(".list-action-popup-btn"),this.multiselectActions)}if((!Galaxy.currUser||Galaxy.currUser.isAnonymous())||(Galaxy.currUser.id!==this.model.get("user_id"))){return}var o=this,p=".controls .name";n.find(p).attr("title",d("Click to rename history")).tooltip({placement:"bottom"}).make_text_editable({on_finish:function(q){var r=o.model.get("name");if(q&&q!==r){o.$el.find(p).text(q);o.model.save({name:q}).fail(function(){o.$el.find(p).text(o.model.previous("name"))})}else{o.$el.find(p).text(r)}}})},_getActions:function(){var n=this,o=[{html:d("Hide datasets"),func:function(){var p=f.HistoryDatasetAssociation.prototype.hide;n.getSelectedModels().ajaxQueue(p)}},{html:d("Unhide datasets"),func:function(){var p=f.HistoryDatasetAssociation.prototype.unhide;n.getSelectedModels().ajaxQueue(p)}},{html:d("Delete datasets"),func:function(){var p=f.HistoryDatasetAssociation.prototype["delete"];n.getSelectedModels().ajaxQueue(p)}},{html:d("Undelete datasets"),func:function(){var p=f.HistoryDatasetAssociation.prototype.undelete;n.getSelectedModels().ajaxQueue(p)}}];if(n.purgeAllowed){o.push({html:d("Permanently delete datasets"),func:function(){if(confirm(d("This will permanently remove the data in your datasets. Are you sure?"))){var p=f.HistoryDatasetAssociation.prototype.purge;n.getSelectedModels().ajaxQueue(p)}}})}o.push({html:d("Build Dataset List"),func:function(){n.getSelectedModels().promoteToHistoryDatasetCollection(n.model,"list")}});o.push({html:d("Build Dataset Pair"),func:function(){n.getSelectedModels().promoteToHistoryDatasetCollection(n.model,"paired")}});o.push({html:d("Build List of Dataset Pairs"),func:_.bind(n._showPairedCollectionModal,n)});return o},_showPairedCollectionModal:function(){var n=this,o=n.getSelectedModels().toJSON().filter(function(p){return p.history_content_type==="dataset"&&p.state===m.OK});if(o.length){require(["mvc/collection/paired-collection-creator"],function(p){window.creator=p.pairedCollectionCreatorModal(o,{historyId:n.model.id})})}else{}},_attachItems:function(n){this.$list(n).append(this.views.reverse().map(function(o){return o.$el}));return this},_attachView:function(o){var n=this;n.views.unshift(o);n.$list().prepend(o.render(0).$el.hide());o.$el.slideDown(n.fxSpeed)},_getItemViewOptions:function(o){var n=k.prototype._getItemViewOptions.call(this,o);_.extend(n,{purgeAllowed:this.purgeAllowed,tagsEditorShown:(this.tagsEditor&&!this.tagsEditor.hidden),annotationEditorShown:(this.annotationEditor&&!this.annotationEditor.hidden)});return n},_handleHdaDeletionChange:function(n){if(n.get("deleted")&&!this.showDeleted){this.removeItemView(n)}this._renderCounts()},_handleHdaVisibleChange:function(n){if(n.hidden()&&!this.storage.showHidden){this.removeItemView(n)}this._renderCounts()},toggleHDATagEditors:function(n){var o=Array.prototype.slice.call(arguments,1);_.each(this.views,function(p){if(p.tagsEditor){p.tagsEditor.toggle.apply(p.tagsEditor,o)}})},toggleHDAAnnotationEditors:function(n){var o=Array.prototype.slice.call(arguments,1);_.each(this.views,function(p){if(p.annotationEditor){p.annotationEditor.toggle.apply(p.annotationEditor,o)}})},events:_.extend(_.clone(k.prototype.events),{"click .show-selectors-btn":"toggleSelectors","click .toggle-deleted-link":function(n){this.toggleShowDeleted()},"click .toggle-hidden-link":function(n){this.toggleShowHidden()}}),updateHistoryDiskSize:function(){this.$el.find(".history-size").text(this.model.get("nice_size"))},dropTargetOn:function(){if(this.dropTarget){return this}this.dropTarget=true;var o={dragenter:_.bind(this.dragenter,this),dragover:_.bind(this.dragover,this),dragleave:_.bind(this.dragleave,this),drop:_.bind(this.drop,this)};var p=this._renderDropTarget();this.$list().before([this._renderDropTargetHelp(),p]);for(var n in o){if(o.hasOwnProperty(n)){p.on(n,o[n])}}return this},_renderDropTarget:function(){return $("<div/>").addClass("history-drop-target").css({height:"64px",margin:"0px 10px 10px 10px",border:"1px dashed black","border-radius":"3px"})},_renderDropTargetHelp:function(){return $("<div/>").addClass("history-drop-target-help").css({margin:"10px 10px 4px 10px",color:"grey","font-size":"80%","font-style":"italic"}).text(d("Drag datasets here to copy them to the current history"))},dropTargetOff:function(){if(!this.dropTarget){return this}this.dropTarget=false;this.$(".history-drop-target").remove();this.$(".history-drop-target-help").remove();return this},dropTargetToggle:function(){if(this.dropTarget){this.dropTargetOff()}else{this.dropTargetOn()}return this},dragenter:function(n){n.preventDefault();n.stopPropagation();this.$(".history-drop-target").css("border","2px solid black")},dragover:function(n){n.preventDefault();n.stopPropagation()},dragleave:function(n){n.preventDefault();n.stopPropagation();this.$(".history-drop-target").css("border","1px dashed black")},drop:function(p){p.preventDefault();p.dataTransfer.dropEffect="move";var n=this,q=p.dataTransfer.getData("text");try{q=JSON.parse(q)}catch(o){this.warn("error parsing JSON from drop:",q)}this.trigger("droptarget:drop",p,q,n);return false},dataDropped:function(o){var n=this;if(_.isObject(o)&&o.model_class==="HistoryDatasetAssociation"&&o.id){return n.model.contents.copy(o.id)}return jQuery.when()},toString:function(){return"HistoryPanelEdit("+((this.model)?(this.model.get("name")):(""))+")"}});return{HistoryPanelEdit:g}});
\ No newline at end of file
+define(["mvc/history/history-panel","mvc/history/history-contents","mvc/dataset/states","mvc/history/hda-model","mvc/history/hda-li-edit","mvc/history/hdca-li-edit","mvc/tags","mvc/annotations","ui/fa-icon-button","mvc/ui/popup-menu","utils/localization"],function(h,j,m,f,e,i,l,c,b,a,d){var k=h.HistoryPanel;var g=k.extend({HDAViewClass:e.HDAListItemEdit,HDCAViewClass:i.HDCAListItemEdit,initialize:function(n){n=n||{};k.prototype.initialize.call(this,n);this.tagsEditor=null;this.annotationEditor=null;this.purgeAllowed=n.purgeAllowed||false;this.annotationEditorShown=n.annotationEditorShown||false;this.tagsEditorShown=n.tagsEditorShown||false;this.multiselectActions=n.multiselectActions||this._getActions()},_setUpListeners:function(){var n=this;k.prototype._setUpListeners.call(n);n.on("drop",function(o,p){n.dataDropped(p);n.dropTargetOff()});n.on("view:attached view:removed",function(){n._renderCounts()},n)},_setUpCollectionListeners:function(){k.prototype._setUpCollectionListeners.call(this);this.collection.on("change:deleted",this._handleHdaDeletionChange,this);this.collection.on("change:visible",this._handleHdaVisibleChange,this);this.collection.on("change:purged",function(n){this.model.fetch()},this);return this},_setUpModelListeners:function(){k.prototype._setUpModelListeners.call(this);this.model.on("change:nice_size",this.updateHistoryDiskSize,this);return this},_buildNewRender:function(){var n=k.prototype._buildNewRender.call(this);if(!this.model){return n}if(Galaxy&&Galaxy.currUser&&Galaxy.currUser.id&&Galaxy.currUser.id===this.model.get("user_id")){this._renderTags(n);this._renderAnnotation(n)}return n},renderItems:function(o){var n=k.prototype.renderItems.call(this,o);this._renderCounts(o);return n},_renderCounts:function(p){function o(s,t){return['<a class="',s,'" href="javascript:void(0);">',t,"</a>"].join("")}p=p||this.$el;var n=this.collection.where({deleted:true}),r=this.collection.where({visible:false}),q=[];if(this.views.length){q.push([this.views.length,d("shown")].join(" "))}if(n.length){q.push((!this.showDeleted)?([n.length,o("toggle-deleted-link",d("deleted"))].join(" ")):(o("toggle-deleted-link",d("hide deleted"))))}if(r.length){q.push((!this.showHidden)?([r.length,o("toggle-hidden-link",d("hidden"))].join(" ")):(o("toggle-hidden-link",d("hide hidden"))))}return p.find("> .controls .subtitle").html(q.join(", "))},_renderTags:function(n){var o=this;this.tagsEditor=new l.TagsEditor({model:this.model,el:n.find(".controls .tags-display"),onshowFirstTime:function(){this.render()},onshow:function(){o.toggleHDATagEditors(true,o.fxSpeed)},onhide:function(){o.toggleHDATagEditors(false,o.fxSpeed)},$activator:b({title:d("Edit history tags"),classes:"history-tag-btn",faIcon:"fa-tags"}).appendTo(n.find(".controls .actions"))})},_renderAnnotation:function(n){var o=this;this.annotationEditor=new c.AnnotationEditor({model:this.model,el:n.find(".controls .annotation-display"),onshowFirstTime:function(){this.render()},onshow:function(){o.toggleHDAAnnotationEditors(true,o.fxSpeed)},onhide:function(){o.toggleHDAAnnotationEditors(false,o.fxSpeed)},$activator:b({title:d("Edit history annotation"),classes:"history-annotate-btn",faIcon:"fa-comment"}).appendTo(n.find(".controls .actions"))})},_setUpBehaviors:function(n){n=n||this.$el;k.prototype._setUpBehaviors.call(this,n);if(!this.model){return}if(this.multiselectActions.length){this.actionsPopup=new a(n.find(".list-action-popup-btn"),this.multiselectActions)}if((!Galaxy.currUser||Galaxy.currUser.isAnonymous())||(Galaxy.currUser.id!==this.model.get("user_id"))){return}var o=this,p=".controls .name";n.find(p).attr("title",d("Click to rename history")).tooltip({placement:"bottom"}).make_text_editable({on_finish:function(q){var r=o.model.get("name");if(q&&q!==r){o.$el.find(p).text(q);o.model.save({name:q}).fail(function(){o.$el.find(p).text(o.model.previous("name"))})}else{o.$el.find(p).text(r)}}})},_getActions:function(){var n=this,o=[{html:d("Hide datasets"),func:function(){var p=f.HistoryDatasetAssociation.prototype.hide;n.getSelectedModels().ajaxQueue(p)}},{html:d("Unhide datasets"),func:function(){var p=f.HistoryDatasetAssociation.prototype.unhide;n.getSelectedModels().ajaxQueue(p)}},{html:d("Delete datasets"),func:function(){var p=f.HistoryDatasetAssociation.prototype["delete"];n.getSelectedModels().ajaxQueue(p)}},{html:d("Undelete datasets"),func:function(){var p=f.HistoryDatasetAssociation.prototype.undelete;n.getSelectedModels().ajaxQueue(p)}}];if(n.purgeAllowed){o.push({html:d("Permanently delete datasets"),func:function(){if(confirm(d("This will permanently remove the data in your datasets. Are you sure?"))){var p=f.HistoryDatasetAssociation.prototype.purge;n.getSelectedModels().ajaxQueue(p)}}})}o.push({html:d("Build Dataset List"),func:function(){n.getSelectedModels().promoteToHistoryDatasetCollection(n.model,"list")}});o.push({html:d("Build Dataset Pair"),func:function(){n.getSelectedModels().promoteToHistoryDatasetCollection(n.model,"paired")}});o.push({html:d("Build List of Dataset Pairs"),func:_.bind(n._showPairedCollectionModal,n)});return o},_showPairedCollectionModal:function(){var n=this,o=n.getSelectedModels().toJSON().filter(function(p){return p.history_content_type==="dataset"&&p.state===m.OK});if(o.length){require(["mvc/collection/paired-collection-creator"],function(p){window.creator=p.pairedCollectionCreatorModal(o,{historyId:n.model.id})})}else{}},_attachItems:function(n){this.$list(n).append(this.views.reverse().map(function(o){return o.$el}));return this},_attachView:function(o){var n=this;n.views.unshift(o);n.$list().prepend(o.render(0).$el.hide());n.trigger("view:attached",o);o.$el.slideDown(n.fxSpeed,function(){n.trigger("view:attached:rendered")})},_getItemViewOptions:function(o){var n=k.prototype._getItemViewOptions.call(this,o);_.extend(n,{purgeAllowed:this.purgeAllowed,tagsEditorShown:(this.tagsEditor&&!this.tagsEditor.hidden),annotationEditorShown:(this.annotationEditor&&!this.annotationEditor.hidden)});return n},_handleHdaDeletionChange:function(n){if(n.get("deleted")&&!this.showDeleted){this.removeItemView(n)}this._renderCounts()},_handleHdaVisibleChange:function(n){if(n.hidden()&&!this.storage.showHidden){this.removeItemView(n)}this._renderCounts()},toggleHDATagEditors:function(n){var o=Array.prototype.slice.call(arguments,1);_.each(this.views,function(p){if(p.tagsEditor){p.tagsEditor.toggle.apply(p.tagsEditor,o)}})},toggleHDAAnnotationEditors:function(n){var o=Array.prototype.slice.call(arguments,1);_.each(this.views,function(p){if(p.annotationEditor){p.annotationEditor.toggle.apply(p.annotationEditor,o)}})},events:_.extend(_.clone(k.prototype.events),{"click .show-selectors-btn":"toggleSelectors","click .toggle-deleted-link":function(n){this.toggleShowDeleted()},"click .toggle-hidden-link":function(n){this.toggleShowHidden()}}),updateHistoryDiskSize:function(){this.$el.find(".history-size").text(this.model.get("nice_size"))},dropTargetOn:function(){if(this.dropTarget){return this}this.dropTarget=true;var o={dragenter:_.bind(this.dragenter,this),dragover:_.bind(this.dragover,this),dragleave:_.bind(this.dragleave,this),drop:_.bind(this.drop,this)};var p=this._renderDropTarget();this.$list().before([this._renderDropTargetHelp(),p]);for(var n in o){if(o.hasOwnProperty(n)){p.on(n,o[n])}}return this},_renderDropTarget:function(){return $("<div/>").addClass("history-drop-target").css({height:"64px",margin:"0px 10px 10px 10px",border:"1px dashed black","border-radius":"3px"})},_renderDropTargetHelp:function(){return $("<div/>").addClass("history-drop-target-help").css({margin:"10px 10px 4px 10px",color:"grey","font-size":"80%","font-style":"italic"}).text(d("Drag datasets here to copy them to the current history"))},dropTargetOff:function(){if(!this.dropTarget){return this}this.dropTarget=false;this.$(".history-drop-target").remove();this.$(".history-drop-target-help").remove();return this},dropTargetToggle:function(){if(this.dropTarget){this.dropTargetOff()}else{this.dropTargetOn()}return this},dragenter:function(n){n.preventDefault();n.stopPropagation();this.$(".history-drop-target").css("border","2px solid black")},dragover:function(n){n.preventDefault();n.stopPropagation()},dragleave:function(n){n.preventDefault();n.stopPropagation();this.$(".history-drop-target").css("border","1px dashed black")},drop:function(p){p.preventDefault();p.dataTransfer.dropEffect="move";var n=this,q=p.dataTransfer.getData("text");try{q=JSON.parse(q)}catch(o){this.warn("error parsing JSON from drop:",q)}this.trigger("droptarget:drop",p,q,n);return false},dataDropped:function(o){var n=this;if(_.isObject(o)&&o.model_class==="HistoryDatasetAssociation"&&o.id){return n.model.contents.copy(o.id)}return jQuery.when()},toString:function(){return"HistoryPanelEdit("+((this.model)?(this.model.get("name")):(""))+")"}});return{HistoryPanelEdit:g}});
\ No newline at end of file
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef static/scripts/packed/mvc/list/list-panel.js
--- a/static/scripts/packed/mvc/list/list-panel.js
+++ b/static/scripts/packed/mvc/list/list-panel.js
@@ -1,1 +1,1 @@
-define(["mvc/list/list-item","ui/loading-indicator","mvc/base-mvc","utils/localization","ui/search-input"],function(d,f,b,c){var e=Backbone.View.extend(b.LoggableMixin).extend({viewClass:d.ListItemView,collectionClass:Backbone.Collection,tagName:"div",className:"list-panel",fxSpeed:"fast",emptyMsg:c("This list is empty"),noneFoundMsg:c("No matching items found"),searchPlaceholder:c("search"),multiselectActions:[],initialize:function(g,h){g=g||{};if(g.logger){this.logger=g.logger}this.log(this+".initialize:",g);this.fxSpeed=_.has(g,"fxSpeed")?(g.fxSpeed):(this.fxSpeed);this.filters=[];this.searchFor=g.searchFor||"";this.indicator=new f(this.$el);this.selecting=(g.selecting!==undefined)?g.selecting:true;this.selected=g.selected||[];this.lastSelected=null;this.dragItems=g.dragItems||false;this.viewClass=g.viewClass||this.viewClass;this.views=[];this.collection=g.collection||(new this.collectionClass([]));this.filters=g.filters||[];this.$scrollContainer=g.$scrollContainer||this.$scrollContainer;this.title=g.title||"";this.subtitle=g.subtitle||"";this.multiselectActions=g.multiselectActions||this.multiselectActions;this.actionsPopup=null;this._setUpListeners()},freeViews:function(){_.each(this.views,function(g){g.off()});this.views=[];return this},_setUpListeners:function(){this.off();this.on("error",function(h,k,g,j,i){console.error(h,k,g,j,i)},this);this.on("loading",function(){this._showLoadingIndicator("loading...",40)},this);this.on("loading-done",function(){this._hideLoadingIndicator(40)},this);this.once("rendered",function(){this.trigger("rendered:initial",this)},this);if(this.logger){this.on("all",function(g){this.log(this+"",arguments)},this)}this._setUpCollectionListeners();this._setUpViewListeners();return this},_setUpCollectionListeners:function(){this.log(this+"._setUpCollectionListeners",this.collection);this.collection.off();this.collection.on("error",function(h,k,g,j,i){this.trigger("error",h,k,g,j,i)},this);this.collection.on("reset",function(){this.renderItems()},this);this.collection.on("add",this.addItemView,this);this.collection.on("remove",this.removeItemView,this);if(this.logger){this.collection.on("all",function(g){this.info(this+"(collection)",arguments)},this)}return this},_setUpViewListeners:function(){this.log(this+"._setUpViewListeners");this.on("view:selected",function(g,h){if(h&&h.shiftKey&&this.lastSelected){var i=this.viewFromModelId(this.lastSelected);if(i){this.selectRange(g,i)}}else{if(h&&h.altKey&&!this.selecting){this.showSelectors()}}this.selected.push(g.model.id);this.lastSelected=g.model.id},this);this.on("view:de-selected",function(g,h){this.selected=_.without(this.selected,g.model.id)},this)},render:function(h){this.log(this+".render",h);var g=this._buildNewRender();this._setUpBehaviors(g);this._queueNewRender(g,h);return this},_buildNewRender:function(){this.debug(this+"(ListPanel)._buildNewRender");var g=$(this.templates.el({},this));this._renderControls(g);this._renderTitle(g);this._renderSubtitle(g);this._renderSearch(g);this.renderItems(g);return g},_renderControls:function(h){this.debug(this+"(ListPanel)._renderControls");var g=$(this.templates.controls({},this));h.find(".controls").replaceWith(g);return g},_renderTitle:function(g){},_renderSubtitle:function(g){},_queueNewRender:function(h,i){i=(i===undefined)?(this.fxSpeed):(i);var g=this;g.log("_queueNewRender:",h,i);$(g).queue("fx",[function(j){this.$el.fadeOut(i,j)},function(j){g._swapNewRender(h);j()},function(j){this.$el.fadeIn(i,j)},function(j){g.trigger("rendered",g);j()}])},_swapNewRender:function(g){this.$el.empty().attr("class",this.className).append(g.children());if(this.selecting){this.showSelectors(0)}return this},_setUpBehaviors:function(g){g=g||this.$el;g.find(".controls [title]").tooltip({placement:"bottom"});return this},$scrollContainer:function(){return this.$el.parent().parent()},$list:function(g){return(g||this.$el).find("> .list-items")},$messages:function(g){return(g||this.$el).find("> .controls .messages")},$emptyMessage:function(g){return(g||this.$el).find("> .empty-message")},renderItems:function(i){i=i||this.$el;var g=this;g.log(this+".renderItems",i);var h=g.$list(i);g.views=g._filterCollection().map(function(j){return g._createItemView(j).render(0)});h.empty();if(g.views.length){g._attachItems(i);g.$emptyMessage(i).hide()}else{g._renderEmptyMessage(i).show()}return g.views},_filterCollection:function(){var g=this;return g.collection.filter(_.bind(g._filterItem,g))},_filterItem:function(h){var g=this;return(_.every(g.filters.map(function(i){return i.call(h)})))&&(!g.searchFor||h.matchesAll(g.searchFor))},_createItemView:function(i){var j=this._getItemViewClass(i),h=_.extend(this._getItemViewOptions(i),{model:i}),g=new j(h);this._setUpItemViewListeners(g);return g},_getItemViewClass:function(g){return this.viewClass},_getItemViewOptions:function(g){return{fxSpeed:this.fxSpeed,expanded:false,selectable:this.selecting,selected:_.contains(this.selected,g.id),draggable:this.dragItems}},_setUpItemViewListeners:function(h){var g=this;h.on("all",function(){var i=Array.prototype.slice.call(arguments,0);i[0]="view:"+i[0];g.trigger.apply(g,i)});h.on("draggable:dragstart",function(l,i){var j={},k=this.getSelectedModels();if(k.length){j=k.toJSON()}else{j=[i.model.toJSON()]}l.dataTransfer.setData("text",JSON.stringify(j))},this);return g},_attachItems:function(g){this.$list(g).append(this.views.map(function(h){return h.$el}));return this},_renderEmptyMessage:function(g){this.debug("_renderEmptyMessage",g,this.searchFor);var h=this.searchFor?this.noneFoundMsg:this.emptyMsg;return this.$emptyMessage(g).text(h)},expandAll:function(){_.each(this.views,function(g){g.expand()})},collapseAll:function(){_.each(this.views,function(g){g.collapse()})},addItemView:function(j,k,i){this.log(this+".addItemView:",j);var h=this;if(!h._filterItem(j)){return undefined}var g=h._createItemView(j);$(g).queue("fx",[function(l){h.$emptyMessage().fadeOut(h.fxSpeed,l)},function(l){h._attachView(g);l()}]);return g},_attachView:function(h){var g=this;g.views.push(h);g.$list().append(h.render(0).$el.hide());h.$el.slideDown(g.fxSpeed)},removeItemView:function(j,k,i){this.log(this+".removeItemView:",j);var h=this,g=h.viewFromModel(j);if(!g){return undefined}h.views=_.without(h.views,g);$({}).queue("fx",[function(l){g.$el.fadeOut(h.fxSpeed,l)},function(l){g.remove();if(!h.views.length){h._renderEmptyMessage().fadeIn(h.fxSpeed,l)}else{l()}}]);return g},viewFromModelId:function(h){for(var g=0;g<this.views.length;g++){if(this.views[g].model.id===h){return this.views[g]}}return undefined},viewFromModel:function(g){if(!g){return undefined}return this.viewFromModelId(g.id)},viewsWhereModel:function(g){return this.views.filter(function(h){var j=h.model.toJSON();for(var i in g){if(g.hasOwnProperty(i)){if(j[i]!==h.model.get(i)){return false}}}return true})},viewRange:function(j,i){if(j===i){return(j)?([j]):([])}var h=this.views.indexOf(j),g=this.views.indexOf(i);if(h===-1||g===-1){if(h===g){return[]}return(h===-1)?([i]):([j])}return(h<g)?this.views.slice(h,g+1):this.views.slice(g,h+1)},_renderSearch:function(g){g.find(".controls .search-input").searchInput({placeholder:this.searchPlaceholder,initialVal:this.searchFor,onfirstsearch:_.bind(this._firstSearch,this),onsearch:_.bind(this.searchItems,this),onclear:_.bind(this.clearSearch,this)});return g},_firstSearch:function(g){this.log("onFirstSearch",g);return this.searchItems(g)},searchItems:function(g){this.searchFor=g;this.trigger("search:searching",g,this);this.renderItems();this.$("> .controls .search-query").val(g);return this},clearSearch:function(g){this.searchFor="";this.trigger("search:clear",this);this.renderItems();this.$("> .controls .search-query").val("");return this},showSelectors:function(g){g=(g!==undefined)?(g):(this.fxSpeed);this.selecting=true;this.$(".list-actions").slideDown(g);_.each(this.views,function(h){h.showSelector(g)})},hideSelectors:function(g){g=(g!==undefined)?(g):(this.fxSpeed);this.selecting=false;this.$(".list-actions").slideUp(g);_.each(this.views,function(h){h.hideSelector(g)});this.selected=[];this.lastSelected=null},toggleSelectors:function(){if(!this.selecting){this.showSelectors()}else{this.hideSelectors()}},selectAll:function(g){_.each(this.views,function(h){h.select(g)})},deselectAll:function(g){this.lastSelected=null;_.each(this.views,function(h){h.deselect(g)})},selectRange:function(i,h){var g=this.viewRange(i,h);_.each(g,function(j){j.select()});return g},getSelectedViews:function(){return _.filter(this.views,function(g){return g.selected})},getSelectedModels:function(){return new this.collection.constructor(_.map(this.getSelectedViews(),function(g){return g.model}))},_showLoadingIndicator:function(h,g,i){this.debug("_showLoadingIndicator",this.indicator,h,g,i);g=(g!==undefined)?(g):(this.fxSpeed);if(!this.indicator){this.indicator=new f(this.$el,this.$el.parent());this.debug("\t created",this.indicator)}if(!this.$el.is(":visible")){this.indicator.show(0,i)}else{this.$el.fadeOut(g);this.indicator.show(h,g,i)}},_hideLoadingIndicator:function(g,h){this.debug("_hideLoadingIndicator",this.indicator,g,h);g=(g!==undefined)?(g):(this.fxSpeed);if(this.indicator){this.indicator.hide(g,h)}},scrollPosition:function(){return this.$scrollContainer().scrollTop()},scrollTo:function(h,g){g=g||0;this.$scrollContainer().animate({scrollTop:h},g);return this},scrollToTop:function(g){return this.scrollTo(0,g)},scrollToItem:function(g,i){if(!g){return this}var h=g.$el.position().top;return this.scrollTo(h,i)},scrollToId:function(h,g){return this.scrollToItem(this.viewFromModelId(h),g)},events:{"click .select-all":"selectAll","click .deselect-all":"deselectAll"},toString:function(){return"ListPanel("+this.collection+")"}});e.prototype.templates=(function(){var h=b.wrapTemplate(["<div>",'<div class="controls"></div>','<div class="list-items"></div>','<div class="empty-message infomessagesmall"></div>',"</div>"]);var g=b.wrapTemplate(['<div class="controls">','<div class="title">','<div class="name"><%= view.title %></div>',"</div>",'<div class="subtitle"><%= view.subtitle %></div>','<div class="actions"></div>','<div class="messages"></div>','<div class="search">','<div class="search-input"></div>',"</div>",'<div class="list-actions">','<div class="btn-group">','<button class="select-all btn btn-default"','data-mode="select">',c("All"),"</button>",'<button class="deselect-all btn btn-default"','data-mode="select">',c("None"),"</button>","</div>","</div>","</div>"]);return{el:h,controls:g}}());var a=e.extend({modelCollectionKey:"contents",initialize:function(g){e.prototype.initialize.call(this,g);this.selecting=(g.selecting!==undefined)?g.selecting:false;this.setModel(this.model,g)},setModel:function(h,g){g=g||{};this.debug(this+".setModel:",h,g);this.freeModel();this.freeViews();if(h){var i=this.model?this.model.get("id"):null;this.model=h;if(this.logger){this.model.logger=this.logger}this._setUpModelListeners();this.collection.off();this.collection=(this.model[this.modelCollectionKey])?this.model[this.modelCollectionKey]:(g.collection||(new this.collectionClass([])));this._setUpCollectionListeners();if(i&&h.get("id")!==i){this.trigger("new-model",this)}}return this},freeModel:function(){if(this.model){this.stopListening(this.model)}return this},_setUpModelListeners:function(){this.log(this+"._setUpModelListeners",this.model);this.model.on("error",function(){var g=Array.prototype.slice.call(arguments,0);g.unshift("error");this.trigger.apply(this,g)},this);return this},_renderControls:function(h){this.debug(this+"(ListPanel)._renderControls");var i=this.model?this.model.toJSON():{},g=$(this.templates.controls(i,this));h.find(".controls").replaceWith(g);return g},toString:function(){return"ModelListPanel("+this.model+")"}});a.prototype.templates=(function(){var g=b.wrapTemplate(['<div class="controls">','<div class="title">','<div class="name"><%= model.name %></div>',"</div>",'<div class="subtitle"><%= view.subtitle %></div>','<div class="actions"></div>','<div class="messages"></div>','<div class="search">','<div class="search-input"></div>',"</div>",'<div class="list-actions">','<div class="btn-group">','<button class="select-all btn btn-default"','data-mode="select">',c("All"),"</button>",'<button class="deselect-all btn btn-default"','data-mode="select">',c("None"),"</button>","</div>","</div>","</div>"]);return _.extend(_.clone(e.prototype.templates),{controls:g})}());return{ListPanel:e,ModelListPanel:a}});
\ No newline at end of file
+define(["mvc/list/list-item","ui/loading-indicator","mvc/base-mvc","utils/localization","ui/search-input"],function(d,f,b,c){var e=Backbone.View.extend(b.LoggableMixin).extend({viewClass:d.ListItemView,collectionClass:Backbone.Collection,tagName:"div",className:"list-panel",fxSpeed:"fast",emptyMsg:c("This list is empty"),noneFoundMsg:c("No matching items found"),searchPlaceholder:c("search"),multiselectActions:[],initialize:function(g,h){g=g||{};if(g.logger){this.logger=g.logger}this.log(this+".initialize:",g);this.fxSpeed=_.has(g,"fxSpeed")?(g.fxSpeed):(this.fxSpeed);this.filters=[];this.searchFor=g.searchFor||"";this.indicator=new f(this.$el);this.selecting=(g.selecting!==undefined)?g.selecting:true;this.selected=g.selected||[];this.lastSelected=null;this.dragItems=g.dragItems||false;this.viewClass=g.viewClass||this.viewClass;this.views=[];this.collection=g.collection||(new this.collectionClass([]));this.filters=g.filters||[];this.$scrollContainer=g.$scrollContainer||this.$scrollContainer;this.title=g.title||"";this.subtitle=g.subtitle||"";this.multiselectActions=g.multiselectActions||this.multiselectActions;this.actionsPopup=null;this._setUpListeners()},freeViews:function(){_.each(this.views,function(g){g.off()});this.views=[];return this},_setUpListeners:function(){this.off();this.on("error",function(h,k,g,j,i){console.error(h,k,g,j,i)},this);this.on("loading",function(){this._showLoadingIndicator("loading...",40)},this);this.on("loading-done",function(){this._hideLoadingIndicator(40)},this);this.once("rendered",function(){this.trigger("rendered:initial",this)},this);if(this.logger){this.on("all",function(g){this.log(this+"",arguments)},this)}this._setUpCollectionListeners();this._setUpViewListeners();return this},_setUpCollectionListeners:function(){this.log(this+"._setUpCollectionListeners",this.collection);this.collection.off();this.collection.on("error",function(h,k,g,j,i){this.trigger("error",h,k,g,j,i)},this);this.collection.on("reset",function(){this.renderItems()},this);this.collection.on("add",this.addItemView,this);this.collection.on("remove",this.removeItemView,this);if(this.logger){this.collection.on("all",function(g){this.info(this+"(collection)",arguments)},this)}return this},_setUpViewListeners:function(){this.log(this+"._setUpViewListeners");this.on("view:selected",function(g,h){if(h&&h.shiftKey&&this.lastSelected){var i=this.viewFromModelId(this.lastSelected);if(i){this.selectRange(g,i)}}else{if(h&&h.altKey&&!this.selecting){this.showSelectors()}}this.selected.push(g.model.id);this.lastSelected=g.model.id},this);this.on("view:de-selected",function(g,h){this.selected=_.without(this.selected,g.model.id)},this)},render:function(h){this.log(this+".render",h);var g=this._buildNewRender();this._setUpBehaviors(g);this._queueNewRender(g,h);return this},_buildNewRender:function(){this.debug(this+"(ListPanel)._buildNewRender");var g=$(this.templates.el({},this));this._renderControls(g);this._renderTitle(g);this._renderSubtitle(g);this._renderSearch(g);this.renderItems(g);return g},_renderControls:function(h){this.debug(this+"(ListPanel)._renderControls");var g=$(this.templates.controls({},this));h.find(".controls").replaceWith(g);return g},_renderTitle:function(g){},_renderSubtitle:function(g){},_queueNewRender:function(h,i){i=(i===undefined)?(this.fxSpeed):(i);var g=this;g.log("_queueNewRender:",h,i);$(g).queue("fx",[function(j){this.$el.fadeOut(i,j)},function(j){g._swapNewRender(h);j()},function(j){this.$el.fadeIn(i,j)},function(j){g.trigger("rendered",g);j()}])},_swapNewRender:function(g){this.$el.empty().attr("class",this.className).append(g.children());if(this.selecting){this.showSelectors(0)}return this},_setUpBehaviors:function(g){g=g||this.$el;g.find(".controls [title]").tooltip({placement:"bottom"});return this},$scrollContainer:function(){return this.$el.parent().parent()},$list:function(g){return(g||this.$el).find("> .list-items")},$messages:function(g){return(g||this.$el).find("> .controls .messages")},$emptyMessage:function(g){return(g||this.$el).find("> .empty-message")},renderItems:function(i){i=i||this.$el;var g=this;g.log(this+".renderItems",i);var h=g.$list(i);g.views=g._filterCollection().map(function(j){return g._createItemView(j).render(0)});h.empty();if(g.views.length){g._attachItems(i);g.$emptyMessage(i).hide()}else{g._renderEmptyMessage(i).show()}return g.views},_filterCollection:function(){var g=this;return g.collection.filter(_.bind(g._filterItem,g))},_filterItem:function(h){var g=this;return(_.every(g.filters.map(function(i){return i.call(h)})))&&(!g.searchFor||h.matchesAll(g.searchFor))},_createItemView:function(i){var j=this._getItemViewClass(i),h=_.extend(this._getItemViewOptions(i),{model:i}),g=new j(h);this._setUpItemViewListeners(g);return g},_getItemViewClass:function(g){return this.viewClass},_getItemViewOptions:function(g){return{fxSpeed:this.fxSpeed,expanded:false,selectable:this.selecting,selected:_.contains(this.selected,g.id),draggable:this.dragItems}},_setUpItemViewListeners:function(h){var g=this;h.on("all",function(){var i=Array.prototype.slice.call(arguments,0);i[0]="view:"+i[0];g.trigger.apply(g,i)});h.on("draggable:dragstart",function(l,i){var j={},k=this.getSelectedModels();if(k.length){j=k.toJSON()}else{j=[i.model.toJSON()]}l.dataTransfer.setData("text",JSON.stringify(j))},this);return g},_attachItems:function(g){this.$list(g).append(this.views.map(function(h){return h.$el}));return this},_renderEmptyMessage:function(g){this.debug("_renderEmptyMessage",g,this.searchFor);var h=this.searchFor?this.noneFoundMsg:this.emptyMsg;return this.$emptyMessage(g).text(h)},expandAll:function(){_.each(this.views,function(g){g.expand()})},collapseAll:function(){_.each(this.views,function(g){g.collapse()})},addItemView:function(j,k,i){this.log(this+".addItemView:",j);var h=this;if(!h._filterItem(j)){return undefined}var g=h._createItemView(j);$(g).queue("fx",[function(l){h.$emptyMessage().fadeOut(h.fxSpeed,l)},function(l){h._attachView(g);l()}]);return g},_attachView:function(h){var g=this;g.views.push(h);g.$list().append(h.render(0).$el.hide());g.trigger("view:attached",h);h.$el.slideDown(g.fxSpeed,function(){g.trigger("view:attached:rendered")})},removeItemView:function(j,k,i){this.log(this+".removeItemView:",j);var h=this,g=h.viewFromModel(j);if(!g){return undefined}h.views=_.without(h.views,g);h.trigger("view:removed",g);$({}).queue("fx",[function(l){g.$el.fadeOut(h.fxSpeed,l)},function(l){g.remove();h.trigger("view:removed:rendered");if(!h.views.length){h._renderEmptyMessage().fadeIn(h.fxSpeed,l)}else{l()}}]);return g},viewFromModelId:function(h){for(var g=0;g<this.views.length;g++){if(this.views[g].model.id===h){return this.views[g]}}return undefined},viewFromModel:function(g){if(!g){return undefined}return this.viewFromModelId(g.id)},viewsWhereModel:function(g){return this.views.filter(function(h){var j=h.model.toJSON();for(var i in g){if(g.hasOwnProperty(i)){if(j[i]!==h.model.get(i)){return false}}}return true})},viewRange:function(j,i){if(j===i){return(j)?([j]):([])}var h=this.views.indexOf(j),g=this.views.indexOf(i);if(h===-1||g===-1){if(h===g){return[]}return(h===-1)?([i]):([j])}return(h<g)?this.views.slice(h,g+1):this.views.slice(g,h+1)},_renderSearch:function(g){g.find(".controls .search-input").searchInput({placeholder:this.searchPlaceholder,initialVal:this.searchFor,onfirstsearch:_.bind(this._firstSearch,this),onsearch:_.bind(this.searchItems,this),onclear:_.bind(this.clearSearch,this)});return g},_firstSearch:function(g){this.log("onFirstSearch",g);return this.searchItems(g)},searchItems:function(g){this.searchFor=g;this.trigger("search:searching",g,this);this.renderItems();this.$("> .controls .search-query").val(g);return this},clearSearch:function(g){this.searchFor="";this.trigger("search:clear",this);this.renderItems();this.$("> .controls .search-query").val("");return this},showSelectors:function(g){g=(g!==undefined)?(g):(this.fxSpeed);this.selecting=true;this.$(".list-actions").slideDown(g);_.each(this.views,function(h){h.showSelector(g)})},hideSelectors:function(g){g=(g!==undefined)?(g):(this.fxSpeed);this.selecting=false;this.$(".list-actions").slideUp(g);_.each(this.views,function(h){h.hideSelector(g)});this.selected=[];this.lastSelected=null},toggleSelectors:function(){if(!this.selecting){this.showSelectors()}else{this.hideSelectors()}},selectAll:function(g){_.each(this.views,function(h){h.select(g)})},deselectAll:function(g){this.lastSelected=null;_.each(this.views,function(h){h.deselect(g)})},selectRange:function(i,h){var g=this.viewRange(i,h);_.each(g,function(j){j.select()});return g},getSelectedViews:function(){return _.filter(this.views,function(g){return g.selected})},getSelectedModels:function(){return new this.collection.constructor(_.map(this.getSelectedViews(),function(g){return g.model}))},_showLoadingIndicator:function(h,g,i){this.debug("_showLoadingIndicator",this.indicator,h,g,i);g=(g!==undefined)?(g):(this.fxSpeed);if(!this.indicator){this.indicator=new f(this.$el,this.$el.parent());this.debug("\t created",this.indicator)}if(!this.$el.is(":visible")){this.indicator.show(0,i)}else{this.$el.fadeOut(g);this.indicator.show(h,g,i)}},_hideLoadingIndicator:function(g,h){this.debug("_hideLoadingIndicator",this.indicator,g,h);g=(g!==undefined)?(g):(this.fxSpeed);if(this.indicator){this.indicator.hide(g,h)}},scrollPosition:function(){return this.$scrollContainer().scrollTop()},scrollTo:function(h,g){g=g||0;this.$scrollContainer().animate({scrollTop:h},g);return this},scrollToTop:function(g){return this.scrollTo(0,g)},scrollToItem:function(g,i){if(!g){return this}var h=g.$el.position().top;return this.scrollTo(h,i)},scrollToId:function(h,g){return this.scrollToItem(this.viewFromModelId(h),g)},events:{"click .select-all":"selectAll","click .deselect-all":"deselectAll"},toString:function(){return"ListPanel("+this.collection+")"}});e.prototype.templates=(function(){var h=b.wrapTemplate(["<div>",'<div class="controls"></div>','<div class="list-items"></div>','<div class="empty-message infomessagesmall"></div>',"</div>"]);var g=b.wrapTemplate(['<div class="controls">','<div class="title">','<div class="name"><%= view.title %></div>',"</div>",'<div class="subtitle"><%= view.subtitle %></div>','<div class="actions"></div>','<div class="messages"></div>','<div class="search">','<div class="search-input"></div>',"</div>",'<div class="list-actions">','<div class="btn-group">','<button class="select-all btn btn-default"','data-mode="select">',c("All"),"</button>",'<button class="deselect-all btn btn-default"','data-mode="select">',c("None"),"</button>","</div>","</div>","</div>"]);return{el:h,controls:g}}());var a=e.extend({modelCollectionKey:"contents",initialize:function(g){e.prototype.initialize.call(this,g);this.selecting=(g.selecting!==undefined)?g.selecting:false;this.setModel(this.model,g)},setModel:function(h,g){g=g||{};this.debug(this+".setModel:",h,g);this.freeModel();this.freeViews();if(h){var i=this.model?this.model.get("id"):null;this.model=h;if(this.logger){this.model.logger=this.logger}this._setUpModelListeners();this.collection.off();this.collection=(this.model[this.modelCollectionKey])?this.model[this.modelCollectionKey]:(g.collection||(new this.collectionClass([])));this._setUpCollectionListeners();if(i&&h.get("id")!==i){this.trigger("new-model",this)}}return this},freeModel:function(){if(this.model){this.stopListening(this.model)}return this},_setUpModelListeners:function(){this.log(this+"._setUpModelListeners",this.model);this.model.on("error",function(){var g=Array.prototype.slice.call(arguments,0);g.unshift("error");this.trigger.apply(this,g)},this);return this},_renderControls:function(h){this.debug(this+"(ListPanel)._renderControls");var i=this.model?this.model.toJSON():{},g=$(this.templates.controls(i,this));h.find(".controls").replaceWith(g);return g},toString:function(){return"ModelListPanel("+this.model+")"}});a.prototype.templates=(function(){var g=b.wrapTemplate(['<div class="controls">','<div class="title">','<div class="name"><%= model.name %></div>',"</div>",'<div class="subtitle"><%= view.subtitle %></div>','<div class="actions"></div>','<div class="messages"></div>','<div class="search">','<div class="search-input"></div>',"</div>",'<div class="list-actions">','<div class="btn-group">','<button class="select-all btn btn-default"','data-mode="select">',c("All"),"</button>",'<button class="deselect-all btn btn-default"','data-mode="select">',c("None"),"</button>","</div>","</div>","</div>"]);return _.extend(_.clone(e.prototype.templates),{controls:g})}());return{ListPanel:e,ModelListPanel:a}});
\ No newline at end of file
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef static/scripts/packed/mvc/upload/upload-row.js
--- a/static/scripts/packed/mvc/upload/upload-row.js
+++ b/static/scripts/packed/mvc/upload/upload-row.js
@@ -1,1 +1,1 @@
-define(["utils/utils","mvc/upload/upload-model","mvc/upload/upload-settings","mvc/ui/ui-popover","mvc/ui/ui-select"],function(d,b,a,c,e){return Backbone.View.extend({options:{padding:8},status_classes:{init:"upload-icon-button fa fa-trash-o",queued:"upload-icon fa fa-spinner fa-spin",running:"upload-icon fa fa-spinner fa-spin",success:"upload-icon-button fa fa-check",error:"upload-icon-button fa fa-exclamation-triangle"},settings:null,select_genome:null,select_extension:null,initialize:function(k,h){this.app=k;var f=this;this.model=new b.Model(h);this.setElement(this._template(h));var j=this.$el;this.settings=new c.View({title:"Upload configuration",container:j.find("#settings"),placement:"bottom"});var i=this.app.select_genome.value();this.select_genome=new e.View({css:"genome",onchange:function(l){f.model.set("genome",l);f.app.updateGenome(l)},data:f.app.list_genomes,container:j.find("#genome"),value:i});this.model.set("genome",i);var g=this.app.select_extension.value();this.select_extension=new e.View({css:"extension",onchange:function(l){f.model.set("extension",l);f.app.updateExtension(l)},data:f.app.list_extensions,container:j.find("#extension"),value:g});this.model.set("extension",g);j.find("#symbol").on("click",function(){f._removeRow()});j.find("#extension-info").on("click",function(l){f.app.showExtensionInfo({$el:$(l.target),title:f.select_extension.text(),extension:f.select_extension.value()})}).on("mousedown",function(l){l.preventDefault()});j.find("#settings").on("click",function(l){f._showSettings()}).on("mousedown",function(l){l.preventDefault()});j.find("#text-content").on("keyup",function(l){f.model.set("url_paste",$(l.target).val());f.model.set("file_size",$(l.target).val().length)});j.find("#space_to_tabs").on("change",function(l){f.model.set("space_to_tabs",$(l.target).prop("checked"))});this.model.on("change:percentage",function(){f._refreshPercentage()});this.model.on("change:status",function(){f._refreshStatus()});this.model.on("change:info",function(){f._refreshInfo()});this.model.on("change:genome",function(){f._refreshGenome()});this.model.on("change:extension",function(){f._refreshExtension()});this.model.on("change:file_size",function(){f._refreshFileSize()});this.model.on("remove",function(){f.remove()});this.app.collection.on("reset",function(){f.remove()})},render:function(){var m=this.model.get("file_name");var g=this.model.get("file_size");var j=this.model.get("file_mode");var i=this.$el;i.find("#title").html(m);i.find("#size").html(d.bytesToString(g));i.find("#mode").removeClass().addClass("mode");if(j=="new"){var l=i.find("#text");var k=this.options.padding;var h=i.width()-2*k;var f=i.height()-k;l.css("width",h+"px");l.css("top",f+"px");i.height(f+l.height()+2*k);l.show();i.find("#mode").addClass("fa fa-pencil")}if(j=="local"){i.find("#mode").addClass("fa fa-laptop")}if(j=="ftp"){i.find("#mode").addClass("fa fa-code-fork")}},remove:function(){this.select_genome.remove();this.select_extension.remove();Backbone.View.prototype.remove.apply(this)},_refreshExtension:function(){this.select_extension.value(this.model.get("extension"))},_refreshGenome:function(){this.select_genome.value(this.model.get("genome"))},_refreshInfo:function(){var f=this.model.get("info");if(f){this.$el.find("#info").html("<strong>Failed: </strong>"+f).show()}else{this.$el.find("#info").hide()}},_refreshPercentage:function(){var f=parseInt(this.model.get("percentage"));this.$el.find(".progress-bar").css({width:f+"%"});if(f!=100){this.$el.find("#percentage").html(f+"%")}else{this.$el.find("#percentage").html("Adding to history...")}},_refreshStatus:function(){var g=this.$el;var f=this.model.get("status");var i=this.status_classes[f];var h=this.$el.find("#symbol");h.removeClass();h.addClass(i);if(f=="init"){this.select_genome.enable();this.select_extension.enable();g.find("#text-content").attr("disabled",false);g.find("#space_to_tabs").attr("disabled",false)}else{this.select_genome.disable();this.select_extension.disable();g.find("#text-content").attr("disabled",true);g.find("#space_to_tabs").attr("disabled",true)}if(f=="success"){g.addClass("success");g.find("#percentage").html("100%")}if(f=="error"){g.addClass("danger");g.find(".progress").remove()}},_refreshFileSize:function(){var f=this.model.get("file_size");this.$el.find("#size").html(d.bytesToString(f))},_removeRow:function(){var f=this.model.get("status");if(f=="init"||f=="success"||f=="error"){this.app.collection.remove(this.model)}},_showSettings:function(){if(!this.settings.visible){this.settings.empty();this.settings.append((new a(this)).$el);this.settings.show()}else{this.settings.hide()}},_template:function(f){return'<tr id="upload-item-'+f.id+'" class="upload-item"><td><div class="name-column"><div id="mode"></div><div id="title" class="title"></div><div id="text" class="text"><div class="text-info">You can tell Galaxy to download data from web by entering URL in this box (one per line). You can also directly paste the contents of a file.</div><textarea id="text-content" class="text-content form-control"></textarea></div></div></td><td><div id="size" class="size"></div></td><td><div id="extension" class="extension" style="float: left;"/>  <div id="extension-info" class="upload-icon-button fa fa-search"/></td><td><div id="genome" class="genome" /></td><td><div id="settings" class="upload-icon-button fa fa-gear"></div><td><div id="info" class="info"><div class="progress"><div class="progress-bar progress-bar-success"></div><div id="percentage" class="percentage">0%</div></div></div></td><td><div id="symbol" class="'+this.status_classes.init+'"></div></td></tr>'}})});
\ No newline at end of file
+define(["utils/utils","mvc/upload/upload-model","mvc/upload/upload-settings","mvc/ui/ui-popover","mvc/ui/ui-select"],function(d,b,a,c,e){return Backbone.View.extend({options:{padding:8},status_classes:{init:"upload-icon-button fa fa-trash-o",queued:"upload-icon fa fa-spinner fa-spin",running:"upload-icon fa fa-spinner fa-spin",success:"upload-icon-button fa fa-check",error:"upload-icon-button fa fa-exclamation-triangle"},settings:null,select_genome:null,select_extension:null,initialize:function(k,h){this.app=k;var f=this;this.model=new b.Model(h);this.setElement(this._template(h));var j=this.$el;this.settings=new c.View({title:"Upload configuration",container:j.find("#settings"),placement:"bottom"});var i=this.app.select_genome.value();this.select_genome=new e.View({css:"genome",onchange:function(l){f.model.set("genome",l);f.app.updateGenome(l,true)},data:f.app.list_genomes,container:j.find("#genome"),value:i});this.model.set("genome",i);var g=this.app.select_extension.value();this.select_extension=new e.View({css:"extension",onchange:function(l){f.model.set("extension",l);f.app.updateExtension(l,true)},data:f.app.list_extensions,container:j.find("#extension"),value:g});this.model.set("extension",g);j.find("#symbol").on("click",function(){f._removeRow()});j.find("#extension-info").on("click",function(l){f.app.showExtensionInfo({$el:$(l.target),title:f.select_extension.text(),extension:f.select_extension.value()})}).on("mousedown",function(l){l.preventDefault()});j.find("#settings").on("click",function(l){f._showSettings()}).on("mousedown",function(l){l.preventDefault()});j.find("#text-content").on("keyup",function(l){f.model.set("url_paste",$(l.target).val());f.model.set("file_size",$(l.target).val().length)});j.find("#space_to_tabs").on("change",function(l){f.model.set("space_to_tabs",$(l.target).prop("checked"))});this.model.on("change:percentage",function(){f._refreshPercentage()});this.model.on("change:status",function(){f._refreshStatus()});this.model.on("change:info",function(){f._refreshInfo()});this.model.on("change:genome",function(){f._refreshGenome()});this.model.on("change:extension",function(){f._refreshExtension()});this.model.on("change:file_size",function(){f._refreshFileSize()});this.model.on("remove",function(){f.remove()});this.app.collection.on("reset",function(){f.remove()})},render:function(){var m=this.model.get("file_name");var g=this.model.get("file_size");var j=this.model.get("file_mode");var i=this.$el;i.find("#title").html(m);i.find("#size").html(d.bytesToString(g));i.find("#mode").removeClass().addClass("mode");if(j=="new"){var l=i.find("#text");var k=this.options.padding;var h=i.width()-2*k;var f=i.height()-k;l.css("width",h+"px");l.css("top",f+"px");i.height(f+l.height()+2*k);l.show();i.find("#mode").addClass("fa fa-pencil")}if(j=="local"){i.find("#mode").addClass("fa fa-laptop")}if(j=="ftp"){i.find("#mode").addClass("fa fa-code-fork")}},remove:function(){this.select_genome.remove();this.select_extension.remove();Backbone.View.prototype.remove.apply(this)},_refreshExtension:function(){this.select_extension.value(this.model.get("extension"))},_refreshGenome:function(){this.select_genome.value(this.model.get("genome"))},_refreshInfo:function(){var f=this.model.get("info");if(f){this.$el.find("#info").html("<strong>Failed: </strong>"+f).show()}else{this.$el.find("#info").hide()}},_refreshPercentage:function(){var f=parseInt(this.model.get("percentage"));this.$el.find(".progress-bar").css({width:f+"%"});if(f!=100){this.$el.find("#percentage").html(f+"%")}else{this.$el.find("#percentage").html("Adding to history...")}},_refreshStatus:function(){var g=this.$el;var f=this.model.get("status");var i=this.status_classes[f];var h=this.$el.find("#symbol");h.removeClass();h.addClass(i);if(f=="init"){this.select_genome.enable();this.select_extension.enable();g.find("#text-content").attr("disabled",false);g.find("#space_to_tabs").attr("disabled",false)}else{this.select_genome.disable();this.select_extension.disable();g.find("#text-content").attr("disabled",true);g.find("#space_to_tabs").attr("disabled",true)}if(f=="success"){g.addClass("success");g.find("#percentage").html("100%")}if(f=="error"){g.addClass("danger");g.find(".progress").remove()}},_refreshFileSize:function(){var f=this.model.get("file_size");this.$el.find("#size").html(d.bytesToString(f))},_removeRow:function(){var f=this.model.get("status");if(f=="init"||f=="success"||f=="error"){this.app.collection.remove(this.model)}},_showSettings:function(){if(!this.settings.visible){this.settings.empty();this.settings.append((new a(this)).$el);this.settings.show()}else{this.settings.hide()}},_template:function(f){return'<tr id="upload-item-'+f.id+'" class="upload-item"><td><div class="name-column"><div id="mode"></div><div id="title" class="title"></div><div id="text" class="text"><div class="text-info">You can tell Galaxy to download data from web by entering URL in this box (one per line). You can also directly paste the contents of a file.</div><textarea id="text-content" class="text-content form-control"></textarea></div></div></td><td><div id="size" class="size"></div></td><td><div id="extension" class="extension" style="float: left;"/>  <div id="extension-info" class="upload-icon-button fa fa-search"/></td><td><div id="genome" class="genome" /></td><td><div id="settings" class="upload-icon-button fa fa-gear"></div><td><div id="info" class="info"><div class="progress"><div class="progress-bar progress-bar-success"></div><div id="percentage" class="percentage">0%</div></div></div></td><td><div id="symbol" class="'+this.status_classes.init+'"></div></td></tr>'}})});
\ No newline at end of file
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef static/scripts/packed/mvc/upload/upload-view.js
--- a/static/scripts/packed/mvc/upload/upload-view.js
+++ b/static/scripts/packed/mvc/upload/upload-view.js
@@ -1,1 +1,1 @@
-define(["utils/utils","mvc/upload/upload-button","mvc/upload/upload-model","mvc/upload/upload-row","mvc/upload/upload-ftp","mvc/ui/ui-popover","mvc/ui/ui-modal","mvc/ui/ui-select","utils/uploadbox"],function(f,e,c,b,g,d,a,h){return Backbone.View.extend({options:{nginx_upload_path:""},modal:null,ui_button:null,uploadbox:null,current_history:null,select_extension:null,select_genome:null,upload_size:0,list_extensions:[],list_genomes:[],auto:{id:"auto",text:"Auto-detect",description:"This system will try to detect the file type automatically. If your file is not detected properly as one of the known formats, it most likely means that it has some format problems (e.g., different number of columns on different rows). You can still coerce the system to set your data to the format you think it should be. You can also upload compressed files, which will automatically be decompressed."},collection:new c.Collection(),ftp:null,counter:{announce:0,success:0,error:0,running:0,reset:function(){this.announce=this.success=this.error=this.running=0}},initialize:function(j){var i=this;if(j){this.options=_.defaults(j,this.options)}this.ui_button=new e.Model({icon:"fa-upload",tooltip:"Download from URL or upload files from disk",label:"Load Data",onclick:function(k){if(k){k.preventDefault();i.show()}},onunload:function(){if(i.counter.running>0){return"Several uploads are still processing."}}});$(".with-upload-button").append((new e.View(this.ui_button)).$el);var i=this;f.get({url:galaxy_config.root+"api/datatypes?extension_only=False",success:function(k){for(key in k){i.list_extensions.push({id:k[key].extension,text:k[key].extension,description:k[key].description,description_url:k[key].description_url})}i.list_extensions.sort(function(m,l){return m.id>l.id?1:m.id<l.id?-1:0});if(!i.options.datatypes_disable_auto){i.list_extensions.unshift(i.auto)}i.default_extension=i.list_extensions[0]&&i.list_extensions[0].id}});f.get({url:galaxy_config.root+"api/genomes",success:function(k){for(key in k){i.list_genomes.push({id:k[key][1],text:k[key][0]})}i.list_genomes.sort(function(m,l){return m.id>l.id?1:m.id<l.id?-1:0});i.default_genome=i.list_genomes[0]&&i.list_genomes[0].id}});this.collection.on("remove",function(k){i._eventRemove(k)})},show:function(){var i=this;if(!Galaxy.currHistoryPanel||!Galaxy.currHistoryPanel.model){window.setTimeout(function(){i.show()},500);return}if(!this.modal){var i=this;this.modal=new a.View({title:"Download data directly from web or upload files from your disk",body:this._template("upload-box","upload-info"),buttons:{"Choose local file":function(){i.uploadbox.select()},"Choose FTP file":function(){i._eventFtp()},"Paste/Fetch data":function(){i._eventCreate()},Start:function(){i._eventStart()},Pause:function(){i._eventStop()},Reset:function(){i._eventReset()},Close:function(){i.modal.hide()},},height:"400",width:"900",closing_events:true});this.setElement("#upload-box");var i=this;this.uploadbox=this.$el.uploadbox({announce:function(k,l,m){i._eventAnnounce(k,l,m)},initialize:function(k,l,m){return i._eventInitialize(k,l,m)},progress:function(k,l,m){i._eventProgress(k,l,m)},success:function(k,l,m){i._eventSuccess(k,l,m)},error:function(k,l,m){i._eventError(k,l,m)},complete:function(){i._eventComplete()}});var j=this.modal.getButton("Choose FTP file");this.ftp=new d.View({title:"FTP files",container:j});this.select_extension=new h.View({css:"header-selection",data:this.list_extensions,container:this.$el.parent().find("#header-extension"),value:this.default_extension,onchange:function(k){i.updateExtension(k)}});i.$el.parent().find("#header-extension-info").on("click",function(k){i.showExtensionInfo({$el:$(k.target),title:i.select_extension.text(),extension:i.select_extension.value(),placement:"top"})}).on("mousedown",function(k){k.preventDefault()});this.select_genome=new h.View({css:"header-selection",data:this.list_genomes,container:this.$el.parent().find("#header-genome"),value:this.default_genome,onchange:function(k){i.updateGenome(k)}})}this.modal.show();this._updateUser();this._updateScreen()},showExtensionInfo:function(j){var i=this;var k=j.$el;var n=j.extension;var m=j.title;var l=_.findWhere(i.list_extensions,{id:n});this.extension_popup&&this.extension_popup.remove();this.extension_popup=new d.View({placement:j.placement||"bottom",container:k,destroy:true});this.extension_popup.title(m);this.extension_popup.empty();this.extension_popup.append(this._templateDescription(l));this.extension_popup.show()},_eventRemove:function(j){var i=j.get("status");if(i=="success"){this.counter.success--}else{if(i=="error"){this.counter.error--}else{this.counter.announce--}}this._updateScreen();this.uploadbox.remove(j.id)},_eventAnnounce:function(i,j,l){this.counter.announce++;this._updateScreen();var k=new b(this,{id:i,file_name:j.name,file_size:j.size,file_mode:j.mode,file_path:j.path});this.collection.add(k.model);$(this.el).find("tbody:first").append(k.$el);k.render()},_eventInitialize:function(n,k,t){var l=this.collection.get(n);l.set("status","running");var p=l.get("file_name");var o=l.get("file_path");var i=l.get("file_mode");var q=l.get("extension");var s=l.get("genome");var r=l.get("url_paste");var m=l.get("space_to_tabs");var j=l.get("to_posix_lines");if(!r&&!(k.size>0)){return null}this.uploadbox.configure({url:this.options.nginx_upload_path});if(i=="local"){this.uploadbox.configure({paramname:"files_0|file_data"})}else{this.uploadbox.configure({paramname:null})}tool_input={};if(i=="new"){tool_input["files_0|url_paste"]=r}if(i=="ftp"){tool_input["files_0|ftp_files"]=o}tool_input.dbkey=s;tool_input.file_type=q;tool_input["files_0|type"]="upload_dataset";tool_input["files_0|space_to_tab"]=m&&"Yes"||null;tool_input["files_0|to_posix_lines"]=j&&"Yes"||null;data={};data.history_id=this.current_history;data.tool_id="upload1";data.inputs=JSON.stringify(tool_input);return data},_eventProgress:function(j,k,i){var l=this.collection.get(j);l.set("percentage",i);this.ui_button.set("percentage",this._uploadPercentage(i,k.size))},_eventSuccess:function(j,k,m){var l=this.collection.get(j);l.set("percentage",100);l.set("status","success");var i=l.get("file_size");this.ui_button.set("percentage",this._uploadPercentage(100,i));this.upload_completed+=i*100;this.counter.announce--;this.counter.success++;this._updateScreen();Galaxy.currHistoryPanel.refreshContents()},_eventError:function(i,j,l){var k=this.collection.get(i);k.set("percentage",100);k.set("status","error");k.set("info",l);this.ui_button.set("percentage",this._uploadPercentage(100,j.size));this.ui_button.set("status","danger");this.upload_completed+=j.size*100;this.counter.announce--;this.counter.error++;this._updateScreen()},_eventComplete:function(){this.collection.each(function(i){if(i.get("status")=="queued"){i.set("status","init")}});this.counter.running=0;this._updateScreen()},_eventFtp:function(){if(!this.ftp.visible){this.ftp.empty();this.ftp.append((new g(this)).$el);this.ftp.show()}else{this.ftp.hide()}},_eventCreate:function(){this.uploadbox.add([{name:"New File",size:0,mode:"new"}])},_eventStart:function(){if(this.counter.announce==0||this.counter.running>0){return}var i=this;this.upload_size=0;this.upload_completed=0;this.collection.each(function(j){if(j.get("status")=="init"){j.set("status","queued");i.upload_size+=j.get("file_size")}});this.ui_button.set("percentage",0);this.ui_button.set("status","success");this.counter.running=this.counter.announce;this._updateScreen();this.uploadbox.start()},_eventStop:function(){if(this.counter.running==0){return}this.ui_button.set("status","info");this.uploadbox.stop();$("#upload-info").html("Queue will pause after completing the current file...")},_eventReset:function(){if(this.counter.running==0){this.collection.reset();this.counter.reset();this._updateScreen();this.uploadbox.reset();this.select_extension.value(this.default_extension);this.select_genome.value(this.default_genome);this.ui_button.set("percentage",0)}},updateExtension:function(j){var i=this;this.collection.each(function(k){if(k.get("status")=="init"&&k.get("extension")==i.default_extension){k.set("extension",j)}})},updateGenome:function(i){var j=this;this.collection.each(function(k){if(k.get("status")=="init"&&k.get("genome")==j.default_genome){k.set("genome",i)}})},_updateUser:function(){this.current_user=Galaxy.currUser.get("id");this.current_history=null;if(this.current_user){this.current_history=Galaxy.currHistoryPanel.model.get("id")}},_updateScreen:function(){if(this.counter.announce==0){if(this.uploadbox.compatible()){message="You can Drag & Drop files into this box."}else{message="Unfortunately, your browser does not support multiple file uploads or drag&drop.<br>Some supported browsers are: Firefox 4+, Chrome 7+, IE 10+, Opera 12+ or Safari 6+."}}else{if(this.counter.running==0){message="You added "+this.counter.announce+" file(s) to the queue. Add more files or click 'Start' to proceed."}else{message="Please wait..."+this.counter.announce+" out of "+this.counter.running+" remaining."}}$("#upload-info").html(message);if(this.counter.running==0&&this.counter.announce+this.counter.success+this.counter.error>0){this.modal.enableButton("Reset")}else{this.modal.disableButton("Reset")}if(this.counter.running==0&&this.counter.announce>0){this.modal.enableButton("Start")}else{this.modal.disableButton("Start")}if(this.counter.running>0){this.modal.enableButton("Pause")}else{this.modal.disableButton("Pause")}if(this.counter.running==0){this.modal.enableButton("Choose local file");this.modal.enableButton("Choose FTP file");this.modal.enableButton("Paste/Fetch data")}else{this.modal.disableButton("Choose local file");this.modal.disableButton("Choose FTP file");this.modal.disableButton("Paste/Fetch data")}if(this.current_user&&this.options.ftp_upload_dir&&this.options.ftp_upload_site){this.modal.showButton("Choose FTP file")}else{this.modal.hideButton("Choose FTP file")}if(this.counter.announce+this.counter.success+this.counter.error>0){$(this.el).find("#upload-table").show()}else{$(this.el).find("#upload-table").hide()}},_uploadPercentage:function(i,j){return(this.upload_completed+(i*j))/this.upload_size},_templateDescription:function(j){if(j.description){var i=j.description;if(j.description_url){i+=' (<a href="'+j.description_url+'" target="_blank">read more</a>)'}return i}else{return"There is no description available for this file extension."}},_template:function(j,i){return'<div class="upload-top"><h6 id="'+i+'" class="upload-info"></h6></div><div id="'+j+'" class="upload-box"><table id="upload-table" class="table table-striped" style="display: none;"><thead><tr><th>Name</th><th>Size</th><th>Type</th><th>Genome</th><th>Settings</th><th>Status</th><th></th></tr></thead><tbody></tbody></table></div><div id="upload-header" class="upload-header"><span class="header-title">Type (default):</span><span id="header-extension"/><span id="header-extension-info" class="upload-icon-button fa fa-search"/><span class="header-title">Genome (default):</span><span id="header-genome"/></div>'}})});
\ No newline at end of file
+define(["utils/utils","mvc/upload/upload-button","mvc/upload/upload-model","mvc/upload/upload-row","mvc/upload/upload-ftp","mvc/ui/ui-popover","mvc/ui/ui-modal","mvc/ui/ui-select","utils/uploadbox"],function(f,e,c,b,g,d,a,h){return Backbone.View.extend({options:{nginx_upload_path:""},default_extension:"auto",default_genome:"?",modal:null,ui_button:null,uploadbox:null,current_history:null,select_extension:null,select_genome:null,upload_size:0,list_extensions:[],list_genomes:[],auto:{id:"auto",text:"Auto-detect",description:"This system will try to detect the file type automatically. If your file is not detected properly as one of the known formats, it most likely means that it has some format problems (e.g., different number of columns on different rows). You can still coerce the system to set your data to the format you think it should be. You can also upload compressed files, which will automatically be decompressed."},collection:new c.Collection(),ftp:null,counter:{announce:0,success:0,error:0,running:0,reset:function(){this.announce=this.success=this.error=this.running=0}},initialize:function(j){var i=this;if(j){this.options=_.defaults(j,this.options)}this.ui_button=new e.Model({icon:"fa-upload",tooltip:"Download from URL or upload files from disk",label:"Load Data",onclick:function(k){if(k){k.preventDefault();i.show()}},onunload:function(){if(i.counter.running>0){return"Several uploads are still processing."}}});$(".with-upload-button").append((new e.View(this.ui_button)).$el);var i=this;f.get({url:galaxy_config.root+"api/datatypes?extension_only=False",success:function(k){for(key in k){i.list_extensions.push({id:k[key].extension,text:k[key].extension,description:k[key].description,description_url:k[key].description_url})}i.list_extensions.sort(function(m,l){return m.text>l.text?1:m.text<l.text?-1:0});if(!i.options.datatypes_disable_auto){i.list_extensions.unshift(i.auto)}}});f.get({url:galaxy_config.root+"api/genomes",success:function(k){for(key in k){i.list_genomes.push({id:k[key][1],text:k[key][0]})}i.list_genomes.sort(function(m,l){if(m.id==i.default_genome){return -1}if(l.id==i.default_genome){return 1}return m.text>l.text?1:m.text<l.text?-1:0})}});this.collection.on("remove",function(k){i._eventRemove(k)})},show:function(){var i=this;if(!Galaxy.currHistoryPanel||!Galaxy.currHistoryPanel.model){window.setTimeout(function(){i.show()},500);return}if(!this.modal){var i=this;this.modal=new a.View({title:"Download data directly from web or upload files from your disk",body:this._template("upload-box","upload-info"),buttons:{"Choose local file":function(){i.uploadbox.select()},"Choose FTP file":function(){i._eventFtp()},"Paste/Fetch data":function(){i._eventCreate()},Start:function(){i._eventStart()},Pause:function(){i._eventStop()},Reset:function(){i._eventReset()},Close:function(){i.modal.hide()},},height:"400",width:"900",closing_events:true});this.setElement("#upload-box");var i=this;this.uploadbox=this.$el.uploadbox({announce:function(k,l,m){i._eventAnnounce(k,l,m)},initialize:function(k,l,m){return i._eventInitialize(k,l,m)},progress:function(k,l,m){i._eventProgress(k,l,m)},success:function(k,l,m){i._eventSuccess(k,l,m)},error:function(k,l,m){i._eventError(k,l,m)},complete:function(){i._eventComplete()}});var j=this.modal.getButton("Choose FTP file");this.ftp=new d.View({title:"FTP files",container:j});this.select_extension=new h.View({css:"header-selection",data:this.list_extensions,container:this.$el.parent().find("#header-extension"),value:this.default_extension,onchange:function(k){i.updateExtension(k)}});i.$el.parent().find("#header-extension-info").on("click",function(k){i.showExtensionInfo({$el:$(k.target),title:i.select_extension.text(),extension:i.select_extension.value(),placement:"top"})}).on("mousedown",function(k){k.preventDefault()});this.select_genome=new h.View({css:"header-selection",data:this.list_genomes,container:this.$el.parent().find("#header-genome"),value:this.default_genome,onchange:function(k){i.updateGenome(k)}})}this.modal.show();this._updateUser();this._updateScreen()},showExtensionInfo:function(j){var i=this;var k=j.$el;var n=j.extension;var m=j.title;var l=_.findWhere(i.list_extensions,{id:n});this.extension_popup&&this.extension_popup.remove();this.extension_popup=new d.View({placement:j.placement||"bottom",container:k,destroy:true});this.extension_popup.title(m);this.extension_popup.empty();this.extension_popup.append(this._templateDescription(l));this.extension_popup.show()},_eventRemove:function(j){var i=j.get("status");if(i=="success"){this.counter.success--}else{if(i=="error"){this.counter.error--}else{this.counter.announce--}}this._updateScreen();this.uploadbox.remove(j.id)},_eventAnnounce:function(i,j,l){this.counter.announce++;this._updateScreen();var k=new b(this,{id:i,file_name:j.name,file_size:j.size,file_mode:j.mode,file_path:j.path});this.collection.add(k.model);$(this.el).find("tbody:first").append(k.$el);k.render()},_eventInitialize:function(n,k,t){var l=this.collection.get(n);l.set("status","running");var p=l.get("file_name");var o=l.get("file_path");var i=l.get("file_mode");var q=l.get("extension");var s=l.get("genome");var r=l.get("url_paste");var m=l.get("space_to_tabs");var j=l.get("to_posix_lines");if(!r&&!(k.size>0)){return null}this.uploadbox.configure({url:this.options.nginx_upload_path});if(i=="local"){this.uploadbox.configure({paramname:"files_0|file_data"})}else{this.uploadbox.configure({paramname:null})}tool_input={};if(i=="new"){tool_input["files_0|url_paste"]=r}if(i=="ftp"){tool_input["files_0|ftp_files"]=o}tool_input.dbkey=s;tool_input.file_type=q;tool_input["files_0|type"]="upload_dataset";tool_input["files_0|space_to_tab"]=m&&"Yes"||null;tool_input["files_0|to_posix_lines"]=j&&"Yes"||null;data={};data.history_id=this.current_history;data.tool_id="upload1";data.inputs=JSON.stringify(tool_input);return data},_eventProgress:function(j,k,i){var l=this.collection.get(j);l.set("percentage",i);this.ui_button.set("percentage",this._uploadPercentage(i,k.size))},_eventSuccess:function(j,k,m){var l=this.collection.get(j);l.set("percentage",100);l.set("status","success");var i=l.get("file_size");this.ui_button.set("percentage",this._uploadPercentage(100,i));this.upload_completed+=i*100;this.counter.announce--;this.counter.success++;this._updateScreen();Galaxy.currHistoryPanel.refreshContents()},_eventError:function(i,j,l){var k=this.collection.get(i);k.set("percentage",100);k.set("status","error");k.set("info",l);this.ui_button.set("percentage",this._uploadPercentage(100,j.size));this.ui_button.set("status","danger");this.upload_completed+=j.size*100;this.counter.announce--;this.counter.error++;this._updateScreen()},_eventComplete:function(){this.collection.each(function(i){if(i.get("status")=="queued"){i.set("status","init")}});this.counter.running=0;this._updateScreen()},_eventFtp:function(){if(!this.ftp.visible){this.ftp.empty();this.ftp.append((new g(this)).$el);this.ftp.show()}else{this.ftp.hide()}},_eventCreate:function(){this.uploadbox.add([{name:"New File",size:0,mode:"new"}])},_eventStart:function(){if(this.counter.announce==0||this.counter.running>0){return}var i=this;this.upload_size=0;this.upload_completed=0;this.collection.each(function(j){if(j.get("status")=="init"){j.set("status","queued");i.upload_size+=j.get("file_size")}});this.ui_button.set("percentage",0);this.ui_button.set("status","success");this.counter.running=this.counter.announce;this._updateScreen();this.uploadbox.start()},_eventStop:function(){if(this.counter.running==0){return}this.ui_button.set("status","info");this.uploadbox.stop();$("#upload-info").html("Queue will pause after completing the current file...")},_eventReset:function(){if(this.counter.running==0){this.collection.reset();this.counter.reset();this._updateScreen();this.uploadbox.reset();this.select_extension.value(this.default_extension);this.select_genome.value(this.default_genome);this.ui_button.set("percentage",0)}},updateExtension:function(k,j){var i=this;this.collection.each(function(l){if(l.get("status")=="init"&&(l.get("extension")==i.default_extension||!j)){l.set("extension",k)}})},updateGenome:function(i,k){var j=this;this.collection.each(function(l){if(l.get("status")=="init"&&(l.get("genome")==j.default_genome||!k)){l.set("genome",i)}})},_updateUser:function(){this.current_user=Galaxy.currUser.get("id");this.current_history=null;if(this.current_user){this.current_history=Galaxy.currHistoryPanel.model.get("id")}},_updateScreen:function(){if(this.counter.announce==0){if(this.uploadbox.compatible()){message="You can Drag & Drop files into this box."}else{message="Unfortunately, your browser does not support multiple file uploads or drag&drop.<br>Some supported browsers are: Firefox 4+, Chrome 7+, IE 10+, Opera 12+ or Safari 6+."}}else{if(this.counter.running==0){message="You added "+this.counter.announce+" file(s) to the queue. Add more files or click 'Start' to proceed."}else{message="Please wait..."+this.counter.announce+" out of "+this.counter.running+" remaining."}}$("#upload-info").html(message);if(this.counter.running==0&&this.counter.announce+this.counter.success+this.counter.error>0){this.modal.enableButton("Reset")}else{this.modal.disableButton("Reset")}if(this.counter.running==0&&this.counter.announce>0){this.modal.enableButton("Start")}else{this.modal.disableButton("Start")}if(this.counter.running>0){this.modal.enableButton("Pause")}else{this.modal.disableButton("Pause")}if(this.counter.running==0){this.modal.enableButton("Choose local file");this.modal.enableButton("Choose FTP file");this.modal.enableButton("Paste/Fetch data")}else{this.modal.disableButton("Choose local file");this.modal.disableButton("Choose FTP file");this.modal.disableButton("Paste/Fetch data")}if(this.current_user&&this.options.ftp_upload_dir&&this.options.ftp_upload_site){this.modal.showButton("Choose FTP file")}else{this.modal.hideButton("Choose FTP file")}if(this.counter.announce+this.counter.success+this.counter.error>0){$(this.el).find("#upload-table").show()}else{$(this.el).find("#upload-table").hide()}},_uploadPercentage:function(i,j){return(this.upload_completed+(i*j))/this.upload_size},_templateDescription:function(j){if(j.description){var i=j.description;if(j.description_url){i+=' (<a href="'+j.description_url+'" target="_blank">read more</a>)'}return i}else{return"There is no description available for this file extension."}},_template:function(j,i){return'<div class="upload-top"><h6 id="'+i+'" class="upload-info"></h6></div><div id="'+j+'" class="upload-box"><table id="upload-table" class="table table-striped" style="display: none;"><thead><tr><th>Name</th><th>Size</th><th>Type</th><th>Genome</th><th>Settings</th><th>Status</th><th></th></tr></thead><tbody></tbody></table></div><div id="upload-header" class="upload-header"><span class="header-title">Type (set all):</span><span id="header-extension"/><span id="header-extension-info" class="upload-icon-button fa fa-search"/><span class="header-title">Genome (set all):</span><span id="header-genome"/></div>'}})});
\ No newline at end of file
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef templates/webapps/galaxy/history/view_multiple.mako
--- a/templates/webapps/galaxy/history/view_multiple.mako
+++ b/templates/webapps/galaxy/history/view_multiple.mako
@@ -33,6 +33,11 @@
display: -ms-flexbox;
display: flex;
+ /* force ff to squish beyond content:
+ https://developer.mozilla.org/en-US/Firefox/Releases/34/Site_Compatibility#… */
+ min-width: 0px;
+ min-height: 0px;
+
-webkit-align-items: stretch;
-ms-align-items: stretch;
align-items: stretch;
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef test/base/asserts/xml.py
--- a/test/base/asserts/xml.py
+++ b/test/base/asserts/xml.py
@@ -1,10 +1,10 @@
-import elementtree.ElementTree
+import xml.etree
import re
# Helper functions used to work with XML output.
def to_xml(output):
- return elementtree.ElementTree.fromstring(output)
+ return xml.etree.fromstring(output)
def xml_find_text(output, path):
@@ -31,8 +31,7 @@
def assert_has_element_with_path(output, path):
""" Asserts the specified output has at least one XML element with a
path matching the specified path argument. Valid paths are the
- simplified subsets of XPath implemented by elementtree (currently
- Galaxy makes use of elementtree 1.2). See
+ simplified subsets of XPath implemented by xml.etree;
http://effbot.org/zone/element-xpath.htm for more information."""
if xml_find(output, path) is None:
errmsg = "Expected to find XML element matching expression %s, not such match was found." % path
@@ -74,13 +73,13 @@
errmsg = "Expected attribute '%s' on element with path '%s' to match '%s', instead attribute value was '%s'." % (attribute, path, expression, attribute_value)
raise AssertionError(errmsg)
-
+
def assert_attribute_is(output, path, attribute, text):
""" Asserts the specified attribute of the first element matching
the specified path matches exactly the specified text."""
assert_attribute_matches(output, path, attribute, re.escape(text))
-
+
def assert_element_text(output, path, verify_assertions_function, children):
""" Recursively checks the specified assertions against the text of
the first element matching the specified path."""
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef test/base/twilltestcase.py
--- a/test/base/twilltestcase.py
+++ b/test/base/twilltestcase.py
@@ -22,10 +22,9 @@
from urlparse import urlparse
from galaxy import eggs
-eggs.require( "elementtree" )
eggs.require( 'twill' )
-from elementtree import ElementTree
+from xml.etree import ElementTree
import twill
import twill.commands as tc
diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef test/casperjs/history-share-tests.js
--- /dev/null
+++ b/test/casperjs/history-share-tests.js
@@ -0,0 +1,227 @@
+var require = patchRequire( require ),
+ spaceghost = require( 'spaceghost' ).fromCasper( casper ),
+ xpath = require( 'casper' ).selectXPath,
+ utils = require( 'utils' ),
+ format = utils.format;
+
+spaceghost.test.begin( 'Testing the user share form for histories', 0, function suite( test ){
+ spaceghost.start();
+
+ // =================================================================== globals and helpers
+ var email = spaceghost.user.getRandomEmail(),
+ password = '123456';
+ if( spaceghost.fixtureData.testUser ){
+ email = spaceghost.fixtureData.testUser.email;
+ password = spaceghost.fixtureData.testUser.password;
+ spaceghost.info( 'Will use fixtureData.testUser: ' + email );
+ }
+ var email2 = spaceghost.user.getRandomEmail(),
+ password2 = '123456';
+ if( spaceghost.fixtureData.testUser2 ){
+ email2 = spaceghost.fixtureData.testUser2.email;
+ password2 = spaceghost.fixtureData.testUser2.password;
+ }
+
+ var shareLink = 'a[href^="/history/share?"]',
+ shareSubmit = 'input[name="share_button"]',
+ firstUserShareButton = '#user-0-popup',
+ shareHistoryId = null,
+ shareUserId = null;
+
+ function fromUserSharePage( fn ){
+ spaceghost.then( function(){
+ this.openHomePage( function(){
+ this.historyoptions.clickOption( 'Share or Publish' );
+ });
+ this.waitForNavigation( 'history/sharing', function(){
+ this.jumpToMain( function(){
+ this.click( shareLink );
+ });
+ });
+ this.waitForNavigation( 'history/share', function(){
+ this.jumpToMain( function(){
+ fn.call( this );
+ });
+ });
+ });
+ }
+
+ function thenSwitchUser( email, password ){
+ spaceghost.then( function(){
+ spaceghost.user.logout();
+ spaceghost.user.loginOrRegisterUser( email, password );
+ });
+ return spaceghost;
+ }
+
+ function thenShareWithUser( comment, emailOrId, thenFn ){
+ spaceghost.then( function(){
+ fromUserSharePage( function(){
+ this.test.comment( comment );
+ this.fill( 'form#share', {
+ email : emailOrId
+ });
+ // strangely, casper's submit=true flag doesn't work well here - need to manually push the button
+ this.click( shareSubmit );
+ });
+ spaceghost.then( function(){
+ this.jumpToMain( function(){
+ thenFn.call( this );
+ });
+ });
+ });
+ return spaceghost;
+ }
+
+ // =================================================================== TESTS
+ // create user 1 and the test/target history
+ spaceghost.user.loginOrRegisterUser( email, password ).openHomePage( function(){
+ shareHistoryId = this.api.histories.index()[0].id;
+ this.info( 'shareHistoryId: ' + shareHistoryId );
+ });
+ spaceghost.then( function(){
+ // can't share an empty history (for some reason)
+ this.api.tools.thenUpload( shareHistoryId, { filepath: '../../test-data/1.bed' });
+ });
+
+ // create user 2 and make sure they can't access the history right now
+ thenSwitchUser( email2, password2 ).openHomePage( function(){
+ shareUserId = this.api.users.index()[0].id;
+ this.info( 'shareUserId: ' + shareUserId );
+
+ this.test.comment( 'user2 should not have access to test history' );
+ this.api.assertRaises( function(){
+ this.api.histories.show( shareHistoryId );
+ }, 403, 'History is not accessible by user', 'show failed with error' );
+ });
+
+ thenSwitchUser( email, password );
+ thenShareWithUser( "should NOT work: share using non-existant user email", 'chunkylover53(a)aol.com', function(){
+ this.test.assertExists( '.errormessage', 'found error message' );
+ this.test.assertSelectorHasText( '.errormessage', 'is not a valid Galaxy user', 'wording is good' );
+ });
+ thenShareWithUser( "should NOT work: share using current user email", email, function(){
+ this.test.assertExists( '.errormessage', 'found error message' );
+ this.test.assertSelectorHasText( '.errormessage', 'You cannot send histories to yourself', 'wording is good' );
+ });
+ thenShareWithUser( "should work: share using email", email2, function(){
+ this.test.assertExists( firstUserShareButton, 'found user share button' );
+ this.test.assertSelectorHasText( firstUserShareButton, email2, 'share button text is email2' );
+ });
+
+ // user 2 can now access the history
+ thenSwitchUser( email2, password2 ).openHomePage( function(){
+ this.test.comment( 'user 2 can now access the history' );
+ this.test.assert( !!this.api.histories.show( shareHistoryId ).id );
+ });
+
+
+ // remove share
+ thenSwitchUser( email, password ).thenOpen( spaceghost.baseUrl + '/history/sharing', function(){
+ this.jumpToMain( function(){
+ this.click( firstUserShareButton );
+ this.wait( 100, function(){
+ this.click( 'a[href^="/history/sharing?unshare_user"]' );
+ });
+ });
+ });
+ spaceghost.then( function(){
+ this.test.assertDoesntExist( firstUserShareButton, 'no user share button seen' );
+ });
+
+ thenSwitchUser( email2, password2 ).openHomePage( function(){
+ this.test.comment( 'user2 should not have access to test history (again)' );
+ this.api.assertRaises( function(){
+ this.api.histories.show( shareHistoryId );
+ }, 403, 'History is not accessible by user', 'show failed with error' );
+ });
+
+
+ // should NOT work: share using malformed id
+ thenSwitchUser( email, password );
+ thenShareWithUser( "should NOT work: share using malformed id", '1234xyz', function(){
+ this.test.assertExists( '.errormessage', 'found error message' );
+ this.test.assertSelectorHasText( '.errormessage', 'is not a valid Galaxy user', 'wording is good' );
+ });
+ //spaceghost.then( function(){
+ // // test user share using email
+ // fromUserSharePage( function(){
+ // this.test.comment( 'should NOT work: share using malformed id' );
+ // this.fill( '#share', {
+ // email : '1234xyz'
+ // });
+ // this.click( shareSubmit );
+ // });
+ // spaceghost.then( function(){
+ // this.jumpToMain( function(){
+ // this.test.assertExists( '.errormessage', 'found error message' );
+ // this.test.assertSelectorHasText( '.errormessage', 'is not a valid Galaxy user', 'wording is good' );
+ // });
+ // });
+ //});
+
+ // should NOT work: share using current user id
+ spaceghost.then( function(){
+ var currUserId = spaceghost.api.users.index()[0].id;
+ thenShareWithUser( "should NOT work: share using current user id", currUserId, function(){
+ this.test.assertExists( '.errormessage', 'found error message' );
+ this.test.assertSelectorHasText( '.errormessage',
+ 'You cannot send histories to yourself', 'wording is good' );
+ });
+ });
+ //// should NOT work: share using current user id
+ //spaceghost.then( function(){
+ // var currUserId = spaceghost.api.users.index()[0].id;
+ // // test user share using email
+ // fromUserSharePage( function(){
+ // this.test.comment( 'should NOT work: share using current user id' );
+ // this.debug( 'currUserId: ' + currUserId );
+ // this.fill( 'form#share', {
+ // email : currUserId
+ // });
+ // this.click( shareSubmit );
+ // });
+ // spaceghost.then( function(){
+ // this.jumpToMain( function(){
+ // this.test.assertExists( '.errormessage', 'found error message' );
+ // this.test.assertSelectorHasText( '.errormessage',
+ // 'You cannot send histories to yourself', 'wording is good' );
+ // });
+ // });
+ //});
+
+ spaceghost.then( function(){
+ thenShareWithUser( "should work: share using id", shareUserId, function(){
+ this.test.assertExists( firstUserShareButton, 'found user share button' );
+ this.test.assertSelectorHasText( firstUserShareButton, email2, 'share button text is email2' );
+ });
+ });
+ //// should work: share using id
+ //spaceghost.then( function(){
+ // // test user share using email
+ // fromUserSharePage( function(){
+ // this.test.comment( 'should work: share using id' );
+ // this.fill( '#share', {
+ // email : shareUserId
+ // });
+ // this.click( shareSubmit );
+ // });
+ // spaceghost.then( function(){
+ // this.jumpToMain( function(){
+ // this.test.assertExists( firstUserShareButton, 'found user share button' );
+ // this.test.assertSelectorHasText( firstUserShareButton, email2, 'share button text is email2' );
+ // });
+ // });
+ //});
+
+ // user 2 can now access the history
+ thenSwitchUser( email2, password2 ).openHomePage( function(){
+ this.test.comment( 'user 2 can now access the history' );
+ this.test.assert( !!this.api.histories.show( shareHistoryId ).id );
+ });
+
+ /*
+ */
+ // ===================================================================
+ spaceghost.run( function(){ test.done(); });
+});
This diff is so big that we needed to truncate the remainder.
https://bitbucket.org/galaxy/galaxy-central/commits/a1a8011ec8d1/
Changeset: a1a8011ec8d1
User: carlfeberhard
Date: 2015-02-02 14:17:51+00:00
Summary: Merge
Affected #: 3 files
diff -r e7560410c0f235df210d29545a51327a1af5f7ef -r a1a8011ec8d14bdb28df805e0493ac3a226698b3 lib/galaxy/config.py
--- a/lib/galaxy/config.py
+++ b/lib/galaxy/config.py
@@ -318,6 +318,9 @@
# Crummy, but PasteScript does not give you a way to determine this
if arg.lower().startswith('--server-name='):
self.server_name = arg.split('=', 1)[-1]
+ # Allow explicit override of server name in confg params
+ if "server_name" in kwargs:
+ self.server_name = kwargs.get("server_name")
# Store all configured server names
self.server_names = []
for section in global_conf_parser.sections():
diff -r e7560410c0f235df210d29545a51327a1af5f7ef -r a1a8011ec8d14bdb28df805e0493ac3a226698b3 lib/galaxy/main.py
--- /dev/null
+++ b/lib/galaxy/main.py
@@ -0,0 +1,245 @@
+""" Entry point for starting Galaxy without starting as part of a web server.
+
+Example Usage: Start a job/workflow handler without a web server and with
+a given name using.
+
+python lib/galaxy/main.py --server-name handler0
+
+Start as a daemon with (requires daemonized - install with 'pip install daemonize'):
+
+python lib/galaxy/main.py -d --daemon-log-file=handler0-daemon.log --pid-file handler0.pid --server-name handler0
+
+In daemon mode logging of Galaxy (as opposed to this script) is configured via
+a loggers section in Galaxy's ini file - this can be overridden with sensible
+defaults logging to a single file with the following:
+
+python lib/galaxy/main.py -d --server-name handler0 --daemon-log-file=handler0-daemon.log --pid-file handler0.pid --log-file handler0.log
+
+"""
+import logging
+from logging.config import fileConfig
+
+import functools
+import os
+import time
+import sys
+
+try:
+ import ConfigParser as configparser
+except ImportError:
+ import configparser
+
+try:
+ from daemonize import Daemonize
+except ImportError:
+ Daemonize = None
+
+# Vaguely Python 2.6 compatibile ArgumentParser import
+try:
+ from argparser import ArgumentParser
+except ImportError:
+ from optparse import OptionParser
+
+ class ArgumentParser(OptionParser):
+
+ def __init__(self, **kwargs):
+ self.delegate = OptionParser(**kwargs)
+
+ def add_argument(self, *args, **kwargs):
+ if "required" in kwargs:
+ del kwargs["required"]
+ return self.delegate.add_option(*args, **kwargs)
+
+ def parse_args(self, args=None):
+ (options, args) = self.delegate.parse_args(args)
+ return options
+
+REQUIRES_DAEMONIZE_MESSAGE = "Attempted to use Galaxy in daemon mode, but daemonize is unavailable."
+
+log = logging.getLogger(__name__)
+
+GALAXY_ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
+GALAXY_LIB_DIR = os.path.join(GALAXY_ROOT_DIR, "lib")
+DEFAULT_INI_APP = "main"
+DEFAULT_INIS = ["config/galaxy.ini", "universe_wsgi.ini", "config/galaxy.ini.sample"]
+
+DEFAULT_PID = "galaxy.pid"
+DEFAULT_VERBOSE = True
+DESCRIPTION = "Daemonized entry point for Galaxy."
+
+
+def load_galaxy_app(
+ config_builder,
+ config_env=False,
+ log=None,
+ **kwds
+):
+ # Allow specification of log so daemon can reuse properly configured one.
+ if log is None:
+ log = logging.getLogger(__name__)
+
+ # If called in daemon mode, set the ROOT directory and ensure Galaxy is on
+ # sys.path.
+ if config_env:
+ try:
+ os.chdir(GALAXY_ROOT_DIR)
+ except Exception:
+ log.exception("Failed to chdir")
+ raise
+ try:
+ sys.path.append(GALAXY_LIB_DIR)
+ except Exception:
+ log.exception("Failed to add Galaxy to sys.path")
+ raise
+
+ config_builder.setup_logging()
+ from galaxy.util.properties import load_app_properties
+ kwds = config_builder.app_kwds()
+ kwds = load_app_properties(**kwds)
+ from galaxy.app import UniverseApplication
+ app = UniverseApplication(
+ global_conf={"__file__": config_builder.ini_path},
+ **kwds
+ )
+ return app
+
+
+def app_loop(args, log):
+ try:
+ config_builder = GalaxyConfigBuilder(args)
+ galaxy_app = load_galaxy_app(
+ config_builder,
+ config_env=True,
+ log=log,
+ )
+ except BaseException:
+ log.exception("Failed to initialize Galaxy application")
+ raise
+ sleep = True
+ while sleep:
+ try:
+ time.sleep(5)
+ except KeyboardInterrupt:
+ sleep = False
+ except SystemExit:
+ sleep = False
+ except Exception:
+ pass
+ try:
+ galaxy_app.shutdown()
+ except Exception:
+ log.exception("Failed to shutdown Galaxy application")
+ raise
+
+
+def absolute_config_path(path, galaxy_root):
+ if path and not os.path.isabs(path):
+ path = os.path.join(galaxy_root, path)
+ return path
+
+
+def find_ini(supplied_ini, galaxy_root):
+ if supplied_ini:
+ return supplied_ini
+
+ # If not explicitly supplied an ini, check server.ini and then
+ # just restort to sample if that has not been configured.
+ for guess in DEFAULT_INIS:
+ ini_path = os.path.join(galaxy_root, guess)
+ if os.path.exists(ini_path):
+ return ini_path
+
+ return guess
+
+
+class GalaxyConfigBuilder(object):
+ """ Generate paste-like configuration from supplied command-line arguments.
+ """
+
+ def __init__(self, args=None, **kwds):
+ ini_path = kwds.get("ini_path", None) or (args and args.ini_path)
+ # If given app_conf_path - use that - else we need to ensure we have an
+ # ini path.
+ if not ini_path:
+ galaxy_root = kwds.get("galaxy_root", GALAXY_ROOT_DIR)
+ ini_path = find_ini(ini_path, galaxy_root)
+ ini_path = absolute_config_path(ini_path, galaxy_root=galaxy_root)
+ self.ini_path = ini_path
+ self.app_name = kwds.get("app") or (args and args.app) or DEFAULT_INI_APP
+ self.log_file = (args and args.log_file)
+
+ @classmethod
+ def populate_options(cls, arg_parser):
+ arg_parser.add_argument("-c", "--ini-path", default=None, help="Galaxy ini config file (defaults to config/galaxy.ini)")
+ arg_parser.add_argument("--app", default=DEFAULT_INI_APP, help="app section in ini file (defaults to main)")
+ arg_parser.add_argument("-d", "--daemonize", default=False, help="Daemonzie process", action="store_true")
+ arg_parser.add_argument("--daemon-log-file", default=None, help="log file for daemon script ")
+ arg_parser.add_argument("--log-file", default=None, help="Galaxy log file (overrides log configuration in ini_path if set)")
+ arg_parser.add_argument("--pid-file", default=DEFAULT_PID, help="pid file (default is %s)" % DEFAULT_PID)
+ arg_parser.add_argument("--server-name", default=None, help="set a galaxy server name")
+
+ def app_kwds(self):
+ config = dict(
+ ini_file=self.ini_path,
+ ini_section="app:%s" % self.app_name,
+ )
+ return config
+
+ def setup_logging(self):
+ # Galaxy will attempt to setup logging if loggers is not present in
+ # ini config file - this handles that loggers block however if present
+ # (the way paste normally would)
+ if not self.ini_path:
+ return
+ raw_config = configparser.ConfigParser()
+ raw_config.read([self.ini_path])
+ if raw_config.has_section('loggers'):
+ config_file = os.path.abspath(self.ini_path)
+ fileConfig(
+ config_file,
+ dict(__file__=config_file, here=os.path.dirname(config_file))
+ )
+
+
+def main():
+ if Daemonize is None:
+ raise ImportError(REQUIRES_DAEMONIZE_MESSAGE)
+
+ arg_parser = ArgumentParser(description=DESCRIPTION)
+ GalaxyConfigBuilder.populate_options(arg_parser)
+ args = arg_parser.parse_args()
+ if args.log_file:
+ os.environ["GALAXY_CONFIG_LOG_DESTINATION"] = os.path.abspath(args.log_file)
+ if args.server_name:
+ os.environ["GALAXY_CONFIG_SERVER_NAME"] = args.server_name
+ pid_file = args.pid_file
+
+ log.setLevel(logging.DEBUG)
+ log.propagate = False
+ if args.daemonize:
+ keep_fds = []
+ if args.daemon_log_file:
+ fh = logging.FileHandler(args.daemon_log_file, "w")
+ fh.setLevel(logging.DEBUG)
+ log.addHandler(fh)
+ keep_fds.append(fh.stream.fileno())
+ else:
+ fh = logging.StreamHandler(sys.stderr)
+ fh.setLevel(logging.DEBUG)
+ log.addHandler(fh)
+
+ daemon = Daemonize(
+ app="galaxy",
+ pid=pid_file,
+ action=functools.partial(app_loop, args, log),
+ verbose=DEFAULT_VERBOSE,
+ logger=log,
+ keep_fds=keep_fds,
+ )
+ daemon.start()
+ else:
+ app_loop(args, log)
+
+
+if __name__ == "__main__":
+ main()
diff -r e7560410c0f235df210d29545a51327a1af5f7ef -r a1a8011ec8d14bdb28df805e0493ac3a226698b3 rolling_restart.sh
--- a/rolling_restart.sh
+++ b/rolling_restart.sh
@@ -1,21 +1,7 @@
#!/bin/sh
+
cd `dirname $0`
-check_if_not_started(){
- # Search for all pids in the logs and tail for the last one
- latest_pid=`egrep '^Starting server in PID [0-9]+\.$' $1 -o | sed 's/Starting server in PID //g;s/\.$//g' | tail -n 1`
- # Grab the current pid from the file we were given
- current_pid_in_file=$(cat $2);
- # If they're equivalent, then the current pid file agrees with our logs
- # and we've succesfully started
- if [ $latest_pid -eq $current_pid_in_file ];
- then
- echo 0;
- else
- echo 1;
- fi
-}
-
# If there is a .venv/ directory, assume it contains a virtualenv that we
# should run this instance in.
if [ -d .venv ];
@@ -23,6 +9,15 @@
. .venv/bin/activate
fi
+python ./scripts/check_python.py
+[ $? -ne 0 ] && exit 1
+
+./scripts/common_startup.sh
+
+if [ -n "$GALAXY_UNIVERSE_CONFIG_DIR" ]; then
+ python ./scripts/build_universe_config.py "$GALAXY_UNIVERSE_CONFIG_DIR"
+fi
+
if [ -z "$GALAXY_CONFIG_FILE" ]; then
if [ -f universe_wsgi.ini ]; then
GALAXY_CONFIG_FILE=universe_wsgi.ini
@@ -35,32 +30,32 @@
fi
servers=`sed -n 's/^\[server:\(.*\)\]/\1/ p' $GALAXY_CONFIG_FILE | xargs echo`
-for server in $servers;
-do
- # If there's a pid
- if [ -e $server.pid ]
- then
- # Then kill it
- echo "Killing $server"
- pid=`cat $server.pid`
- kill $pid
- else
- # Otherwise just continue
- echo "$server not running"
- fi
- # Start the server (and background) (should this be nohup'd?)
- python ./scripts/paster.py serve $GALAXY_CONFIG_FILE --server-name=$server --pid-file=$server.pid --log-file=$server.log --daemon $@
- # Wait for the server to start
- sleep 1
- # Grab the new pid
- pid=`cat $server.pid`
- result=1
- # Wait for the latest pid in the file to be the pid we've grabbed
- while [ $result -eq 1 ]
- do
- result=$(check_if_not_started $server.log $server.pid)
- printf "."
- sleep 1
- done
- echo
+for server in $servers; do
+ # If there's a pid
+ if [ -e $server.pid ]; then
+ # Then kill it
+ echo "Killing $server"
+ pid=`cat $server.pid`
+ kill $pid
+ else
+ # Otherwise just continue
+ echo "$server not running"
+ fi
+ # Start the server (and background) (should this be nohup'd?)
+ python ./scripts/paster.py serve $GALAXY_CONFIG_FILE --server-name=$server --pid-file=$server.pid --log-file=$server.log --daemon $@
+ while true; do
+ sleep 1
+ printf "."
+ # Grab the current pid from the pid file
+ if ! current_pid_in_file=$(cat $server.pid); then
+ echo "A Galaxy process died, interrupting" >&2
+ exit 1
+ fi
+ # Search for all pids in the logs and tail for the last one
+ latest_pid=`egrep '^Starting server in PID [0-9]+\.$' $server.log -o | sed 's/Starting server in PID //g;s/\.$//g' | tail -n 1`
+ # If they're equivalent, then the current pid file agrees with our logs
+ # and we've succesfully started
+ [ -n "$latest_pid" ] && [ $latest_pid -eq $current_pid_in_file ] && break
+ done
+ echo
done
https://bitbucket.org/galaxy/galaxy-central/commits/0d92ab68b8b3/
Changeset: 0d92ab68b8b3
User: carlfeberhard
Date: 2015-02-02 14:19:13+00:00
Summary: HDAs, Histories: remove set_from_dict; minor fixes
Affected #: 3 files
diff -r a1a8011ec8d14bdb28df805e0493ac3a226698b3 -r 0d92ab68b8b3764e98d338c7d594b8e77aaf5b99 lib/galaxy/managers/datasets.py
--- a/lib/galaxy/managers/datasets.py
+++ b/lib/galaxy/managers/datasets.py
@@ -232,6 +232,7 @@
deletable.PurgableDeserializerMixin.add_deserializers( self )
+# ============================================================================= AKA DatasetInstanceManager
class DatasetAssociationManager( base.ModelManager, secured.AccessibleManagerMixin, deletable.PurgableManagerMixin ):
"""
DatasetAssociation/DatasetInstances are intended to be working
diff -r a1a8011ec8d14bdb28df805e0493ac3a226698b3 -r 0d92ab68b8b3764e98d338c7d594b8e77aaf5b99 lib/galaxy/managers/histories.py
--- a/lib/galaxy/managers/histories.py
+++ b/lib/galaxy/managers/histories.py
@@ -49,8 +49,7 @@
"""
# handle default and/or anonymous user (which still may not have a history yet)
if self.user_manager.is_anonymous( user ):
- #TODO: trans
- current_history = trans.get_history()
+ current_history = self.get_current( trans )
return [ current_history ] if current_history else []
return super( HistoryManager, self ).by_user( trans, user, **kwargs )
@@ -60,8 +59,7 @@
True if the current user is the owner of the given history.
"""
# anon users are only allowed to view their current history
- #TODO: trans
- if self.user_manager.is_anonymous( user ) and history == trans.get_history():
+ if self.user_manager.is_anonymous( user ) and history == self.get_current( trans ):
return True
return super( HistoryManager, self ).is_owner( trans, history, user )
@@ -69,10 +67,14 @@
def most_recent( self, trans, user, filters=None, **kwargs ):
"""
Return the most recently update history for the user.
+
+ If user is anonymous, return the current history. If the user is anonymous
+ and the current history is deleted, return None.
"""
#TODO: trans
if not user:
- return None if trans.history.deleted else trans.history
+ current_history = self.get_current( trans )
+ return None if ( not current_history or current_history.deleted ) else current_history
desc_update_time = self.model_class.table.c.update_time
filters = self._munge_filters( filters, self.model_class.user_id == user.id )
#TODO: normalize this return value
diff -r a1a8011ec8d14bdb28df805e0493ac3a226698b3 -r 0d92ab68b8b3764e98d338c7d594b8e77aaf5b99 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -1074,28 +1074,6 @@
return rval
- def set_from_dict( self, new_data ):
- #AKA: set_api_value
- """
- Set object attributes to the values in dictionary new_data limiting
- to only those keys in dict_element_visible_keys.
-
- Returns a dictionary of the keys, values that have been changed.
- """
- # precondition: keys are proper, values are parsed and validated
- changed = {}
- # unknown keys are ignored here
- for key in [ k for k in new_data.keys() if k in self.dict_element_visible_keys ]:
- new_val = new_data[ key ]
- old_val = self.__getattribute__( key )
- if new_val == old_val:
- continue
-
- self.__setattr__( key, new_val )
- changed[ key ] = new_val
-
- return changed
-
@property
def latest_export( self ):
exports = self.exports
@@ -2120,36 +2098,6 @@
rval['metadata_' + name] = val
return rval
- def set_from_dict( self, new_data ):
- #AKA: set_api_value
- """
- Set object attributes to the values in dictionary new_data limiting
- to only the following keys: name, deleted, visible, genome_build,
- info, and blurb.
-
- Returns a dictionary of the keys, values that have been changed.
- """
- # precondition: keys are proper, values are parsed and validated
- #NOTE!: does not handle metadata
- editable_keys = ( 'name', 'deleted', 'visible', 'dbkey', 'info', 'blurb' )
-
- changed = {}
- # unknown keys are ignored here
- for key in [ k for k in new_data.keys() if k in editable_keys ]:
- new_val = new_data[ key ]
- old_val = self.__getattribute__( key )
- if new_val == old_val:
- continue
-
- # special cases here
- if key == 'deleted' and new_val is False and self.purged:
- raise Exception( 'Cannot undelete a purged dataset' )
-
- self.__setattr__( key, new_val )
- changed[ key ] = new_val
-
- return changed
-
@property
def history_content_type( self ):
return "dataset"
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: jmchilton: Merged in nsoranzo/galaxy-central (pull request #647)
by commits-noreply@bitbucket.org 02 Feb '15
by commits-noreply@bitbucket.org 02 Feb '15
02 Feb '15
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/a56fdaab0e25/
Changeset: a56fdaab0e25
User: jmchilton
Date: 2015-02-02 14:14:09+00:00
Summary: Merged in nsoranzo/galaxy-central (pull request #647)
More enhancements to rolling_restart.sh
Affected #: 1 file
diff -r 16efc5713d85f631c8ef9ad11895730f4747ea7e -r a56fdaab0e25011febcd98fe376adf39c7ebfb10 rolling_restart.sh
--- a/rolling_restart.sh
+++ b/rolling_restart.sh
@@ -1,21 +1,7 @@
#!/bin/sh
+
cd `dirname $0`
-check_if_not_started(){
- # Search for all pids in the logs and tail for the last one
- latest_pid=`egrep '^Starting server in PID [0-9]+\.$' $1 -o | sed 's/Starting server in PID //g;s/\.$//g' | tail -n 1`
- # Grab the current pid from the file we were given
- current_pid_in_file=$(cat $2);
- # If they're equivalent, then the current pid file agrees with our logs
- # and we've succesfully started
- if [ $latest_pid -eq $current_pid_in_file ];
- then
- echo 0;
- else
- echo 1;
- fi
-}
-
# If there is a .venv/ directory, assume it contains a virtualenv that we
# should run this instance in.
if [ -d .venv ];
@@ -23,6 +9,15 @@
. .venv/bin/activate
fi
+python ./scripts/check_python.py
+[ $? -ne 0 ] && exit 1
+
+./scripts/common_startup.sh
+
+if [ -n "$GALAXY_UNIVERSE_CONFIG_DIR" ]; then
+ python ./scripts/build_universe_config.py "$GALAXY_UNIVERSE_CONFIG_DIR"
+fi
+
if [ -z "$GALAXY_CONFIG_FILE" ]; then
if [ -f universe_wsgi.ini ]; then
GALAXY_CONFIG_FILE=universe_wsgi.ini
@@ -35,32 +30,32 @@
fi
servers=`sed -n 's/^\[server:\(.*\)\]/\1/ p' $GALAXY_CONFIG_FILE | xargs echo`
-for server in $servers;
-do
- # If there's a pid
- if [ -e $server.pid ]
- then
- # Then kill it
- echo "Killing $server"
- pid=`cat $server.pid`
- kill $pid
- else
- # Otherwise just continue
- echo "$server not running"
- fi
- # Start the server (and background) (should this be nohup'd?)
- python ./scripts/paster.py serve $GALAXY_CONFIG_FILE --server-name=$server --pid-file=$server.pid --log-file=$server.log --daemon $@
- # Wait for the server to start
- sleep 1
- # Grab the new pid
- pid=`cat $server.pid`
- result=1
- # Wait for the latest pid in the file to be the pid we've grabbed
- while [ $result -eq 1 ]
- do
- result=$(check_if_not_started $server.log $server.pid)
- printf "."
- sleep 1
- done
- echo
+for server in $servers; do
+ # If there's a pid
+ if [ -e $server.pid ]; then
+ # Then kill it
+ echo "Killing $server"
+ pid=`cat $server.pid`
+ kill $pid
+ else
+ # Otherwise just continue
+ echo "$server not running"
+ fi
+ # Start the server (and background) (should this be nohup'd?)
+ python ./scripts/paster.py serve $GALAXY_CONFIG_FILE --server-name=$server --pid-file=$server.pid --log-file=$server.log --daemon $@
+ while true; do
+ sleep 1
+ printf "."
+ # Grab the current pid from the pid file
+ if ! current_pid_in_file=$(cat $server.pid); then
+ echo "A Galaxy process died, interrupting" >&2
+ exit 1
+ fi
+ # Search for all pids in the logs and tail for the last one
+ latest_pid=`egrep '^Starting server in PID [0-9]+\.$' $server.log -o | sed 's/Starting server in PID //g;s/\.$//g' | tail -n 1`
+ # If they're equivalent, then the current pid file agrees with our logs
+ # and we've succesfully started
+ [ -n "$latest_pid" ] && [ $latest_pid -eq $current_pid_in_file ] && break
+ done
+ echo
done
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