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
January 2014
- 1 participants
- 280 discussions
commit/galaxy-central: greg: When exporting a repository and dependency hierarchy from the tool shed, always make sure the primary repository entry is ordered last in the manifest.
by commits-noreply@bitbucket.org 07 Jan '14
by commits-noreply@bitbucket.org 07 Jan '14
07 Jan '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/64c6c98b9976/
Changeset: 64c6c98b9976
User: greg
Date: 2014-01-07 22:55:59
Summary: When exporting a repository and dependency hierarchy from the tool shed, always make sure the primary repository entry is ordered last in the manifest.
Affected #: 4 files
diff -r 98f5685a42d2b32481db28b80dedca1b9b6791cf -r 64c6c98b9976c3dadb77426b6a6805922aa7810c lib/tool_shed/galaxy_install/install_manager.py
--- a/lib/tool_shed/galaxy_install/install_manager.py
+++ b/lib/tool_shed/galaxy_install/install_manager.py
@@ -567,14 +567,17 @@
def order_repositories_for_installation( self, tool_shed_repositories, repository_dependencies_dict ):
"""
- Some repositories may have repository dependencies that are required to be installed before the dependent repository. This method will
- inspect the list of repositories about to be installed and make sure to order them appropriately. For each repository about to be installed,
- if required repositories are not contained in the list of repositories about to be installed, then they are not considered. Repository
- dependency definitions that contain circular dependencies should not result in an infinite loop, but obviously prior installation will not be
- handled for one or more of the repositories that require prior installation. This process is similar to the process used when installing tool
- shed repositories (i.e., the order_components_for_installation() method in ~/lib/tool_shed/galaxy_install/repository_util), but does not handle
- managing tool panel sections and other components since repository dependency definitions contained in tool shed repositories with migrated
- tools must never define a relationship to a repository dependency that contains a tool.
+ Some repositories may have repository dependencies that are required to be installed before the dependent
+ repository. This method will inspect the list of repositories about to be installed and make sure to order
+ them appropriately. For each repository about to be installed, if required repositories are not contained
+ in the list of repositories about to be installed, then they are not considered. Repository dependency
+ definitions that contain circular dependencies should not result in an infinite loop, but obviously prior
+ installation will not be handled for one or more of the repositories that require prior installation. This
+ process is similar to the process used when installing tool shed repositories (i.e., the
+ order_components_for_installation() method in ~/lib/tool_shed/galaxy_install/repository_util), but does not
+ handle managing tool panel sections and other components since repository dependency definitions contained
+ in tool shed repositories with migrated tools must never define a relationship to a repository dependency
+ that contains a tool.
"""
ordered_tool_shed_repositories = []
ordered_tsr_ids = []
diff -r 98f5685a42d2b32481db28b80dedca1b9b6791cf -r 64c6c98b9976c3dadb77426b6a6805922aa7810c lib/tool_shed/util/commit_util.py
--- a/lib/tool_shed/util/commit_util.py
+++ b/lib/tool_shed/util/commit_util.py
@@ -313,6 +313,9 @@
if lastest_installable_changeset_revision != suc.INITIAL_CHANGELOG_HASH:
elem.attrib[ 'changeset_revision' ] = lastest_installable_changeset_revision
revised = True
+ else:
+ error_message = 'Invalid latest installable changeset_revision %s ' % str( lastest_installable_changeset_revision )
+ error_message += 'retrieved for repository %s owned by %s. ' % ( str( name ), str( owner ) )
else:
error_message = 'Unable to locate repository with name %s and owner %s. ' % ( str( name ), str( owner ) )
return revised, elem, error_message
diff -r 98f5685a42d2b32481db28b80dedca1b9b6791cf -r 64c6c98b9976c3dadb77426b6a6805922aa7810c lib/tool_shed/util/export_util.py
--- a/lib/tool_shed/util/export_util.py
+++ b/lib/tool_shed/util/export_util.py
@@ -55,7 +55,8 @@
base = base.rstrip( '/' )
return base
-def export_repository( trans, tool_shed_url, repository_id, repository_name, changeset_revision, file_type, export_repository_dependencies, api=False ):
+def export_repository( trans, tool_shed_url, repository_id, repository_name, changeset_revision, file_type,
+ export_repository_dependencies, api=False ):
repository = suc.get_repository_in_tool_shed( trans, repository_id )
repositories_archive_filename = generate_repository_archive_filename( tool_shed_url,
str( repository.name ),
@@ -67,7 +68,8 @@
if export_repository_dependencies:
repo_info_dicts = get_repo_info_dicts( trans, tool_shed_url, repository_id, changeset_revision )
repository_ids = get_repository_ids( trans, repo_info_dicts )
- ordered_repository_ids, ordered_repositories, ordered_changeset_revisions = order_components_for_import( trans, repository_ids, repo_info_dicts )
+ ordered_repository_ids, ordered_repositories, ordered_changeset_revisions = \
+ order_components_for_import( trans, repository_id, repository_ids, repo_info_dicts )
else:
ordered_repository_ids = []
ordered_repositories = []
@@ -193,8 +195,8 @@
def get_components_from_repo_info_dict( trans, repo_info_dict ):
"""
- Return the repository and the associated latest installable changeset_revision (including updates) for the repository defined by the received
- repo_info_dict.
+ Return the repository and the associated latest installable changeset_revision (including updates) for the
+ repository defined by the received repo_info_dict.
"""
for repository_name, repo_info_tup in repo_info_dict.items():
# There should only be one entry in the received repo_info_dict.
@@ -208,8 +210,9 @@
def get_repo_info_dict_for_import( encoded_repository_id, encoded_repository_ids, repo_info_dicts ):
"""
- The received encoded_repository_ids and repo_info_dicts are lists that contain associated elements at each location in the list. This method will return the element
- from repo_info_dicts associated with the received encoded_repository_id by determining it's location in the received encoded_repository_ids list.
+ The received encoded_repository_ids and repo_info_dicts are lists that contain associated elements at each
+ location in the list. This method will return the element from repo_info_dicts associated with the received
+ encoded_repository_id by determining it's location in the received encoded_repository_ids list.
"""
for index, repository_id in enumerate( encoded_repository_ids ):
if repository_id == encoded_repository_id:
@@ -218,6 +221,10 @@
return None
def get_repo_info_dicts( trans, tool_shed_url, repository_id, changeset_revision ):
+ """
+ Return a list of dictionaries defining repositories that are required by the repository associated with the
+ received repository_id.
+ """
repository = suc.get_repository_in_tool_shed( trans, repository_id )
repository_metadata = suc.get_repository_metadata_by_changeset_revision( trans, repository_id, changeset_revision )
# Get a dictionary of all repositories upon which the contents of the current repository_metadata record depend.
@@ -246,8 +253,9 @@
def get_repository_attributes_and_sub_elements( repository, archive_name ):
"""
- Get the information about a repository to create and populate an XML tag set. The generated attributes will be contained within the <repository>
- tag, while the sub_elements will be tag sets contained within the <repository> tag set.
+ Get the information about a repository to create and populate an XML tag set. The generated attributes will
+ be contained within the <repository> tag, while the sub_elements will be tag sets contained within the <repository>
+ tag set.
"""
attributes = odict()
sub_elements = odict()
@@ -268,6 +276,7 @@
return attributes, sub_elements
def get_repository_ids( trans, repo_info_dicts ):
+ """Return a list of repository ids associated with each dictionary in the received repo_info_dicts."""
repository_ids = []
for repo_info_dict in repo_info_dicts:
for repository_name, repo_info_tup in repo_info_dict.items():
@@ -277,23 +286,36 @@
repository_ids.append( trans.security.encode_id( repository.id ) )
return repository_ids
-def order_components_for_import( trans, repository_ids, repo_info_dicts ):
+def order_components_for_import( trans, primary_repository_id, repository_ids, repo_info_dicts ):
"""
- Some repositories may have repository dependencies that must be imported and have metadata set on them before the dependent repository is imported. This method
- will inspect the list of repositories about to be exported and make sure to order them appropriately for proper import. For each repository about to be exported,
- if required repositories are not contained in the list of repositories about to be exported, then they are not considered. Repository dependency definitions that
- contain circular dependencies should not result in an infinite loop, but obviously ordering the list will not be handled for one or more of the repositories that
- require prior import.
+ Some repositories may have repository dependencies that must be imported and have metadata set on
+ them before the dependent repository is imported. This method will inspect the list of repositories
+ about to be exported and make sure to order them appropriately for proper import. For each repository
+ about to be exported, if required repositories are not contained in the list of repositories about to
+ be exported, then they are not considered. Repository dependency definitions that contain circular
+ dependencies should not result in an infinite loop, but obviously ordering the list will not be handled
+ for one or more of the repositories that require prior import.
"""
+ # The received primary_repository_id is the id of the repository being exported, with the received list
+ # of repository_ids being only the ids of all of its repository dependencies. The primary repository will
+ # always be last in the returned lists.
ordered_repository_ids = []
ordered_repositories = []
ordered_changeset_revisions = []
- # Create a dictionary whose keys are the received repository_ids and whose values are a list of repository_ids, each of which is contained in the received list of
- # repository_ids and whose associated repository must be imported prior to the repository associated with the repository_id key.
+ # Create a dictionary whose keys are the received repository_ids and whose values are a list of
+ # repository_ids, each of which is contained in the received list of repository_ids and whose associated
+ # repository must be imported prior to the repository associated with the repository_id key.
prior_import_required_dict = suc.get_prior_import_or_install_required_dict( trans, repository_ids, repo_info_dicts )
processed_repository_ids = []
+ # Process the list of repository dependencies defined for the repository associated with the received
+ # primary_repository_id.
while len( processed_repository_ids ) != len( prior_import_required_dict.keys() ):
repository_id = suc.get_next_prior_import_or_install_required_dict_entry( prior_import_required_dict, processed_repository_ids )
+ if repository_id == primary_repository_id:
+ # Append the primary_repository_id without processing it since it must be returned last in the order.
+ # It will be processed below after all dependencies are processed.
+ processed_repository_ids.append( primary_repository_id )
+ continue
processed_repository_ids.append( repository_id )
if repository_id not in ordered_repository_ids:
prior_import_required_ids = prior_import_required_dict[ repository_id ]
@@ -312,4 +334,11 @@
ordered_repository_ids.append( repository_id )
ordered_repositories.append( repository )
ordered_changeset_revisions.append( changeset_revision )
+ # Process the repository associated with the received primary_repository_id last.
+ repo_info_dict = get_repo_info_dict_for_import( primary_repository_id, repository_ids, repo_info_dicts )
+ repository, changeset_revision = get_components_from_repo_info_dict( trans, repo_info_dict )
+ if repository and changeset_revision:
+ ordered_repository_ids.append( repository_id )
+ ordered_repositories.append( repository )
+ ordered_changeset_revisions.append( changeset_revision )
return ordered_repository_ids, ordered_repositories, ordered_changeset_revisions
diff -r 98f5685a42d2b32481db28b80dedca1b9b6791cf -r 64c6c98b9976c3dadb77426b6a6805922aa7810c lib/tool_shed/util/shed_util_common.py
--- a/lib/tool_shed/util/shed_util_common.py
+++ b/lib/tool_shed/util/shed_util_common.py
@@ -745,10 +745,11 @@
def get_next_prior_import_or_install_required_dict_entry( prior_required_dict, processed_tsr_ids ):
"""
- This method is used in the Tool Shed when exporting a repository and it's dependencies, and in Galaxy when a repository and it's dependencies
- are being installed. The order in which the prior_required_dict is processed is critical in order to ensure that the ultimate repository import
- or installation order is correctly defined. This method determines the next key / value pair from the received prior_required_dict that should
- be processed.
+ This method is used in the Tool Shed when exporting a repository and it's dependencies, and in Galaxy
+ when a repository and it's dependencies are being installed. The order in which the prior_required_dict
+ is processed is critical in order to ensure that the ultimate repository import or installation order is
+ correctly defined. This method determines the next key / value pair from the received prior_required_dict
+ that should be processed.
"""
# Return the first key / value pair that is not yet processed and whose value is an empty list.
for key, value in prior_required_dict.items():
@@ -756,7 +757,8 @@
continue
if not value:
return key
- # Return the first key / value pair that is not yet processed and whose ids in value are all included in processed_tsr_ids.
+ # Return the first key / value pair that is not yet processed and whose ids in value are all included
+ # in processed_tsr_ids.
for key, value in prior_required_dict.items():
if key in processed_tsr_ids:
continue
@@ -767,7 +769,8 @@
break
if all_contained:
return key
- # Return the first key / value pair that is not yet processed. Hopefully this is all that is necessary at this point.
+ # Return the first key / value pair that is not yet processed. Hopefully this is all that is necessary
+ # at this point.
for key, value in prior_required_dict.items():
if key in processed_tsr_ids:
continue
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: martenson: extensive logging for folder_contents API datasets
by commits-noreply@bitbucket.org 07 Jan '14
by commits-noreply@bitbucket.org 07 Jan '14
07 Jan '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/98f5685a42d2/
Changeset: 98f5685a42d2
User: martenson
Date: 2014-01-07 22:08:20
Summary: extensive logging for folder_contents API datasets
Affected #: 1 file
diff -r 6f63fc8b7464f58952522dc35df1544cd40563e3 -r 98f5685a42d2b32481db28b80dedca1b9b6791cf lib/galaxy/webapps/galaxy/api/folder_contents.py
--- a/lib/galaxy/webapps/galaxy/api/folder_contents.py
+++ b/lib/galaxy/webapps/galaxy/api/folder_contents.py
@@ -108,6 +108,8 @@
return_item.update ( dict ( item_count = content_item.item_count ) )
if content_item.api_type == 'file':
+ log.debug( 'FolderContentsController, method index(), dataset name: ' + content_item.name )
+ log.debug( 'vars: ' + str(vars( content_item )) )
library_dataset_dict = content_item.to_dict()
library_dataset_dict['data_type']
library_dataset_dict['file_size']
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
3 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/d8c2fe8e1594/
Changeset: d8c2fe8e1594
User: jmchilton
Date: 2014-01-04 04:58:12
Summary: Backout of Backout of 714f5b1 for now.
... or would that be back-into 714f5b1?
714f5b1 - Move history contents filtering logic into model.
Filtering for deleted, visible, and ids with ORM should prevent loading unneeded objects into memory (a history with 1000 items and 5 visible will now only cause 5 items to be loaded instead of 1000 just to filter out 5). Simplifies history_contents API logic somewhat, allows easier 'unit' testing (included), and provides a clearer entry point for additional 'showing' additional contents types (read dataset collections).
Affected #: 3 files
diff -r f11a729d42cb66753b8b1616ad625c1b2a306b3d -r d8c2fe8e159494e972f624cc4c73b7ff7ce4099f lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -19,6 +19,7 @@
import socket
import time
from string import Template
+from itertools import ifilter
import galaxy.datatypes
import galaxy.datatypes.registry
@@ -43,6 +44,11 @@
# Default Value Required for unit tests
datatypes_registry.load_datatypes()
+# When constructing filters with in for a fixed set of ids, maximum
+# number of items to place in the IN statement. Different databases
+# are going to have different limits so it is likely best to not let
+# this be unlimited - filter in Python if over this limit.
+MAX_IN_FILTER_LENGTH = 100
class NoConverterException(Exception):
def __init__(self, value):
@@ -893,6 +899,32 @@
rval = galaxy.datatypes.data.nice_size( rval )
return rval
+ def contents_iter( self, **kwds ):
+ """
+ Fetch filtered list of contents of history.
+ """
+ python_filter = None
+ db_session = object_session( self )
+ assert db_session != None
+ query = db_session.query( HistoryDatasetAssociation ).filter( HistoryDatasetAssociation.table.c.history_id == self.id )
+ deleted = galaxy.util.string_as_bool_or_none( kwds.get( 'deleted', None ) )
+ if deleted is not None:
+ query = query.filter( HistoryDatasetAssociation.deleted == bool( kwds['deleted'] ) )
+ visible = galaxy.util.string_as_bool_or_none( kwds.get( 'visible', None ) )
+ if visible is not None:
+ query = query.filter( HistoryDatasetAssociation.visible == bool( kwds['visible'] ) )
+ if 'ids' in kwds:
+ ids = kwds['ids']
+ max_in_filter_length = kwds.get('max_in_filter_length', MAX_IN_FILTER_LENGTH)
+ if len(ids) < max_in_filter_length:
+ query = query.filter( HistoryDatasetAssociation.id.in_(ids) )
+ else:
+ python_filter = lambda hda: hda.id in ids
+ if python_filter:
+ return ifilter(python_filter, query)
+ else:
+ return query
+
def copy_tags_from(self,target_user,source_history):
for src_shta in source_history.tags:
new_shta = src_shta.copy()
diff -r f11a729d42cb66753b8b1616ad625c1b2a306b3d -r d8c2fe8e159494e972f624cc4c73b7ff7ce4099f lib/galaxy/webapps/galaxy/api/history_contents.py
--- a/lib/galaxy/webapps/galaxy/api/history_contents.py
+++ b/lib/galaxy/webapps/galaxy/api/history_contents.py
@@ -51,47 +51,28 @@
else:
history = self.get_history( trans, history_id, check_ownership=True, check_accessible=True )
- # if ids, return _FULL_ data (as show) for each id passed
+ contents_kwds = {}
if ids:
- ids = ids.split( ',' )
- for index, hda in enumerate( history.datasets ):
- encoded_hda_id = trans.security.encode_id( hda.id )
- if encoded_hda_id in ids:
- #TODO: share code with show
- rval.append( self._detailed_hda_dict( trans, hda ) )
-
- # if no ids passed, return a _SUMMARY_ of _all_ datasets in the history
+ ids = map( lambda id: trans.security.decode_id( id ), ids.split( ',' ) )
+ contents_kwds[ 'ids' ] = ids
+ # If explicit ids given, always used detailed result.
+ details = 'all'
else:
+ contents_kwds[ 'deleted' ] = kwd.get( 'deleted', None )
+ contents_kwds[ 'visible' ] = kwd.get( 'visible', None )
# details param allows a mixed set of summary and detailed hdas
#TODO: this is getting convoluted due to backwards compat
details = kwd.get( 'details', None ) or []
if details and details != 'all':
details = util.listify( details )
- # by default return all datasets - even if deleted or hidden (defaulting the next switches to None)
- # if specified return those datasets that match the setting
- # backwards compat
- return_deleted = util.string_as_bool_or_none( kwd.get( 'deleted', None ) )
- return_visible = util.string_as_bool_or_none( kwd.get( 'visible', None ) )
-
- for hda in history.datasets:
- # if either return_ setting has been requested (!= None), skip hdas that don't match the request
- if return_deleted is not None:
- if( ( return_deleted and not hda.deleted )
- or ( not return_deleted and hda.deleted ) ):
- continue
- if return_visible is not None:
- if( ( return_visible and not hda.visible )
- or ( not return_visible and hda.visible ) ):
- continue
-
- encoded_hda_id = trans.security.encode_id( hda.id )
- if( ( encoded_hda_id in details )
- or ( details == 'all' ) ):
- rval.append( self._detailed_hda_dict( trans, hda ) )
- else:
- rval.append( self._summary_hda_dict( trans, history_id, hda ) )
-
+ for hda in history.contents_iter( **contents_kwds ):
+ encoded_hda_id = trans.security.encode_id( hda.id )
+ detailed = details == 'all' or ( encoded_hda_id in details )
+ if detailed:
+ rval.append( self._detailed_hda_dict( trans, hda ) )
+ else:
+ rval.append( self._summary_hda_dict( trans, history_id, hda ) )
except Exception, e:
# for errors that are not specific to one hda (history lookup or summary list)
rval = "Error in history API at listing contents: " + str( e )
diff -r f11a729d42cb66753b8b1616ad625c1b2a306b3d -r d8c2fe8e159494e972f624cc4c73b7ff7ce4099f test/unit/test_galaxy_mapping.py
--- a/test/unit/test_galaxy_mapping.py
+++ b/test/unit/test_galaxy_mapping.py
@@ -180,6 +180,39 @@
assert hist1.name == "History 2b"
# gvk TODO need to ad test for GalaxySessions, but not yet sure what they should look like.
+ def test_history_contents( self ):
+ model = self.model
+ u = model.User( email="contents(a)foo.bar.baz", password="password" )
+ # gs = model.GalaxySession()
+ h1 = model.History( name="HistoryContentsHistory1", user=u)
+
+ self.persist( u, h1, expunge=True )
+
+ d1 = self.new_hda( h1, name="1" )
+ d2 = self.new_hda( h1, name="2", visible=False )
+ d3 = self.new_hda( h1, name="3", deleted=True )
+ d4 = self.new_hda( h1, name="4", visible=False, deleted=True )
+
+ def contents_iter_names(**kwds):
+ history = model.context.query( model.History ).filter(
+ model.History.name == "HistoryContentsHistory1"
+ ).first()
+ return set( map( lambda hda: hda.name, history.contents_iter( **kwds ) ) )
+
+ assert contents_iter_names() == set( [ "1", "2", "3", "4" ] )
+ assert contents_iter_names( deleted=False ) == set( [ "1", "2" ] )
+ assert contents_iter_names( visible=True ) == set( [ "1", "3" ] )
+ assert contents_iter_names( visible=False ) == set( [ "2", "4" ] )
+ assert contents_iter_names( deleted=True, visible=False ) == set( [ "4" ] )
+
+ assert contents_iter_names( ids=[ d1.id, d2.id, d3.id, d4.id ] ) == set( [ "1", "2", "3", "4" ] )
+ assert contents_iter_names( ids=[ d1.id, d2.id, d3.id, d4.id ], max_in_filter_length=1 ) == set( [ "1", "2", "3", "4" ] )
+
+ assert contents_iter_names( ids=[ d1.id, d3.id ] ) == set( [ "1", "3" ] )
+
+ def new_hda( self, history, **kwds ):
+ return self.persist( self.model.HistoryDatasetAssociation( history=history, create_dataset=True, sa_session=self.model.session, **kwds ), flush=True )
+
@classmethod
def setUpClass(cls):
# Start the database and connect the mapping
@@ -191,10 +224,16 @@
return cls.model.session.query( type )
@classmethod
- def persist(cls, *args):
+ def persist(cls, *args, **kwargs):
+ session = cls.model.session
+ flush = kwargs.get('flush', True)
for arg in args:
- cls.model.session.add( arg )
- cls.expunge()
+ session.add( arg )
+ if flush:
+ session.flush()
+ if kwargs.get('expunge', not flush):
+ cls.expunge()
+ return arg # Return last or only arg.
@classmethod
def expunge(cls):
https://bitbucket.org/galaxy/galaxy-central/commits/d055c34bc4e0/
Changeset: d055c34bc4e0
User: jmchilton
Date: 2014-01-04 04:58:13
Summary: Fix history contents ordering broken with previous changeset.
Add stronger test cases, though these are still insufficient to exercise the error. The only way I was able to get the history contents to come out misordered was to use postgres. Nonetheless, using postgres this now orders datasets properly.
Affected #: 2 files
diff -r d8c2fe8e159494e972f624cc4c73b7ff7ce4099f -r d055c34bc4e06c58dae118eeb8861cfed479fc52 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -907,6 +907,7 @@
db_session = object_session( self )
assert db_session != None
query = db_session.query( HistoryDatasetAssociation ).filter( HistoryDatasetAssociation.table.c.history_id == self.id )
+ query = query.order_by( HistoryDatasetAssociation.table.c.hid.asc() )
deleted = galaxy.util.string_as_bool_or_none( kwds.get( 'deleted', None ) )
if deleted is not None:
query = query.filter( HistoryDatasetAssociation.deleted == bool( kwds['deleted'] ) )
diff -r d8c2fe8e159494e972f624cc4c73b7ff7ce4099f -r d055c34bc4e06c58dae118eeb8861cfed479fc52 test/unit/test_galaxy_mapping.py
--- a/test/unit/test_galaxy_mapping.py
+++ b/test/unit/test_galaxy_mapping.py
@@ -186,32 +186,34 @@
# gs = model.GalaxySession()
h1 = model.History( name="HistoryContentsHistory1", user=u)
- self.persist( u, h1, expunge=True )
+ self.persist( u, h1, expunge=False )
d1 = self.new_hda( h1, name="1" )
d2 = self.new_hda( h1, name="2", visible=False )
d3 = self.new_hda( h1, name="3", deleted=True )
d4 = self.new_hda( h1, name="4", visible=False, deleted=True )
+ self.session().flush()
+
def contents_iter_names(**kwds):
history = model.context.query( model.History ).filter(
model.History.name == "HistoryContentsHistory1"
).first()
- return set( map( lambda hda: hda.name, history.contents_iter( **kwds ) ) )
+ return list( map( lambda hda: hda.name, history.contents_iter( **kwds ) ) )
- assert contents_iter_names() == set( [ "1", "2", "3", "4" ] )
- assert contents_iter_names( deleted=False ) == set( [ "1", "2" ] )
- assert contents_iter_names( visible=True ) == set( [ "1", "3" ] )
- assert contents_iter_names( visible=False ) == set( [ "2", "4" ] )
- assert contents_iter_names( deleted=True, visible=False ) == set( [ "4" ] )
+ self.assertEquals(contents_iter_names(), [ "1", "2", "3", "4" ])
+ assert contents_iter_names( deleted=False ) == [ "1", "2" ]
+ assert contents_iter_names( visible=True ) == [ "1", "3" ]
+ assert contents_iter_names( visible=False ) == [ "2", "4" ]
+ assert contents_iter_names( deleted=True, visible=False ) == [ "4" ]
- assert contents_iter_names( ids=[ d1.id, d2.id, d3.id, d4.id ] ) == set( [ "1", "2", "3", "4" ] )
- assert contents_iter_names( ids=[ d1.id, d2.id, d3.id, d4.id ], max_in_filter_length=1 ) == set( [ "1", "2", "3", "4" ] )
+ assert contents_iter_names( ids=[ d1.id, d2.id, d3.id, d4.id ] ) == [ "1", "2", "3", "4" ]
+ assert contents_iter_names( ids=[ d1.id, d2.id, d3.id, d4.id ], max_in_filter_length=1 ) == [ "1", "2", "3", "4" ]
- assert contents_iter_names( ids=[ d1.id, d3.id ] ) == set( [ "1", "3" ] )
+ assert contents_iter_names( ids=[ d1.id, d3.id ] ) == [ "1", "3" ]
def new_hda( self, history, **kwds ):
- return self.persist( self.model.HistoryDatasetAssociation( history=history, create_dataset=True, sa_session=self.model.session, **kwds ), flush=True )
+ return history.add_dataset( self.model.HistoryDatasetAssociation( create_dataset=True, sa_session=self.model.session, **kwds ) )
@classmethod
def setUpClass(cls):
@@ -225,7 +227,7 @@
@classmethod
def persist(cls, *args, **kwargs):
- session = cls.model.session
+ session = cls.session()
flush = kwargs.get('flush', True)
for arg in args:
session.add( arg )
@@ -236,6 +238,10 @@
return arg # Return last or only arg.
@classmethod
+ def session(cls):
+ return cls.model.session
+
+ @classmethod
def expunge(cls):
cls.model.session.flush()
cls.model.session.expunge_all()
https://bitbucket.org/galaxy/galaxy-central/commits/6f63fc8b7464/
Changeset: 6f63fc8b7464
User: carlfeberhard
Date: 2014-01-07 21:48:55
Summary: Merged in jmchilton/galaxy-central-fork-1 (pull request #289)
Move history contents filtering logic into model.
Affected #: 3 files
diff -r 0c5e20558f6bb8d690dd9c9d25c172f08e837b1b -r 6f63fc8b7464f58952522dc35df1544cd40563e3 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -19,6 +19,7 @@
import socket
import time
from string import Template
+from itertools import ifilter
import galaxy.datatypes
import galaxy.datatypes.registry
@@ -43,6 +44,11 @@
# Default Value Required for unit tests
datatypes_registry.load_datatypes()
+# When constructing filters with in for a fixed set of ids, maximum
+# number of items to place in the IN statement. Different databases
+# are going to have different limits so it is likely best to not let
+# this be unlimited - filter in Python if over this limit.
+MAX_IN_FILTER_LENGTH = 100
class NoConverterException(Exception):
def __init__(self, value):
@@ -893,6 +899,33 @@
rval = galaxy.datatypes.data.nice_size( rval )
return rval
+ def contents_iter( self, **kwds ):
+ """
+ Fetch filtered list of contents of history.
+ """
+ python_filter = None
+ db_session = object_session( self )
+ assert db_session != None
+ query = db_session.query( HistoryDatasetAssociation ).filter( HistoryDatasetAssociation.table.c.history_id == self.id )
+ query = query.order_by( HistoryDatasetAssociation.table.c.hid.asc() )
+ deleted = galaxy.util.string_as_bool_or_none( kwds.get( 'deleted', None ) )
+ if deleted is not None:
+ query = query.filter( HistoryDatasetAssociation.deleted == bool( kwds['deleted'] ) )
+ visible = galaxy.util.string_as_bool_or_none( kwds.get( 'visible', None ) )
+ if visible is not None:
+ query = query.filter( HistoryDatasetAssociation.visible == bool( kwds['visible'] ) )
+ if 'ids' in kwds:
+ ids = kwds['ids']
+ max_in_filter_length = kwds.get('max_in_filter_length', MAX_IN_FILTER_LENGTH)
+ if len(ids) < max_in_filter_length:
+ query = query.filter( HistoryDatasetAssociation.id.in_(ids) )
+ else:
+ python_filter = lambda hda: hda.id in ids
+ if python_filter:
+ return ifilter(python_filter, query)
+ else:
+ return query
+
def copy_tags_from(self,target_user,source_history):
for src_shta in source_history.tags:
new_shta = src_shta.copy()
diff -r 0c5e20558f6bb8d690dd9c9d25c172f08e837b1b -r 6f63fc8b7464f58952522dc35df1544cd40563e3 lib/galaxy/webapps/galaxy/api/history_contents.py
--- a/lib/galaxy/webapps/galaxy/api/history_contents.py
+++ b/lib/galaxy/webapps/galaxy/api/history_contents.py
@@ -51,47 +51,28 @@
else:
history = self.get_history( trans, history_id, check_ownership=True, check_accessible=True )
- # if ids, return _FULL_ data (as show) for each id passed
+ contents_kwds = {}
if ids:
- ids = ids.split( ',' )
- for index, hda in enumerate( history.datasets ):
- encoded_hda_id = trans.security.encode_id( hda.id )
- if encoded_hda_id in ids:
- #TODO: share code with show
- rval.append( self._detailed_hda_dict( trans, hda ) )
-
- # if no ids passed, return a _SUMMARY_ of _all_ datasets in the history
+ ids = map( lambda id: trans.security.decode_id( id ), ids.split( ',' ) )
+ contents_kwds[ 'ids' ] = ids
+ # If explicit ids given, always used detailed result.
+ details = 'all'
else:
+ contents_kwds[ 'deleted' ] = kwd.get( 'deleted', None )
+ contents_kwds[ 'visible' ] = kwd.get( 'visible', None )
# details param allows a mixed set of summary and detailed hdas
#TODO: this is getting convoluted due to backwards compat
details = kwd.get( 'details', None ) or []
if details and details != 'all':
details = util.listify( details )
- # by default return all datasets - even if deleted or hidden (defaulting the next switches to None)
- # if specified return those datasets that match the setting
- # backwards compat
- return_deleted = util.string_as_bool_or_none( kwd.get( 'deleted', None ) )
- return_visible = util.string_as_bool_or_none( kwd.get( 'visible', None ) )
-
- for hda in history.datasets:
- # if either return_ setting has been requested (!= None), skip hdas that don't match the request
- if return_deleted is not None:
- if( ( return_deleted and not hda.deleted )
- or ( not return_deleted and hda.deleted ) ):
- continue
- if return_visible is not None:
- if( ( return_visible and not hda.visible )
- or ( not return_visible and hda.visible ) ):
- continue
-
- encoded_hda_id = trans.security.encode_id( hda.id )
- if( ( encoded_hda_id in details )
- or ( details == 'all' ) ):
- rval.append( self._detailed_hda_dict( trans, hda ) )
- else:
- rval.append( self._summary_hda_dict( trans, history_id, hda ) )
-
+ for hda in history.contents_iter( **contents_kwds ):
+ encoded_hda_id = trans.security.encode_id( hda.id )
+ detailed = details == 'all' or ( encoded_hda_id in details )
+ if detailed:
+ rval.append( self._detailed_hda_dict( trans, hda ) )
+ else:
+ rval.append( self._summary_hda_dict( trans, history_id, hda ) )
except Exception, e:
# for errors that are not specific to one hda (history lookup or summary list)
rval = "Error in history API at listing contents: " + str( e )
diff -r 0c5e20558f6bb8d690dd9c9d25c172f08e837b1b -r 6f63fc8b7464f58952522dc35df1544cd40563e3 test/unit/test_galaxy_mapping.py
--- a/test/unit/test_galaxy_mapping.py
+++ b/test/unit/test_galaxy_mapping.py
@@ -180,6 +180,41 @@
assert hist1.name == "History 2b"
# gvk TODO need to ad test for GalaxySessions, but not yet sure what they should look like.
+ def test_history_contents( self ):
+ model = self.model
+ u = model.User( email="contents(a)foo.bar.baz", password="password" )
+ # gs = model.GalaxySession()
+ h1 = model.History( name="HistoryContentsHistory1", user=u)
+
+ self.persist( u, h1, expunge=False )
+
+ d1 = self.new_hda( h1, name="1" )
+ d2 = self.new_hda( h1, name="2", visible=False )
+ d3 = self.new_hda( h1, name="3", deleted=True )
+ d4 = self.new_hda( h1, name="4", visible=False, deleted=True )
+
+ self.session().flush()
+
+ def contents_iter_names(**kwds):
+ history = model.context.query( model.History ).filter(
+ model.History.name == "HistoryContentsHistory1"
+ ).first()
+ return list( map( lambda hda: hda.name, history.contents_iter( **kwds ) ) )
+
+ self.assertEquals(contents_iter_names(), [ "1", "2", "3", "4" ])
+ assert contents_iter_names( deleted=False ) == [ "1", "2" ]
+ assert contents_iter_names( visible=True ) == [ "1", "3" ]
+ assert contents_iter_names( visible=False ) == [ "2", "4" ]
+ assert contents_iter_names( deleted=True, visible=False ) == [ "4" ]
+
+ assert contents_iter_names( ids=[ d1.id, d2.id, d3.id, d4.id ] ) == [ "1", "2", "3", "4" ]
+ assert contents_iter_names( ids=[ d1.id, d2.id, d3.id, d4.id ], max_in_filter_length=1 ) == [ "1", "2", "3", "4" ]
+
+ assert contents_iter_names( ids=[ d1.id, d3.id ] ) == [ "1", "3" ]
+
+ def new_hda( self, history, **kwds ):
+ return history.add_dataset( self.model.HistoryDatasetAssociation( create_dataset=True, sa_session=self.model.session, **kwds ) )
+
@classmethod
def setUpClass(cls):
# Start the database and connect the mapping
@@ -191,10 +226,20 @@
return cls.model.session.query( type )
@classmethod
- def persist(cls, *args):
+ def persist(cls, *args, **kwargs):
+ session = cls.session()
+ flush = kwargs.get('flush', True)
for arg in args:
- cls.model.session.add( arg )
- cls.expunge()
+ session.add( arg )
+ if flush:
+ session.flush()
+ if kwargs.get('expunge', not flush):
+ cls.expunge()
+ return arg # Return last or only arg.
+
+ @classmethod
+ def session(cls):
+ return cls.model.session
@classmethod
def expunge(cls):
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: martenson: data libraries fix for modal handling and folder creation
by commits-noreply@bitbucket.org 07 Jan '14
by commits-noreply@bitbucket.org 07 Jan '14
07 Jan '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/0c5e20558f6b/
Changeset: 0c5e20558f6b
User: martenson
Date: 2014-01-07 21:39:17
Summary: data libraries fix for modal handling and folder creation
Affected #: 3 files
diff -r 968389346b3491bf1a90cf359ff258075816b1e8 -r 0c5e20558f6bb8d690dd9c9d25c172f08e837b1b static/scripts/galaxy.library.js
--- a/static/scripts/galaxy.library.js
+++ b/static/scripts/galaxy.library.js
@@ -408,7 +408,6 @@
renderModalAfterFetch : function(item, histories){
var size = this.size_to_string(item.get('file_size'));
var template = _.template(this.templateDatasetModal(), { item : item, size : size });
- this.modal = null;
// make modal
var self = this;
this.modal = new mod_modal.GalaxyModal({
@@ -418,7 +417,7 @@
buttons : {
'Import' : function() { self.importCurrentIntoHistory() },
'Download' : function() { self.downloadCurrent() },
- 'Close' : function() { self.modal.hideOrDestroy; self.modal = null; }
+ 'Close' : function() { self.modal.hideOrDestroy() }
}
});
@@ -574,7 +573,7 @@
body : history_modal_tmpl,
buttons : {
'Import' : function() {self.importAllIntoHistory()},
- 'Close' : function() {self.modal.hideOrDestroy; self.modal = null;}
+ 'Close' : function() {self.modal.hideOrDestroy();}
}
});
self.modal.bindEvents();
@@ -626,9 +625,7 @@
var popped_item = history_item_set.pop();
if (typeof popped_item === "undefined") {
mod_toastr.success('All datasets imported');
-
- //this will destroy other modals too - including those hidden!!!
- this.modal.destroy();
+ this.modal.hideOrDestroy();
return
}
var promise = $.when(popped_item.save({content: popped_item.content, source: popped_item.source})).done(function(a1){
@@ -712,7 +709,7 @@
body : this.template_new_folder(),
buttons : {
'Create' : function() {self.create_new_folder_event()},
- 'Close' : function() {self.modal.destroy(); self.modal = null;}
+ 'Close' : function() {self.modal.hideOrDestroy(); self.modal = null;}
}
});
this.modal.bindEvents();
@@ -725,13 +722,17 @@
var folderDetails = this.serialize_new_folder();
if (this.validate_new_folder(folderDetails)){
var folder = new FolderAsModel();
- folder.url = folder.urlRoot + '/F2fdbd5c5858e78fb' //load real ID
+
+ url_items = Backbone.history.fragment.split('/');
+ current_folder_id = url_items[url_items.length-1];
+ folder.url = folder.urlRoot + '/' + current_folder_id ;
+
var self = this;
folder.save(folderDetails, {
success: function (folder) {
- self.modal.destroy();
+ self.modal.hideOrDestroy();
mod_toastr.success('Folder created');
- self.render({id: 'F2fdbd5c5858e78fb'}); //put real id
+ self.render({id: current_folder_id});
},
error: function(){
mod_toastr.error('An error occured :(');
@@ -865,7 +866,7 @@
body : this.template_new_library(),
buttons : {
'Create' : function() {self.create_new_library_event()},
- 'Close' : function() {self.modal.hideOrDestroy(); self.modal = null;}
+ 'Close' : function() {self.modal.hideOrDestroy();}
}
});
this.modal.bindEvents();
@@ -881,7 +882,7 @@
var self = this;
library.save(libraryDetails, {
success: function (library) {
- self.modal.destroy();
+ self.modal.hideOrDestroy();
self.clear_library_modal();
self.render();
mod_toastr.success('Library created');
diff -r 968389346b3491bf1a90cf359ff258075816b1e8 -r 0c5e20558f6bb8d690dd9c9d25c172f08e837b1b static/scripts/galaxy.modal.js
--- a/static/scripts/galaxy.modal.js
+++ b/static/scripts/galaxy.modal.js
@@ -95,7 +95,7 @@
this.visible = true;
},
- // hide modal
+ // hide modal, shouldn't be called directly but through hideOrDestroy()
hide: function(){
// fade out
this.$el.fadeOut('fast');
@@ -105,7 +105,7 @@
this.unbindEvents();
},
- // destroy modal
+ // destroy modal, shouldn't be called directly but through hideOrDestroy()
destroy: function(){
// set flag
this.visible = false;
diff -r 968389346b3491bf1a90cf359ff258075816b1e8 -r 0c5e20558f6bb8d690dd9c9d25c172f08e837b1b static/scripts/packed/galaxy.library.js
--- a/static/scripts/packed/galaxy.library.js
+++ b/static/scripts/packed/galaxy.library.js
@@ -1,1 +1,1 @@
-var view=null;var library_router=null;var responses=[];define(["galaxy.modal","galaxy.masthead","utils/galaxy.utils","libs/toastr"],function(k,l,h,n){var f=Backbone.Model.extend({urlRoot:"/api/libraries"});var c=Backbone.Model.extend({urlRoot:"/api/folders"});var o=Backbone.Collection.extend({url:"/api/libraries",model:f});var i=Backbone.Model.extend({urlRoot:"/api/libraries/datasets"});var d=Backbone.Collection.extend({model:i});var e=Backbone.Model.extend({defaults:{folder:new d(),full_path:"unknown",urlRoot:"/api/folders/",id:"unknown"},parse:function(r){this.full_path=r[0].full_path;this.get("folder").reset(r[1].folder_contents);return r}});var b=Backbone.Model.extend({urlRoot:"/api/histories/"});var j=Backbone.Model.extend({url:"/api/histories/"});var p=Backbone.Collection.extend({url:"/api/histories",model:j});var q=Backbone.Router.extend({routes:{"":"libraries","folders/:id":"folder_content","folders/:folder_id/download/:format":"download"}});var m=Backbone.View.extend({el:"#center",progress:0,progressStep:1,lastSelectedHistory:"",modal:null,folders:null,initialize:function(){this.folders=[];this.queue=jQuery.Deferred();this.queue.resolve()},templateFolder:function(){var r=[];r.push('<div id="library_container" style="width: 90%; margin: auto; margin-top: 2em; ">');r.push('<h3>New Data Libraries. This is work in progress. Report problems & ideas to <a href="mailto:marten@bx.psu.edu?Subject=DataLibraries_Feedback" target="_blank">Marten</a>.</h3>');r.push('<div id="library_folder_toolbar" >');r.push(' <button title="Create New Folder" id="toolbtn_create_folder" class="btn btn-primary" type="button"><span class="fa fa-plus"></span><span class="fa fa-folder-close"></span> folder</button>');r.push(' <button id="toolbtn_bulk_import" class="btn btn-primary" style="display: none; margin-left: 0.5em;" type="button"><span class="fa fa-external-link"></span> to history</button>');r.push(' <div id="toolbtn_dl" class="btn-group" style="margin-left: 0.5em; display: none; ">');r.push(' <button id="drop_toggle" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown">');r.push(' <span class="fa fa-download"></span> download <span class="caret"></span>');r.push(" </button>");r.push(' <ul class="dropdown-menu" role="menu">');r.push(' <li><a href="#/folders/<%= id %>/download/tgz">.tar.gz</a></li>');r.push(' <li><a href="#/folders/<%= id %>/download/tbz">.tar.bz</a></li>');r.push(' <li><a href="#/folders/<%= id %>/download/zip">.zip</a></li>');r.push(" </ul>");r.push(" </div>");r.push("</div>");r.push('<div class="library_breadcrumb">');r.push('<a title="Return to the list of libraries" href="#">Libraries</a><b>|</b> ');r.push("<% _.each(path, function(path_item) { %>");r.push("<% if (path_item[0] != id) { %>");r.push('<a title="Return to this folder" href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a><b>|</b> ');r.push("<% } else { %>");r.push('<span title="You are in this folder"><%- path_item[1] %></span>');r.push("<% } %>");r.push("<% }); %>");r.push("</div>");r.push('<table id="folder_table" class="table table-condensed">');r.push(" <thead>");r.push(' <th style="text-align: center; width: 20px; "><input id="select-all-checkboxes" style="margin: 0;" type="checkbox"></th>');r.push(' <th class="button_heading">view</th>');r.push(" <th>name</th>");r.push(" <th>data type</th>");r.push(" <th>size</th>");r.push(" <th>date</th>");r.push(" </thead>");r.push(" <tbody>");r.push(" <td></td>");r.push(' <td><button title="Go to parent folder" type="button" data-id="<%- upper_folder_id %>" class="btn_open_folder btn btn-default btn-xs">');r.push(' <span class="fa fa-arrow-up"></span> .. go up</td>');r.push(" <td></td>");r.push(" <td></td>");r.push(" <td></td>");r.push(" <td></td>");r.push(" </tr>");r.push(" <% _.each(items, function(content_item) { %>");r.push(' <tr class="folder_row light" id="<%- content_item.id %>">');r.push(' <% if (content_item.get("type") === "folder") { %>');r.push(" <td></td>");r.push(' <td><button title="Open this folder" type="button" data-id="<%- content_item.id %>" class="btn_open_folder btn btn-default btn-xs">');r.push(' <span class="fa fa-folder-open"></span> browse</td>');r.push(' <td><%- content_item.get("name") %>');r.push(' <% if (content_item.get("item_count") === 0) { %>');r.push(' <span class="muted">(empty folder)</span>');r.push(" <% } %>");r.push(" </td>");r.push(" <td>folder</td>");r.push(' <td><%= _.escape(content_item.get("item_count")) %> item(s)</td>');r.push(" <% } else { %>");r.push(' <td style="text-align: center; "><input style="margin: 0;" type="checkbox"></td>');r.push(" <td>");r.push(' <button title="See details of this dataset" type="button" class="library-dataset btn btn-default btn-xs">');r.push(' <span class="fa fa-eye"></span> details');r.push(" </button>");r.push(" </td>");r.push(' <td><%- content_item.get("name") %></td>');r.push(' <td><%= _.escape(content_item.get("data_type")) %></td>');r.push(' <td><%= _.escape(content_item.get("readable_size")) %></td>');r.push(" <% } %> ");r.push(' <td><%= _.escape(content_item.get("time_updated")) %></td>');r.push(" </tr>");r.push(" <% }); %>");r.push(" ");r.push(" </tbody>");r.push("</table>");r.push("</div>");return r.join("")},templateDatasetModal:function(){var r=[];r.push('<div id="dataset_info_modal">');r.push(' <table class="table table-striped table-condensed">');r.push(" <tr>");r.push(' <th scope="row" id="id_row" data-id="<%= _.escape(item.get("ldda_id")) %>">Name</th>');r.push(' <td><%= _.escape(item.get("name")) %></td>');r.push(" </tr>");r.push(" <tr>");r.push(' <th scope="row">Data type</th>');r.push(' <td><%= _.escape(item.get("data_type")) %></td>');r.push(" </tr>");r.push(" <tr>");r.push(' <th scope="row">Genome build</th>');r.push(' <td><%= _.escape(item.get("genome_build")) %></td>');r.push(" </tr>");r.push(' <th scope="row">Size</th>');r.push(" <td><%= _.escape(size) %></td>");r.push(" </tr>");r.push(" <tr>");r.push(' <th scope="row">Date uploaded</th>');r.push(' <td><%= _.escape(item.get("date_uploaded")) %></td>');r.push(" </tr>");r.push(" <tr>");r.push(' <th scope="row">Uploaded by</th>');r.push(' <td><%= _.escape(item.get("uploaded_by")) %></td>');r.push(" </tr>");r.push(' <tr scope="row">');r.push(' <th scope="row">Data Lines</th>');r.push(' <td scope="row"><%= _.escape(item.get("metadata_data_lines")) %></td>');r.push(" </tr>");r.push(' <th scope="row">Comment Lines</th>');r.push(' <% if (item.get("metadata_comment_lines") === "") { %>');r.push(' <td scope="row"><%= _.escape(item.get("metadata_comment_lines")) %></td>');r.push(" <% } else { %>");r.push(' <td scope="row">unknown</td>');r.push(" <% } %>");r.push(" </tr>");r.push(" <tr>");r.push(' <th scope="row">Number of Columns</th>');r.push(' <td scope="row"><%= _.escape(item.get("metadata_columns")) %></td>');r.push(" </tr>");r.push(" <tr>");r.push(' <th scope="row">Column Types</th>');r.push(' <td scope="row"><%= _.escape(item.get("metadata_column_types")) %></td>');r.push(" </tr>");r.push(" <tr>");r.push(' <th scope="row">Miscellaneous information</th>');r.push(' <td scope="row"><%= _.escape(item.get("misc_blurb")) %></td>');r.push(" </tr>");r.push(" </table>");r.push(' <pre class="peek">');r.push(" </pre>");r.push("</div>");return r.join("")},templateHistorySelectInModal:function(){var r=[];r.push('<span id="history_modal_combo" style="width:90%; margin-left: 1em; margin-right: 1em; ">');r.push("Select history: ");r.push('<select id="dataset_import_single" name="dataset_import_single" style="width:50%; margin-bottom: 1em; "> ');r.push(" <% _.each(histories, function(history) { %>");r.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');r.push(" <% }); %>");r.push("</select>");r.push("</span>");return r.join("")},templateBulkImportInModal:function(){var r=[];r.push('<span id="history_modal_combo_bulk" style="width:90%; margin-left: 1em; margin-right: 1em; ">');r.push("Select history: ");r.push('<select id="dataset_import_bulk" name="dataset_import_bulk" style="width:50%; margin-bottom: 1em; "> ');r.push(" <% _.each(histories, function(history) { %>");r.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');r.push(" <% }); %>");r.push("</select>");r.push("</span>");return r.join("")},size_to_string:function(r){var s="";if(r>=100000000000){r=r/100000000000;s="TB"}else{if(r>=100000000){r=r/100000000;s="GB"}else{if(r>=100000){r=r/100000;s="MB"}else{if(r>=100){r=r/100;s="KB"}else{r=r*10;s="b"}}}}return(Math.round(r)/10)+s},events:{"click #select-all-checkboxes":"selectAll","click .folder_row":"selectClickedRow","click #toolbtn_bulk_import":"modalBulkImport","click #toolbtn_dl":"bulkDownload","click .library-dataset":"showDatasetDetails","click #toolbtn_create_folder":"createFolderFromModal","click .btn_open_folder":"navigateToFolder"},render:function(r){$("#center").css("overflow","auto");view=this;var t=this;var s=new e({id:r.id});s.url=s.attributes.urlRoot+r.id+"/contents";s.fetch({success:function(u){for(var w=0;w<s.attributes.folder.models.length;w++){var v=s.attributes.folder.models[w];if(v.get("type")==="file"){v.set("readable_size",t.size_to_string(v.get("file_size")))}}var y=s.full_path;var z;if(y.length===1){z=0}else{z=y[y.length-2][0]}var x=_.template(t.templateFolder(),{path:s.full_path,items:s.attributes.folder.models,id:r.id,upper_folder_id:z});t.$el.html(x)}})},navigateToFolder:function(s){var r=$(s.target).attr("data-id");if(typeof r==="undefined"){return false}else{if(r==="0"){library_router.navigate("#",{trigger:true,replace:true})}else{library_router.navigate("folders/"+r,{trigger:true,replace:true})}}},showDatasetDetails:function(u){u.preventDefault();var v=$(u.target).parent().parent().attr("id");var t=new i();var s=new p();t.id=v;var r=this;t.fetch({success:function(w){s.fetch({success:function(x){r.renderModalAfterFetch(w,x)}})}})},renderModalAfterFetch:function(w,t){var u=this.size_to_string(w.get("file_size"));var v=_.template(this.templateDatasetModal(),{item:w,size:u});this.modal=null;var s=this;this.modal=new k.GalaxyModal({destructible:true,title:"Dataset Details",body:v,buttons:{Import:function(){s.importCurrentIntoHistory()},Download:function(){s.downloadCurrent()},Close:function(){s.modal.hideOrDestroy;s.modal=null}}});$(".peek").html(w.get("peek"));var r=_.template(this.templateHistorySelectInModal(),{histories:t.models});$(this.modal.elMain).find(".buttons").prepend(r);if(s.lastSelectedHistory.length>0){$(this.modal.elMain).find("#dataset_import_single").val(s.lastSelectedHistory)}this.modal.bindEvents();this.modal.show()},downloadCurrent:function(){this.modal.disableButton("Import");this.modal.disableButton("Download");var r=[];r.push($("#id_row").attr("data-id"));var s="/api/libraries/datasets/download/uncompressed";var t={ldda_ids:r};folderContentView.processDownload(s,t);this.modal.enableButton("Import");this.modal.enableButton("Download")},importCurrentIntoHistory:function(){this.modal.disableButton("Import");this.modal.disableButton("Download");var t=$(this.modal.elMain).find("select[name=dataset_import_single] option:selected").val();this.lastSelectedHistory=t;var r=$("#id_row").attr("data-id");var u=new b();var s=this;u.url=u.urlRoot+t+"/contents";u.save({content:r,source:"library"},{success:function(){n.success("Dataset imported");s.modal.enableButton("Import");s.modal.enableButton("Download")},error:function(){n.error("An error occured! Dataset not imported. Please try again.");s.modal.enableButton("Import");s.modal.enableButton("Download")}})},selectAll:function(s){var r=s.target.checked;that=this;$(":checkbox").each(function(){this.checked=r;$row=$(this.parentElement.parentElement);(r)?that.makeDarkRow($row):that.makeWhiteRow($row)});this.checkTools()},selectClickedRow:function(s){var u="";var r;var t;if(s.target.localName==="input"){u=s.target;r=$(s.target.parentElement.parentElement);t="input"}else{if(s.target.localName==="td"){u=$("#"+s.target.parentElement.id).find(":checkbox")[0];r=$(s.target.parentElement);t="td"}}if(u===""){s.stopPropagation();return}if(u===undefined){s.stopPropagation();return}if(u.checked){if(t==="td"){u.checked="";this.makeWhiteRow(r)}else{if(t==="input"){this.makeDarkRow(r)}}}else{if(t==="td"){u.checked="selected";this.makeDarkRow(r)}else{if(t==="input"){this.makeWhiteRow(r)}}}this.checkTools()},makeDarkRow:function(r){r.removeClass("light");r.find("a").removeClass("light");r.addClass("dark");r.find("a").addClass("dark")},makeWhiteRow:function(r){r.removeClass("dark");r.find("a").removeClass("dark");r.addClass("light");r.find("a").addClass("light")},checkTools:function(){var r=$("#folder_table").find(":checked");if(r.length>0){$("#toolbtn_bulk_import").show();$("#toolbtn_dl").show()}else{$("#toolbtn_bulk_import").hide();$("#toolbtn_dl").hide()}},modalBulkImport:function(){var s=this;var r=new p();r.fetch({success:function(t){var u=_.template(s.templateBulkImportInModal(),{histories:t.models});s.modal=new k.GalaxyModal({destructible:true,title:"Import into History",body:u,buttons:{Import:function(){s.importAllIntoHistory()},Close:function(){s.modal.hideOrDestroy;s.modal=null}}});s.modal.bindEvents();s.modal.show()}})},importAllIntoHistory:function(){this.modal.disableButton("Import");var t=$("select[name=dataset_import_bulk] option:selected").val();var x=$("select[name=dataset_import_bulk] option:selected").text();var z=[];$("#folder_table").find(":checked").each(function(){if(this.parentElement.parentElement.id!=""){z.push(this.parentElement.parentElement.id)}});var y=_.template(this.templateProgressBar(),{history_name:x});$(this.modal.elMain).find(".modal-body").html(y);var u=100/z.length;this.initProgress(u);var r=[];for(var s=z.length-1;s>=0;s--){library_dataset_id=z[s];var v=new b();var w=this;v.url=v.urlRoot+t+"/contents";v.content=library_dataset_id;v.source="library";r.push(v)}this.chainCall(r)},chainCall:function(s){var r=this;var t=s.pop();if(typeof t==="undefined"){n.success("All datasets imported");this.modal.destroy();return}var u=$.when(t.save({content:t.content,source:t.source})).done(function(v){r.updateProgress();responses.push(v);r.chainCall(s)})},initProgress:function(r){this.progress=0;this.progressStep=r},updateProgress:function(){this.progress+=this.progressStep;$(".progress-bar-import").width(Math.round(this.progress)+"%");txt_representation=Math.round(this.progress)+"% Complete";$(".completion_span").text(txt_representation)},templateProgressBar:function(){var r=[];r.push('<div class="import_text">');r.push("Importing selected datasets to history <b><%= _.escape(history_name) %></b>");r.push("</div>");r.push('<div class="progress">');r.push(' <div class="progress-bar progress-bar-import" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 00%;">');r.push(' <span class="completion_span">0% Complete</span>');r.push(" </div>");r.push("</div>");r.push("");return r.join("")},download:function(r,v){var t=[];$("#folder_table").find(":checked").each(function(){if(this.parentElement.parentElement.id!=""){t.push(this.parentElement.parentElement.id)}});var s="/api/libraries/datasets/download/"+v;var u={ldda_ids:t};this.processDownload(s,u,"get")},processDownload:function(s,t,u){if(s&&t){t=typeof t=="string"?t:$.param(t);var r="";$.each(t.split("&"),function(){var v=this.split("=");r+='<input type="hidden" name="'+v[0]+'" value="'+v[1]+'" />'});$('<form action="'+s+'" method="'+(u||"post")+'">'+r+"</form>").appendTo("body").submit().remove();n.info("Your download will begin soon")}},createFolderFromModal:function(){event.preventDefault();event.stopPropagation();var r=this;this.modal=new k.GalaxyModal({destructible:true,title:"Create New Folder",body:this.template_new_folder(),buttons:{Create:function(){r.create_new_folder_event()},Close:function(){r.modal.destroy();r.modal=null}}});this.modal.bindEvents();this.modal.show()},create_new_folder_event:function(){var r=this.serialize_new_folder();if(this.validate_new_folder(r)){var t=new c();t.url=t.urlRoot+"/F2fdbd5c5858e78fb";var s=this;t.save(r,{success:function(u){s.modal.destroy();n.success("Folder created");s.render({id:"F2fdbd5c5858e78fb"})},error:function(){n.error("An error occured :(")}})}else{n.error("Folder's name is missing")}return false},serialize_new_folder:function(){return{name:$("input[name='Name']").val(),description:$("input[name='Description']").val()}},validate_new_folder:function(r){return r.name!==""},template_new_folder:function(){tmpl_array=[];tmpl_array.push('<div id="new_folder_modal">');tmpl_array.push("<form>");tmpl_array.push('<input type="text" name="Name" value="" placeholder="Name">');tmpl_array.push('<input type="text" name="Description" value="" placeholder="Description">');tmpl_array.push("</form>");tmpl_array.push("</div>");return tmpl_array.join("")}});var a=Backbone.View.extend({el:"#center",events:{"click #create_new_library_btn":"show_library_modal"},initialize:function(){},template_library_list:function(){tmpl_array=[];tmpl_array.push('<div id="library_container" style="width: 90%; margin: auto; margin-top: 2em; overflow: auto !important; ">');tmpl_array.push("");tmpl_array.push('<h3>New Data Libraries. This is work in progress. Report problems & ideas to <a href="mailto:marten@bx.psu.edu?Subject=DataLibraries_Feedback" target="_blank">Marten</a>.</h3>');tmpl_array.push('<a href="" id="create_new_library_btn" class="btn btn-primary file ">New Library</a>');tmpl_array.push('<table class="table table-condensed">');tmpl_array.push(" <thead>");tmpl_array.push(' <th class="button_heading"></th>');tmpl_array.push(" <th>name</th>");tmpl_array.push(" <th>description</th>");tmpl_array.push(" <th>synopsis</th> ");tmpl_array.push(" <th>model type</th> ");tmpl_array.push(" </thead>");tmpl_array.push(" <tbody>");tmpl_array.push(" <% _.each(libraries, function(library) { %>");tmpl_array.push(" <tr>");tmpl_array.push(' <td><button title="Open this library" type="button" data-id="<%- library.get("root_folder_id") %>" class="btn_open_folder btn btn-default btn-xs">');tmpl_array.push(' <span class="fa fa-folder-open"></span> browse</td>');tmpl_array.push(' <td><%- library.get("name") %></td>');tmpl_array.push(' <td><%= _.escape(library.get("description")) %></td>');tmpl_array.push(' <td><%= _.escape(library.get("synopsis")) %></td>');tmpl_array.push(' <td><%= _.escape(library.get("model_class")) %></td>');tmpl_array.push(" </tr>");tmpl_array.push(" <% }); %>");tmpl_array.push(" </tbody>");tmpl_array.push("</table>");tmpl_array.push("</div>");return tmpl_array.join("")},render:function(){$("#center").css("overflow","auto");var r=this;libraries=new o();libraries.fetch({success:function(s){var t=_.template(r.template_library_list(),{libraries:s.models});r.$el.html(t)},error:function(t,s){if(s.statusCode().status===403){n.error("Please log in first. Redirecting to login page in 3s.");setTimeout(r.redirectToLogin,3000)}else{n.error("An error occured. Please try again.")}}})},redirectToHome:function(){window.location="../"},redirectToLogin:function(){window.location="/user/login"},modal:null,show_library_modal:function(s){s.preventDefault();s.stopPropagation();var r=this;this.modal=new k.GalaxyModal({destructible:true,title:"Create New Library",body:this.template_new_library(),buttons:{Create:function(){r.create_new_library_event()},Close:function(){r.modal.hideOrDestroy();r.modal=null}}});this.modal.bindEvents();this.modal.show()},create_new_library_event:function(){var t=this.serialize_new_library();if(this.validate_new_library(t)){var s=new f();var r=this;s.save(t,{success:function(u){r.modal.destroy();r.clear_library_modal();r.render();n.success("Library created")},error:function(){n.error("An error occured :(")}})}else{n.error("Library's name is missing")}return false},clear_library_modal:function(){$("input[name='Name']").val("");$("input[name='Description']").val("");$("input[name='Synopsis']").val("")},serialize_new_library:function(){return{name:$("input[name='Name']").val(),description:$("input[name='Description']").val(),synopsis:$("input[name='Synopsis']").val()}},validate_new_library:function(r){return r.name!==""},template_new_library:function(){tmpl_array=[];tmpl_array.push('<div id="new_library_modal">');tmpl_array.push("<form>");tmpl_array.push('<input type="text" name="Name" value="" placeholder="Name">');tmpl_array.push('<input type="text" name="Description" value="" placeholder="Description">');tmpl_array.push('<input type="text" name="Synopsis" value="" placeholder="Synopsis">');tmpl_array.push("</form>");tmpl_array.push("</div>");return tmpl_array.join("")}});var g=Backbone.View.extend({folderContentView:null,galaxyLibraryview:null,initialize:function(){folderContentView=new m();galaxyLibraryview=new a();library_router=new q();library_router.on("route:libraries",function(){galaxyLibraryview.render()});library_router.on("route:folder_content",function(r){folderContentView.render({id:r})});library_router.on("route:download",function(r,s){if($("#center").find(":checked").length===0){library_router.navigate("folders/"+r,{trigger:true,replace:true})}else{folderContentView.download(r,s);library_router.navigate("folders/"+r,{trigger:false,replace:true})}});Backbone.history.start();return this}});return{GalaxyApp:g}});
\ No newline at end of file
+var view=null;var library_router=null;var responses=[];define(["galaxy.modal","galaxy.masthead","utils/galaxy.utils","libs/toastr"],function(k,l,h,n){var f=Backbone.Model.extend({urlRoot:"/api/libraries"});var c=Backbone.Model.extend({urlRoot:"/api/folders"});var o=Backbone.Collection.extend({url:"/api/libraries",model:f});var i=Backbone.Model.extend({urlRoot:"/api/libraries/datasets"});var d=Backbone.Collection.extend({model:i});var e=Backbone.Model.extend({defaults:{folder:new d(),full_path:"unknown",urlRoot:"/api/folders/",id:"unknown"},parse:function(r){this.full_path=r[0].full_path;this.get("folder").reset(r[1].folder_contents);return r}});var b=Backbone.Model.extend({urlRoot:"/api/histories/"});var j=Backbone.Model.extend({url:"/api/histories/"});var p=Backbone.Collection.extend({url:"/api/histories",model:j});var q=Backbone.Router.extend({routes:{"":"libraries","folders/:id":"folder_content","folders/:folder_id/download/:format":"download"}});var m=Backbone.View.extend({el:"#center",progress:0,progressStep:1,lastSelectedHistory:"",modal:null,folders:null,initialize:function(){this.folders=[];this.queue=jQuery.Deferred();this.queue.resolve()},templateFolder:function(){var r=[];r.push('<div id="library_container" style="width: 90%; margin: auto; margin-top: 2em; ">');r.push('<h3>New Data Libraries. This is work in progress. Report problems & ideas to <a href="mailto:marten@bx.psu.edu?Subject=DataLibraries_Feedback" target="_blank">Marten</a>.</h3>');r.push('<div id="library_folder_toolbar" >');r.push(' <button title="Create New Folder" id="toolbtn_create_folder" class="btn btn-primary" type="button"><span class="fa fa-plus"></span><span class="fa fa-folder-close"></span> folder</button>');r.push(' <button id="toolbtn_bulk_import" class="btn btn-primary" style="display: none; margin-left: 0.5em;" type="button"><span class="fa fa-external-link"></span> to history</button>');r.push(' <div id="toolbtn_dl" class="btn-group" style="margin-left: 0.5em; display: none; ">');r.push(' <button id="drop_toggle" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown">');r.push(' <span class="fa fa-download"></span> download <span class="caret"></span>');r.push(" </button>");r.push(' <ul class="dropdown-menu" role="menu">');r.push(' <li><a href="#/folders/<%= id %>/download/tgz">.tar.gz</a></li>');r.push(' <li><a href="#/folders/<%= id %>/download/tbz">.tar.bz</a></li>');r.push(' <li><a href="#/folders/<%= id %>/download/zip">.zip</a></li>');r.push(" </ul>");r.push(" </div>");r.push("</div>");r.push('<div class="library_breadcrumb">');r.push('<a title="Return to the list of libraries" href="#">Libraries</a><b>|</b> ');r.push("<% _.each(path, function(path_item) { %>");r.push("<% if (path_item[0] != id) { %>");r.push('<a title="Return to this folder" href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a><b>|</b> ');r.push("<% } else { %>");r.push('<span title="You are in this folder"><%- path_item[1] %></span>');r.push("<% } %>");r.push("<% }); %>");r.push("</div>");r.push('<table id="folder_table" class="table table-condensed">');r.push(" <thead>");r.push(' <th style="text-align: center; width: 20px; "><input id="select-all-checkboxes" style="margin: 0;" type="checkbox"></th>');r.push(' <th class="button_heading">view</th>');r.push(" <th>name</th>");r.push(" <th>data type</th>");r.push(" <th>size</th>");r.push(" <th>date</th>");r.push(" </thead>");r.push(" <tbody>");r.push(" <td></td>");r.push(' <td><button title="Go to parent folder" type="button" data-id="<%- upper_folder_id %>" class="btn_open_folder btn btn-default btn-xs">');r.push(' <span class="fa fa-arrow-up"></span> .. go up</td>');r.push(" <td></td>");r.push(" <td></td>");r.push(" <td></td>");r.push(" <td></td>");r.push(" </tr>");r.push(" <% _.each(items, function(content_item) { %>");r.push(' <tr class="folder_row light" id="<%- content_item.id %>">');r.push(' <% if (content_item.get("type") === "folder") { %>');r.push(" <td></td>");r.push(' <td><button title="Open this folder" type="button" data-id="<%- content_item.id %>" class="btn_open_folder btn btn-default btn-xs">');r.push(' <span class="fa fa-folder-open"></span> browse</td>');r.push(' <td><%- content_item.get("name") %>');r.push(' <% if (content_item.get("item_count") === 0) { %>');r.push(' <span class="muted">(empty folder)</span>');r.push(" <% } %>");r.push(" </td>");r.push(" <td>folder</td>");r.push(' <td><%= _.escape(content_item.get("item_count")) %> item(s)</td>');r.push(" <% } else { %>");r.push(' <td style="text-align: center; "><input style="margin: 0;" type="checkbox"></td>');r.push(" <td>");r.push(' <button title="See details of this dataset" type="button" class="library-dataset btn btn-default btn-xs">');r.push(' <span class="fa fa-eye"></span> details');r.push(" </button>");r.push(" </td>");r.push(' <td><%- content_item.get("name") %></td>');r.push(' <td><%= _.escape(content_item.get("data_type")) %></td>');r.push(' <td><%= _.escape(content_item.get("readable_size")) %></td>');r.push(" <% } %> ");r.push(' <td><%= _.escape(content_item.get("time_updated")) %></td>');r.push(" </tr>");r.push(" <% }); %>");r.push(" ");r.push(" </tbody>");r.push("</table>");r.push("</div>");return r.join("")},templateDatasetModal:function(){var r=[];r.push('<div id="dataset_info_modal">');r.push(' <table class="table table-striped table-condensed">');r.push(" <tr>");r.push(' <th scope="row" id="id_row" data-id="<%= _.escape(item.get("ldda_id")) %>">Name</th>');r.push(' <td><%= _.escape(item.get("name")) %></td>');r.push(" </tr>");r.push(" <tr>");r.push(' <th scope="row">Data type</th>');r.push(' <td><%= _.escape(item.get("data_type")) %></td>');r.push(" </tr>");r.push(" <tr>");r.push(' <th scope="row">Genome build</th>');r.push(' <td><%= _.escape(item.get("genome_build")) %></td>');r.push(" </tr>");r.push(' <th scope="row">Size</th>');r.push(" <td><%= _.escape(size) %></td>");r.push(" </tr>");r.push(" <tr>");r.push(' <th scope="row">Date uploaded</th>');r.push(' <td><%= _.escape(item.get("date_uploaded")) %></td>');r.push(" </tr>");r.push(" <tr>");r.push(' <th scope="row">Uploaded by</th>');r.push(' <td><%= _.escape(item.get("uploaded_by")) %></td>');r.push(" </tr>");r.push(' <tr scope="row">');r.push(' <th scope="row">Data Lines</th>');r.push(' <td scope="row"><%= _.escape(item.get("metadata_data_lines")) %></td>');r.push(" </tr>");r.push(' <th scope="row">Comment Lines</th>');r.push(' <% if (item.get("metadata_comment_lines") === "") { %>');r.push(' <td scope="row"><%= _.escape(item.get("metadata_comment_lines")) %></td>');r.push(" <% } else { %>");r.push(' <td scope="row">unknown</td>');r.push(" <% } %>");r.push(" </tr>");r.push(" <tr>");r.push(' <th scope="row">Number of Columns</th>');r.push(' <td scope="row"><%= _.escape(item.get("metadata_columns")) %></td>');r.push(" </tr>");r.push(" <tr>");r.push(' <th scope="row">Column Types</th>');r.push(' <td scope="row"><%= _.escape(item.get("metadata_column_types")) %></td>');r.push(" </tr>");r.push(" <tr>");r.push(' <th scope="row">Miscellaneous information</th>');r.push(' <td scope="row"><%= _.escape(item.get("misc_blurb")) %></td>');r.push(" </tr>");r.push(" </table>");r.push(' <pre class="peek">');r.push(" </pre>");r.push("</div>");return r.join("")},templateHistorySelectInModal:function(){var r=[];r.push('<span id="history_modal_combo" style="width:90%; margin-left: 1em; margin-right: 1em; ">');r.push("Select history: ");r.push('<select id="dataset_import_single" name="dataset_import_single" style="width:50%; margin-bottom: 1em; "> ');r.push(" <% _.each(histories, function(history) { %>");r.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');r.push(" <% }); %>");r.push("</select>");r.push("</span>");return r.join("")},templateBulkImportInModal:function(){var r=[];r.push('<span id="history_modal_combo_bulk" style="width:90%; margin-left: 1em; margin-right: 1em; ">');r.push("Select history: ");r.push('<select id="dataset_import_bulk" name="dataset_import_bulk" style="width:50%; margin-bottom: 1em; "> ');r.push(" <% _.each(histories, function(history) { %>");r.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');r.push(" <% }); %>");r.push("</select>");r.push("</span>");return r.join("")},size_to_string:function(r){var s="";if(r>=100000000000){r=r/100000000000;s="TB"}else{if(r>=100000000){r=r/100000000;s="GB"}else{if(r>=100000){r=r/100000;s="MB"}else{if(r>=100){r=r/100;s="KB"}else{r=r*10;s="b"}}}}return(Math.round(r)/10)+s},events:{"click #select-all-checkboxes":"selectAll","click .folder_row":"selectClickedRow","click #toolbtn_bulk_import":"modalBulkImport","click #toolbtn_dl":"bulkDownload","click .library-dataset":"showDatasetDetails","click #toolbtn_create_folder":"createFolderFromModal","click .btn_open_folder":"navigateToFolder"},render:function(r){$("#center").css("overflow","auto");view=this;var t=this;var s=new e({id:r.id});s.url=s.attributes.urlRoot+r.id+"/contents";s.fetch({success:function(u){for(var w=0;w<s.attributes.folder.models.length;w++){var v=s.attributes.folder.models[w];if(v.get("type")==="file"){v.set("readable_size",t.size_to_string(v.get("file_size")))}}var y=s.full_path;var z;if(y.length===1){z=0}else{z=y[y.length-2][0]}var x=_.template(t.templateFolder(),{path:s.full_path,items:s.attributes.folder.models,id:r.id,upper_folder_id:z});t.$el.html(x)}})},navigateToFolder:function(s){var r=$(s.target).attr("data-id");if(typeof r==="undefined"){return false}else{if(r==="0"){library_router.navigate("#",{trigger:true,replace:true})}else{library_router.navigate("folders/"+r,{trigger:true,replace:true})}}},showDatasetDetails:function(u){u.preventDefault();var v=$(u.target).parent().parent().attr("id");var t=new i();var s=new p();t.id=v;var r=this;t.fetch({success:function(w){s.fetch({success:function(x){r.renderModalAfterFetch(w,x)}})}})},renderModalAfterFetch:function(w,t){var u=this.size_to_string(w.get("file_size"));var v=_.template(this.templateDatasetModal(),{item:w,size:u});var s=this;this.modal=new k.GalaxyModal({destructible:true,title:"Dataset Details",body:v,buttons:{Import:function(){s.importCurrentIntoHistory()},Download:function(){s.downloadCurrent()},Close:function(){s.modal.hideOrDestroy()}}});$(".peek").html(w.get("peek"));var r=_.template(this.templateHistorySelectInModal(),{histories:t.models});$(this.modal.elMain).find(".buttons").prepend(r);if(s.lastSelectedHistory.length>0){$(this.modal.elMain).find("#dataset_import_single").val(s.lastSelectedHistory)}this.modal.bindEvents();this.modal.show()},downloadCurrent:function(){this.modal.disableButton("Import");this.modal.disableButton("Download");var r=[];r.push($("#id_row").attr("data-id"));var s="/api/libraries/datasets/download/uncompressed";var t={ldda_ids:r};folderContentView.processDownload(s,t);this.modal.enableButton("Import");this.modal.enableButton("Download")},importCurrentIntoHistory:function(){this.modal.disableButton("Import");this.modal.disableButton("Download");var t=$(this.modal.elMain).find("select[name=dataset_import_single] option:selected").val();this.lastSelectedHistory=t;var r=$("#id_row").attr("data-id");var u=new b();var s=this;u.url=u.urlRoot+t+"/contents";u.save({content:r,source:"library"},{success:function(){n.success("Dataset imported");s.modal.enableButton("Import");s.modal.enableButton("Download")},error:function(){n.error("An error occured! Dataset not imported. Please try again.");s.modal.enableButton("Import");s.modal.enableButton("Download")}})},selectAll:function(s){var r=s.target.checked;that=this;$(":checkbox").each(function(){this.checked=r;$row=$(this.parentElement.parentElement);(r)?that.makeDarkRow($row):that.makeWhiteRow($row)});this.checkTools()},selectClickedRow:function(s){var u="";var r;var t;if(s.target.localName==="input"){u=s.target;r=$(s.target.parentElement.parentElement);t="input"}else{if(s.target.localName==="td"){u=$("#"+s.target.parentElement.id).find(":checkbox")[0];r=$(s.target.parentElement);t="td"}}if(u===""){s.stopPropagation();return}if(u===undefined){s.stopPropagation();return}if(u.checked){if(t==="td"){u.checked="";this.makeWhiteRow(r)}else{if(t==="input"){this.makeDarkRow(r)}}}else{if(t==="td"){u.checked="selected";this.makeDarkRow(r)}else{if(t==="input"){this.makeWhiteRow(r)}}}this.checkTools()},makeDarkRow:function(r){r.removeClass("light");r.find("a").removeClass("light");r.addClass("dark");r.find("a").addClass("dark")},makeWhiteRow:function(r){r.removeClass("dark");r.find("a").removeClass("dark");r.addClass("light");r.find("a").addClass("light")},checkTools:function(){var r=$("#folder_table").find(":checked");if(r.length>0){$("#toolbtn_bulk_import").show();$("#toolbtn_dl").show()}else{$("#toolbtn_bulk_import").hide();$("#toolbtn_dl").hide()}},modalBulkImport:function(){var s=this;var r=new p();r.fetch({success:function(t){var u=_.template(s.templateBulkImportInModal(),{histories:t.models});s.modal=new k.GalaxyModal({destructible:true,title:"Import into History",body:u,buttons:{Import:function(){s.importAllIntoHistory()},Close:function(){s.modal.hideOrDestroy()}}});s.modal.bindEvents();s.modal.show()}})},importAllIntoHistory:function(){this.modal.disableButton("Import");var t=$("select[name=dataset_import_bulk] option:selected").val();var x=$("select[name=dataset_import_bulk] option:selected").text();var z=[];$("#folder_table").find(":checked").each(function(){if(this.parentElement.parentElement.id!=""){z.push(this.parentElement.parentElement.id)}});var y=_.template(this.templateProgressBar(),{history_name:x});$(this.modal.elMain).find(".modal-body").html(y);var u=100/z.length;this.initProgress(u);var r=[];for(var s=z.length-1;s>=0;s--){library_dataset_id=z[s];var v=new b();var w=this;v.url=v.urlRoot+t+"/contents";v.content=library_dataset_id;v.source="library";r.push(v)}this.chainCall(r)},chainCall:function(s){var r=this;var t=s.pop();if(typeof t==="undefined"){n.success("All datasets imported");this.modal.hideOrDestroy();return}var u=$.when(t.save({content:t.content,source:t.source})).done(function(v){r.updateProgress();responses.push(v);r.chainCall(s)})},initProgress:function(r){this.progress=0;this.progressStep=r},updateProgress:function(){this.progress+=this.progressStep;$(".progress-bar-import").width(Math.round(this.progress)+"%");txt_representation=Math.round(this.progress)+"% Complete";$(".completion_span").text(txt_representation)},templateProgressBar:function(){var r=[];r.push('<div class="import_text">');r.push("Importing selected datasets to history <b><%= _.escape(history_name) %></b>");r.push("</div>");r.push('<div class="progress">');r.push(' <div class="progress-bar progress-bar-import" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 00%;">');r.push(' <span class="completion_span">0% Complete</span>');r.push(" </div>");r.push("</div>");r.push("");return r.join("")},download:function(r,v){var t=[];$("#folder_table").find(":checked").each(function(){if(this.parentElement.parentElement.id!=""){t.push(this.parentElement.parentElement.id)}});var s="/api/libraries/datasets/download/"+v;var u={ldda_ids:t};this.processDownload(s,u,"get")},processDownload:function(s,t,u){if(s&&t){t=typeof t=="string"?t:$.param(t);var r="";$.each(t.split("&"),function(){var v=this.split("=");r+='<input type="hidden" name="'+v[0]+'" value="'+v[1]+'" />'});$('<form action="'+s+'" method="'+(u||"post")+'">'+r+"</form>").appendTo("body").submit().remove();n.info("Your download will begin soon")}},createFolderFromModal:function(){event.preventDefault();event.stopPropagation();var r=this;this.modal=new k.GalaxyModal({destructible:true,title:"Create New Folder",body:this.template_new_folder(),buttons:{Create:function(){r.create_new_folder_event()},Close:function(){r.modal.hideOrDestroy();r.modal=null}}});this.modal.bindEvents();this.modal.show()},create_new_folder_event:function(){var r=this.serialize_new_folder();if(this.validate_new_folder(r)){var t=new c();url_items=Backbone.history.fragment.split("/");current_folder_id=url_items[url_items.length-1];t.url=t.urlRoot+"/"+current_folder_id;var s=this;t.save(r,{success:function(u){s.modal.hideOrDestroy();n.success("Folder created");s.render({id:current_folder_id})},error:function(){n.error("An error occured :(")}})}else{n.error("Folder's name is missing")}return false},serialize_new_folder:function(){return{name:$("input[name='Name']").val(),description:$("input[name='Description']").val()}},validate_new_folder:function(r){return r.name!==""},template_new_folder:function(){tmpl_array=[];tmpl_array.push('<div id="new_folder_modal">');tmpl_array.push("<form>");tmpl_array.push('<input type="text" name="Name" value="" placeholder="Name">');tmpl_array.push('<input type="text" name="Description" value="" placeholder="Description">');tmpl_array.push("</form>");tmpl_array.push("</div>");return tmpl_array.join("")}});var a=Backbone.View.extend({el:"#center",events:{"click #create_new_library_btn":"show_library_modal"},initialize:function(){},template_library_list:function(){tmpl_array=[];tmpl_array.push('<div id="library_container" style="width: 90%; margin: auto; margin-top: 2em; overflow: auto !important; ">');tmpl_array.push("");tmpl_array.push('<h3>New Data Libraries. This is work in progress. Report problems & ideas to <a href="mailto:marten@bx.psu.edu?Subject=DataLibraries_Feedback" target="_blank">Marten</a>.</h3>');tmpl_array.push('<a href="" id="create_new_library_btn" class="btn btn-primary file ">New Library</a>');tmpl_array.push('<table class="table table-condensed">');tmpl_array.push(" <thead>");tmpl_array.push(' <th class="button_heading"></th>');tmpl_array.push(" <th>name</th>");tmpl_array.push(" <th>description</th>");tmpl_array.push(" <th>synopsis</th> ");tmpl_array.push(" <th>model type</th> ");tmpl_array.push(" </thead>");tmpl_array.push(" <tbody>");tmpl_array.push(" <% _.each(libraries, function(library) { %>");tmpl_array.push(" <tr>");tmpl_array.push(' <td><button title="Open this library" type="button" data-id="<%- library.get("root_folder_id") %>" class="btn_open_folder btn btn-default btn-xs">');tmpl_array.push(' <span class="fa fa-folder-open"></span> browse</td>');tmpl_array.push(' <td><%- library.get("name") %></td>');tmpl_array.push(' <td><%= _.escape(library.get("description")) %></td>');tmpl_array.push(' <td><%= _.escape(library.get("synopsis")) %></td>');tmpl_array.push(' <td><%= _.escape(library.get("model_class")) %></td>');tmpl_array.push(" </tr>");tmpl_array.push(" <% }); %>");tmpl_array.push(" </tbody>");tmpl_array.push("</table>");tmpl_array.push("</div>");return tmpl_array.join("")},render:function(){$("#center").css("overflow","auto");var r=this;libraries=new o();libraries.fetch({success:function(s){var t=_.template(r.template_library_list(),{libraries:s.models});r.$el.html(t)},error:function(t,s){if(s.statusCode().status===403){n.error("Please log in first. Redirecting to login page in 3s.");setTimeout(r.redirectToLogin,3000)}else{n.error("An error occured. Please try again.")}}})},redirectToHome:function(){window.location="../"},redirectToLogin:function(){window.location="/user/login"},modal:null,show_library_modal:function(s){s.preventDefault();s.stopPropagation();var r=this;this.modal=new k.GalaxyModal({destructible:true,title:"Create New Library",body:this.template_new_library(),buttons:{Create:function(){r.create_new_library_event()},Close:function(){r.modal.hideOrDestroy()}}});this.modal.bindEvents();this.modal.show()},create_new_library_event:function(){var t=this.serialize_new_library();if(this.validate_new_library(t)){var s=new f();var r=this;s.save(t,{success:function(u){r.modal.hideOrDestroy();r.clear_library_modal();r.render();n.success("Library created")},error:function(){n.error("An error occured :(")}})}else{n.error("Library's name is missing")}return false},clear_library_modal:function(){$("input[name='Name']").val("");$("input[name='Description']").val("");$("input[name='Synopsis']").val("")},serialize_new_library:function(){return{name:$("input[name='Name']").val(),description:$("input[name='Description']").val(),synopsis:$("input[name='Synopsis']").val()}},validate_new_library:function(r){return r.name!==""},template_new_library:function(){tmpl_array=[];tmpl_array.push('<div id="new_library_modal">');tmpl_array.push("<form>");tmpl_array.push('<input type="text" name="Name" value="" placeholder="Name">');tmpl_array.push('<input type="text" name="Description" value="" placeholder="Description">');tmpl_array.push('<input type="text" name="Synopsis" value="" placeholder="Synopsis">');tmpl_array.push("</form>");tmpl_array.push("</div>");return tmpl_array.join("")}});var g=Backbone.View.extend({folderContentView:null,galaxyLibraryview:null,initialize:function(){folderContentView=new m();galaxyLibraryview=new a();library_router=new q();library_router.on("route:libraries",function(){galaxyLibraryview.render()});library_router.on("route:folder_content",function(r){folderContentView.render({id:r})});library_router.on("route:download",function(r,s){if($("#center").find(":checked").length===0){library_router.navigate("folders/"+r,{trigger:true,replace:true})}else{folderContentView.download(r,s);library_router.navigate("folders/"+r,{trigger:false,replace:true})}});Backbone.history.start();return this}});return{GalaxyApp:g}});
\ No newline at end of file
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
07 Jan '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/e30627f8eac5/
Changeset: e30627f8eac5
Branch: search
User: dannon
Date: 2014-01-07 20:42:20
Summary: Close search branch.
Affected #: 0 files
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: Update to PR285, use datetime.isoformat() instead of str(). Left in stringifying bit (commented) for future reference.
by commits-noreply@bitbucket.org 07 Jan '14
by commits-noreply@bitbucket.org 07 Jan '14
07 Jan '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/968389346b34/
Changeset: 968389346b34
User: dannon
Date: 2014-01-07 20:41:53
Summary: Update to PR285, use datetime.isoformat() instead of str(). Left in stringifying bit (commented) for future reference.
Affected #: 1 file
diff -r d05b1d649290a8563558e8721f4a1ca8336e8180 -r 968389346b3491bf1a90cf359ff258075816b1e8 lib/galaxy/model/item_attrs.py
--- a/lib/galaxy/model/item_attrs.py
+++ b/lib/galaxy/model/item_attrs.py
@@ -184,9 +184,13 @@
except:
if key in value_mapper:
return value_mapper.get( key )( item )
- #If the item is of a class that needs to be 'stringified' before being put into a JSON data structure
- if type(item) in [datetime.datetime]:
- return str(item)
+ if type(item) == datetime.datetime:
+ return item.isoformat()
+ # Leaving this for future reference, though we may want a more
+ # generic way to handle special type mappings going forward.
+ # If the item is of a class that needs to be 'stringified' before being put into a JSON data structure
+ # elif type(item) in []:
+ # return str(item)
return item
# Create dict to represent item.
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
3 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/8388f17e522a/
Changeset: 8388f17e522a
Branch: search
User: Kyle Ellrott
Date: 2014-01-02 20:37:37
Summary: Default merge
Affected #: 467 files
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 .hgignore
--- a/.hgignore
+++ b/.hgignore
@@ -60,6 +60,7 @@
job_conf.xml
data_manager_conf.xml
shed_data_manager_conf.xml
+object_store_conf.xml
config/*
static/welcome.html.*
static/welcome.html
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 config/plugins/visualizations/scatterplot/Gruntfile.js
--- a/config/plugins/visualizations/scatterplot/Gruntfile.js
+++ b/config/plugins/visualizations/scatterplot/Gruntfile.js
@@ -9,7 +9,7 @@
// compile all hb templates into a single file in the build dir
compile: {
options: {
- namespace: 'Templates',
+ namespace: 'scatterplot',
processName : function( filepath ){
return filepath.match( /\w*\.handlebars/ )[0].replace( '.handlebars', '' );
}
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 config/plugins/visualizations/scatterplot/src/scatterplot-config-editor.js
--- a/config/plugins/visualizations/scatterplot/src/scatterplot-config-editor.js
+++ b/config/plugins/visualizations/scatterplot/src/scatterplot-config-editor.js
@@ -37,14 +37,19 @@
/** initialize requires a configuration Object containing a dataset Object */
initialize : function( attributes ){
//console.log( this + '.initialize, attributes:', attributes );
- if( !attributes || !attributes.config || !attributes.config.dataset ){
+ if( !attributes || !attributes.config || !attributes.dataset ){
throw new Error( "ScatterplotView requires a configuration and dataset" );
}
- this.dataset = attributes.config.dataset;
+ //console.log( 'config:', attributes.config );
+
+ this.dataset = attributes.dataset;
//console.log( 'dataset:', this.dataset );
+//TODO: ScatterplotView -> ScatterplotDisplay, this.plotView -> this.display
this.plotView = new ScatterplotView({
+ dataset : attributes.dataset,
config : attributes.config
+//TODO: if data
});
},
@@ -197,8 +202,8 @@
// parse the column values for both indeces (for the data fetch) and names (for the chart)
var $dataControls = this.$el.find( '#data-control' );
var settings = {
- xColumn : $dataControls.find( '[name="xColumn"]' ).val(),
- yColumn : $dataControls.find( '[name="yColumn"]' ).val()
+ xColumn : Number( $dataControls.find( '[name="xColumn"]' ).val() ),
+ yColumn : Number( $dataControls.find( '[name="yColumn"]' ).val() )
};
if( $dataControls.find( '#include-id-checkbox' ).prop( 'checked' ) ){
settings.idColumn = $dataControls.find( '[name="idColumn"]' ).val();
@@ -229,9 +234,9 @@
});
ScatterplotConfigEditor.templates = {
- mainLayout : Templates.editor,
- dataControl : Templates.datacontrol,
- chartControl : Templates.chartcontrol
+ mainLayout : scatterplot.editor,
+ dataControl : scatterplot.datacontrol,
+ chartControl : scatterplot.chartcontrol
};
//==============================================================================
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 config/plugins/visualizations/scatterplot/src/scatterplot-display.js
--- a/config/plugins/visualizations/scatterplot/src/scatterplot-display.js
+++ b/config/plugins/visualizations/scatterplot/src/scatterplot-display.js
@@ -10,14 +10,10 @@
//TODO: should be a view on visualization(revision) model
defaults : {
- dataset : {
- },
metadata : {
dataLines : undefined
},
- ajaxFn : null,
-
pagination : {
currPage : 0,
perPage : 3000
@@ -48,6 +44,7 @@
initialize : function( attributes ){
this.config = _.extend( _.clone( this.defaults ), attributes.config || {});
+ this.dataset = attributes.dataset;
//console.debug( this + '.config:', this.config );
},
@@ -65,7 +62,7 @@
//console.debug( 'currPage', this.config.pagination.currPage );
var view = this;
//TODO: very tied to datasets - should be generalized eventually
- xhr = jQuery.getJSON( '/api/datasets/' + this.config.dataset.id, {
+ xhr = jQuery.getJSON( '/api/datasets/' + this.dataset.id, {
data_type : 'raw_data',
provider : 'dataset-column',
limit : this.config.pagination.perPage,
@@ -151,7 +148,7 @@
},
renderLineInfo : function( data ){
- var totalLines = this.config.dataset.metadata_data_lines || 'an unknown number of',
+ var totalLines = this.dataset.metadata_data_lines || 'an unknown number of',
lineStart = ( this.config.pagination.currPage * this.config.pagination.perPage ),
lineEnd = lineStart + data.length;
return $( '<p/>' ).addClass( 'scatterplot-data-info' )
@@ -168,9 +165,9 @@
}
//TODO: cache numPages/numLines in config
var view = this,
- dataLines = this.config.dataset.metadata_data_lines,
+ dataLines = this.dataset.metadata_data_lines,
numPages = ( dataLines )?( Math.ceil( dataLines / this.config.pagination.perPage ) ):( undefined );
- //console.debug( 'data:', this.config.dataset.metadata_data_lines, 'numPages:', numPages );
+ //console.debug( 'data:', this.dataset.metadata_data_lines, 'numPages:', numPages );
// prev next buttons
var $prev = makePage$Li( 'Prev' ).click( function(){
@@ -207,9 +204,9 @@
}
//TODO: cache numPages/numLines in config
var view = this,
- dataLines = this.config.dataset.metadata_data_lines,
+ dataLines = this.dataset.metadata_data_lines,
numPages = ( dataLines )?( Math.ceil( dataLines / this.config.pagination.perPage ) ):( undefined );
- //console.debug( 'data:', this.config.dataset.metadata_data_lines, 'numPages:', numPages );
+ //console.debug( 'data:', this.dataset.metadata_data_lines, 'numPages:', numPages );
// page numbers (as separate control)
//var $paginationContainer = $( '<div/>' ).addClass( 'pagination-container' ),
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 config/plugins/visualizations/scatterplot/src/visualization-templates.html
--- a/config/plugins/visualizations/scatterplot/src/visualization-templates.html
+++ /dev/null
@@ -1,197 +0,0 @@
-<script type="text/template" class="template-visualization" id="template-visualization-scatterplotControlForm">
-{{! main layout }}
-
-<h1>WHAAAAA?</h1>
-<div class="scatterplot-container chart-container tabbable tabs-left">
- {{! tab buttons/headers using Bootstrap }}
- <ul class="nav nav-tabs">
- {{! start with the data controls as the displayed tab }}
- <li class="active">
- <a title="Use this tab to change which data are used"
- href="#data-control" data-toggle="tab">Data Controls</a>
- </li>
- <li>
- <a title="Use this tab to change how the chart is drawn"
- href="#chart-control" data-toggle="tab" >Chart Controls</a>
- </li>
- <li>
- <a title="This tab will display overall statistics for your data"
- href="#stats-display" data-toggle="tab">Statistics</a>
- </li>
- <li>
- <a title="This tab will display the chart"
- href="#chart-display" data-toggle="tab">Chart</a>
-
- <div id="loading-indicator" style="display: none;">
- <img class="loading-img" src="{{loadingIndicatorImagePath}}" />
- <span class="loading-message">{{message}}</span>
- </div>
- </li>
- </ul>
-
- {{! data form, chart config form, stats, and chart all get their own tab }}
- <div class="tab-content">
- {{! ---------------------------- tab for data settings form }}
- <div id="data-control" class="tab-pane active">
- {{! rendered separately }}
- </div>
-
- {{! ---------------------------- tab for chart graphics control form }}
- <div id="chart-control" class="tab-pane">
- {{! rendered separately }}
- </div>
-
- {{! ---------------------------- tab for data statistics }}
- <div id="stats-display" class="tab-pane">
- <p class="help-text">By column:</p>
- <table id="chart-stats-table">
- <thead><th></th><th>X</th><th>Y</th></thead>
- {{#each stats}}
- <tr><td>{{name}}</td><td>{{xval}}</td><td>{{yval}}</td></tr>
- </tr>
- {{/each}}
- </table>
- </div>
-
- {{! ---------------------------- tab for actual chart }}
- <div id="chart-display" class="tab-pane">
- <svg width="{{width}}" height="{{height}}"></svg>
- </div>
-
- </div>{{! end .tab-content }}
-</div>{{! end .chart-control }}
-</script>
-
-<script type="text/template" class="template-visualization" id="template-visualization-dataControl">
-
- <p class="help-text">
- Use the following controls to change the data used by the chart.
- Use the 'Draw' button to render (or re-render) the chart with the current settings.
- </p>
-
- {{! column selector containers }}
- <div class="column-select">
- <label for="X-select">Data column for X: </label>
- <select name="X" id="X-select">
- {{#each numericColumns}}
- <option value="{{index}}">{{name}}</option>
- {{/each}}
- </select>
- </div>
- <div class="column-select">
- <label for="Y-select">Data column for Y: </label>
- <select name="Y" id="Y-select">
- {{#each numericColumns}}
- <option value="{{index}}">{{name}}</option>
- {{/each}}
- </select>
- </div>
-
- {{! optional id column }}
- <div id="include-id">
- <label for="include-id-checkbox">Include a third column as data point IDs?</label>
- <input type="checkbox" name="include-id" id="include-id-checkbox" />
- <p class="help-text-small">
- These will be displayed (along with the x and y values) when you hover over
- a data point.
- </p>
- </div>
- <div class="column-select" style="display: none">
- <label for="ID-select">Data column for IDs: </label>
- <select name="ID" id="ID-select">
- {{#each allColumns}}
- <option value="{{index}}">{{name}}</option>
- {{/each}}
- </select>
- </div>
-
- {{! if we're using generic column selection names ('column 1') - allow the user to use the first line }}
- <div id="first-line-header" style="display: none;">
- <p>Possible headers: {{ possibleHeaders }}
- </p>
- <label for="first-line-header-checkbox">Use the above as column headers?</label>
- <input type="checkbox" name="include-id" id="first-line-header-checkbox"
- {{#if usePossibleHeaders }}checked="true"{{/if}}/>
- <p class="help-text-small">
- It looks like Galaxy couldn't get proper column headers for this data.
- Would you like to use the column headers above as column names to select columns?
- </p>
- </div>
-
- <input id="render-button" type="button" value="Draw" />
- <div class="clear"></div>
-</script>
-
-<script type="text/template" class="template-visualization" id="template-visualization-chartControl">
- <p class="help-text">
- Use the following controls to how the chart is displayed.
- The slide controls can be moved by the mouse or, if the 'handle' is in focus, your keyboard's arrow keys.
- Move the focus between controls by using the tab or shift+tab keys on your keyboard.
- Use the 'Draw' button to render (or re-render) the chart with the current settings.
- </p>
-
- <div id="datapointSize" class="form-input numeric-slider-input">
- <label for="datapointSize">Size of data point: </label>
- <div class="slider-output">{{datapointSize}}</div>
- <div class="slider"></div>
- <p class="form-help help-text-small">
- Size of the graphic representation of each data point
- </p>
- </div>
-
- <div id="animDuration" class="form-input checkbox-input">
- <label for="animate-chart">Animate chart transitions?: </label>
- <input type="checkbox" id="animate-chart"
- class="checkbox control"{{#if animDuration}} checked="true"{{/if}} />
- <p class="form-help help-text-small">
- Uncheck this to disable the animations used on the chart
- </p>
- </div>
-
- <div id="width" class="form-input numeric-slider-input">
- <label for="width">Chart width: </label>
- <div class="slider-output">{{width}}</div>
- <div class="slider"></div>
- <p class="form-help help-text-small">
- (not including chart margins and axes)
- </p>
- </div>
-
- <div id="height" class="form-input numeric-slider-input">
- <label for="height">Chart height: </label>
- <div class="slider-output">{{height}}</div>
- <div class="slider"></div>
- <p class="form-help help-text-small">
- (not including chart margins and axes)
- </p>
- </div>
-
- <div id="X-axis-label"class="text-input form-input">
- <label for="X-axis-label">Re-label the X axis: </label>
- <input type="text" name="X-axis-label" id="X-axis-label" value="{{xLabel}}" />
- <p class="form-help help-text-small"></p>
- </div>
-
- <div id="Y-axis-label" class="text-input form-input">
- <label for="Y-axis-label">Re-label the Y axis: </label>
- <input type="text" name="Y-axis-label" id="Y-axis-label" value="{{yLabel}}" />
- <p class="form-help help-text-small"></p>
- </div>
-
- <input id="render-button" type="button" value="Draw" />
-</script>
-
-<script type="text/template" class="template-visualization" id="template-visualization-statsDisplay">
- <p class="help-text">By column:</p>
- <table id="chart-stats-table">
- <thead><th></th><th>X</th><th>Y</th></thead>
- {{#each stats}}
- <tr><td>{{name}}</td><td>{{xval}}</td><td>{{yval}}</td></tr>
- </tr>
- {{/each}}
- </table>
-</script>
-
-<script type="text/template" class="template-visualization" id="template-visualization-chartDisplay">
- <svg width="{{width}}" height="{{height}}"></svg>
-</script>
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 config/plugins/visualizations/scatterplot/static/scatterplot-edit.js
--- a/config/plugins/visualizations/scatterplot/static/scatterplot-edit.js
+++ b/config/plugins/visualizations/scatterplot/static/scatterplot-edit.js
@@ -1,1 +1,1 @@
-function scatterplot(a,b,c){function d(){var a={v:{},h:{}};return a.v.lines=p.selectAll("line.v-grid-line").data(m.x.ticks(q.x.fn.ticks()[0])),a.v.lines.enter().append("svg:line").classed("grid-line v-grid-line",!0),a.v.lines.attr("x1",m.x).attr("x2",m.x).attr("y1",0).attr("y2",b.height),a.v.lines.exit().remove(),a.h.lines=p.selectAll("line.h-grid-line").data(m.y.ticks(q.y.fn.ticks()[0])),a.h.lines.enter().append("svg:line").classed("grid-line h-grid-line",!0),a.h.lines.attr("x1",0).attr("x2",b.width).attr("y1",m.y).attr("y2",m.y),a.h.lines.exit().remove(),a}function e(){return t.attr("cx",function(a,b){return m.x(j(a,b))}).attr("cy",function(a,b){return m.y(k(a,b))}).style("display","block").filter(function(){var a=d3.select(this).attr("cx"),c=d3.select(this).attr("cy");return 0>a||a>b.width?!0:0>c||c>b.height?!0:!1}).style("display","none")}function f(){q.redraw(),e(),s=d(),$(".chart-info-box").remove(),$(o.node()).trigger("zoom.scatterplot",[])}function g(a,c,d){return c+=8,$(['<div class="chart-info-box" style="position: absolute">',b.idColumn?"<div>"+d[b.idColumn]+"</div>":"","<div>",j(d),"</div>","<div>",k(d),"</div>","</div>"].join("")).css({top:a,left:c,"z-index":2})}var h=function(a,b){return"translate("+a+","+b+")"},i=function(a,b,c){return"rotate("+a+","+b+","+c+")"},j=function(a){return a[b.xColumn]},k=function(a){return a[b.yColumn]},l={x:{extent:d3.extent(c,j)},y:{extent:d3.extent(c,k)}},m={x:d3.scale.linear().domain(l.x.extent).range([0,b.width]),y:d3.scale.linear().domain(l.y.extent).range([b.height,0])},n=d3.behavior.zoom().x(m.x).y(m.y).scaleExtent([1,10]),o=d3.select(a).attr("class","scatterplot").attr("width","100%").attr("height",b.height+(b.margin.top+b.margin.bottom)),p=o.append("g").attr("class","content").attr("transform",h(b.margin.left,b.margin.top)).call(n);p.append("rect").attr("class","zoom-rect").attr("width",b.width).attr("height",b.height).style("fill","transparent");var q={x:{},y:{}};q.x.fn=d3.svg.axis().orient("bottom").scale(m.x).ticks(b.x.ticks).tickFormat(d3.format("s")),q.y.fn=d3.svg.axis().orient("left").scale(m.y).ticks(b.y.ticks).tickFormat(d3.format("s")),q.x.g=p.append("g").attr("class","x axis").attr("transform",h(0,b.height)).call(q.x.fn),q.y.g=p.append("g").attr("class","y axis").call(q.y.fn);var r=4;q.x.label=o.append("text").attr("class","axis-label").text(b.x.label).attr("text-anchor","middle").attr("dominant-baseline","text-after-edge").attr("x",b.width/2+b.margin.left).attr("y",b.height+b.margin.bottom+b.margin.top-r),q.y.label=o.append("text").attr("class","axis-label").text(b.y.label).attr("text-anchor","middle").attr("dominant-baseline","text-before-edge").attr("x",r).attr("y",b.height/2).attr("transform",i(-90,r,b.height/2)),q.redraw=function(){o.select(".x.axis").call(q.x.fn),o.select(".y.axis").call(q.y.fn)};var s=d(),t=p.selectAll(".glyph").data(c).enter().append("svg:circle").classed("glyph",!0).attr("cx",function(a,b){return m.x(j(a,b))}).attr("cy",b.height).attr("r",0);t.transition().duration(b.animDuration).attr("cy",function(a,b){return m.y(k(a,b))}).attr("r",b.datapointSize),n.on("zoom",f),t.on("mouseover",function(a,c){var d=d3.select(this);d.style("fill","red").style("fill-opacity",1),p.append("line").attr("stroke","red").attr("stroke-width",1).attr("x1",d.attr("cx")-b.datapointSize).attr("y1",d.attr("cy")).attr("x2",0).attr("y2",d.attr("cy")).classed("hoverline",!0),d.attr("cy")<b.height&&p.append("line").attr("stroke","red").attr("stroke-width",1).attr("x1",d.attr("cx")).attr("y1",+d.attr("cy")+b.datapointSize).attr("x2",d.attr("cx")).attr("y2",b.height).classed("hoverline",!0);var e=this.getBoundingClientRect();$("body").append(g(e.top,e.right,a)),$(o.node()).trigger("mouseover-datapoint.scatterplot",[this,a,c])}),t.on("mouseout",function(){d3.select(this).style("fill","black").style("fill-opacity",.2),p.selectAll(".hoverline").remove(),$(".chart-info-box").remove()})}this.Templates=this.Templates||{},this.Templates.chartcontrol=Handlebars.template(function(a,b,c,d,e){this.compilerInfo=[4,">= 1.0.0"],c=this.merge(c,a.helpers),e=e||{};var f,g="",h="function",i=this.escapeExpression;return g+='<p class="help-text">\n Use the following controls to how the chart is displayed.\n The slide controls can be moved by the mouse or, if the \'handle\' is in focus, your keyboard\'s arrow keys.\n Move the focus between controls by using the tab or shift+tab keys on your keyboard.\n Use the \'Draw\' button to render (or re-render) the chart with the current settings.\n</p>\n\n<div data-config-key="datapointSize" class="form-input numeric-slider-input">\n <label for="datapointSize">Size of data point: </label>\n <div class="slider-output">',(f=c.datapointSize)?f=f.call(b,{hash:{},data:e}):(f=b.datapointSize,f=typeof f===h?f.apply(b):f),g+=i(f)+'</div>\n <div class="slider"></div>\n <p class="form-help help-text-small">\n Size of the graphic representation of each data point\n </p>\n</div>\n\n<div data-config-key="width" class="form-input numeric-slider-input">\n <label for="width">Chart width: </label>\n <div class="slider-output">',(f=c.width)?f=f.call(b,{hash:{},data:e}):(f=b.width,f=typeof f===h?f.apply(b):f),g+=i(f)+'</div>\n <div class="slider"></div>\n <p class="form-help help-text-small">\n (not including chart margins and axes)\n </p>\n</div>\n\n<div data-config-key="height" class="form-input numeric-slider-input">\n <label for="height">Chart height: </label>\n <div class="slider-output">',(f=c.height)?f=f.call(b,{hash:{},data:e}):(f=b.height,f=typeof f===h?f.apply(b):f),g+=i(f)+'</div>\n <div class="slider"></div>\n <p class="form-help help-text-small">\n (not including chart margins and axes)\n </p>\n</div>\n\n<div data-config-key="X-axis-label"class="text-input form-input">\n <label for="X-axis-label">Re-label the X axis: </label>\n <input type="text" name="X-axis-label" id="X-axis-label" value="'+i((f=b.x,f=null==f||f===!1?f:f.label,typeof f===h?f.apply(b):f))+'" />\n <p class="form-help help-text-small"></p>\n</div>\n\n<div data-config-key="Y-axis-label" class="text-input form-input">\n <label for="Y-axis-label">Re-label the Y axis: </label>\n <input type="text" name="Y-axis-label" id="Y-axis-label" value="'+i((f=b.y,f=null==f||f===!1?f:f.label,typeof f===h?f.apply(b):f))+'" />\n <p class="form-help help-text-small"></p>\n</div>\n\n<button class="render-button btn btn-primary active">Draw</button>\n'}),this.Templates.datacontrol=Handlebars.template(function(a,b,c,d,e){function f(a,b){var d,e="";return e+='\n <option value="',(d=c.index)?d=d.call(a,{hash:{},data:b}):(d=a.index,d=typeof d===j?d.apply(a):d),e+=k(d)+'">',(d=c.name)?d=d.call(a,{hash:{},data:b}):(d=a.name,d=typeof d===j?d.apply(a):d),e+=k(d)+"</option>\n "}function g(){return'checked="true"'}this.compilerInfo=[4,">= 1.0.0"],c=this.merge(c,a.helpers),e=e||{};var h,i="",j="function",k=this.escapeExpression,l=this;return i+='<p class="help-text">\n Use the following controls to change the data used by the chart.\n Use the \'Draw\' button to render (or re-render) the chart with the current settings.\n</p>\n\n\n<div class="column-select">\n <label>Data column for X: </label>\n <select name="xColumn">\n ',h=c.each.call(b,b.numericColumns,{hash:{},inverse:l.noop,fn:l.program(1,f,e),data:e}),(h||0===h)&&(i+=h),i+='\n </select>\n</div>\n<div class="column-select">\n <label>Data column for Y: </label>\n <select name="yColumn">\n ',h=c.each.call(b,b.numericColumns,{hash:{},inverse:l.noop,fn:l.program(1,f,e),data:e}),(h||0===h)&&(i+=h),i+='\n </select>\n</div>\n\n\n<div id="include-id">\n <label for="include-id-checkbox">Include a third column as data point IDs?</label>\n <input type="checkbox" name="include-id" id="include-id-checkbox" />\n <p class="help-text-small">\n These will be displayed (along with the x and y values) when you hover over\n a data point.\n </p>\n</div>\n<div class="column-select" style="display: none">\n <label for="ID-select">Data column for IDs: </label>\n <select name="idColumn">\n ',h=c.each.call(b,b.allColumns,{hash:{},inverse:l.noop,fn:l.program(1,f,e),data:e}),(h||0===h)&&(i+=h),i+='\n </select>\n</div>\n\n\n<div id="first-line-header" style="display: none;">\n <p>Possible headers: ',(h=c.possibleHeaders)?h=h.call(b,{hash:{},data:e}):(h=b.possibleHeaders,h=typeof h===j?h.apply(b):h),i+=k(h)+'\n </p>\n <label for="first-line-header-checkbox">Use the above as column headers?</label>\n <input type="checkbox" name="include-id" id="first-line-header-checkbox"\n ',h=c["if"].call(b,b.usePossibleHeaders,{hash:{},inverse:l.noop,fn:l.program(3,g,e),data:e}),(h||0===h)&&(i+=h),i+='/>\n <p class="help-text-small">\n It looks like Galaxy couldn\'t get proper column headers for this data.\n Would you like to use the column headers above as column names to select columns?\n </p>\n</div>\n\n<button class="render-button btn btn-primary active">Draw</button>\n'}),this.Templates.editor=Handlebars.template(function(a,b,c,d,e){this.compilerInfo=[4,">= 1.0.0"],c=this.merge(c,a.helpers),e=e||{};var f="";return f+='<div class="scatterplot-editor tabbable tabs-left">\n \n <ul class="nav nav-tabs">\n \n <li class="active">\n <a title="Use this tab to change which data are used"\n href="#data-control" data-toggle="tab">Data Controls</a>\n </li>\n <li>\n <a title="Use this tab to change how the chart is drawn"\n href="#chart-control" data-toggle="tab" >Chart Controls</a>\n </li>\n \n <li class="disabled">\n <a title="This tab will display the chart"\n href="#chart-display" data-toggle="tab">Chart</a>\n </li>\n </ul>\n\n \n <div class="tab-content">\n \n <div id="data-control" class="scatterplot-config-control tab-pane active">\n \n </div>\n \n \n <div id="chart-control" class="scatterplot-config-control tab-pane">\n \n </div>\n\n \n <div id="chart-display" class="scatterplot-display tab-pane"></div>\n\n </div>\n</div>\n'});var ScatterplotConfigEditor=BaseView.extend(LoggableMixin).extend({className:"scatterplot-control-form",initialize:function(a){if(!a||!a.config||!a.config.dataset)throw new Error("ScatterplotView requires a configuration and dataset");this.dataset=a.config.dataset,this.plotView=new ScatterplotView({config:a.config})},render:function(){return this.$el.append(ScatterplotConfigEditor.templates.mainLayout({})),this.$el.find("#data-control").append(this._render_dataControl()),this._render_chartControls(this.$el.find("#chart-control")),this._render_chartDisplay(),this.$el.find("[title]").tooltip(),this},_render_dataControl:function(){var a=this.dataset,b=_.map(a.metadata_column_types,function(b,c){var d={index:c,type:b,name:"column "+(c+1)};return a.metadata_column_names&&a.metadata_column_names[c]&&(d.name=a.metadata_column_names[c]),d}),c=_.filter(b,function(a){return"int"===a.type||"float"===a.type});2>c&&(c=b);var d=this.$el.find(".tab-pane#data-control");return d.html(ScatterplotConfigEditor.templates.dataControl({allColumns:b,numericColumns:c})),d.find('[name="xColumn"]').val(this.plotView.config.xColumn||c[0].index),d.find('[name="yColumn"]').val(this.plotView.config.yColumn||c[1].index),void 0!==this.plotView.config.idColumn&&(d.find("#include-id-checkbox").prop("checked",!0).trigger("change"),d.find('select[name="idColumn"]').val(this.plotView.config.idColumn)),d},_render_chartControls:function(a){function b(){var a=$(this);a.siblings(".slider-output").text(a.slider("value"))}a.html(ScatterplotConfigEditor.templates.chartControl(this.plotView.config));var c=this,d={datapointSize:{min:2,max:10,step:1},width:{min:200,max:800,step:20},height:{min:200,max:800,step:20}};return a.find(".numeric-slider-input").each(function(){var a=$(this),e=a.attr("data-config-key"),f=_.extend(d[e],{value:c.plotView.config[e],change:b,slide:b});a.find(".slider").slider(f)}),this.dataset.metadata_column_names,a},_render_chartDisplay:function(){var a=this.$el.find(".tab-pane#chart-display");return this.plotView.setElement(a),this.plotView.render(),a},events:{"change #include-id-checkbox":"toggleThirdColumnSelector","click #data-control .render-button":"renderChart","click #chart-control .render-button":"renderChart"},toggleThirdColumnSelector:function(){this.$el.find('select[name="idColumn"]').parent().toggle()},renderChart:function(){this.$el.find(".nav li.disabled").removeClass("disabled"),this.updateConfigWithDataSettings(),this.updateConfigWithChartSettings(),this.$el.find("ul.nav").find('a[href="#chart-display"]').tab("show"),this.plotView.fetchData()},updateConfigWithDataSettings:function(){var a=this.$el.find("#data-control"),b={xColumn:a.find('[name="xColumn"]').val(),yColumn:a.find('[name="yColumn"]').val()};return a.find("#include-id-checkbox").prop("checked")&&(b.idColumn=a.find('[name="idColumn"]').val()),_.extend(this.plotView.config,b)},updateConfigWithChartSettings:function(){var a=this.plotView,b=this.$el.find("#chart-control");return["datapointSize","width","height"].forEach(function(c){a.config[c]=b.find('.numeric-slider-input[data-config-key="'+c+'"]').find(".slider").slider("value")}),a.config.x.label=b.find('input[name="X-axis-label"]').val(),a.config.y.label=b.find('input[name="Y-axis-label"]').val(),a.config},toString:function(){return"ScatterplotConfigEditor("+(this.dataset?this.dataset.id:"")+")"}});ScatterplotConfigEditor.templates={mainLayout:Templates.editor,dataControl:Templates.datacontrol,chartControl:Templates.chartcontrol};var ScatterplotView=Backbone.View.extend({defaults:{dataset:{},metadata:{dataLines:void 0},ajaxFn:null,pagination:{currPage:0,perPage:3e3},width:400,height:400,margin:{top:16,right:16,bottom:40,left:54},x:{ticks:10,label:"X"},y:{ticks:10,label:"Y"},datapointSize:4,animDuration:500},initialize:function(a){this.config=_.extend(_.clone(this.defaults),a.config||{})},updateConfig:function(a){this.config=this.config||{},_.extend(this.config,a)},fetchData:function(){this.showLoadingIndicator("getting data");var a=this;return xhr=jQuery.getJSON("/api/datasets/"+this.config.dataset.id,{data_type:"raw_data",provider:"dataset-column",limit:this.config.pagination.perPage,offset:this.config.pagination.currPage*this.config.pagination.perPage}),xhr.done(function(b){a.renderData(b.data)}),xhr.fail(function(a,b,c){alert("Error loading data:\n"+a.responseText),console.error(a,b,c)}),xhr.always(function(){a.hideLoadingIndicator()}),xhr},render:function(a){return this.$el.addClass("scatterplot-display").html(['<div class="controls clear"></div>','<div class="loading-indicator">','<span class="fa fa-spinner fa-spin"></span>','<span class="loading-indicator-message"></span>',"</div>","<svg/>",'<div class="stats-display"></div>'].join("")),this.$el.children().hide(),a&&this.renderData(a),this},showLoadingIndicator:function(a,b){a=a||"",b=b||"fast";var c=this.$el.find(".loading-indicator");a&&c.find(".loading-indicator-message").text(a),c.is(":visible")||(this.toggleStats(!1),c.css({left:this.config.width/2,top:this.config.height/2}).show())},hideLoadingIndicator:function(a){a=a||"fast",this.$el.find(".loading-indicator").hide()},renderData:function(a){this.$el.find(".controls").empty().append(this.renderControls(a)).show(),this.renderPlot(a),this.getStats(a)},renderControls:function(a){var b=this,c=$('<div class="left"></div>'),d=$('<div class="right"></div>');return c.append([this.renderPrevNext(a),this.renderPagination(a)]),d.append([this.renderLineInfo(a),$("<button>Stats</button>").addClass("stats-toggle-btn").click(function(){b.toggleStats()}),$("<button>Redraw</button>").addClass("rerender-btn").click(function(){b.renderPlot(a)})]),[c,d]},renderLineInfo:function(a){var b=this.config.dataset.metadata_data_lines||"an unknown number of",c=this.config.pagination.currPage*this.config.pagination.perPage,d=c+a.length;return $("<p/>").addClass("scatterplot-data-info").text(["Displaying lines",c+1,"to",d,"of",b,"lines"].join(" "))},renderPrevNext:function(a){function b(a){return $(['<li><a href="javascript:void(0);">',a,"</a></li>"].join(""))}if(!a||0===this.config.pagination.currPage&&a.length<this.config.pagination.perPage)return null;var c=this,d=this.config.dataset.metadata_data_lines,e=d?Math.ceil(d/this.config.pagination.perPage):void 0,f=b("Prev").click(function(){c.config.pagination.currPage>0&&(c.config.pagination.currPage-=1,c.fetchData())}),g=b("Next").click(function(){(!e||c.config.pagination.currPage<e-1)&&(c.config.pagination.currPage+=1,c.fetchData())}),h=$("<ul/>").addClass("pagination data-prev-next").append([f,g]);return 0===c.config.pagination.currPage&&f.addClass("disabled"),e&&c.config.pagination.currPage===e-1&&g.addClass("disabled"),h},renderPagination:function(a){function b(a){return $(['<li><a href="javascript:void(0);">',a,"</a></li>"].join(""))}function c(){d.config.pagination.currPage=$(this).data("page"),d.fetchData()}if(!a||0===this.config.pagination.currPage&&a.length<this.config.pagination.perPage)return null;for(var d=this,e=this.config.dataset.metadata_data_lines,f=e?Math.ceil(e/this.config.pagination.perPage):void 0,g=$("<ul/>").addClass("pagination data-pages"),h=0;f>h;h+=1){var i=b(h+1).attr("data-page",h).click(c);h===this.config.pagination.currPage&&i.addClass("active"),g.append(i)}return g},renderPlot:function(a){this.toggleStats(!1);var b=this.$el.find("svg");b.off().empty().show(),scatterplot(b.get(0),this.config,a)},getStats:function(a){var b=this;meanWorker=new Worker("/plugins/visualizations/scatterplot/static/worker-stats.js"),meanWorker.postMessage({data:a,keys:[this.config.xColumn,this.config.yColumn]}),meanWorker.onerror=function(){meanWorker.terminate()},meanWorker.onmessage=function(a){b.renderStats(a.data)}},renderStats:function(a){var b=this.$el.find(".stats-display"),c=this.config.x.label,d=this.config.y.label,e=$("<table/>").addClass("table").append(["<thead><th></th><th>",c,"</th><th>",d,"</th></thead>"].join("")).append(_.map(a,function(a,b){return $(["<tr><td>",b,"</td><td>",a[0],"</td><td>",a[1],"</td></tr>"].join(""))}));b.empty().append(e)},toggleStats:function(a){var b=this.$el.find(".stats-display");a=void 0===a?b.is(":hidden"):a,a?(this.$el.find("svg").hide(),b.show(),this.$el.find(".controls .stats-toggle-btn").text("Plot")):(b.hide(),this.$el.find("svg").show(),this.$el.find(".controls .stats-toggle-btn").text("Stats"))},toString:function(){return"ScatterplotView()"}});
\ No newline at end of file
+function scatterplot(a,b,c){function d(){var a={v:{},h:{}};return a.v.lines=p.selectAll("line.v-grid-line").data(m.x.ticks(q.x.fn.ticks()[0])),a.v.lines.enter().append("svg:line").classed("grid-line v-grid-line",!0),a.v.lines.attr("x1",m.x).attr("x2",m.x).attr("y1",0).attr("y2",b.height),a.v.lines.exit().remove(),a.h.lines=p.selectAll("line.h-grid-line").data(m.y.ticks(q.y.fn.ticks()[0])),a.h.lines.enter().append("svg:line").classed("grid-line h-grid-line",!0),a.h.lines.attr("x1",0).attr("x2",b.width).attr("y1",m.y).attr("y2",m.y),a.h.lines.exit().remove(),a}function e(){return t.attr("cx",function(a,b){return m.x(j(a,b))}).attr("cy",function(a,b){return m.y(k(a,b))}).style("display","block").filter(function(){var a=d3.select(this).attr("cx"),c=d3.select(this).attr("cy");return 0>a||a>b.width?!0:0>c||c>b.height?!0:!1}).style("display","none")}function f(){q.redraw(),e(),s=d(),$(".chart-info-box").remove(),$(o.node()).trigger("zoom.scatterplot",[])}function g(a,c,d){return c+=8,$(['<div class="chart-info-box" style="position: absolute">',b.idColumn?"<div>"+d[b.idColumn]+"</div>":"","<div>",j(d),"</div>","<div>",k(d),"</div>","</div>"].join("")).css({top:a,left:c,"z-index":2})}var h=function(a,b){return"translate("+a+","+b+")"},i=function(a,b,c){return"rotate("+a+","+b+","+c+")"},j=function(a){return a[b.xColumn]},k=function(a){return a[b.yColumn]},l={x:{extent:d3.extent(c,j)},y:{extent:d3.extent(c,k)}},m={x:d3.scale.linear().domain(l.x.extent).range([0,b.width]),y:d3.scale.linear().domain(l.y.extent).range([b.height,0])},n=d3.behavior.zoom().x(m.x).y(m.y).scaleExtent([1,10]),o=d3.select(a).attr("class","scatterplot").attr("width","100%").attr("height",b.height+(b.margin.top+b.margin.bottom)),p=o.append("g").attr("class","content").attr("transform",h(b.margin.left,b.margin.top)).call(n);p.append("rect").attr("class","zoom-rect").attr("width",b.width).attr("height",b.height).style("fill","transparent");var q={x:{},y:{}};q.x.fn=d3.svg.axis().orient("bottom").scale(m.x).ticks(b.x.ticks).tickFormat(d3.format("s")),q.y.fn=d3.svg.axis().orient("left").scale(m.y).ticks(b.y.ticks).tickFormat(d3.format("s")),q.x.g=p.append("g").attr("class","x axis").attr("transform",h(0,b.height)).call(q.x.fn),q.y.g=p.append("g").attr("class","y axis").call(q.y.fn);var r=4;q.x.label=o.append("text").attr("class","axis-label").text(b.x.label).attr("text-anchor","middle").attr("dominant-baseline","text-after-edge").attr("x",b.width/2+b.margin.left).attr("y",b.height+b.margin.bottom+b.margin.top-r),q.y.label=o.append("text").attr("class","axis-label").text(b.y.label).attr("text-anchor","middle").attr("dominant-baseline","text-before-edge").attr("x",r).attr("y",b.height/2).attr("transform",i(-90,r,b.height/2)),q.redraw=function(){o.select(".x.axis").call(q.x.fn),o.select(".y.axis").call(q.y.fn)};var s=d(),t=p.selectAll(".glyph").data(c).enter().append("svg:circle").classed("glyph",!0).attr("cx",function(a,b){return m.x(j(a,b))}).attr("cy",b.height).attr("r",0);t.transition().duration(b.animDuration).attr("cy",function(a,b){return m.y(k(a,b))}).attr("r",b.datapointSize),n.on("zoom",f),t.on("mouseover",function(a,c){var d=d3.select(this);d.style("fill","red").style("fill-opacity",1),p.append("line").attr("stroke","red").attr("stroke-width",1).attr("x1",d.attr("cx")-b.datapointSize).attr("y1",d.attr("cy")).attr("x2",0).attr("y2",d.attr("cy")).classed("hoverline",!0),d.attr("cy")<b.height&&p.append("line").attr("stroke","red").attr("stroke-width",1).attr("x1",d.attr("cx")).attr("y1",+d.attr("cy")+b.datapointSize).attr("x2",d.attr("cx")).attr("y2",b.height).classed("hoverline",!0);var e=this.getBoundingClientRect();$("body").append(g(e.top,e.right,a)),$(o.node()).trigger("mouseover-datapoint.scatterplot",[this,a,c])}),t.on("mouseout",function(){d3.select(this).style("fill","black").style("fill-opacity",.2),p.selectAll(".hoverline").remove(),$(".chart-info-box").remove()})}this.Templates=this.Templates||{},this.Templates.chartcontrol=Handlebars.template(function(a,b,c,d,e){this.compilerInfo=[4,">= 1.0.0"],c=this.merge(c,a.helpers),e=e||{};var f,g="",h="function",i=this.escapeExpression;return g+='<p class="help-text">\n Use the following controls to how the chart is displayed.\n The slide controls can be moved by the mouse or, if the \'handle\' is in focus, your keyboard\'s arrow keys.\n Move the focus between controls by using the tab or shift+tab keys on your keyboard.\n Use the \'Draw\' button to render (or re-render) the chart with the current settings.\n</p>\n\n<div data-config-key="datapointSize" class="form-input numeric-slider-input">\n <label for="datapointSize">Size of data point: </label>\n <div class="slider-output">',(f=c.datapointSize)?f=f.call(b,{hash:{},data:e}):(f=b.datapointSize,f=typeof f===h?f.apply(b):f),g+=i(f)+'</div>\n <div class="slider"></div>\n <p class="form-help help-text-small">\n Size of the graphic representation of each data point\n </p>\n</div>\n\n<div data-config-key="width" class="form-input numeric-slider-input">\n <label for="width">Chart width: </label>\n <div class="slider-output">',(f=c.width)?f=f.call(b,{hash:{},data:e}):(f=b.width,f=typeof f===h?f.apply(b):f),g+=i(f)+'</div>\n <div class="slider"></div>\n <p class="form-help help-text-small">\n (not including chart margins and axes)\n </p>\n</div>\n\n<div data-config-key="height" class="form-input numeric-slider-input">\n <label for="height">Chart height: </label>\n <div class="slider-output">',(f=c.height)?f=f.call(b,{hash:{},data:e}):(f=b.height,f=typeof f===h?f.apply(b):f),g+=i(f)+'</div>\n <div class="slider"></div>\n <p class="form-help help-text-small">\n (not including chart margins and axes)\n </p>\n</div>\n\n<div data-config-key="X-axis-label"class="text-input form-input">\n <label for="X-axis-label">Re-label the X axis: </label>\n <input type="text" name="X-axis-label" id="X-axis-label" value="'+i((f=b.x,f=null==f||f===!1?f:f.label,typeof f===h?f.apply(b):f))+'" />\n <p class="form-help help-text-small"></p>\n</div>\n\n<div data-config-key="Y-axis-label" class="text-input form-input">\n <label for="Y-axis-label">Re-label the Y axis: </label>\n <input type="text" name="Y-axis-label" id="Y-axis-label" value="'+i((f=b.y,f=null==f||f===!1?f:f.label,typeof f===h?f.apply(b):f))+'" />\n <p class="form-help help-text-small"></p>\n</div>\n\n<button class="render-button btn btn-primary active">Draw</button>\n'}),this.Templates.datacontrol=Handlebars.template(function(a,b,c,d,e){function f(a,b){var d,e="";return e+='\n <option value="',(d=c.index)?d=d.call(a,{hash:{},data:b}):(d=a.index,d=typeof d===j?d.apply(a):d),e+=k(d)+'">',(d=c.name)?d=d.call(a,{hash:{},data:b}):(d=a.name,d=typeof d===j?d.apply(a):d),e+=k(d)+"</option>\n "}function g(){return'checked="true"'}this.compilerInfo=[4,">= 1.0.0"],c=this.merge(c,a.helpers),e=e||{};var h,i="",j="function",k=this.escapeExpression,l=this;return i+='<p class="help-text">\n Use the following controls to change the data used by the chart.\n Use the \'Draw\' button to render (or re-render) the chart with the current settings.\n</p>\n\n\n<div class="column-select">\n <label>Data column for X: </label>\n <select name="xColumn">\n ',h=c.each.call(b,b.numericColumns,{hash:{},inverse:l.noop,fn:l.program(1,f,e),data:e}),(h||0===h)&&(i+=h),i+='\n </select>\n</div>\n<div class="column-select">\n <label>Data column for Y: </label>\n <select name="yColumn">\n ',h=c.each.call(b,b.numericColumns,{hash:{},inverse:l.noop,fn:l.program(1,f,e),data:e}),(h||0===h)&&(i+=h),i+='\n </select>\n</div>\n\n\n<div id="include-id">\n <label for="include-id-checkbox">Include a third column as data point IDs?</label>\n <input type="checkbox" name="include-id" id="include-id-checkbox" />\n <p class="help-text-small">\n These will be displayed (along with the x and y values) when you hover over\n a data point.\n </p>\n</div>\n<div class="column-select" style="display: none">\n <label for="ID-select">Data column for IDs: </label>\n <select name="idColumn">\n ',h=c.each.call(b,b.allColumns,{hash:{},inverse:l.noop,fn:l.program(1,f,e),data:e}),(h||0===h)&&(i+=h),i+='\n </select>\n</div>\n\n\n<div id="first-line-header" style="display: none;">\n <p>Possible headers: ',(h=c.possibleHeaders)?h=h.call(b,{hash:{},data:e}):(h=b.possibleHeaders,h=typeof h===j?h.apply(b):h),i+=k(h)+'\n </p>\n <label for="first-line-header-checkbox">Use the above as column headers?</label>\n <input type="checkbox" name="include-id" id="first-line-header-checkbox"\n ',h=c["if"].call(b,b.usePossibleHeaders,{hash:{},inverse:l.noop,fn:l.program(3,g,e),data:e}),(h||0===h)&&(i+=h),i+='/>\n <p class="help-text-small">\n It looks like Galaxy couldn\'t get proper column headers for this data.\n Would you like to use the column headers above as column names to select columns?\n </p>\n</div>\n\n<button class="render-button btn btn-primary active">Draw</button>\n'}),this.Templates.editor=Handlebars.template(function(a,b,c,d,e){this.compilerInfo=[4,">= 1.0.0"],c=this.merge(c,a.helpers),e=e||{};var f="";return f+='<div class="scatterplot-editor tabbable tabs-left">\n \n <ul class="nav nav-tabs">\n \n <li class="active">\n <a title="Use this tab to change which data are used"\n href="#data-control" data-toggle="tab">Data Controls</a>\n </li>\n <li>\n <a title="Use this tab to change how the chart is drawn"\n href="#chart-control" data-toggle="tab" >Chart Controls</a>\n </li>\n \n <li class="disabled">\n <a title="This tab will display the chart"\n href="#chart-display" data-toggle="tab">Chart</a>\n </li>\n </ul>\n\n \n <div class="tab-content">\n \n <div id="data-control" class="scatterplot-config-control tab-pane active">\n \n </div>\n \n \n <div id="chart-control" class="scatterplot-config-control tab-pane">\n \n </div>\n\n \n <div id="chart-display" class="scatterplot-display tab-pane"></div>\n\n </div>\n</div>\n'});var ScatterplotConfigEditor=BaseView.extend(LoggableMixin).extend({className:"scatterplot-control-form",initialize:function(a){if(!a||!a.config||!a.dataset)throw new Error("ScatterplotView requires a configuration and dataset");this.dataset=a.dataset,this.plotView=new ScatterplotView({dataset:a.dataset,config:a.config})},render:function(){return this.$el.append(ScatterplotConfigEditor.templates.mainLayout({})),this.$el.find("#data-control").append(this._render_dataControl()),this._render_chartControls(this.$el.find("#chart-control")),this._render_chartDisplay(),this.$el.find("[title]").tooltip(),this},_render_dataControl:function(){var a=this.dataset,b=_.map(a.metadata_column_types,function(b,c){var d={index:c,type:b,name:"column "+(c+1)};return a.metadata_column_names&&a.metadata_column_names[c]&&(d.name=a.metadata_column_names[c]),d}),c=_.filter(b,function(a){return"int"===a.type||"float"===a.type});2>c&&(c=b);var d=this.$el.find(".tab-pane#data-control");return d.html(ScatterplotConfigEditor.templates.dataControl({allColumns:b,numericColumns:c})),d.find('[name="xColumn"]').val(this.plotView.config.xColumn||c[0].index),d.find('[name="yColumn"]').val(this.plotView.config.yColumn||c[1].index),void 0!==this.plotView.config.idColumn&&(d.find("#include-id-checkbox").prop("checked",!0).trigger("change"),d.find('select[name="idColumn"]').val(this.plotView.config.idColumn)),d},_render_chartControls:function(a){function b(){var a=$(this);a.siblings(".slider-output").text(a.slider("value"))}a.html(ScatterplotConfigEditor.templates.chartControl(this.plotView.config));var c=this,d={datapointSize:{min:2,max:10,step:1},width:{min:200,max:800,step:20},height:{min:200,max:800,step:20}};return a.find(".numeric-slider-input").each(function(){var a=$(this),e=a.attr("data-config-key"),f=_.extend(d[e],{value:c.plotView.config[e],change:b,slide:b});a.find(".slider").slider(f)}),this.dataset.metadata_column_names,a},_render_chartDisplay:function(){var a=this.$el.find(".tab-pane#chart-display");return this.plotView.setElement(a),this.plotView.render(),a},events:{"change #include-id-checkbox":"toggleThirdColumnSelector","click #data-control .render-button":"renderChart","click #chart-control .render-button":"renderChart"},toggleThirdColumnSelector:function(){this.$el.find('select[name="idColumn"]').parent().toggle()},renderChart:function(){this.$el.find(".nav li.disabled").removeClass("disabled"),this.updateConfigWithDataSettings(),this.updateConfigWithChartSettings(),this.$el.find("ul.nav").find('a[href="#chart-display"]').tab("show"),this.plotView.fetchData()},updateConfigWithDataSettings:function(){var a=this.$el.find("#data-control"),b={xColumn:Number(a.find('[name="xColumn"]').val()),yColumn:Number(a.find('[name="yColumn"]').val())};return a.find("#include-id-checkbox").prop("checked")&&(b.idColumn=a.find('[name="idColumn"]').val()),_.extend(this.plotView.config,b)},updateConfigWithChartSettings:function(){var a=this.plotView,b=this.$el.find("#chart-control");return["datapointSize","width","height"].forEach(function(c){a.config[c]=b.find('.numeric-slider-input[data-config-key="'+c+'"]').find(".slider").slider("value")}),a.config.x.label=b.find('input[name="X-axis-label"]').val(),a.config.y.label=b.find('input[name="Y-axis-label"]').val(),a.config},toString:function(){return"ScatterplotConfigEditor("+(this.dataset?this.dataset.id:"")+")"}});ScatterplotConfigEditor.templates={mainLayout:Templates.editor,dataControl:Templates.datacontrol,chartControl:Templates.chartcontrol};var ScatterplotView=Backbone.View.extend({defaults:{metadata:{dataLines:void 0},pagination:{currPage:0,perPage:3e3},width:400,height:400,margin:{top:16,right:16,bottom:40,left:54},x:{ticks:10,label:"X"},y:{ticks:10,label:"Y"},datapointSize:4,animDuration:500},initialize:function(a){this.config=_.extend(_.clone(this.defaults),a.config||{}),this.dataset=a.dataset},updateConfig:function(a){this.config=this.config||{},_.extend(this.config,a)},fetchData:function(){this.showLoadingIndicator("getting data");var a=this;return xhr=jQuery.getJSON("/api/datasets/"+this.dataset.id,{data_type:"raw_data",provider:"dataset-column",limit:this.config.pagination.perPage,offset:this.config.pagination.currPage*this.config.pagination.perPage}),xhr.done(function(b){a.renderData(b.data)}),xhr.fail(function(a,b,c){alert("Error loading data:\n"+a.responseText),console.error(a,b,c)}),xhr.always(function(){a.hideLoadingIndicator()}),xhr},render:function(a){return this.$el.addClass("scatterplot-display").html(['<div class="controls clear"></div>','<div class="loading-indicator">','<span class="fa fa-spinner fa-spin"></span>','<span class="loading-indicator-message"></span>',"</div>","<svg/>",'<div class="stats-display"></div>'].join("")),this.$el.children().hide(),a&&this.renderData(a),this},showLoadingIndicator:function(a,b){a=a||"",b=b||"fast";var c=this.$el.find(".loading-indicator");a&&c.find(".loading-indicator-message").text(a),c.is(":visible")||(this.toggleStats(!1),c.css({left:this.config.width/2,top:this.config.height/2}).show())},hideLoadingIndicator:function(a){a=a||"fast",this.$el.find(".loading-indicator").hide()},renderData:function(a){this.$el.find(".controls").empty().append(this.renderControls(a)).show(),this.renderPlot(a),this.getStats(a)},renderControls:function(a){var b=this,c=$('<div class="left"></div>'),d=$('<div class="right"></div>');return c.append([this.renderPrevNext(a),this.renderPagination(a)]),d.append([this.renderLineInfo(a),$("<button>Stats</button>").addClass("stats-toggle-btn").click(function(){b.toggleStats()}),$("<button>Redraw</button>").addClass("rerender-btn").click(function(){b.renderPlot(a)})]),[c,d]},renderLineInfo:function(a){var b=this.dataset.metadata_data_lines||"an unknown number of",c=this.config.pagination.currPage*this.config.pagination.perPage,d=c+a.length;return $("<p/>").addClass("scatterplot-data-info").text(["Displaying lines",c+1,"to",d,"of",b,"lines"].join(" "))},renderPrevNext:function(a){function b(a){return $(['<li><a href="javascript:void(0);">',a,"</a></li>"].join(""))}if(!a||0===this.config.pagination.currPage&&a.length<this.config.pagination.perPage)return null;var c=this,d=this.dataset.metadata_data_lines,e=d?Math.ceil(d/this.config.pagination.perPage):void 0,f=b("Prev").click(function(){c.config.pagination.currPage>0&&(c.config.pagination.currPage-=1,c.fetchData())}),g=b("Next").click(function(){(!e||c.config.pagination.currPage<e-1)&&(c.config.pagination.currPage+=1,c.fetchData())}),h=$("<ul/>").addClass("pagination data-prev-next").append([f,g]);return 0===c.config.pagination.currPage&&f.addClass("disabled"),e&&c.config.pagination.currPage===e-1&&g.addClass("disabled"),h},renderPagination:function(a){function b(a){return $(['<li><a href="javascript:void(0);">',a,"</a></li>"].join(""))}function c(){d.config.pagination.currPage=$(this).data("page"),d.fetchData()}if(!a||0===this.config.pagination.currPage&&a.length<this.config.pagination.perPage)return null;for(var d=this,e=this.dataset.metadata_data_lines,f=e?Math.ceil(e/this.config.pagination.perPage):void 0,g=$("<ul/>").addClass("pagination data-pages"),h=0;f>h;h+=1){var i=b(h+1).attr("data-page",h).click(c);h===this.config.pagination.currPage&&i.addClass("active"),g.append(i)}return g},renderPlot:function(a){this.toggleStats(!1);var b=this.$el.find("svg");b.off().empty().show(),scatterplot(b.get(0),this.config,a)},getStats:function(a){var b=this;meanWorker=new Worker("/plugins/visualizations/scatterplot/static/worker-stats.js"),meanWorker.postMessage({data:a,keys:[this.config.xColumn,this.config.yColumn]}),meanWorker.onerror=function(){meanWorker.terminate()},meanWorker.onmessage=function(a){b.renderStats(a.data)}},renderStats:function(a){var b=this.$el.find(".stats-display"),c=this.config.x.label,d=this.config.y.label,e=$("<table/>").addClass("table").append(["<thead><th></th><th>",c,"</th><th>",d,"</th></thead>"].join("")).append(_.map(a,function(a,b){return $(["<tr><td>",b,"</td><td>",a[0],"</td><td>",a[1],"</td></tr>"].join(""))}));b.empty().append(e)},toggleStats:function(a){var b=this.$el.find(".stats-display");a=void 0===a?b.is(":hidden"):a,a?(this.$el.find("svg").hide(),b.show(),this.$el.find(".controls .stats-toggle-btn").text("Plot")):(b.hide(),this.$el.find("svg").show(),this.$el.find(".controls .stats-toggle-btn").text("Stats"))},toString:function(){return"ScatterplotView()"}});
\ No newline at end of file
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 config/plugins/visualizations/scatterplot/templates/scatterplot.mako
--- a/config/plugins/visualizations/scatterplot/templates/scatterplot.mako
+++ b/config/plugins/visualizations/scatterplot/templates/scatterplot.mako
@@ -15,7 +15,6 @@
<script type="text/javascript" src="/static/scripts/libs/jquery/jquery.migrate.js"></script><script type="text/javascript" src="/static/scripts/libs/underscore.js"></script><script type="text/javascript" src="/static/scripts/libs/backbone/backbone.js"></script>
-<script type="text/javascript" src="/static/scripts/libs/backbone/backbone-relational.js"></script><script type="text/javascript" src="/static/scripts/libs/handlebars.runtime.js"></script><script type="text/javascript" src="/static/scripts/libs/d3.js"></script><script type="text/javascript" src="/static/scripts/libs/bootstrap.js"></script>
@@ -43,22 +42,18 @@
data = None
##data = list( hda.datatype.dataset_column_dataprovider( hda, limit=10000 ) )
%>
- var hda = ${h.to_json_string( trans.security.encode_dict_ids( hda.to_dict() ) )},
- data = ${h.to_json_string( data )},
- querySettings = ${h.to_json_string( query_args )},
- config = _.extend( querySettings, {
- containerSelector : '#chart',
- dataset : hda,
- });
- //console.debug( querySettings );
+ var hda = ${h.to_json_string( trans.security.encode_dict_ids( hda.to_dict() ) )};
var editor = new ScatterplotConfigEditor({
el : $( '.scatterplot-editor' ).attr( 'id', 'scatterplot-editor-hda-' + hda.id ),
- config : config
+ config : ${h.to_json_string( query_args )},
+ dataset : ${h.to_json_string( trans.security.encode_dict_ids( hda.to_dict() ) )}
}).render();
+ window.editor = editor;
// uncomment to auto render for development
//$( '.render-button:visible' ).click();
});
+
</script>
%endif
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 doc/source/lib/galaxy.webapps.galaxy.api.rst
--- a/doc/source/lib/galaxy.webapps.galaxy.api.rst
+++ b/doc/source/lib/galaxy.webapps.galaxy.api.rst
@@ -205,6 +205,11 @@
The request and response format should be considered alpha and are subject to change.
+API Return Codes and Formats
+==================
+
+A set of error codes for API requests is being established and will be
+documented here. This is a long-term project however so stayed tuned.
API Controllers
===============
@@ -297,6 +302,14 @@
:undoc-members:
:show-inheritance:
+:mod:`lda_datasets` Module
+--------------------------
+
+.. automodule:: galaxy.webapps.galaxy.api.lda_datasets
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
:mod:`libraries` Module
-----------------------
@@ -393,3 +406,67 @@
:undoc-members:
:show-inheritance:
+
+API Design Guidelines
+=====================
+
+The following section outlines guidelines related to extending and/or modifing
+the Galaxy API. The Galaxy API has grown in an ad-hoc fashion over time by
+many contributors and so clients SHOULD NOT expect the API will conform to
+these guidelines - but developers contributing to the Galaxy API SHOULD follow
+these guidelines.
+
+ - API functionality should include docstring documentation for consumption
+ by readthedocs.org.
+ - Developers should familarize themselves with the HTTP status code definitions
+ http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html. The API responses
+ should properly set the status code according to the result - in particular
+ 2XX responses should be used for successful requests, 4XX for various
+ kinds of client errors, and 5XX for errors on the server side.
+ - If there is an error processing some part of request (one item in a list
+ for instance), the status code should be set to reflect the error and the
+ partial result may or may not be returned depending on the controller -
+ this behavior should be documented.
+ - (TODO) API methods should throw a finite number of exceptions (defined in)
+ `galaxy.exceptions` and these should subclass `MessageException` and not
+ paste/wsgi HTTP exceptions. When possible, the framework itself should be
+ responsible catching these exceptions, setting the status code, and
+ building an error response.
+ - Error responses should not consist of plain text strings - they should be
+ dictionaries describing the error and containing the following keys (TODO:
+ spell out nature of this.) Various error conditions (once a format has
+ been chosen and framework to enforce it in place) should be spelled out
+ in this document.
+ - Backward compatibility is important and should maintained when possible.
+ If changing behavior in a non-backward compatibile way please ensure one
+ of the following holds - there is a strong reason to believe no consumers
+ depend on a behavior, the behavior is effectively broken, or the API
+ method being modified has not been part of a tagged dist release.
+
+The following bullet points represent good practices more than guidelines, please
+consider them when modifying the API.
+
+ - Functionality should not be copied and pasted between controllers -
+ consider refactoring functionality into associated classes or short of
+ that into Mixins (http://en.wikipedia.org/wiki/Composition_over_inheritance)
+ - API additions are more permanent changes to Galaxy than many other potential
+ changes and so a second opinion on API changes should be sought. (Consider a
+ pull request!)
+ - New API functionality should include functional tests. These functional
+ tests should be implemented in Python and placed in
+ `test/functional/api`. (Once such a framework is in place - it is not
+ right now).
+ - Changes to reflect modifications to the API should be pushed upstream to
+ the BioBlend project possible.
+
+Longer term goals/notes.
+
+ - It would be advantageous to have a clearer separation of anonymous and
+ admin handling functionality.
+ - If at some point in the future, functionality needs to be added that
+ breaks backward compatibility in a significant way to a compontent used by
+ the community should be alerted - a "dev" variant of the API will be
+ established and the community should be alerted and given a timeframe
+ for when the old behavior will be replaced with the new behavior.
+ - Consistent standards for range-based requests, batch requests, filtered
+ requests, etc... should be established and documented here.
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 install_and_test_tool_shed_repositories.sh
--- a/install_and_test_tool_shed_repositories.sh
+++ b/install_and_test_tool_shed_repositories.sh
@@ -2,14 +2,20 @@
# A good place to look for nose info: http://somethingaboutorange.com/mrl/projects/nose/
-# The test/install_and_test_tool_shed_repositories/functional_tests.py can not be executed directly, because it must have certain functional test definitions
-# in sys.argv. Running it through this shell script is the best way to ensure that it has the required definitions.
+# The test/install_and_test_tool_shed_repositories/functional_tests.py cannot be executed directly because it must
+# have certain functional test definitions in sys.argv. Running it through this shell script is the best way to
+# ensure that it has the required definitions.
-# This script requires the following environment variables:
+# This script requires setting of the following environment variables:
# GALAXY_INSTALL_TEST_TOOL_SHED_API_KEY - must be set to the API key for the tool shed that is being checked.
# GALAXY_INSTALL_TEST_TOOL_SHED_URL - must be set to a URL that the tool shed is listening on.
-# If the tool shed url is not specified in tool_sheds_conf.xml, GALAXY_INSTALL_TEST_TOOL_SHEDS_CONF must be set to a tool sheds configuration file
-# that does specify that url, otherwise repository installation will fail.
+
+# If the tool shed url is not specified in tool_sheds_conf.xml, GALAXY_INSTALL_TEST_TOOL_SHEDS_CONF must be set to
+# a tool sheds configuration file that does specify that url or repository installation will fail.
+
+# This script accepts the command line option -w to select which set of tests to run. The default behavior is to test
+# first tool_dependency_definition repositories and then repositories with tools. Provide the value 'dependencies'
+# to test only tool_dependency_definition repositories or 'tools' to test only repositories with tools.
if [ -z $GALAXY_INSTALL_TEST_TOOL_SHED_API_KEY ] ; then
echo "This script requires the GALAXY_INSTALL_TEST_TOOL_SHED_API_KEY environment variable to be set and non-empty."
@@ -37,7 +43,45 @@
fi
fi
-python test/install_and_test_tool_shed_repositories/functional_tests.py $* -v --with-nosehtml --html-report-file \
- test/install_and_test_tool_shed_repositories/run_functional_tests.html \
- test/install_and_test_tool_shed_repositories/functional/test_install_repositories.py \
- test/functional/test_toolbox.py
+test_tool_dependency_definitions () {
+ # Test installation of repositories of type tool_dependency_definition.
+ python test/install_and_test_tool_shed_repositories/tool_dependency_definitions/functional_tests.py $* -v --with-nosehtml --html-report-file \
+ test/install_and_test_tool_shed_repositories/tool_dependency_definitions/run_functional_tests.html \
+ test/install_and_test_tool_shed_repositories/functional/test_install_repositories.py \
+ test/functional/test_toolbox.py
+}
+
+test_repositories_with_tools () {
+ # Test installation of repositories that contain valid tools with defined functional tests and a test-data directory containing test files.
+ python test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py $* -v --with-nosehtml --html-report-file \
+ test/install_and_test_tool_shed_repositories/repositories_with_tools/run_functional_tests.html \
+ test/install_and_test_tool_shed_repositories/functional/test_install_repositories.py \
+ test/functional/test_toolbox.py
+}
+
+which='both'
+
+while getopts "w:" arg; do
+ case $arg in
+ w)
+ which=$OPTARG
+ ;;
+ esac
+done
+
+case $which in
+ # Use "-w tool_dependency_definitions" when you want to test repositories of type tool_dependency_definition.
+ tool_dependency_definitions)
+ test_tool_dependency_definitions
+ ;;
+ # Use "-w repositories_with_tools" parameter when you want to test repositories that contain tools.
+ repositories_with_tools)
+ test_repositories_with_tools
+ ;;
+ # No received parameters or any received parameter not in [ tool_dependency_definitions, repositories_with_tools ]
+ # will execute both scripts.
+ *)
+ test_tool_dependency_definitions
+ test_repositories_with_tools
+ ;;
+esac
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 job_conf.xml.sample_advanced
--- a/job_conf.xml.sample_advanced
+++ b/job_conf.xml.sample_advanced
@@ -52,15 +52,44 @@
<param id="private_token">123456789changeme</param><!-- Uncomment the following statement to disable file staging (e.g.
if there is a shared file system between Galaxy and the LWR
- server). -->
+ server). Alternatively action can be set to 'copy' - to replace
+ http transfers with file system copies. --><!-- <param id="default_file_action">none</param> -->
+ <!-- The above option is just the default, the transfer behavior
+ none|copy|http can be configured on a per path basis via the
+ following file. See lib/galaxy/jobs/runners/lwr_client/action_mapper.py
+ for examples of how to configure this file. This is very beta
+ and nature of file will likely change.
+ -->
+ <!-- <param id="file_action_config">file_actions.json</param> -->
+ <!-- Uncomment following option to disable Galaxy tool dependency
+ resolution and utilize remote LWR's configuraiton of tool
+ dependency resolution instead (same options as Galaxy for
+ dependency resolution are available in LWR).
+ -->
+ <!-- <param id="dependency_resolution">remote</params> -->
+ <!-- Uncomment following option to enable setting metadata on remote
+ LWR server. The 'use_remote_datatypes' option is available for
+ determining whether to use remotely configured datatypes or local
+ ones (both alternatives are a little brittle). -->
+ <!-- <param id="remote_metadata">true</param> -->
+ <!-- <param id="use_remote_datatypes">false</param> -->
+ <!-- If remote LWR server is configured to run jobs as the real user,
+ uncomment the following line to pass the current Galaxy user
+ along. -->
+ <!-- <param id="submit_user">$__user_name__</param> -->
+ <!-- Various other submission parameters can be passed along to the LWR
+ whose use will depend on the remote LWR's configured job manager.
+ For instance:
+ -->
+ <!-- <param id="submit_native_specification">-P bignodes -R y -pe threads 8</param> --></destination><destination id="ssh_torque" runner="cli"><param id="shell_plugin">SecureShell</param><param id="job_plugin">Torque</param><param id="shell_username">foo</param><param id="shell_hostname">foo.example.org</param>
- <param id="Job_Execution_Time">24:00:00</param>
+ <param id="job_Resource_List">walltime=24:00:00,ncpus=4</param></destination><destination id="condor" runner="condor"><!-- With no params, jobs are submitted to the 'vanilla' universe with:
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/app.py
--- a/lib/galaxy/app.py
+++ b/lib/galaxy/app.py
@@ -1,16 +1,10 @@
from __future__ import absolute_import
-import sys, os, atexit
+import sys
+import os
-from galaxy import config, jobs, util, tools, web
-import galaxy.tools.search
-import galaxy.tools.data
-import tool_shed.galaxy_install
-import tool_shed.tool_shed_registry
-from galaxy.web import security
+from galaxy import config, jobs
import galaxy.model
-import galaxy.datatypes.registry
import galaxy.security
-from galaxy.objectstore import build_object_store_from_config
import galaxy.quota
from galaxy.tags.tag_handler import GalaxyTagHandler
from galaxy.visualization.genomes import Genomes
@@ -27,7 +21,8 @@
import logging
log = logging.getLogger( __name__ )
-class UniverseApplication( object ):
+
+class UniverseApplication( object, config.ConfiguresGalaxyMixin ):
"""Encapsulates the state of a Universe application"""
def __init__( self, **kwargs ):
print >> sys.stderr, "python path is: " + ", ".join( sys.path )
@@ -38,72 +33,38 @@
self.config.check()
config.configure_logging( self.config )
self.configure_fluent_log()
- # Determine the database url
- if self.config.database_connection:
- db_url = self.config.database_connection
- else:
- db_url = "sqlite:///%s?isolation_level=IMMEDIATE" % self.config.database
- # Set up the tool sheds registry
- if os.path.isfile( self.config.tool_sheds_config ):
- self.tool_shed_registry = tool_shed.tool_shed_registry.Registry( self.config.root, self.config.tool_sheds_config )
- else:
- self.tool_shed_registry = None
- # Initialize database / check for appropriate schema version. # If this
- # is a new installation, we'll restrict the tool migration messaging.
- from galaxy.model.migrate.check import create_or_verify_database
- create_or_verify_database( db_url, kwargs.get( 'global_conf', {} ).get( '__file__', None ), self.config.database_engine_options, app=self )
- # Alert the Galaxy admin to tools that have been moved from the distribution to the tool shed.
- from tool_shed.galaxy_install.migrate.check import verify_tools
- verify_tools( self, db_url, kwargs.get( 'global_conf', {} ).get( '__file__', None ), self.config.database_engine_options )
- # Object store manager
- self.object_store = build_object_store_from_config(self.config, fsmon=True)
+
+ self._configure_tool_shed_registry()
+
+ self._configure_object_store( fsmon=True )
+
# Setup the database engine and ORM
- from galaxy.model import mapping
- self.model = mapping.init( self.config.file_path,
- db_url,
- self.config.database_engine_options,
- database_query_profiling_proxy = self.config.database_query_profiling_proxy,
- object_store = self.object_store,
- trace_logger=self.trace_logger,
- use_pbkdf2=self.config.get_bool( 'use_pbkdf2', True ) )
+ config_file = kwargs.get( 'global_conf', {} ).get( '__file__', None )
+ self._configure_models( check_migrate_databases=True, check_migrate_tools=True, config_file=config_file )
+
# Manage installed tool shed repositories.
- self.installed_repository_manager = tool_shed.galaxy_install.InstalledRepositoryManager( self )
- # Create an empty datatypes registry.
- self.datatypes_registry = galaxy.datatypes.registry.Registry()
- # Load proprietary datatypes defined in datatypes_conf.xml files in all installed tool shed repositories. We
- # load proprietary datatypes before datatypes in the distribution because Galaxy's default sniffers include some
- # generic sniffers (eg text,xml) which catch anything, so it's impossible for proprietary sniffers to be used.
- # However, if there is a conflict (2 datatypes with the same extension) between a proprietary datatype and a datatype
- # in the Galaxy distribution, the datatype in the Galaxy distribution will take precedence. If there is a conflict
- # between 2 proprietary datatypes, the datatype from the repository that was installed earliest will take precedence.
- self.installed_repository_manager.load_proprietary_datatypes()
- # Load the data types in the Galaxy distribution, which are defined in self.config.datatypes_config.
- self.datatypes_registry.load_datatypes( self.config.root, self.config.datatypes_config )
+ from tool_shed.galaxy_install import installed_repository_manager
+ self.installed_repository_manager = installed_repository_manager.InstalledRepositoryManager( self )
+
+ self._configure_datatypes_registry( self.installed_repository_manager )
galaxy.model.set_datatypes_registry( self.datatypes_registry )
+
# Security helper
- self.security = security.SecurityHelper( id_secret=self.config.id_secret )
+ self._configure_security()
# Tag handler
self.tag_handler = GalaxyTagHandler()
# Genomes
self.genomes = Genomes( self )
# Data providers registry.
self.data_provider_registry = DataProviderRegistry()
- # Initialize tool data tables using the config defined by self.config.tool_data_table_config_path.
- self.tool_data_tables = galaxy.tools.data.ToolDataTableManager( tool_data_path=self.config.tool_data_path,
- config_filename=self.config.tool_data_table_config_path )
- # Load additional entries defined by self.config.shed_tool_data_table_config into tool data tables.
- self.tool_data_tables.load_from_config_file( config_filename=self.config.shed_tool_data_table_config,
- tool_data_path=self.tool_data_tables.tool_data_path,
- from_shed_config=False )
+
+ self._configure_tool_data_tables( from_shed_config=False )
+
# Initialize the job management configuration
self.job_config = jobs.JobConfiguration(self)
- # Initialize the tools, making sure the list of tool configs includes the reserved migrated_tools_conf.xml file.
- tool_configs = self.config.tool_configs
- if self.config.migrated_tools_config not in tool_configs:
- tool_configs.append( self.config.migrated_tools_config )
- self.toolbox = tools.ToolBox( tool_configs, self.config.tool_path, self )
- # Search support for tools
- self.toolbox_search = galaxy.tools.search.ToolBoxSearch( self.toolbox )
+
+ self._configure_toolbox()
+
# Load Data Manager
self.data_managers = DataManagers( self )
# If enabled, poll respective tool sheds to see if updates are available for any installed tool shed repositories.
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/config.py
--- a/lib/galaxy/config.py
+++ b/lib/galaxy/config.py
@@ -1,6 +1,8 @@
"""
Universe configuration builder.
"""
+# absolute_import needed for tool_shed package.
+from __future__ import absolute_import
import sys, os, tempfile, re
import logging, logging.config
@@ -33,15 +35,22 @@
self.umask = os.umask( 077 ) # get the current umask
os.umask( self.umask ) # can't get w/o set, so set it back
self.gid = os.getgid() # if running under newgrp(1) we'll need to fix the group of data created on the cluster
+
# Database related configuration
self.database = resolve_path( kwargs.get( "database_file", "database/universe.sqlite" ), self.root )
self.database_connection = kwargs.get( "database_connection", False )
self.database_engine_options = get_database_engine_options( kwargs )
self.database_create_tables = string_as_bool( kwargs.get( "database_create_tables", "True" ) )
self.database_query_profiling_proxy = string_as_bool( kwargs.get( "database_query_profiling_proxy", "False" ) )
+
# Don't set this to true for production databases, but probably should
# default to True for sqlite databases.
self.database_auto_migrate = string_as_bool( kwargs.get( "database_auto_migrate", "False" ) )
+
+ # Install database related configuration (if different).
+ self.install_database_connection = kwargs.get( "install_database_connection", None )
+ self.install_database_engine_options = get_database_engine_options( kwargs, model_prefix="install_" )
+
# Where dataset files are stored
self.file_path = resolve_path( kwargs.get( "file_path", "database/files" ), self.root )
self.new_file_path = resolve_path( kwargs.get( "new_file_path", "database/tmp" ), self.root )
@@ -439,7 +448,7 @@
admin_users = [ x.strip() for x in self.get( "admin_users", "" ).split( "," ) ]
return ( user is not None and user.email in admin_users )
-def get_database_engine_options( kwargs ):
+def get_database_engine_options( kwargs, model_prefix='' ):
"""
Allow options for the SQLAlchemy database engine to be passed by using
the prefix "database_engine_option".
@@ -455,7 +464,7 @@
'pool_threadlocal': string_as_bool,
'server_side_cursors': string_as_bool
}
- prefix = "database_engine_option_"
+ prefix = "%sdatabase_engine_option_" % model_prefix
prefix_len = len( prefix )
rval = {}
for key, value in kwargs.iteritems():
@@ -466,6 +475,7 @@
rval[ key ] = value
return rval
+
def configure_logging( config ):
"""
Allow some basic logging configuration to be read from ini file.
@@ -506,3 +516,110 @@
sentry_handler.setLevel( logging.WARN )
root.addHandler( sentry_handler )
+
+class ConfiguresGalaxyMixin:
+ """ Shared code for configuring Galaxy-like app objects.
+ """
+
+ def _configure_toolbox( self ):
+ # Initialize the tools, making sure the list of tool configs includes the reserved migrated_tools_conf.xml file.
+ tool_configs = self.config.tool_configs
+ if self.config.migrated_tools_config not in tool_configs:
+ tool_configs.append( self.config.migrated_tools_config )
+ from galaxy import tools
+ self.toolbox = tools.ToolBox( tool_configs, self.config.tool_path, self )
+ # Search support for tools
+ import galaxy.tools.search
+ self.toolbox_search = galaxy.tools.search.ToolBoxSearch( self.toolbox )
+
+ def _configure_tool_data_tables( self, from_shed_config ):
+ from galaxy.tools.data import ToolDataTableManager
+
+ # Initialize tool data tables using the config defined by self.config.tool_data_table_config_path.
+ self.tool_data_tables = ToolDataTableManager( tool_data_path=self.config.tool_data_path,
+ config_filename=self.config.tool_data_table_config_path )
+ # Load additional entries defined by self.config.shed_tool_data_table_config into tool data tables.
+ self.tool_data_tables.load_from_config_file( config_filename=self.config.shed_tool_data_table_config,
+ tool_data_path=self.tool_data_tables.tool_data_path,
+ from_shed_config=from_shed_config )
+
+ def _configure_datatypes_registry( self, installed_repository_manager=None ):
+ from galaxy.datatypes import registry
+ # Create an empty datatypes registry.
+ self.datatypes_registry = registry.Registry()
+ if installed_repository_manager:
+ # Load proprietary datatypes defined in datatypes_conf.xml files in all installed tool shed repositories. We
+ # load proprietary datatypes before datatypes in the distribution because Galaxy's default sniffers include some
+ # generic sniffers (eg text,xml) which catch anything, so it's impossible for proprietary sniffers to be used.
+ # However, if there is a conflict (2 datatypes with the same extension) between a proprietary datatype and a datatype
+ # in the Galaxy distribution, the datatype in the Galaxy distribution will take precedence. If there is a conflict
+ # between 2 proprietary datatypes, the datatype from the repository that was installed earliest will take precedence.
+ installed_repository_manager.load_proprietary_datatypes()
+ # Load the data types in the Galaxy distribution, which are defined in self.config.datatypes_config.
+ self.datatypes_registry.load_datatypes( self.config.root, self.config.datatypes_config )
+
+ def _configure_object_store( self, **kwds ):
+ from galaxy.objectstore import build_object_store_from_config
+ self.object_store = build_object_store_from_config( self.config, **kwds )
+
+ def _configure_security( self ):
+ from galaxy.web import security
+ self.security = security.SecurityHelper( id_secret=self.config.id_secret )
+
+ def _configure_tool_shed_registry( self ):
+ import tool_shed.tool_shed_registry
+
+ # Set up the tool sheds registry
+ if os.path.isfile( self.config.tool_sheds_config ):
+ self.tool_shed_registry = tool_shed.tool_shed_registry.Registry( self.config.root, self.config.tool_sheds_config )
+ else:
+ self.tool_shed_registry = None
+
+ def _configure_models( self, check_migrate_databases=False, check_migrate_tools=False, config_file=None ):
+ """
+ Preconditions: object_store must be set on self.
+ """
+ if self.config.database_connection:
+ db_url = self.config.database_connection
+ else:
+ db_url = "sqlite:///%s?isolation_level=IMMEDIATE" % self.config.database
+ install_db_url = self.config.install_database_connection
+ # TODO: Consider more aggressive check here that this is not the same
+ # database file under the hood.
+ combined_install_database = not( install_db_url and install_db_url != db_url )
+ install_db_url = install_db_url or db_url
+
+ if check_migrate_databases:
+ # Initialize database / check for appropriate schema version. # If this
+ # is a new installation, we'll restrict the tool migration messaging.
+ from galaxy.model.migrate.check import create_or_verify_database
+ create_or_verify_database( db_url, config_file, self.config.database_engine_options, app=self )
+ if not combined_install_database:
+ from galaxy.model.tool_shed_install.migrate.check import create_or_verify_database as tsi_create_or_verify_database
+ tsi_create_or_verify_database( install_db_url, self.config.install_database_engine_options, app=self )
+
+ if check_migrate_tools:
+ # Alert the Galaxy admin to tools that have been moved from the distribution to the tool shed.
+ from tool_shed.galaxy_install.migrate.check import verify_tools
+ verify_tools( self, install_db_url, config_file, self.config.database_engine_options )
+
+ from galaxy.model import mapping
+ self.model = mapping.init( self.config.file_path,
+ db_url,
+ self.config.database_engine_options,
+ map_install_models=combined_install_database,
+ database_query_profiling_proxy=self.config.database_query_profiling_proxy,
+ object_store=self.object_store,
+ trace_logger=getattr(self, "trace_logger", None),
+ use_pbkdf2=self.config.get_bool( 'use_pbkdf2', True ) )
+
+ if combined_install_database:
+ log.info("Install database targetting Galaxy's database configuration.")
+ self.install_model = self.model
+ else:
+ from galaxy.model.tool_shed_install import mapping as install_mapping
+ install_db_url = self.config.install_database_connection
+ log.info("Install database using its own connection %s" % install_db_url)
+ install_db_engine_options = self.config.install_database_engine_options
+ self.install_model = install_mapping.init( install_db_url,
+ install_db_engine_options )
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/datatypes/checkers.py
--- a/lib/galaxy/datatypes/checkers.py
+++ b/lib/galaxy/datatypes/checkers.py
@@ -2,6 +2,8 @@
from galaxy import util
from StringIO import StringIO
+HTML_CHECK_LINES = 100
+
try:
import Image as PIL
except ImportError:
@@ -32,9 +34,11 @@
regexp1 = re.compile( "<A\s+[^>]*HREF[^>]+>", re.I )
regexp2 = re.compile( "<IFRAME[^>]*>", re.I )
regexp3 = re.compile( "<FRAMESET[^>]*>", re.I )
- regexp4 = re.compile( "<META[^>]*>", re.I )
+ regexp4 = re.compile( "<META[\W][^>]*>", re.I )
regexp5 = re.compile( "<SCRIPT[^>]*>", re.I )
lineno = 0
+ # TODO: Potentially reading huge lines into string here, this should be
+ # reworked.
for line in temp:
lineno += 1
matches = regexp1.search( line ) or regexp2.search( line ) or regexp3.search( line ) or regexp4.search( line ) or regexp5.search( line )
@@ -42,7 +46,7 @@
if chunk is None:
temp.close()
return True
- if lineno > 100:
+ if HTML_CHECK_LINES and (lineno > HTML_CHECK_LINES):
break
if chunk is None:
temp.close()
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/datatypes/data.py
--- a/lib/galaxy/datatypes/data.py
+++ b/lib/galaxy/datatypes/data.py
@@ -199,6 +199,26 @@
out = "Can't create peek %s" % str( exc )
return out
+ def _archive_main_file(self, archive, display_name, data_filename):
+ """Called from _archive_composite_dataset to add central file to archive.
+
+ Unless subclassed, this will add the main dataset file (argument data_filename)
+ to the archive, as an HTML file with its filename derived from the dataset name
+ (argument outfname).
+
+ Returns a tuple of boolean, string, string: (error, msg, messagetype)
+ """
+ error, msg, messagetype = False, "", ""
+ archname = '%s.html' % display_name # fake the real nature of the html file
+ try:
+ archive.add(data_filename, archname)
+ except IOError:
+ error = True
+ log.exception("Unable to add composite parent %s to temporary library download archive" % data_filename)
+ msg = "Unable to create archive for download, please report this error"
+ messagetype = "error"
+ return error, msg, messagetype
+
def _archive_composite_dataset( self, trans, data=None, **kwd ):
# save a composite object into a compressed archive for downloading
params = util.Params( kwd )
@@ -237,29 +257,27 @@
path = data.file_name
fname = os.path.split(path)[-1]
efp = data.extra_files_path
- htmlname = os.path.splitext(outfname)[0]
- if not htmlname.endswith(ext):
- htmlname = '%s_%s' % (htmlname,ext)
- archname = '%s.html' % htmlname # fake the real nature of the html file
- try:
- archive.add(data.file_name,archname)
- except IOError:
- error = True
- log.exception( "Unable to add composite parent %s to temporary library download archive" % data.file_name)
- msg = "Unable to create archive for download, please report this error"
- messagetype = 'error'
- for root, dirs, files in os.walk(efp):
- for fname in files:
- fpath = os.path.join(root,fname)
- rpath = os.path.relpath(fpath,efp)
- try:
- archive.add( fpath,rpath )
- except IOError:
- error = True
- log.exception( "Unable to add %s to temporary library download archive" % rpath)
- msg = "Unable to create archive for download, please report this error"
- messagetype = 'error'
- continue
+ #Add any central file to the archive,
+
+ display_name = os.path.splitext(outfname)[0]
+ if not display_name.endswith(ext):
+ display_name = '%s_%s' % (display_name, ext)
+
+ error, msg, messagetype = self._archive_main_file(archive, display_name, path)
+ if not error:
+ #Add any child files to the archive,
+ for root, dirs, files in os.walk(efp):
+ for fname in files:
+ fpath = os.path.join(root,fname)
+ rpath = os.path.relpath(fpath,efp)
+ try:
+ archive.add( fpath,rpath )
+ except IOError:
+ error = True
+ log.exception( "Unable to add %s to temporary library download archive" % rpath)
+ msg = "Unable to create archive for download, please report this error"
+ messagetype = 'error'
+ continue
if not error:
if params.do_action == 'zip':
archive.close()
@@ -288,7 +306,14 @@
return open( dataset.file_name )
def display_data(self, trans, data, preview=False, filename=None, to_ext=None, size=None, offset=None, **kwd):
- """ Old display method, for transition """
+ """ Old display method, for transition - though still used by API and
+ test framework. Datatypes should be very careful if overridding this
+ method and this interface between datatypes and Galaxy will likely
+ change.
+
+ TOOD: Document alternatives to overridding this method (data
+ providers?).
+ """
#Relocate all composite datatype display to a common location.
composite_extensions = trans.app.datatypes_registry.get_composite_extensions( )
composite_extensions.append('html') # for archiving composite datatypes
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/datatypes/interval.py
--- a/lib/galaxy/datatypes/interval.py
+++ b/lib/galaxy/datatypes/interval.py
@@ -389,7 +389,7 @@
MetadataElement( name="endCol", default=3, desc="End column", param=metadata.ColumnParameter )
MetadataElement( name="strandCol", desc="Strand column (click box & select)", param=metadata.ColumnParameter, optional=True, no_value=0 )
MetadataElement( name="columns", default=3, desc="Number of columns", readonly=True, visible=False )
- MetadataElement( name="viz_filter_cols", desc="Score column for visualization", default=[4], param=metadata.ColumnParameter, multiple=True )
+ MetadataElement( name="viz_filter_cols", desc="Score column for visualization", default=[4], param=metadata.ColumnParameter, optional=True, multiple=True )
###do we need to repeat these? they are the same as should be inherited from interval type
def set_meta( self, dataset, overwrite = True, **kwd ):
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/datatypes/registry.py
--- a/lib/galaxy/datatypes/registry.py
+++ b/lib/galaxy/datatypes/registry.py
@@ -348,7 +348,7 @@
try:
aclass = getattr( module, datatype_class_name )()
except Exception, e:
- self.log.exception( 'Error calling method %s from class %s: %s' ( str( datatype_class_name ), str( module ), str( e ) ) )
+ self.log.exception( 'Error calling method %s from class %s: %s', str( datatype_class_name ), str( module ), str( e ) )
ok = False
if ok:
if deactivate:
@@ -598,6 +598,9 @@
tool_xml_text = """
<tool id="__SET_METADATA__" name="Set External Metadata" version="1.0.1" tool_type="set_metadata"><type class="SetMetadataTool" module="galaxy.tools"/>
+ <requirements>
+ <requirement type="package">samtools</requirement>
+ </requirements><action module="galaxy.tools.actions.metadata" class="SetMetadataToolAction"/><command>$__SET_EXTERNAL_METADATA_COMMAND_LINE__</command><inputs>
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/datatypes/tabular.py
--- a/lib/galaxy/datatypes/tabular.py
+++ b/lib/galaxy/datatypes/tabular.py
@@ -652,7 +652,7 @@
MetadataElement( name="columns", default=10, desc="Number of columns", readonly=True, visible=False )
MetadataElement( name="column_types", default=['str','int','str','str','str','int','str','list','str','str'], param=metadata.ColumnTypesParameter, desc="Column types", readonly=True, visible=False )
- MetadataElement( name="viz_filter_cols", desc="Score column for visualization", default=[5], param=metadata.ColumnParameter, multiple=True, visible=False )
+ MetadataElement( name="viz_filter_cols", desc="Score column for visualization", default=[5], param=metadata.ColumnParameter, optional=True, multiple=True, visible=False )
MetadataElement( name="sample_names", default=[], desc="Sample names", readonly=True, visible=False, optional=True, no_value=[] )
def sniff( self, filename ):
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/exceptions/__init__.py
--- a/lib/galaxy/exceptions/__init__.py
+++ b/lib/galaxy/exceptions/__init__.py
@@ -1,6 +1,10 @@
"""
Custom exceptions for Galaxy
"""
+
+from galaxy import eggs
+eggs.require( "Paste" )
+
from paste import httpexceptions
class MessageException( Exception ):
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/jobs/__init__.py
--- a/lib/galaxy/jobs/__init__.py
+++ b/lib/galaxy/jobs/__init__.py
@@ -14,7 +14,6 @@
import shutil
import subprocess
import sys
-import threading
import traceback
from galaxy import model, util
from galaxy.datatypes import metadata
@@ -39,21 +38,6 @@
# and should eventually become API'd
TOOL_PROVIDED_JOB_METADATA_FILE = 'galaxy.json'
-class Sleeper( object ):
- """
- Provides a 'sleep' method that sleeps for a number of seconds *unless*
- the notify method is called (from a different thread).
- """
- def __init__( self ):
- self.condition = threading.Condition()
- def sleep( self, seconds ):
- self.condition.acquire()
- self.condition.wait( seconds )
- self.condition.release()
- def wake( self ):
- self.condition.acquire()
- self.condition.notify()
- self.condition.release()
class JobDestination( Bunch ):
"""
@@ -704,10 +688,7 @@
if self.command_line and self.command_line.startswith( 'python' ):
self.galaxy_lib_dir = os.path.abspath( "lib" ) # cwd = galaxy root
# Shell fragment to inject dependencies
- if self.app.config.use_tool_dependencies:
- self.dependency_shell_commands = self.tool.build_dependency_shell_commands()
- else:
- self.dependency_shell_commands = None
+ self.dependency_shell_commands = self.tool.build_dependency_shell_commands()
# We need command_line persisted to the db in order for Galaxy to re-queue the job
# if the server was stopped and restarted before the job finished
job.command_line = self.command_line
@@ -1451,10 +1432,7 @@
if self.command_line and self.command_line.startswith( 'python' ):
self.galaxy_lib_dir = os.path.abspath( "lib" ) # cwd = galaxy root
# Shell fragment to inject dependencies
- if self.app.config.use_tool_dependencies:
- self.dependency_shell_commands = self.tool.build_dependency_shell_commands()
- else:
- self.dependency_shell_commands = None
+ self.dependency_shell_commands = self.tool.build_dependency_shell_commands()
# We need command_line persisted to the db in order for Galaxy to re-queue the job
# if the server was stopped and restarted before the job finished
task.command_line = self.command_line
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/jobs/actions/post.py
--- a/lib/galaxy/jobs/actions/post.py
+++ b/lib/galaxy/jobs/actions/post.py
@@ -12,7 +12,7 @@
form = """
if (pja.action_type == "%s"){
p_str = "<div class='pjaForm toolForm'><span class='action_tag' style='display:none'>"+ pja.action_type + pja.output_name + "</span><div class='toolFormTitle'> %s <br/> on " + pja.output_name + "\
- <div style='float: right;' class='buttons'><img src='/static/images/delete_icon.png'></div></div><div class='toolFormBody'>";
+ <div style='float: right;' class='buttons'><img src='/static/images/history-buttons/delete_icon.png'></div></div><div class='toolFormBody'>";
%s
p_str += "</div><div class='toolParamHelp'>%s</div></div>";
}""" % (action_type, title, content, help)
@@ -20,7 +20,7 @@
form = """
if (pja.action_type == "%s"){
p_str = "<div class='pjaForm toolForm'><span class='action_tag' style='display:none'>"+ pja.action_type + "</span><div class='toolFormTitle'> %s \
- <div style='float: right;' class='buttons'><img src='/static/images/delete_icon.png'></div></div><div class='toolFormBody'>";
+ <div style='float: right;' class='buttons'><img src='/static/images/history-buttons/delete_icon.png'></div></div><div class='toolFormBody'>";
%s
p_str += "</div><div class='toolParamHelp'>%s</div></div>";
}""" % (action_type, title, content, help)
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/jobs/command_factory.py
--- a/lib/galaxy/jobs/command_factory.py
+++ b/lib/galaxy/jobs/command_factory.py
@@ -1,8 +1,11 @@
from os import getcwd
from os.path import abspath
+CAPTURE_RETURN_CODE = "return_code=$?"
+YIELD_CAPTURED_CODE = 'sh -c "exit $return_code"'
-def build_command( job, job_wrapper, include_metadata=False, include_work_dir_outputs=True ):
+
+def build_command( runner, job_wrapper, include_metadata=False, include_work_dir_outputs=True, remote_command_params={} ):
"""
Compose the sequence of commands necessary to execute a job. This will
currently include:
@@ -13,64 +16,125 @@
- commands to set metadata (if include_metadata is True)
"""
- commands = job_wrapper.get_command_line()
+ commands_builder = CommandsBuilder(job_wrapper.get_command_line())
# All job runners currently handle this case which should never occur
- if not commands:
+ if not commands_builder.commands:
return None
- # Remove trailing semi-colon so we can start hacking up this command.
- # TODO: Refactor to compose a list and join with ';', would be more clean.
- commands = commands.rstrip("; ")
+ __handle_version_command(commands_builder, job_wrapper)
+ __handle_task_splitting(commands_builder, job_wrapper)
+ __handle_dependency_resolution(commands_builder, job_wrapper, remote_command_params)
+ if include_work_dir_outputs:
+ __handle_work_dir_outputs(commands_builder, job_wrapper, runner, remote_command_params)
+
+ if include_metadata and job_wrapper.requires_setting_metadata:
+ __handle_metadata(commands_builder, job_wrapper, runner, remote_command_params)
+
+ return commands_builder.build()
+
+
+def __handle_version_command(commands_builder, job_wrapper):
# Prepend version string
if job_wrapper.version_string_cmd:
- commands = "%s &> %s; " % ( job_wrapper.version_string_cmd, job_wrapper.get_version_string_path() ) + commands
+ version_command = "%s &> %s" % ( job_wrapper.version_string_cmd, job_wrapper.get_version_string_path() )
+ commands_builder.prepend_command(version_command)
+
+def __handle_task_splitting(commands_builder, job_wrapper):
# prepend getting input files (if defined)
- if hasattr(job_wrapper, 'prepare_input_files_cmds') and job_wrapper.prepare_input_files_cmds is not None:
- commands = "; ".join( job_wrapper.prepare_input_files_cmds + [ commands ] )
+ if getattr(job_wrapper, 'prepare_input_files_cmds', None):
+ commands_builder.prepend_commands(job_wrapper.prepare_input_files_cmds)
+
+
+def __handle_dependency_resolution(commands_builder, job_wrapper, remote_command_params):
+ local_dependency_resolution = remote_command_params.get("dependency_resolution", "local") == "local"
# Prepend dependency injection
- if job_wrapper.dependency_shell_commands:
- commands = "; ".join( job_wrapper.dependency_shell_commands + [ commands ] )
+ if job_wrapper.dependency_shell_commands and local_dependency_resolution:
+ commands_builder.prepend_commands(job_wrapper.dependency_shell_commands)
- # Coping work dir outputs or setting metadata will mask return code of
- # tool command. If these are used capture the return code and ensure
- # the last thing that happens is an exit with return code.
- capture_return_code_command = "; return_code=$?"
- captured_return_code = False
+def __handle_work_dir_outputs(commands_builder, job_wrapper, runner, remote_command_params):
# Append commands to copy job outputs based on from_work_dir attribute.
- if include_work_dir_outputs:
- work_dir_outputs = job.get_work_dir_outputs( job_wrapper )
- if work_dir_outputs:
- if not captured_return_code:
- commands += capture_return_code_command
- captured_return_code = True
+ work_dir_outputs_kwds = {}
+ if 'working_directory' in remote_command_params:
+ work_dir_outputs_kwds['job_working_directory'] = remote_command_params['working_directory']
+ work_dir_outputs = runner.get_work_dir_outputs( job_wrapper, **work_dir_outputs_kwds )
+ if work_dir_outputs:
+ commands_builder.capture_return_code()
+ copy_commands = map(__copy_if_exists_command, work_dir_outputs)
+ commands_builder.append_commands(copy_commands)
- commands += "; " + "; ".join( [ "if [ -f %s ] ; then cp %s %s ; fi" %
- ( source_file, source_file, destination ) for ( source_file, destination ) in work_dir_outputs ] )
+def __handle_metadata(commands_builder, job_wrapper, runner, remote_command_params):
# Append metadata setting commands, we don't want to overwrite metadata
# that was copied over in init_meta(), as per established behavior
- if include_metadata and job_wrapper.requires_setting_metadata:
- metadata_command = job_wrapper.setup_external_metadata(
- exec_dir=abspath( getcwd() ),
- tmp_dir=job_wrapper.working_directory,
- dataset_files_path=job.app.model.Dataset.file_path,
- output_fnames=job_wrapper.get_output_fnames(),
- set_extension=False,
- kwds={ 'overwrite' : False }
- ) or ''
- metadata_command = metadata_command.strip()
- if metadata_command:
- if not captured_return_code:
- commands += capture_return_code_command
- captured_return_code = True
- commands += "; cd %s; %s" % (abspath( getcwd() ), metadata_command)
+ metadata_kwds = remote_command_params.get('metadata_kwds', {})
+ exec_dir = metadata_kwds.get( 'exec_dir', abspath( getcwd() ) )
+ tmp_dir = metadata_kwds.get( 'tmp_dir', job_wrapper.working_directory )
+ dataset_files_path = metadata_kwds.get( 'dataset_files_path', runner.app.model.Dataset.file_path )
+ output_fnames = metadata_kwds.get( 'output_fnames', job_wrapper.get_output_fnames() )
+ config_root = metadata_kwds.get( 'config_root', None )
+ config_file = metadata_kwds.get( 'config_file', None )
+ datatypes_config = metadata_kwds.get( 'datatypes_config', None )
+ metadata_command = job_wrapper.setup_external_metadata(
+ exec_dir=exec_dir,
+ tmp_dir=tmp_dir,
+ dataset_files_path=dataset_files_path,
+ output_fnames=output_fnames,
+ set_extension=False,
+ config_root=config_root,
+ config_file=config_file,
+ datatypes_config=datatypes_config,
+ kwds={ 'overwrite' : False }
+ ) or ''
+ metadata_command = metadata_command.strip()
+ if metadata_command:
+ commands_builder.capture_return_code()
+ commands_builder.append_command("cd %s; %s" % (exec_dir, metadata_command))
- if captured_return_code:
- commands += '; sh -c "exit $return_code"'
- return commands
+def __copy_if_exists_command(work_dir_output):
+ source_file, destination = work_dir_output
+ return "if [ -f %s ] ; then cp %s %s ; fi" % ( source_file, source_file, destination )
+
+
+class CommandsBuilder(object):
+
+ def __init__(self, initial_command):
+ # Remove trailing semi-colon so we can start hacking up this command.
+ # TODO: Refactor to compose a list and join with ';', would be more clean.
+ commands = initial_command.rstrip("; ")
+ self.commands = commands
+
+ # Coping work dir outputs or setting metadata will mask return code of
+ # tool command. If these are used capture the return code and ensure
+ # the last thing that happens is an exit with return code.
+ self.return_code_captured = False
+
+ def prepend_command(self, command):
+ self.commands = "%s; %s" % (command, self.commands)
+ return self
+
+ def prepend_commands(self, commands):
+ return self.prepend_command("; ".join(commands))
+
+ def append_command(self, command):
+ self.commands = "%s; %s" % (self.commands, command)
+
+ def append_commands(self, commands):
+ self.append_command("; ".join(commands))
+
+ def capture_return_code(self):
+ if not self.return_code_captured:
+ self.return_code_captured = True
+ self.append_command(CAPTURE_RETURN_CODE)
+
+ def build(self):
+ if self.return_code_captured:
+ self.append_command(YIELD_CAPTURED_CODE)
+ return self.commands
+
+__all__ = [build_command]
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/jobs/handler.py
--- a/lib/galaxy/jobs/handler.py
+++ b/lib/galaxy/jobs/handler.py
@@ -11,7 +11,8 @@
from sqlalchemy.sql.expression import and_, or_, select, func
from galaxy import model
-from galaxy.jobs import Sleeper, JobWrapper, TaskWrapper, JobDestination
+from galaxy.util.sleeper import Sleeper
+from galaxy.jobs import JobWrapper, TaskWrapper, JobDestination
log = logging.getLogger( __name__ )
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/jobs/manager.py
--- a/lib/galaxy/jobs/manager.py
+++ b/lib/galaxy/jobs/manager.py
@@ -11,7 +11,8 @@
from Queue import Empty, Queue
from galaxy import model
-from galaxy.jobs import handler, JobWrapper, NoopQueue, Sleeper
+from galaxy.util.sleeper import Sleeper
+from galaxy.jobs import handler, JobWrapper, NoopQueue
from galaxy.util.json import from_json_string
log = logging.getLogger( __name__ )
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/jobs/runners/__init__.py
--- a/lib/galaxy/jobs/runners/__init__.py
+++ b/lib/galaxy/jobs/runners/__init__.py
@@ -146,11 +146,13 @@
def build_command_line( self, job_wrapper, include_metadata=False, include_work_dir_outputs=True ):
return build_command( self, job_wrapper, include_metadata=include_metadata, include_work_dir_outputs=include_work_dir_outputs )
- def get_work_dir_outputs( self, job_wrapper ):
+ def get_work_dir_outputs( self, job_wrapper, job_working_directory=None ):
"""
Returns list of pairs (source_file, destination) describing path
to work_dir output file and ultimate destination.
"""
+ if not job_working_directory:
+ job_working_directory = os.path.abspath( job_wrapper.working_directory )
def in_directory( file, directory ):
"""
@@ -186,7 +188,7 @@
if hda_tool_output and hda_tool_output.from_work_dir:
# Copy from working dir to HDA.
# TODO: move instead of copy to save time?
- source_file = os.path.join( os.path.abspath( job_wrapper.working_directory ), hda_tool_output.from_work_dir )
+ source_file = os.path.join( job_working_directory, hda_tool_output.from_work_dir )
destination = job_wrapper.get_output_destination( output_paths[ dataset.dataset_id ] )
if in_directory( source_file, job_wrapper.working_directory ):
output_pairs.append( ( source_file, destination ) )
@@ -196,7 +198,7 @@
log.exception( "from_work_dir specified a location not in the working directory: %s, %s" % ( source_file, job_wrapper.working_directory ) )
return output_pairs
- def _handle_metadata_externally(self, job_wrapper):
+ def _handle_metadata_externally( self, job_wrapper, resolve_requirements=False ):
"""
Set metadata externally. Used by the local and lwr job runners where this
shouldn't be attached to command-line to execute.
@@ -210,6 +212,12 @@
tmp_dir=job_wrapper.working_directory,
#we don't want to overwrite metadata that was copied over in init_meta(), as per established behavior
kwds={ 'overwrite' : False } )
+ if resolve_requirements:
+ dependency_shell_commands = self.app.datatypes_registry.set_external_metadata_tool.build_dependency_shell_commands()
+ if dependency_shell_commands:
+ if isinstance( dependency_shell_commands, list ):
+ dependency_shell_commands = "&&".join( dependency_shell_commands )
+ external_metadata_script = "%s&&%s" % ( dependency_shell_commands, external_metadata_script )
log.debug( 'executing external set_meta script for job %d: %s' % ( job_wrapper.job_id, external_metadata_script ) )
external_metadata_proc = subprocess.Popen( args=external_metadata_script,
shell=True,
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/jobs/runners/cli_job/torque.py
--- a/lib/galaxy/jobs/runners/cli_job/torque.py
+++ b/lib/galaxy/jobs/runners/cli_job/torque.py
@@ -35,7 +35,8 @@
echo $? > %s
"""
-argmap = { 'Execution_Time' : '-a',
+argmap = { 'destination' : '-q',
+ 'Execution_Time' : '-a',
'Account_Name' : '-A',
'Checkpoint' : '-c',
'Error_Path' : '-e',
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/jobs/runners/drmaa.py
--- a/lib/galaxy/jobs/runners/drmaa.py
+++ b/lib/galaxy/jobs/runners/drmaa.py
@@ -302,7 +302,15 @@
The external script will be run with sudo, and will setuid() to the specified user.
Effectively, will QSUB as a different user (then the one used by Galaxy).
"""
- p = subprocess.Popen([ '/usr/bin/sudo', '-E', self.external_runJob_script, str(username), jobtemplate_filename ],
+ script_parts = self.external_runJob_script.split()
+ script = script_parts[0]
+ command = [ '/usr/bin/sudo', '-E', script]
+ for script_argument in script_parts[1:]:
+ command.append(script_argument)
+
+ command.extend( [ str(username), jobtemplate_filename ] )
+ log.info("Running command %s" % command)
+ p = subprocess.Popen(command,
shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(stdoutdata, stderrdata) = p.communicate()
exitcode = p.returncode
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/jobs/runners/local.py
--- a/lib/galaxy/jobs/runners/local.py
+++ b/lib/galaxy/jobs/runners/local.py
@@ -110,7 +110,7 @@
job_wrapper.fail( "failure running job", exception=True )
log.exception("failure running job %d" % job_wrapper.job_id)
return
- self._handle_metadata_externally( job_wrapper )
+ self._handle_metadata_externally( job_wrapper, resolve_requirements=True )
# Finish the job!
try:
job_wrapper.finish( stdout, stderr, exit_code )
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/jobs/runners/lwr.py
--- a/lib/galaxy/jobs/runners/lwr.py
+++ b/lib/galaxy/jobs/runners/lwr.py
@@ -3,7 +3,9 @@
from galaxy import model
from galaxy.jobs.runners import AsynchronousJobState, AsynchronousJobRunner
from galaxy.jobs import JobDestination
+from galaxy.jobs.command_factory import build_command
from galaxy.util import string_as_bool_or_none
+from galaxy.util.bunch import Bunch
import errno
from time import sleep
@@ -12,11 +14,15 @@
from .lwr_client import ClientManager, url_to_destination_params
from .lwr_client import finish_job as lwr_finish_job
from .lwr_client import submit_job as lwr_submit_job
+from .lwr_client import ClientJobDescription
log = logging.getLogger( __name__ )
__all__ = [ 'LwrJobRunner' ]
+NO_REMOTE_GALAXY_FOR_METADATA_MESSAGE = "LWR misconfiguration - LWR client configured to set metadata remotely, but remote LWR isn't properly configured with a galaxy_home directory."
+NO_REMOTE_DATATYPES_CONFIG = "LWR client is configured to use remote datatypes configuration when setting metadata externally, but LWR is not configured with this information. Defaulting to datatypes_conf.xml."
+
class LwrJobRunner( AsynchronousJobRunner ):
"""
@@ -54,40 +60,31 @@
return job_state
def queue_job(self, job_wrapper):
- command_line = ''
job_destination = job_wrapper.job_destination
- try:
- job_wrapper.prepare()
- if hasattr(job_wrapper, 'prepare_input_files_cmds') and job_wrapper.prepare_input_files_cmds is not None:
- for cmd in job_wrapper.prepare_input_files_cmds: # run the commands to stage the input files
- #log.debug( 'executing: %s' % cmd )
- if 0 != os.system(cmd):
- raise Exception('Error running file staging command: %s' % cmd)
- job_wrapper.prepare_input_files_cmds = None # prevent them from being used in-line
- command_line = self.build_command_line( job_wrapper, include_metadata=False, include_work_dir_outputs=False )
- except:
- job_wrapper.fail( "failure preparing job", exception=True )
- log.exception("failure running job %d" % job_wrapper.job_id)
- return
+ command_line, client, remote_job_config = self.__prepare_job( job_wrapper, job_destination )
- # If we were able to get a command line, run the job
if not command_line:
- job_wrapper.finish( '', '' )
return
try:
- client = self.get_client_from_wrapper(job_wrapper)
- output_files = self.get_output_files(job_wrapper)
- input_files = job_wrapper.get_input_fnames()
- working_directory = job_wrapper.working_directory
- tool = job_wrapper.tool
- config_files = job_wrapper.extra_filenames
- job_id = lwr_submit_job(client, tool, command_line, config_files, input_files, output_files, working_directory)
+ dependency_resolution = LwrJobRunner.__dependency_resolution( client )
+ remote_dependency_resolution = dependency_resolution == "remote"
+ requirements = job_wrapper.tool.requirements if remote_dependency_resolution else []
+ client_job_description = ClientJobDescription(
+ command_line=command_line,
+ output_files=self.get_output_files(job_wrapper),
+ input_files=job_wrapper.get_input_fnames(),
+ working_directory=job_wrapper.working_directory,
+ tool=job_wrapper.tool,
+ config_files=job_wrapper.extra_filenames,
+ requirements=requirements,
+ )
+ job_id = lwr_submit_job(client, client_job_description, remote_job_config)
log.info("lwr job submitted with job_id %s" % job_id)
job_wrapper.set_job_destination( job_destination, job_id )
job_wrapper.change_state( model.Job.states.QUEUED )
- except:
+ except Exception:
job_wrapper.fail( "failure running job", exception=True )
log.exception("failure running job %d" % job_wrapper.job_id)
return
@@ -100,6 +97,52 @@
lwr_job_state.job_destination = job_destination
self.monitor_job(lwr_job_state)
+ def __prepare_job(self, job_wrapper, job_destination):
+ """ Build command-line and LWR client for this job. """
+ command_line = None
+ client = None
+ remote_job_config = None
+ try:
+ job_wrapper.prepare()
+ self.__prepare_input_files_locally(job_wrapper)
+ client = self.get_client_from_wrapper(job_wrapper)
+ tool = job_wrapper.tool
+ remote_job_config = client.setup(tool.id, tool.version)
+ remote_metadata = LwrJobRunner.__remote_metadata( client )
+ remote_work_dir_copy = LwrJobRunner.__remote_work_dir_copy( client )
+ dependency_resolution = LwrJobRunner.__dependency_resolution( client )
+ metadata_kwds = self.__build_metadata_configuration(client, job_wrapper, remote_metadata, remote_job_config)
+ remote_command_params = dict(
+ working_directory=remote_job_config['working_directory'],
+ metadata_kwds=metadata_kwds,
+ dependency_resolution=dependency_resolution,
+ )
+ command_line = build_command(
+ self,
+ job_wrapper=job_wrapper,
+ include_metadata=remote_metadata,
+ include_work_dir_outputs=remote_work_dir_copy,
+ remote_command_params=remote_command_params,
+ )
+ except Exception:
+ job_wrapper.fail( "failure preparing job", exception=True )
+ log.exception("failure running job %d" % job_wrapper.job_id)
+
+ # If we were able to get a command line, run the job
+ if not command_line:
+ job_wrapper.finish( '', '' )
+
+ return command_line, client, remote_job_config
+
+ def __prepare_input_files_locally(self, job_wrapper):
+ """Run task splitting commands locally."""
+ prepare_input_files_cmds = getattr(job_wrapper, 'prepare_input_files_cmds', None)
+ if prepare_input_files_cmds is not None:
+ for cmd in prepare_input_files_cmds: # run the commands to stage the input files
+ if 0 != os.system(cmd):
+ raise Exception('Error running file staging command: %s' % cmd)
+ job_wrapper.prepare_input_files_cmds = None # prevent them from being used in-line
+
def get_output_files(self, job_wrapper):
output_fnames = job_wrapper.get_output_fnames()
return [ str( o ) for o in output_fnames ]
@@ -130,34 +173,42 @@
run_results = client.raw_check_complete()
stdout = run_results.get('stdout', '')
stderr = run_results.get('stderr', '')
-
+ working_directory_contents = run_results.get('working_directory_contents', [])
# Use LWR client code to transfer/copy files back
# and cleanup job if needed.
completed_normally = \
job_wrapper.get_state() not in [ model.Job.states.ERROR, model.Job.states.DELETED ]
cleanup_job = self.app.config.cleanup_job
- work_dir_outputs = self.get_work_dir_outputs( job_wrapper )
+ remote_work_dir_copy = LwrJobRunner.__remote_work_dir_copy( client )
+ if not remote_work_dir_copy:
+ work_dir_outputs = self.get_work_dir_outputs( job_wrapper )
+ else:
+ # They have already been copied over to look like regular outputs remotely,
+ # no need to handle them differently here.
+ work_dir_outputs = []
output_files = self.get_output_files( job_wrapper )
finish_args = dict( client=client,
working_directory=job_wrapper.working_directory,
job_completed_normally=completed_normally,
cleanup_job=cleanup_job,
work_dir_outputs=work_dir_outputs,
- output_files=output_files )
+ output_files=output_files,
+ working_directory_contents=working_directory_contents )
failed = lwr_finish_job( **finish_args )
if failed:
job_wrapper.fail("Failed to find or download one or more job outputs from remote server.", exception=True)
- except:
+ except Exception:
message = "Failed to communicate with remote job server."
job_wrapper.fail( message, exception=True )
log.exception("failure running job %d" % job_wrapper.job_id)
return
- self._handle_metadata_externally( job_wrapper )
+ if not LwrJobRunner.__remote_metadata( client ):
+ self._handle_metadata_externally( job_wrapper, resolve_requirements=True )
# Finish the job
try:
job_wrapper.finish( stdout, stderr )
- except:
+ except Exception:
log.exception("Job wrapper finish method failed")
job_wrapper.fail("Unable to finish job", exception=True)
@@ -225,3 +276,71 @@
job_state.old_state = True
job_state.running = state == model.Job.states.RUNNING
self.monitor_queue.put( job_state )
+
+ @staticmethod
+ def __dependency_resolution( lwr_client ):
+ dependency_resolution = lwr_client.destination_params.get( "dependency_resolution", "local" )
+ if dependency_resolution not in ["none", "local", "remote"]:
+ raise Exception("Unknown dependency_resolution value encountered %s" % dependency_resolution)
+ return dependency_resolution
+
+ @staticmethod
+ def __remote_metadata( lwr_client ):
+ remote_metadata = string_as_bool_or_none( lwr_client.destination_params.get( "remote_metadata", False ) )
+ return remote_metadata
+
+ @staticmethod
+ def __remote_work_dir_copy( lwr_client ):
+ # Right now remote metadata handling assumes from_work_dir outputs
+ # have been copied over before it runs. So do that remotely. This is
+ # not the default though because adding it to the command line is not
+ # cross-platform (no cp on Windows) and its un-needed work outside
+ # the context of metadata settting (just as easy to download from
+ # either place.)
+ return LwrJobRunner.__remote_metadata( lwr_client )
+
+ @staticmethod
+ def __use_remote_datatypes_conf( lwr_client ):
+ """ When setting remote metadata, use integrated datatypes from this
+ Galaxy instance or use the datatypes config configured via the remote
+ LWR.
+
+ Both options are broken in different ways for same reason - datatypes
+ may not match. One can push the local datatypes config to the remote
+ server - but there is no guarentee these datatypes will be defined
+ there. Alternatively, one can use the remote datatype config - but
+ there is no guarentee that it will contain all the datatypes available
+ to this Galaxy.
+ """
+ use_remote_datatypes = string_as_bool_or_none( lwr_client.destination_params.get( "use_remote_datatypes", False ) )
+ return use_remote_datatypes
+
+ def __build_metadata_configuration(self, client, job_wrapper, remote_metadata, remote_job_config):
+ metadata_kwds = {}
+ if remote_metadata:
+ remote_system_properties = remote_job_config.get("system_properties", {})
+ remote_galaxy_home = remote_system_properties.get("galaxy_home", None)
+ if not remote_galaxy_home:
+ raise Exception(NO_REMOTE_GALAXY_FOR_METADATA_MESSAGE)
+ metadata_kwds['exec_dir'] = remote_galaxy_home
+ outputs_directory = remote_job_config['outputs_directory']
+ configs_directory = remote_job_config['configs_directory']
+ outputs = [Bunch(false_path=os.path.join(outputs_directory, os.path.basename(path)), real_path=path) for path in self.get_output_files(job_wrapper)]
+ metadata_kwds['output_fnames'] = outputs
+ metadata_kwds['config_root'] = remote_galaxy_home
+ default_config_file = os.path.join(remote_galaxy_home, 'universe_wsgi.ini')
+ metadata_kwds['config_file'] = remote_system_properties.get('galaxy_config_file', default_config_file)
+ metadata_kwds['dataset_files_path'] = remote_system_properties.get('galaxy_dataset_files_path', None)
+ if LwrJobRunner.__use_remote_datatypes_conf( client ):
+ remote_datatypes_config = remote_system_properties.get('galaxy_datatypes_config_file', None)
+ if not remote_datatypes_config:
+ log.warn(NO_REMOTE_DATATYPES_CONFIG)
+ remote_datatypes_config = os.path.join(remote_galaxy_home, 'datatypes_conf.xml')
+ metadata_kwds['datatypes_config'] = remote_datatypes_config
+ else:
+ integrates_datatypes_config = self.app.datatypes_registry.integrated_datatypes_configs
+ # Ensure this file gets pushed out to the remote config dir.
+ job_wrapper.extra_filenames.append(integrates_datatypes_config)
+
+ metadata_kwds['datatypes_config'] = os.path.join(configs_directory, os.path.basename(integrates_datatypes_config))
+ return metadata_kwds
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/jobs/runners/lwr_client/__init__.py
--- a/lib/galaxy/jobs/runners/lwr_client/__init__.py
+++ b/lib/galaxy/jobs/runners/lwr_client/__init__.py
@@ -6,9 +6,9 @@
"""
-from .stager import submit_job, finish_job
+from .stager import submit_job, finish_job, ClientJobDescription
from .client import OutputNotFoundException
from .manager import ClientManager
from .destination import url_to_destination_params
-__all__ = [ClientManager, OutputNotFoundException, url_to_destination_params, finish_job, submit_job]
+__all__ = [ClientManager, OutputNotFoundException, url_to_destination_params, finish_job, submit_job, ClientJobDescription]
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/jobs/runners/lwr_client/action_mapper.py
--- a/lib/galaxy/jobs/runners/lwr_client/action_mapper.py
+++ b/lib/galaxy/jobs/runners/lwr_client/action_mapper.py
@@ -21,7 +21,7 @@
>>> from tempfile import NamedTemporaryFile
>>> from os import unlink
>>> f = NamedTemporaryFile(delete=False)
- >>> f.write(json_string)
+ >>> write_result = f.write(json_string.encode('UTF-8'))
>>> f.close()
>>> class MockClient():
... default_file_action = 'none'
@@ -30,23 +30,23 @@
>>> mapper = FileActionMapper(MockClient())
>>> unlink(f.name)
>>> # Test first config line above, implicit path prefix mapper
- >>> mapper.action('/opt/galaxy/tools/filters/catWrapper.py', 'input')
- ('none',)
+ >>> mapper.action('/opt/galaxy/tools/filters/catWrapper.py', 'input')[0] == u'none'
+ True
>>> # Test another (2nd) mapper, this one with a different action
- >>> mapper.action('/galaxy/data/files/000/dataset_1.dat', 'input')
- ('transfer',)
+ >>> mapper.action('/galaxy/data/files/000/dataset_1.dat', 'input')[0] == u'transfer'
+ True
>>> # Always at least copy work_dir outputs.
- >>> mapper.action('/opt/galaxy/database/working_directory/45.sh', 'work_dir')
- ('copy',)
+ >>> mapper.action('/opt/galaxy/database/working_directory/45.sh', 'work_dir')[0] == u'copy'
+ True
>>> # Test glob mapper (matching test)
- >>> mapper.action('/cool/bamfiles/projectABC/study1/patient3.bam', 'input')
- ('copy',)
+ >>> mapper.action('/cool/bamfiles/projectABC/study1/patient3.bam', 'input')[0] == u'copy'
+ True
>>> # Test glob mapper (non-matching test)
- >>> mapper.action('/cool/bamfiles/projectABC/study1/patient3.bam.bai', 'input')
- ('none',)
+ >>> mapper.action('/cool/bamfiles/projectABC/study1/patient3.bam.bai', 'input')[0] == u'none'
+ True
>>> # Regex mapper test.
- >>> mapper.action('/old/galaxy/data/dataset_10245.dat', 'input')
- ('copy',)
+ >>> mapper.action('/old/galaxy/data/dataset_10245.dat', 'input')[0] == u'copy'
+ True
"""
def __init__(self, client):
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/jobs/runners/lwr_client/client.py
--- a/lib/galaxy/jobs/runners/lwr_client/client.py
+++ b/lib/galaxy/jobs/runners/lwr_client/client.py
@@ -50,7 +50,7 @@
return "No remote output found for path %s" % self.path
-class Client(object):
+class JobClient(object):
"""
Objects of this client class perform low-level communication with a remote LWR server.
@@ -161,25 +161,23 @@
raise Exception("Unknown output_type returned from LWR server %s" % output_type)
return output_path
- def fetch_work_dir_output(self, source, working_directory, output_path, action='transfer'):
+ def fetch_work_dir_output(self, name, working_directory, output_path, action='transfer'):
"""
Download an output dataset specified with from_work_dir from the
remote server.
**Parameters**
- source : str
+ name : str
Path in job's working_directory to find output in.
working_directory : str
Local working_directory for the job.
output_path : str
Full path to output dataset.
"""
- output = open(output_path, "wb")
- name = os.path.basename(source)
if action == 'transfer':
- self.__raw_download_output(name, self.job_id, "work_dir", output)
- elif action == 'copy':
+ self.__raw_download_output(name, self.job_id, "work_dir", output_path)
+ else: # Even if action is none - LWR has a different work_dir so this needs to be copied.
lwr_path = self._output_path(name, self.job_id, 'work_dir')['path']
self._copy(lwr_path, output_path)
@@ -199,7 +197,7 @@
}
self._raw_execute("download_output", output_params, output_path=output_path)
- def launch(self, command_line):
+ def launch(self, command_line, requirements=[]):
"""
Run or queue up the execution of the supplied
`command_line` on the remote server.
@@ -213,6 +211,8 @@
submit_params = self._submit_params
if submit_params:
launch_params['params'] = dumps(submit_params)
+ if requirements:
+ launch_params['requirements'] = dumps([requirement.to_dict() for requirement in requirements])
return self._raw_execute("launch", launch_params)
def kill(self):
@@ -285,13 +285,13 @@
shutil.copyfile(source, destination)
-class InputCachingClient(Client):
+class InputCachingJobClient(JobClient):
"""
Beta client that cache's staged files to prevent duplication.
"""
def __init__(self, destination_params, job_id, job_manager_interface, client_cacher):
- super(InputCachingClient, self).__init__(destination_params, job_id, job_manager_interface)
+ super(InputCachingJobClient, self).__init__(destination_params, job_id, job_manager_interface)
self.client_cacher = client_cacher
@parseJson()
@@ -326,3 +326,55 @@
@parseJson()
def file_available(self, path):
return self._raw_execute("file_available", {"path": path})
+
+
+class ObjectStoreClient(object):
+
+ def __init__(self, lwr_interface):
+ self.lwr_interface = lwr_interface
+
+ @parseJson()
+ def exists(self, **kwds):
+ return self._raw_execute("object_store_exists", args=self.__data(**kwds))
+
+ @parseJson()
+ def file_ready(self, **kwds):
+ return self._raw_execute("object_store_file_ready", args=self.__data(**kwds))
+
+ @parseJson()
+ def create(self, **kwds):
+ return self._raw_execute("object_store_create", args=self.__data(**kwds))
+
+ @parseJson()
+ def empty(self, **kwds):
+ return self._raw_execute("object_store_empty", args=self.__data(**kwds))
+
+ @parseJson()
+ def size(self, **kwds):
+ return self._raw_execute("object_store_size", args=self.__data(**kwds))
+
+ @parseJson()
+ def delete(self, **kwds):
+ return self._raw_execute("object_store_delete", args=self.__data(**kwds))
+
+ @parseJson()
+ def get_data(self, **kwds):
+ return self._raw_execute("object_store_get_data", args=self.__data(**kwds))
+
+ @parseJson()
+ def get_filename(self, **kwds):
+ return self._raw_execute("object_store_get_filename", args=self.__data(**kwds))
+
+ @parseJson()
+ def update_from_file(self, **kwds):
+ return self._raw_execute("object_store_update_from_file", args=self.__data(**kwds))
+
+ @parseJson()
+ def get_store_usage_percent(self):
+ return self._raw_execute("object_store_get_store_usage_percent", args={})
+
+ def __data(self, **kwds):
+ return kwds
+
+ def _raw_execute(self, command, args={}):
+ return self.lwr_interface.execute(command, args, data=None, input_path=None, output_path=None)
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/jobs/runners/lwr_client/destination.py
--- a/lib/galaxy/jobs/runners/lwr_client/destination.py
+++ b/lib/galaxy/jobs/runners/lwr_client/destination.py
@@ -51,9 +51,10 @@
>>> destination_params = {"private_token": "12345", "submit_native_specification": "-q batch"}
>>> result = submit_params(destination_params)
- >>> result.items()
- [('native_specification', '-q batch')]
+ >>> result
+ {'native_specification': '-q batch'}
"""
- return dict([(key[len(SUBMIT_PREFIX):], value)
- for key, value in (destination_params or {}).iteritems()
+ destination_params = destination_params or {}
+ return dict([(key[len(SUBMIT_PREFIX):], destination_params[key])
+ for key in destination_params
if key.startswith(SUBMIT_PREFIX)])
This diff is so big that we needed to truncate the remainder.
https://bitbucket.org/galaxy/galaxy-central/commits/1bdc7d3beff4/
Changeset: 1bdc7d3beff4
Branch: search
User: Kyle Ellrott
Date: 2014-01-02 20:51:31
Summary: Fix to 'stringify' datetime object that was getting passed from to_dict LibraryFolder and into JSON converter.
Affected #: 1 file
diff -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 -r 1bdc7d3beff411a31ac0656f648d433d3da04d69 lib/galaxy/model/item_attrs.py
--- a/lib/galaxy/model/item_attrs.py
+++ b/lib/galaxy/model/item_attrs.py
@@ -3,6 +3,8 @@
# Cannot import galaxy.model b/c it creates a circular import graph.
import galaxy
import logging
+import datetime
+
log = logging.getLogger( __name__ )
class RuntimeException( Exception ):
@@ -182,6 +184,9 @@
except:
if key in value_mapper:
return value_mapper.get( key )( item )
+ #If the item is of a class that needs to be 'stringified' before being put into a JSON data structure
+ if type(item) in [datetime.datetime]:
+ return str(item)
return item
# Create dict to represent item.
https://bitbucket.org/galaxy/galaxy-central/commits/d05b1d649290/
Changeset: d05b1d649290
User: dannon
Date: 2014-01-07 20:32:27
Summary: Merged in kellrott/galaxy-central/search (pull request #285)
Make to_dict method stringify non-JSONable objects
Affected #: 1 file
diff -r abcc67f37be7f9450749fa87f6588e90964a6681 -r d05b1d649290a8563558e8721f4a1ca8336e8180 lib/galaxy/model/item_attrs.py
--- a/lib/galaxy/model/item_attrs.py
+++ b/lib/galaxy/model/item_attrs.py
@@ -3,6 +3,8 @@
# Cannot import galaxy.model b/c it creates a circular import graph.
import galaxy
import logging
+import datetime
+
log = logging.getLogger( __name__ )
class RuntimeException( Exception ):
@@ -182,6 +184,9 @@
except:
if key in value_mapper:
return value_mapper.get( key )( item )
+ #If the item is of a class that needs to be 'stringified' before being put into a JSON data structure
+ if type(item) in [datetime.datetime]:
+ return str(item)
return item
# Create dict to represent item.
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: Remove redundant run_as_users check.
by commits-noreply@bitbucket.org 07 Jan '14
by commits-noreply@bitbucket.org 07 Jan '14
07 Jan '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/abcc67f37be7/
Changeset: abcc67f37be7
User: dannon
Date: 2014-01-07 20:25:11
Summary: Remove redundant run_as_users check.
Affected #: 1 file
diff -r 7e11c0d36c85a0e916534ef6733c35fbf5046c41 -r abcc67f37be7f9450749fa87f6588e90964a6681 lib/galaxy/web/framework/__init__.py
--- a/lib/galaxy/web/framework/__init__.py
+++ b/lib/galaxy/web/framework/__init__.py
@@ -950,7 +950,7 @@
run_as_users = [ user for user in self.app.config.get( "api_allow_run_as", "" ).split( "," ) if user ]
if not run_as_users:
return False
- user_in_run_as_users = self.user and run_as_users and self.user.email in run_as_users
+ user_in_run_as_users = self.user and self.user.email in run_as_users
# Can do if explicitly in list or master_api_key supplied.
can_do_run_as = user_in_run_as_users or self.api_inherit_admin
return can_do_run_as
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/ce1977c956b3/
Changeset: ce1977c956b3
User: jmchilton
Date: 2014-01-04 05:04:36
Summary: If master_api_key set, allow it to run_as if any user can run_as.
If no master_api_key set no change in behavior, if api_allow_run_as is empty no change in behavior (i.e. run_as will not work with master_api_key if no api_allow_run_as users are configured - as to not increase attack surface for these Galaxy instances).
This changeset contains no tests, but pull request #278 included functional tests for these changes (as well as original behavior) which passed.
Affected #: 1 file
diff -r f0f7c3cd2e8af64243f878c04f418c1ee054bc55 -r ce1977c956b3457bd3b7ee50506b5e2675192f17 lib/galaxy/web/framework/__init__.py
--- a/lib/galaxy/web/framework/__init__.py
+++ b/lib/galaxy/web/framework/__init__.py
@@ -947,8 +947,13 @@
return self.user and admin_users and self.user.email in admin_users
def user_can_do_run_as( self ):
- run_as_users = self.app.config.get( "api_allow_run_as", "" ).split( "," )
- return self.user and run_as_users and self.user.email in run_as_users
+ run_as_users = [ user for user in self.app.config.get( "api_allow_run_as", "" ).split( "," ) if user ]
+ if not run_as_users:
+ return False
+ user_in_run_as_users = self.user and run_as_users and self.user.email in run_as_users
+ # Can do if explicitly in list or master_api_key supplied.
+ can_do_run_as = user_in_run_as_users or self.api_inherit_admin
+ return can_do_run_as
def get_toolbox(self):
"""Returns the application toolbox"""
https://bitbucket.org/galaxy/galaxy-central/commits/7e11c0d36c85/
Changeset: 7e11c0d36c85
User: dannon
Date: 2014-01-07 20:24:11
Summary: Merged in jmchilton/galaxy-central-fork-1 (pull request #288)
If master_api_key set, allow it to run_as if any user can run_as.
Affected #: 1 file
diff -r f2e3d2e41c1d2e90d089c1ac9ea4d626b620a94d -r 7e11c0d36c85a0e916534ef6733c35fbf5046c41 lib/galaxy/web/framework/__init__.py
--- a/lib/galaxy/web/framework/__init__.py
+++ b/lib/galaxy/web/framework/__init__.py
@@ -947,8 +947,13 @@
return self.user and admin_users and self.user.email in admin_users
def user_can_do_run_as( self ):
- run_as_users = self.app.config.get( "api_allow_run_as", "" ).split( "," )
- return self.user and run_as_users and self.user.email in run_as_users
+ run_as_users = [ user for user in self.app.config.get( "api_allow_run_as", "" ).split( "," ) if user ]
+ if not run_as_users:
+ return False
+ user_in_run_as_users = self.user and run_as_users and self.user.email in run_as_users
+ # Can do if explicitly in list or master_api_key supplied.
+ can_do_run_as = user_in_run_as_users or self.api_inherit_admin
+ return can_do_run_as
def get_toolbox(self):
"""Returns the application toolbox"""
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: martenson: galaxy.modal refactoring; API documentation; folders API refactoring and implementation; libraries API refactoring
by commits-noreply@bitbucket.org 07 Jan '14
by commits-noreply@bitbucket.org 07 Jan '14
07 Jan '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/f2e3d2e41c1d/
Changeset: f2e3d2e41c1d
User: martenson
Date: 2014-01-07 19:52:04
Summary: galaxy.modal refactoring; API documentation; folders API refactoring and implementation; libraries API refactoring
Affected #: 7 files
diff -r ef98f48690fc9ce5d1b8f662a2e91f384514013d -r f2e3d2e41c1d2e90d089c1ac9ea4d626b620a94d lib/galaxy/webapps/galaxy/api/folders.py
--- a/lib/galaxy/webapps/galaxy/api/folders.py
+++ b/lib/galaxy/webapps/galaxy/api/folders.py
@@ -2,11 +2,12 @@
API operations on folders
"""
import logging, os, string, shutil, urllib, re, socket, traceback
+from galaxy import datatypes, jobs, web, security
+from galaxy.web.base.controller import BaseAPIController,UsesLibraryMixin,UsesLibraryMixinItems
+from galaxy.util.sanitize_html import sanitize_html
+
from cgi import escape, FieldStorage
-from galaxy import util, datatypes, jobs, web, util
-from galaxy.web.base.controller import *
-from galaxy.util.sanitize_html import sanitize_html
-from galaxy.model.orm import *
+from paste.httpexceptions import HTTPBadRequest
log = logging.getLogger( __name__ )
@@ -24,8 +25,13 @@
@web.expose_api
def show( self, trans, id, **kwd ):
"""
- GET /api/folders/{encoded_folder_id}
- Displays information about a folder
+ show( self, trans, id, **kwd )
+ *GET /api/folders/{encoded_folder_id}
+
+ Displays information about a folder.
+
+ :param encoded_parent_folder_id: the parent folder's id (required)
+ :type encoded_parent_folder_id: an encoded id string (should be prefixed by 'F')
"""
# Eliminate any 'F' in front of the folder id. Just take the
# last 16 characters:
@@ -38,59 +44,64 @@
return self.encode_all_ids( trans, content.to_dict( view='element' ) )
@web.expose_api
- def create( self, trans, payload, **kwd ):
+ def create( self, trans, encoded_parent_folder_id, **kwd ):
+
+ # is_admin = trans.user_is_admin()
+ # current_user_roles = trans.get_current_user_roles()
+
"""
- POST /api/folders/{encoded_folder_id}
- Create a new object underneath the one specified in the parameters.
- This will use the same parameters and semantics as
- /api/libraries/{LibID}/contents/{ContentId} for consistency.
- This means that datasets and folders can be generated. Note that
- /api/libraries/{LibID}/contents/{ContentId} did not need the library
- id to function properly, which is why this functionality has been
- moved here.
+ create( self, trans, encoded_parent_folder_id, **kwd )
+ *POST /api/folders/{encoded_parent_folder_id}
- o payload's relevant params:
- - folder_id: This is the parent folder's id (required)
+ Create a new folder object underneath the one specified in the parameters.
+
+ :param encoded_parent_folder_id: the parent folder's id (required)
+ :type encoded_parent_folder_id: an encoded id string (should be prefixed by 'F')
+ :param name: the name of the new folder (required)
+ :type name: str
+ :param description: the description of the new folder
+ :type description: str
+
+ :rtype: dictionary
+ :returns: information about newly created folder, notably including ID
+
+ :raises: HTTPBadRequest, MessageException
"""
-# log.debug( "FoldersController.create: enter" )
- # TODO: Create a single point of exit if possible. For now we only
- # exit at the end and on exceptions.
- if 'folder_id' not in payload:
- trans.response.status = 400
- return "Missing requred 'folder_id' parameter."
- else:
- folder_id = payload.pop( 'folder_id' )
- class_name, folder_id = self.__decode_library_content_id( trans, folder_id )
+ payload = kwd.get('payload', None)
+ if payload == None:
+ raise HTTPBadRequest( detail="Missing required parameters 'encoded_parent_folder_id' and 'name'." )
+
+ name = payload.get('name', None)
+ description = payload.get('description', '')
+ if encoded_parent_folder_id == None:
+ raise HTTPBadRequest( detail="Missing required parameter 'encoded_parent_folder_id'." )
+ elif name == None:
+ raise HTTPBadRequest( detail="Missing required parameter 'name'." )
+
+ # encoded_parent_folder_id may be prefixed by 'F'
+ encoded_parent_folder_id = self.__cut_the_prefix( encoded_parent_folder_id )
+
+ # if ( len( encoded_parent_folder_id ) == 17 and encoded_parent_folder_id.startswith( 'F' ) ):
+ # encoded_parent_folder_id = encoded_parent_folder_id[-16:]
+
try:
- # security is checked in the downstream controller
- parent_folder = self.get_library_folder( trans, folder_id, check_ownership=False, check_accessible=False )
- except Exception, e:
- return str( e )
+ decoded_parent_folder_id = trans.security.decode_id( encoded_parent_folder_id )
+ except:
+ raise MessageException( "Malformed folder id ( %s ) specified, unable to decode" % ( str( id ) ), type='error' )
- real_parent_folder_id = trans.security.encode_id( parent_folder.id )
-
- # Note that this will only create a folder; the library_contents will
- # also allow files to be generated, though that encompasses generic
- # contents:
- # TODO: reference or change create_folder, which points to the
- # library browsing; we just want to point to the /api/folders URI.
- status, output = trans.webapp.controllers['library_common'].create_folder( trans, 'api', real_parent_folder_id, '', **payload )
+ # TODO: refactor the functionality for use here instead of calling another controller
+ params = dict( [ ( "name", name ), ( "description", description ) ] )
+ status, output = trans.webapp.controllers['library_common'].create_folder( trans, 'api', encoded_parent_folder_id, '', **params )
rval = []
-
- # SM: When a folder is successfully created:
- # - get all of the created folders. We know that they're
- # folders, so prepend an "F" to them.
if 200 == status:
for k, v in output.items():
if type( v ) == trans.app.model.LibraryDatasetDatasetAssociation:
v = v.library_dataset
encoded_id = 'F' + trans.security.encode_id( v.id )
rval.append( dict( id = encoded_id,
- name = v.name,
- url = url_for( 'folder', id=encoded_id ) ) )
+ name = v.name ) )
else:
-# log.debug( "Error creating folder; setting output and status" )
trans.response.status = status
rval = output
return rval
@@ -105,12 +116,9 @@
"""
pass
- # TODO: Move this to library_common. This doesn't really belong in any
- # of the other base controllers.
- def __decode_library_content_id( self, trans, content_id ):
- if ( len( content_id ) % 16 == 0 ):
- return 'LibraryDataset', content_id
- elif ( content_id.startswith( 'F' ) ):
- return 'LibraryFolder', content_id[1:]
+ def __cut_the_prefix(self, encoded_id):
+ if ( len( encoded_id ) == 17 and encoded_id.startswith( 'F' ) ):
+ return encoded_id[-16:]
else:
- raise HTTPBadRequest( 'Malformed library content id ( %s ) specified, unable to decode.' % str( content_id ) )
+ return encoded_id
+
diff -r ef98f48690fc9ce5d1b8f662a2e91f384514013d -r f2e3d2e41c1d2e90d089c1ac9ea4d626b620a94d lib/galaxy/webapps/galaxy/api/libraries.py
--- a/lib/galaxy/webapps/galaxy/api/libraries.py
+++ b/lib/galaxy/webapps/galaxy/api/libraries.py
@@ -1,13 +1,13 @@
"""
-API operations on a library.
+API operations on a data library.
"""
-import logging
from galaxy import util
from galaxy import web
from galaxy.model.orm import and_, not_, or_
from galaxy.web.base.controller import BaseAPIController, url_for
from paste.httpexceptions import HTTPBadRequest, HTTPForbidden
+import logging
log = logging.getLogger( __name__ )
class LibrariesController( BaseAPIController ):
@@ -17,18 +17,17 @@
"""
index( self, trans, deleted='False', **kwd )
* GET /api/libraries:
- returns a list of summary data for libraries
+ Returns a list of summary data for all libraries that are ``non-deleted``
* GET /api/libraries/deleted:
- returns a list of summary data for deleted libraries
+ Returns a list of summary data for ``deleted`` libraries.
+ :param deleted: if True, show only deleted libraries, if False, ``non-deleted``
:type deleted: boolean
- :param deleted: if True, show only deleted libraries, if False, non-deleted
:rtype: list
:returns: list of dictionaries containing library information
.. seealso:: :attr:`galaxy.model.Library.dict_collection_visible_keys`
"""
-# log.debug( "LibrariesController.index: enter" )
query = trans.sa_session.query( trans.app.model.Library )
deleted = util.string_as_bool( deleted )
if deleted:
@@ -47,34 +46,33 @@
trans.model.LibraryPermissions.table.c.role_id.in_( current_user_role_ids ) ) ) ]
query = query.filter( or_( not_( trans.model.Library.table.c.id.in_( restricted_library_ids ) ),
trans.model.Library.table.c.id.in_( accessible_restricted_library_ids ) ) )
- rval = []
+ libraries = []
for library in query:
item = library.to_dict( view='element' )
item['url'] = url_for( route, id=trans.security.encode_id( library.id ) )
item['id'] = 'F' + trans.security.encode_id( item['id'] )
item['root_folder_id'] = 'F' + trans.security.encode_id( item['root_folder_id'] )
- rval.append( item )
- return rval
+ libraries.append( item )
+ return libraries
@web.expose_api
def show( self, trans, id, deleted='False', **kwd ):
"""
show( self, trans, id, deleted='False', **kwd )
- * GET /api/libraries/{id}:
+ * GET /api/libraries/{encoded_id}:
returns detailed information about a library
- * GET /api/libraries/deleted/{id}:
- returns detailed information about a deleted library
+ * GET /api/libraries/deleted/{encoded_id}:
+ returns detailed information about a ``deleted`` library
+ :param id: the encoded id of the library
:type id: an encoded id string
- :param id: the encoded id of the library
+ :param deleted: if True, allow information on a ``deleted`` library
:type deleted: boolean
- :param deleted: if True, allow information on a deleted library
:rtype: dictionary
:returns: detailed library information
.. seealso:: :attr:`galaxy.model.Library.dict_element_visible_keys`
"""
-# log.debug( "LibraryContentsController.show: enter" )
library_id = id
deleted = util.string_as_bool( deleted )
try:
@@ -89,7 +87,6 @@
if not library or not ( trans.user_is_admin() or trans.app.security_agent.can_access_library( trans.get_current_user_roles(), library ) ):
raise HTTPBadRequest( detail='Invalid library id ( %s ) specified.' % id )
item = library.to_dict( view='element' )
- #item['contents_url'] = url_for( 'contents', library_id=library_id )
item['contents_url'] = url_for( 'library_contents', library_id=library_id )
return item
@@ -98,18 +95,17 @@
"""
create( self, trans, payload, **kwd )
* POST /api/libraries:
- create a new library
+ Creates a new library. Only ``name`` parameter is required.
.. note:: Currently, only admin users can create libraries.
- :type payload: dict
:param payload: (optional) dictionary structure containing::
'name': the new library's name
'description': the new library's description
'synopsis': the new library's synopsis
+ :type payload: dict
:rtype: dict
- :returns: a dictionary containing the id, name, and 'show' url
- of the new library
+ :returns: a dictionary containing the encoded_id, name, description, synopsis, and 'show' url of the new library
"""
if not trans.user_is_admin():
raise HTTPForbidden( detail='You are not authorized to create a new library.' )
@@ -127,13 +123,30 @@
trans.sa_session.add_all( ( library, root_folder ) )
trans.sa_session.flush()
encoded_id = trans.security.encode_id( library.id )
- rval = {}
- rval['url'] = url_for( 'library', id=encoded_id )
- rval['name'] = name
- rval['id'] = encoded_id
- return rval
+ new_library = {}
+ new_library['url'] = url_for( 'library', id=encoded_id )
+ new_library['name'] = name
+ new_library['description'] = description
+ new_library['synopsis'] = synopsis
+ new_library['id'] = encoded_id
+ return new_library
- def edit( self, trans, payload, **kwd ):
+ def edit( self, trans, encoded_id, payload, **kwd ):
+ """
+ * PUT /api/libraries/{encoded_id}
+ Updates the library defined by an ``encoded_id`` with the data in the payload.
+
+ .. note:: Currently, only admin users can edit libraries.
+
+ :param payload: (required) dictionary structure containing::
+ 'name': new library's name
+ 'description': new library's description
+ 'synopsis': new library's synopsis
+ :type payload: dict
+
+ :rtype: dict
+ :returns: a dictionary containing the encoded_id, name, description, synopsis, and 'show' url of the updated library
+ """
return "Not implemented yet"
@web.expose_api
@@ -142,10 +155,11 @@
delete( self, trans, id, **kwd )
* DELETE /api/histories/{id}
mark the library with the given ``id`` as deleted
+
.. note:: Currently, only admin users can delete libraries.
+ :param id: the encoded id of the library to delete
:type id: str
- :param id: the encoded id of the library to delete
:rtype: dictionary
:returns: detailed library information
diff -r ef98f48690fc9ce5d1b8f662a2e91f384514013d -r f2e3d2e41c1d2e90d089c1ac9ea4d626b620a94d lib/galaxy/webapps/galaxy/buildapp.py
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -184,6 +184,12 @@
action='download',
conditions=dict( method=[ "POST", "GET" ] ) )
+ webapp.mapper.connect( 'create_folder',
+ '/api/folders/:encoded_parent_folder_id',
+ controller='folders',
+ action='create',
+ conditions=dict( method=[ "POST" ] ) )
+
webapp.mapper.resource_with_deleted( 'library',
'libraries',
path_prefix='/api' )
@@ -196,7 +202,8 @@
controller='folder_contents',
name_prefix='folder_',
path_prefix='/api/folders/:folder_id',
- parent_resources=dict( member_name='folder', collection_name='folders' ) )
+ parent_resources=dict( member_name='folder', collection_name='folders' ),
+ conditions=dict( method=[ "GET" ] ) )
webapp.mapper.resource( 'content',
'contents',
diff -r ef98f48690fc9ce5d1b8f662a2e91f384514013d -r f2e3d2e41c1d2e90d089c1ac9ea4d626b620a94d static/scripts/galaxy.library.js
--- a/static/scripts/galaxy.library.js
+++ b/static/scripts/galaxy.library.js
@@ -23,6 +23,11 @@
urlRoot: '/api/libraries'
});
+ // FOLDER AS MODEL
+ var FolderAsModel = Backbone.Model.extend({
+ urlRoot: '/api/folders'
+ });
+
// LIBRARIES
var Libraries = Backbone.Collection.extend({
url: '/api/libraries',
@@ -34,7 +39,7 @@
urlRoot : '/api/libraries/datasets'
})
- // FOLDER
+ // FOLDER AS COLLECTION
var Folder = Backbone.Collection.extend({
model: Item
})
@@ -316,7 +321,7 @@
'click #toolbtn_bulk_import' : 'modalBulkImport',
'click #toolbtn_dl' : 'bulkDownload',
'click .library-dataset' : 'showDatasetDetails',
- 'click #toolbtn_create_folder' : 'createFolderModal',
+ 'click #toolbtn_create_folder' : 'createFolderFromModal',
'click .btn_open_folder' : 'navigateToFolder'
},
@@ -407,15 +412,15 @@
// make modal
var self = this;
this.modal = new mod_modal.GalaxyModal({
+ destructible : true,
title : 'Dataset Details',
body : template,
buttons : {
'Import' : function() { self.importCurrentIntoHistory() },
'Download' : function() { self.downloadCurrent() },
- 'Close' : function() { self.modal.hide(); $('.modal').remove(); self.modal = null; } // TODO refill nicely modal with data
+ 'Close' : function() { self.modal.hideOrDestroy; self.modal = null; }
}
});
- this.modal.bindEvents(event);
$(".peek").html(item.get("peek"));
var history_footer_tmpl = _.template(this.templateHistorySelectInModal(), {histories : histories.models});
@@ -426,6 +431,7 @@
$(this.modal.elMain).find('#dataset_import_single').val(self.lastSelectedHistory);
}
+ this.modal.bindEvents();
// show the prepared modal
this.modal.show();
},
@@ -563,14 +569,15 @@
// make modal
var history_modal_tmpl = _.template(self.templateBulkImportInModal(), {histories : histories.models});
self.modal = new mod_modal.GalaxyModal({
+ destructible : true,
title : 'Import into History',
body : history_modal_tmpl,
buttons : {
'Import' : function() {self.importAllIntoHistory()},
- 'Close' : function() {self.modal.hide(); $('.modal').remove(); self.modal = null;}
+ 'Close' : function() {self.modal.hideOrDestroy; self.modal = null;}
}
});
- self.modal.bindEvents(event);
+ self.modal.bindEvents();
// show the prepared modal
self.modal.show();
}
@@ -693,9 +700,75 @@
},
// shows modal for creating folder
- createFolderModal: function(){
- mod_toastr.info('This will create folder...in the future');
- }
+ createFolderFromModal: function(){
+ event.preventDefault();
+ event.stopPropagation();
+
+ // create modal
+ var self = this;
+ this.modal = new mod_modal.GalaxyModal({
+ destructible : true,
+ title : 'Create New Folder',
+ body : this.template_new_folder(),
+ buttons : {
+ 'Create' : function() {self.create_new_folder_event()},
+ 'Close' : function() {self.modal.destroy(); self.modal = null;}
+ }
+ });
+ this.modal.bindEvents();
+ // show prepared modal
+ this.modal.show();
+ },
+
+ // create the new folder from modal
+ create_new_folder_event: function(){
+ var folderDetails = this.serialize_new_folder();
+ if (this.validate_new_folder(folderDetails)){
+ var folder = new FolderAsModel();
+ folder.url = folder.urlRoot + '/F2fdbd5c5858e78fb' //load real ID
+ var self = this;
+ folder.save(folderDetails, {
+ success: function (folder) {
+ self.modal.destroy();
+ mod_toastr.success('Folder created');
+ self.render({id: 'F2fdbd5c5858e78fb'}); //put real id
+ },
+ error: function(){
+ mod_toastr.error('An error occured :(');
+ }
+ });
+ } else {
+ mod_toastr.error('Folder\'s name is missing');
+ }
+ return false;
+ },
+
+ // serialize data from the form
+ serialize_new_folder : function(){
+ return {
+ name: $("input[name='Name']").val(),
+ description: $("input[name='Description']").val()
+ };
+ },
+
+ // validate new library info
+ validate_new_folder: function(folderDetails){
+ return folderDetails.name !== '';
+ },
+
+ // template for new library modal
+ template_new_folder: function(){
+ tmpl_array = [];
+
+ tmpl_array.push('<div id="new_folder_modal">');
+ tmpl_array.push('<form>');
+ tmpl_array.push('<input type="text" name="Name" value="" placeholder="Name">');
+ tmpl_array.push('<input type="text" name="Description" value="" placeholder="Description">');
+ tmpl_array.push('</form>');
+ tmpl_array.push('</div>');
+
+ return tmpl_array.join('');
+ }
});
@@ -786,16 +859,16 @@
// create modal
var self = this;
- this.modal = new mod_modal.GalaxyModal(
- {
+ this.modal = new mod_modal.GalaxyModal({
+ destructible : true,
title : 'Create New Library',
body : this.template_new_library(),
buttons : {
'Create' : function() {self.create_new_library_event()},
- 'Close' : function() {self.modal.hide()}
+ 'Close' : function() {self.modal.hideOrDestroy(); self.modal = null;}
}
});
-
+ this.modal.bindEvents();
// show prepared modal
this.modal.show();
},
@@ -808,7 +881,7 @@
var self = this;
library.save(libraryDetails, {
success: function (library) {
- self.modal.hide();
+ self.modal.destroy();
self.clear_library_modal();
self.render();
mod_toastr.success('Library created');
diff -r ef98f48690fc9ce5d1b8f662a2e91f384514013d -r f2e3d2e41c1d2e90d089c1ac9ea4d626b620a94d static/scripts/galaxy.modal.js
--- a/static/scripts/galaxy.modal.js
+++ b/static/scripts/galaxy.modal.js
@@ -18,7 +18,9 @@
},
// options
- options : {},
+ options : {
+ destructible: false // by default the modal cannot be removed by theself.destroy() method
+ },
// initialize
initialize : function(options) {
@@ -27,16 +29,23 @@
this.create(options);
},
+ hideOrDestroy: function(){
+ if (this.options.destructible){
+ self.destroy();
+ } else {
+ self.hide();
+ }
+ },
+
// bind the click-to-hide function
- bindEvents: function(event) {
- // bind the ESC key to hide() function
+ bindEvents: function() {
+ // bind the ESC key to hideOrDestroy() function
$(document).on('keyup', function(event){
- if (event.keyCode == 27) { self.hide(); $('.modal').remove();}
+ if (event.keyCode == 27) { self.hideOrDestroy() }
})
- // bind the 'click anywhere' to hide() function...
- $('html').on('click', function(event){
- self.hide();
- $('.modal').remove();
+ // bind the 'click anywhere' to hideOrDestroy() function...
+ $('html').on('click', function(event){
+ self.hideOrDestroy()
})
// ...but don't hide if the click is on modal content
$('.modal-content').on('click', function(event){
@@ -45,28 +54,20 @@
},
// unbind the click-to-hide function
- unbindEvents: function(event){
- // bind the ESC key to hide() function
+ unbindEvents: function(){
+ // unbind the ESC key to hideOrDestroy() function
$(document).off('keyup', function(event){
- if (event.keyCode == 27) { self.hide(); $('.modal').remove();}
+ if (event.keyCode == 27) { self.hideOrDestroy() }
})
- // unbind the 'click anywhere' to hide() function...
+ // unbind the 'click anywhere' to hideOrDestroy() function...
$('html').off('click', function(event){
- self.hide();
- $('.modal').remove();
+ self.hideOrDestroy()
})
$('.modal-content').off('click', function(event){
event.stopPropagation();
})
},
- // destroy
- destroy : function(){
- this.hide();
- this.unbindEvents();
- $('.modal').remove();
- },
-
// adds and displays a new frame/window
show: function(options) {
// create
@@ -102,6 +103,16 @@
// set flag
this.visible = false;
this.unbindEvents();
+ },
+
+ // destroy modal
+ destroy: function(){
+ // set flag
+ this.visible = false;
+ this.unbindEvents();
+
+ // remove
+ this.$el.remove();
},
// create
diff -r ef98f48690fc9ce5d1b8f662a2e91f384514013d -r f2e3d2e41c1d2e90d089c1ac9ea4d626b620a94d static/scripts/packed/galaxy.library.js
--- a/static/scripts/packed/galaxy.library.js
+++ b/static/scripts/packed/galaxy.library.js
@@ -1,1 +1,1 @@
-var view=null;var library_router=null;var responses=[];define(["galaxy.modal","galaxy.masthead","utils/galaxy.utils","libs/toastr"],function(j,k,g,m){var e=Backbone.Model.extend({urlRoot:"/api/libraries"});var n=Backbone.Collection.extend({url:"/api/libraries",model:e});var h=Backbone.Model.extend({urlRoot:"/api/libraries/datasets"});var c=Backbone.Collection.extend({model:h});var d=Backbone.Model.extend({defaults:{folder:new c(),full_path:"unknown",urlRoot:"/api/folders/",id:"unknown"},parse:function(q){this.full_path=q[0].full_path;this.get("folder").reset(q[1].folder_contents);return q}});var b=Backbone.Model.extend({urlRoot:"/api/histories/"});var i=Backbone.Model.extend({url:"/api/histories/"});var o=Backbone.Collection.extend({url:"/api/histories",model:i});var p=Backbone.Router.extend({routes:{"":"libraries","folders/:id":"folder_content","folders/:folder_id/download/:format":"download"}});var l=Backbone.View.extend({el:"#center",progress:0,progressStep:1,lastSelectedHistory:"",modal:null,folders:null,initialize:function(){this.folders=[];this.queue=jQuery.Deferred();this.queue.resolve()},templateFolder:function(){var q=[];q.push('<div id="library_container" style="width: 90%; margin: auto; margin-top: 2em; ">');q.push('<h3>New Data Libraries. This is work in progress. Report problems & ideas to <a href="mailto:marten@bx.psu.edu?Subject=DataLibraries_Feedback" target="_blank">Marten</a>.</h3>');q.push('<div id="library_folder_toolbar" >');q.push(' <button title="Create New Folder" id="toolbtn_create_folder" class="btn btn-primary" type="button"><span class="fa fa-plus"></span><span class="fa fa-folder-close"></span> folder</button>');q.push(' <button id="toolbtn_bulk_import" class="btn btn-primary" style="display: none; margin-left: 0.5em;" type="button"><span class="fa fa-external-link"></span> to history</button>');q.push(' <div id="toolbtn_dl" class="btn-group" style="margin-left: 0.5em; display: none; ">');q.push(' <button id="drop_toggle" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown">');q.push(' <span class="fa fa-download"></span> download <span class="caret"></span>');q.push(" </button>");q.push(' <ul class="dropdown-menu" role="menu">');q.push(' <li><a href="#/folders/<%= id %>/download/tgz">.tar.gz</a></li>');q.push(' <li><a href="#/folders/<%= id %>/download/tbz">.tar.bz</a></li>');q.push(' <li><a href="#/folders/<%= id %>/download/zip">.zip</a></li>');q.push(" </ul>");q.push(" </div>");q.push("</div>");q.push('<div class="library_breadcrumb">');q.push('<a title="Return to the list of libraries" href="#">Libraries</a><b>|</b> ');q.push("<% _.each(path, function(path_item) { %>");q.push("<% if (path_item[0] != id) { %>");q.push('<a title="Return to this folder" href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a><b>|</b> ');q.push("<% } else { %>");q.push('<span title="You are in this folder"><%- path_item[1] %></span>');q.push("<% } %>");q.push("<% }); %>");q.push("</div>");q.push('<table id="folder_table" class="table table-condensed">');q.push(" <thead>");q.push(' <th style="text-align: center; width: 20px; "><input id="select-all-checkboxes" style="margin: 0;" type="checkbox"></th>');q.push(' <th class="button_heading">view</th>');q.push(" <th>name</th>");q.push(" <th>data type</th>");q.push(" <th>size</th>");q.push(" <th>date</th>");q.push(" </thead>");q.push(" <tbody>");q.push(" <td></td>");q.push(' <td><button title="Go to parent folder" type="button" data-id="<%- upper_folder_id %>" class="btn_open_folder btn btn-default btn-xs">');q.push(' <span class="fa fa-arrow-up"></span> .. go up</td>');q.push(" <td></td>");q.push(" <td></td>");q.push(" <td></td>");q.push(" <td></td>");q.push(" </tr>");q.push(" <% _.each(items, function(content_item) { %>");q.push(' <tr class="folder_row light" id="<%- content_item.id %>">');q.push(' <% if (content_item.get("type") === "folder") { %>');q.push(" <td></td>");q.push(' <td><button title="Open this folder" type="button" data-id="<%- content_item.id %>" class="btn_open_folder btn btn-default btn-xs">');q.push(' <span class="fa fa-folder-open"></span> browse</td>');q.push(' <td><%- content_item.get("name") %>');q.push(' <% if (content_item.get("item_count") === 0) { %>');q.push(' <span class="muted">(empty folder)</span>');q.push(" <% } %>");q.push(" </td>");q.push(" <td>folder</td>");q.push(' <td><%= _.escape(content_item.get("item_count")) %> item(s)</td>');q.push(" <% } else { %>");q.push(' <td style="text-align: center; "><input style="margin: 0;" type="checkbox"></td>');q.push(" <td>");q.push(' <button title="See details of this dataset" type="button" class="library-dataset btn btn-default btn-xs">');q.push(' <span class="fa fa-eye"></span> details');q.push(" </button>");q.push(" </td>");q.push(' <td><%- content_item.get("name") %></td>');q.push(' <td><%= _.escape(content_item.get("data_type")) %></td>');q.push(' <td><%= _.escape(content_item.get("readable_size")) %></td>');q.push(" <% } %> ");q.push(' <td><%= _.escape(content_item.get("time_updated")) %></td>');q.push(" </tr>");q.push(" <% }); %>");q.push(" ");q.push(" </tbody>");q.push("</table>");q.push("</div>");return q.join("")},templateDatasetModal:function(){var q=[];q.push('<div id="dataset_info_modal">');q.push(' <table class="table table-striped table-condensed">');q.push(" <tr>");q.push(' <th scope="row" id="id_row" data-id="<%= _.escape(item.get("ldda_id")) %>">Name</th>');q.push(' <td><%= _.escape(item.get("name")) %></td>');q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Data type</th>');q.push(' <td><%= _.escape(item.get("data_type")) %></td>');q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Genome build</th>');q.push(' <td><%= _.escape(item.get("genome_build")) %></td>');q.push(" </tr>");q.push(' <th scope="row">Size</th>');q.push(" <td><%= _.escape(size) %></td>");q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Date uploaded</th>');q.push(' <td><%= _.escape(item.get("date_uploaded")) %></td>');q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Uploaded by</th>');q.push(' <td><%= _.escape(item.get("uploaded_by")) %></td>');q.push(" </tr>");q.push(' <tr scope="row">');q.push(' <th scope="row">Data Lines</th>');q.push(' <td scope="row"><%= _.escape(item.get("metadata_data_lines")) %></td>');q.push(" </tr>");q.push(' <th scope="row">Comment Lines</th>');q.push(' <% if (item.get("metadata_comment_lines") === "") { %>');q.push(' <td scope="row"><%= _.escape(item.get("metadata_comment_lines")) %></td>');q.push(" <% } else { %>");q.push(' <td scope="row">unknown</td>');q.push(" <% } %>");q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Number of Columns</th>');q.push(' <td scope="row"><%= _.escape(item.get("metadata_columns")) %></td>');q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Column Types</th>');q.push(' <td scope="row"><%= _.escape(item.get("metadata_column_types")) %></td>');q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Miscellaneous information</th>');q.push(' <td scope="row"><%= _.escape(item.get("misc_blurb")) %></td>');q.push(" </tr>");q.push(" </table>");q.push(' <pre class="peek">');q.push(" </pre>");q.push("</div>");return q.join("")},templateHistorySelectInModal:function(){var q=[];q.push('<span id="history_modal_combo" style="width:90%; margin-left: 1em; margin-right: 1em; ">');q.push("Select history: ");q.push('<select id="dataset_import_single" name="dataset_import_single" style="width:50%; margin-bottom: 1em; "> ');q.push(" <% _.each(histories, function(history) { %>");q.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');q.push(" <% }); %>");q.push("</select>");q.push("</span>");return q.join("")},templateBulkImportInModal:function(){var q=[];q.push('<span id="history_modal_combo_bulk" style="width:90%; margin-left: 1em; margin-right: 1em; ">');q.push("Select history: ");q.push('<select id="dataset_import_bulk" name="dataset_import_bulk" style="width:50%; margin-bottom: 1em; "> ');q.push(" <% _.each(histories, function(history) { %>");q.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');q.push(" <% }); %>");q.push("</select>");q.push("</span>");return q.join("")},size_to_string:function(q){var r="";if(q>=100000000000){q=q/100000000000;r="TB"}else{if(q>=100000000){q=q/100000000;r="GB"}else{if(q>=100000){q=q/100000;r="MB"}else{if(q>=100){q=q/100;r="KB"}else{q=q*10;r="b"}}}}return(Math.round(q)/10)+r},events:{"click #select-all-checkboxes":"selectAll","click .folder_row":"selectClickedRow","click #toolbtn_bulk_import":"modalBulkImport","click #toolbtn_dl":"bulkDownload","click .library-dataset":"showDatasetDetails","click #toolbtn_create_folder":"createFolderModal","click .btn_open_folder":"navigateToFolder"},render:function(q){$("#center").css("overflow","auto");view=this;var s=this;var r=new d({id:q.id});r.url=r.attributes.urlRoot+q.id+"/contents";r.fetch({success:function(t){for(var v=0;v<r.attributes.folder.models.length;v++){var u=r.attributes.folder.models[v];if(u.get("type")==="file"){u.set("readable_size",s.size_to_string(u.get("file_size")))}}var x=r.full_path;var y;if(x.length===1){y=0}else{y=x[x.length-2][0]}var w=_.template(s.templateFolder(),{path:r.full_path,items:r.attributes.folder.models,id:q.id,upper_folder_id:y});s.$el.html(w)}})},navigateToFolder:function(r){var q=$(r.target).attr("data-id");if(typeof q==="undefined"){return false}else{if(q==="0"){library_router.navigate("#",{trigger:true,replace:true})}else{library_router.navigate("folders/"+q,{trigger:true,replace:true})}}},showDatasetDetails:function(t){t.preventDefault();var u=$(t.target).parent().parent().attr("id");var s=new h();var r=new o();s.id=u;var q=this;s.fetch({success:function(v){r.fetch({success:function(w){q.renderModalAfterFetch(v,w)}})}})},renderModalAfterFetch:function(v,s){var t=this.size_to_string(v.get("file_size"));var u=_.template(this.templateDatasetModal(),{item:v,size:t});this.modal=null;var r=this;this.modal=new j.GalaxyModal({title:"Dataset Details",body:u,buttons:{Import:function(){r.importCurrentIntoHistory()},Download:function(){r.downloadCurrent()},Close:function(){r.modal.hide();$(".modal").remove();r.modal=null}}});this.modal.bindEvents(event);$(".peek").html(v.get("peek"));var q=_.template(this.templateHistorySelectInModal(),{histories:s.models});$(this.modal.elMain).find(".buttons").prepend(q);if(r.lastSelectedHistory.length>0){$(this.modal.elMain).find("#dataset_import_single").val(r.lastSelectedHistory)}this.modal.show()},downloadCurrent:function(){this.modal.disableButton("Import");this.modal.disableButton("Download");var q=[];q.push($("#id_row").attr("data-id"));var r="/api/libraries/datasets/download/uncompressed";var s={ldda_ids:q};folderContentView.processDownload(r,s);this.modal.enableButton("Import");this.modal.enableButton("Download")},importCurrentIntoHistory:function(){this.modal.disableButton("Import");this.modal.disableButton("Download");var s=$(this.modal.elMain).find("select[name=dataset_import_single] option:selected").val();this.lastSelectedHistory=s;var q=$("#id_row").attr("data-id");var t=new b();var r=this;t.url=t.urlRoot+s+"/contents";t.save({content:q,source:"library"},{success:function(){m.success("Dataset imported");r.modal.enableButton("Import");r.modal.enableButton("Download")},error:function(){m.error("An error occured! Dataset not imported. Please try again.");r.modal.enableButton("Import");r.modal.enableButton("Download")}})},selectAll:function(r){var q=r.target.checked;that=this;$(":checkbox").each(function(){this.checked=q;$row=$(this.parentElement.parentElement);(q)?that.makeDarkRow($row):that.makeWhiteRow($row)});this.checkTools()},selectClickedRow:function(r){var t="";var q;var s;if(r.target.localName==="input"){t=r.target;q=$(r.target.parentElement.parentElement);s="input"}else{if(r.target.localName==="td"){t=$("#"+r.target.parentElement.id).find(":checkbox")[0];q=$(r.target.parentElement);s="td"}}if(t===""){r.stopPropagation();return}if(t===undefined){r.stopPropagation();return}if(t.checked){if(s==="td"){t.checked="";this.makeWhiteRow(q)}else{if(s==="input"){this.makeDarkRow(q)}}}else{if(s==="td"){t.checked="selected";this.makeDarkRow(q)}else{if(s==="input"){this.makeWhiteRow(q)}}}this.checkTools()},makeDarkRow:function(q){q.removeClass("light");q.find("a").removeClass("light");q.addClass("dark");q.find("a").addClass("dark")},makeWhiteRow:function(q){q.removeClass("dark");q.find("a").removeClass("dark");q.addClass("light");q.find("a").addClass("light")},checkTools:function(){var q=$("#folder_table").find(":checked");if(q.length>0){$("#toolbtn_bulk_import").show();$("#toolbtn_dl").show()}else{$("#toolbtn_bulk_import").hide();$("#toolbtn_dl").hide()}},modalBulkImport:function(){var r=this;var q=new o();q.fetch({success:function(s){var t=_.template(r.templateBulkImportInModal(),{histories:s.models});r.modal=new j.GalaxyModal({title:"Import into History",body:t,buttons:{Import:function(){r.importAllIntoHistory()},Close:function(){r.modal.hide();$(".modal").remove();r.modal=null}}});r.modal.bindEvents(event);r.modal.show()}})},importAllIntoHistory:function(){this.modal.disableButton("Import");var s=$("select[name=dataset_import_bulk] option:selected").val();var w=$("select[name=dataset_import_bulk] option:selected").text();var y=[];$("#folder_table").find(":checked").each(function(){if(this.parentElement.parentElement.id!=""){y.push(this.parentElement.parentElement.id)}});var x=_.template(this.templateProgressBar(),{history_name:w});$(this.modal.elMain).find(".modal-body").html(x);var t=100/y.length;this.initProgress(t);var q=[];for(var r=y.length-1;r>=0;r--){library_dataset_id=y[r];var u=new b();var v=this;u.url=u.urlRoot+s+"/contents";u.content=library_dataset_id;u.source="library";q.push(u)}this.chainCall(q)},chainCall:function(r){var q=this;var s=r.pop();if(typeof s==="undefined"){m.success("All datasets imported");this.modal.destroy();return}var t=$.when(s.save({content:s.content,source:s.source})).done(function(u){q.updateProgress();responses.push(u);q.chainCall(r)})},initProgress:function(q){this.progress=0;this.progressStep=q},updateProgress:function(){this.progress+=this.progressStep;$(".progress-bar-import").width(Math.round(this.progress)+"%");txt_representation=Math.round(this.progress)+"% Complete";$(".completion_span").text(txt_representation)},templateProgressBar:function(){var q=[];q.push('<div class="import_text">');q.push("Importing selected datasets to history <b><%= _.escape(history_name) %></b>");q.push("</div>");q.push('<div class="progress">');q.push(' <div class="progress-bar progress-bar-import" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 00%;">');q.push(' <span class="completion_span">0% Complete</span>');q.push(" </div>");q.push("</div>");q.push("");return q.join("")},download:function(q,u){var s=[];$("#folder_table").find(":checked").each(function(){if(this.parentElement.parentElement.id!=""){s.push(this.parentElement.parentElement.id)}});var r="/api/libraries/datasets/download/"+u;var t={ldda_ids:s};this.processDownload(r,t,"get")},processDownload:function(r,s,t){if(r&&s){s=typeof s=="string"?s:$.param(s);var q="";$.each(s.split("&"),function(){var u=this.split("=");q+='<input type="hidden" name="'+u[0]+'" value="'+u[1]+'" />'});$('<form action="'+r+'" method="'+(t||"post")+'">'+q+"</form>").appendTo("body").submit().remove();m.info("Your download will begin soon")}},createFolderModal:function(){m.info("This will create folder...in the future")}});var a=Backbone.View.extend({el:"#center",events:{"click #create_new_library_btn":"show_library_modal"},initialize:function(){},template_library_list:function(){tmpl_array=[];tmpl_array.push('<div id="library_container" style="width: 90%; margin: auto; margin-top: 2em; overflow: auto !important; ">');tmpl_array.push("");tmpl_array.push('<h3>New Data Libraries. This is work in progress. Report problems & ideas to <a href="mailto:marten@bx.psu.edu?Subject=DataLibraries_Feedback" target="_blank">Marten</a>.</h3>');tmpl_array.push('<a href="" id="create_new_library_btn" class="btn btn-primary file ">New Library</a>');tmpl_array.push('<table class="table table-condensed">');tmpl_array.push(" <thead>");tmpl_array.push(' <th class="button_heading"></th>');tmpl_array.push(" <th>name</th>");tmpl_array.push(" <th>description</th>");tmpl_array.push(" <th>synopsis</th> ");tmpl_array.push(" <th>model type</th> ");tmpl_array.push(" </thead>");tmpl_array.push(" <tbody>");tmpl_array.push(" <% _.each(libraries, function(library) { %>");tmpl_array.push(" <tr>");tmpl_array.push(' <td><button title="Open this library" type="button" data-id="<%- library.get("root_folder_id") %>" class="btn_open_folder btn btn-default btn-xs">');tmpl_array.push(' <span class="fa fa-folder-open"></span> browse</td>');tmpl_array.push(' <td><%- library.get("name") %></td>');tmpl_array.push(' <td><%= _.escape(library.get("description")) %></td>');tmpl_array.push(' <td><%= _.escape(library.get("synopsis")) %></td>');tmpl_array.push(' <td><%= _.escape(library.get("model_class")) %></td>');tmpl_array.push(" </tr>");tmpl_array.push(" <% }); %>");tmpl_array.push(" </tbody>");tmpl_array.push("</table>");tmpl_array.push("</div>");return tmpl_array.join("")},render:function(){$("#center").css("overflow","auto");var q=this;libraries=new n();libraries.fetch({success:function(r){var s=_.template(q.template_library_list(),{libraries:r.models});q.$el.html(s)},error:function(s,r){if(r.statusCode().status===403){m.error("Please log in first. Redirecting to login page in 3s.");setTimeout(q.redirectToLogin,3000)}else{m.error("An error occured. Please try again.")}}})},redirectToHome:function(){window.location="../"},redirectToLogin:function(){window.location="/user/login"},modal:null,show_library_modal:function(r){r.preventDefault();r.stopPropagation();var q=this;this.modal=new j.GalaxyModal({title:"Create New Library",body:this.template_new_library(),buttons:{Create:function(){q.create_new_library_event()},Close:function(){q.modal.hide()}}});this.modal.show()},create_new_library_event:function(){var s=this.serialize_new_library();if(this.validate_new_library(s)){var r=new e();var q=this;r.save(s,{success:function(t){q.modal.hide();q.clear_library_modal();q.render();m.success("Library created")},error:function(){m.error("An error occured :(")}})}else{m.error("Library's name is missing")}return false},clear_library_modal:function(){$("input[name='Name']").val("");$("input[name='Description']").val("");$("input[name='Synopsis']").val("")},serialize_new_library:function(){return{name:$("input[name='Name']").val(),description:$("input[name='Description']").val(),synopsis:$("input[name='Synopsis']").val()}},validate_new_library:function(q){return q.name!==""},template_new_library:function(){tmpl_array=[];tmpl_array.push('<div id="new_library_modal">');tmpl_array.push("<form>");tmpl_array.push('<input type="text" name="Name" value="" placeholder="Name">');tmpl_array.push('<input type="text" name="Description" value="" placeholder="Description">');tmpl_array.push('<input type="text" name="Synopsis" value="" placeholder="Synopsis">');tmpl_array.push("</form>");tmpl_array.push("</div>");return tmpl_array.join("")}});var f=Backbone.View.extend({folderContentView:null,galaxyLibraryview:null,initialize:function(){folderContentView=new l();galaxyLibraryview=new a();library_router=new p();library_router.on("route:libraries",function(){galaxyLibraryview.render()});library_router.on("route:folder_content",function(q){folderContentView.render({id:q})});library_router.on("route:download",function(q,r){if($("#center").find(":checked").length===0){library_router.navigate("folders/"+q,{trigger:true,replace:true})}else{folderContentView.download(q,r);library_router.navigate("folders/"+q,{trigger:false,replace:true})}});Backbone.history.start();return this}});return{GalaxyApp:f}});
\ No newline at end of file
+var view=null;var library_router=null;var responses=[];define(["galaxy.modal","galaxy.masthead","utils/galaxy.utils","libs/toastr"],function(k,l,h,n){var f=Backbone.Model.extend({urlRoot:"/api/libraries"});var c=Backbone.Model.extend({urlRoot:"/api/folders"});var o=Backbone.Collection.extend({url:"/api/libraries",model:f});var i=Backbone.Model.extend({urlRoot:"/api/libraries/datasets"});var d=Backbone.Collection.extend({model:i});var e=Backbone.Model.extend({defaults:{folder:new d(),full_path:"unknown",urlRoot:"/api/folders/",id:"unknown"},parse:function(r){this.full_path=r[0].full_path;this.get("folder").reset(r[1].folder_contents);return r}});var b=Backbone.Model.extend({urlRoot:"/api/histories/"});var j=Backbone.Model.extend({url:"/api/histories/"});var p=Backbone.Collection.extend({url:"/api/histories",model:j});var q=Backbone.Router.extend({routes:{"":"libraries","folders/:id":"folder_content","folders/:folder_id/download/:format":"download"}});var m=Backbone.View.extend({el:"#center",progress:0,progressStep:1,lastSelectedHistory:"",modal:null,folders:null,initialize:function(){this.folders=[];this.queue=jQuery.Deferred();this.queue.resolve()},templateFolder:function(){var r=[];r.push('<div id="library_container" style="width: 90%; margin: auto; margin-top: 2em; ">');r.push('<h3>New Data Libraries. This is work in progress. Report problems & ideas to <a href="mailto:marten@bx.psu.edu?Subject=DataLibraries_Feedback" target="_blank">Marten</a>.</h3>');r.push('<div id="library_folder_toolbar" >');r.push(' <button title="Create New Folder" id="toolbtn_create_folder" class="btn btn-primary" type="button"><span class="fa fa-plus"></span><span class="fa fa-folder-close"></span> folder</button>');r.push(' <button id="toolbtn_bulk_import" class="btn btn-primary" style="display: none; margin-left: 0.5em;" type="button"><span class="fa fa-external-link"></span> to history</button>');r.push(' <div id="toolbtn_dl" class="btn-group" style="margin-left: 0.5em; display: none; ">');r.push(' <button id="drop_toggle" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown">');r.push(' <span class="fa fa-download"></span> download <span class="caret"></span>');r.push(" </button>");r.push(' <ul class="dropdown-menu" role="menu">');r.push(' <li><a href="#/folders/<%= id %>/download/tgz">.tar.gz</a></li>');r.push(' <li><a href="#/folders/<%= id %>/download/tbz">.tar.bz</a></li>');r.push(' <li><a href="#/folders/<%= id %>/download/zip">.zip</a></li>');r.push(" </ul>");r.push(" </div>");r.push("</div>");r.push('<div class="library_breadcrumb">');r.push('<a title="Return to the list of libraries" href="#">Libraries</a><b>|</b> ');r.push("<% _.each(path, function(path_item) { %>");r.push("<% if (path_item[0] != id) { %>");r.push('<a title="Return to this folder" href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a><b>|</b> ');r.push("<% } else { %>");r.push('<span title="You are in this folder"><%- path_item[1] %></span>');r.push("<% } %>");r.push("<% }); %>");r.push("</div>");r.push('<table id="folder_table" class="table table-condensed">');r.push(" <thead>");r.push(' <th style="text-align: center; width: 20px; "><input id="select-all-checkboxes" style="margin: 0;" type="checkbox"></th>');r.push(' <th class="button_heading">view</th>');r.push(" <th>name</th>");r.push(" <th>data type</th>");r.push(" <th>size</th>");r.push(" <th>date</th>");r.push(" </thead>");r.push(" <tbody>");r.push(" <td></td>");r.push(' <td><button title="Go to parent folder" type="button" data-id="<%- upper_folder_id %>" class="btn_open_folder btn btn-default btn-xs">');r.push(' <span class="fa fa-arrow-up"></span> .. go up</td>');r.push(" <td></td>");r.push(" <td></td>");r.push(" <td></td>");r.push(" <td></td>");r.push(" </tr>");r.push(" <% _.each(items, function(content_item) { %>");r.push(' <tr class="folder_row light" id="<%- content_item.id %>">');r.push(' <% if (content_item.get("type") === "folder") { %>');r.push(" <td></td>");r.push(' <td><button title="Open this folder" type="button" data-id="<%- content_item.id %>" class="btn_open_folder btn btn-default btn-xs">');r.push(' <span class="fa fa-folder-open"></span> browse</td>');r.push(' <td><%- content_item.get("name") %>');r.push(' <% if (content_item.get("item_count") === 0) { %>');r.push(' <span class="muted">(empty folder)</span>');r.push(" <% } %>");r.push(" </td>");r.push(" <td>folder</td>");r.push(' <td><%= _.escape(content_item.get("item_count")) %> item(s)</td>');r.push(" <% } else { %>");r.push(' <td style="text-align: center; "><input style="margin: 0;" type="checkbox"></td>');r.push(" <td>");r.push(' <button title="See details of this dataset" type="button" class="library-dataset btn btn-default btn-xs">');r.push(' <span class="fa fa-eye"></span> details');r.push(" </button>");r.push(" </td>");r.push(' <td><%- content_item.get("name") %></td>');r.push(' <td><%= _.escape(content_item.get("data_type")) %></td>');r.push(' <td><%= _.escape(content_item.get("readable_size")) %></td>');r.push(" <% } %> ");r.push(' <td><%= _.escape(content_item.get("time_updated")) %></td>');r.push(" </tr>");r.push(" <% }); %>");r.push(" ");r.push(" </tbody>");r.push("</table>");r.push("</div>");return r.join("")},templateDatasetModal:function(){var r=[];r.push('<div id="dataset_info_modal">');r.push(' <table class="table table-striped table-condensed">');r.push(" <tr>");r.push(' <th scope="row" id="id_row" data-id="<%= _.escape(item.get("ldda_id")) %>">Name</th>');r.push(' <td><%= _.escape(item.get("name")) %></td>');r.push(" </tr>");r.push(" <tr>");r.push(' <th scope="row">Data type</th>');r.push(' <td><%= _.escape(item.get("data_type")) %></td>');r.push(" </tr>");r.push(" <tr>");r.push(' <th scope="row">Genome build</th>');r.push(' <td><%= _.escape(item.get("genome_build")) %></td>');r.push(" </tr>");r.push(' <th scope="row">Size</th>');r.push(" <td><%= _.escape(size) %></td>");r.push(" </tr>");r.push(" <tr>");r.push(' <th scope="row">Date uploaded</th>');r.push(' <td><%= _.escape(item.get("date_uploaded")) %></td>');r.push(" </tr>");r.push(" <tr>");r.push(' <th scope="row">Uploaded by</th>');r.push(' <td><%= _.escape(item.get("uploaded_by")) %></td>');r.push(" </tr>");r.push(' <tr scope="row">');r.push(' <th scope="row">Data Lines</th>');r.push(' <td scope="row"><%= _.escape(item.get("metadata_data_lines")) %></td>');r.push(" </tr>");r.push(' <th scope="row">Comment Lines</th>');r.push(' <% if (item.get("metadata_comment_lines") === "") { %>');r.push(' <td scope="row"><%= _.escape(item.get("metadata_comment_lines")) %></td>');r.push(" <% } else { %>");r.push(' <td scope="row">unknown</td>');r.push(" <% } %>");r.push(" </tr>");r.push(" <tr>");r.push(' <th scope="row">Number of Columns</th>');r.push(' <td scope="row"><%= _.escape(item.get("metadata_columns")) %></td>');r.push(" </tr>");r.push(" <tr>");r.push(' <th scope="row">Column Types</th>');r.push(' <td scope="row"><%= _.escape(item.get("metadata_column_types")) %></td>');r.push(" </tr>");r.push(" <tr>");r.push(' <th scope="row">Miscellaneous information</th>');r.push(' <td scope="row"><%= _.escape(item.get("misc_blurb")) %></td>');r.push(" </tr>");r.push(" </table>");r.push(' <pre class="peek">');r.push(" </pre>");r.push("</div>");return r.join("")},templateHistorySelectInModal:function(){var r=[];r.push('<span id="history_modal_combo" style="width:90%; margin-left: 1em; margin-right: 1em; ">');r.push("Select history: ");r.push('<select id="dataset_import_single" name="dataset_import_single" style="width:50%; margin-bottom: 1em; "> ');r.push(" <% _.each(histories, function(history) { %>");r.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');r.push(" <% }); %>");r.push("</select>");r.push("</span>");return r.join("")},templateBulkImportInModal:function(){var r=[];r.push('<span id="history_modal_combo_bulk" style="width:90%; margin-left: 1em; margin-right: 1em; ">');r.push("Select history: ");r.push('<select id="dataset_import_bulk" name="dataset_import_bulk" style="width:50%; margin-bottom: 1em; "> ');r.push(" <% _.each(histories, function(history) { %>");r.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');r.push(" <% }); %>");r.push("</select>");r.push("</span>");return r.join("")},size_to_string:function(r){var s="";if(r>=100000000000){r=r/100000000000;s="TB"}else{if(r>=100000000){r=r/100000000;s="GB"}else{if(r>=100000){r=r/100000;s="MB"}else{if(r>=100){r=r/100;s="KB"}else{r=r*10;s="b"}}}}return(Math.round(r)/10)+s},events:{"click #select-all-checkboxes":"selectAll","click .folder_row":"selectClickedRow","click #toolbtn_bulk_import":"modalBulkImport","click #toolbtn_dl":"bulkDownload","click .library-dataset":"showDatasetDetails","click #toolbtn_create_folder":"createFolderFromModal","click .btn_open_folder":"navigateToFolder"},render:function(r){$("#center").css("overflow","auto");view=this;var t=this;var s=new e({id:r.id});s.url=s.attributes.urlRoot+r.id+"/contents";s.fetch({success:function(u){for(var w=0;w<s.attributes.folder.models.length;w++){var v=s.attributes.folder.models[w];if(v.get("type")==="file"){v.set("readable_size",t.size_to_string(v.get("file_size")))}}var y=s.full_path;var z;if(y.length===1){z=0}else{z=y[y.length-2][0]}var x=_.template(t.templateFolder(),{path:s.full_path,items:s.attributes.folder.models,id:r.id,upper_folder_id:z});t.$el.html(x)}})},navigateToFolder:function(s){var r=$(s.target).attr("data-id");if(typeof r==="undefined"){return false}else{if(r==="0"){library_router.navigate("#",{trigger:true,replace:true})}else{library_router.navigate("folders/"+r,{trigger:true,replace:true})}}},showDatasetDetails:function(u){u.preventDefault();var v=$(u.target).parent().parent().attr("id");var t=new i();var s=new p();t.id=v;var r=this;t.fetch({success:function(w){s.fetch({success:function(x){r.renderModalAfterFetch(w,x)}})}})},renderModalAfterFetch:function(w,t){var u=this.size_to_string(w.get("file_size"));var v=_.template(this.templateDatasetModal(),{item:w,size:u});this.modal=null;var s=this;this.modal=new k.GalaxyModal({destructible:true,title:"Dataset Details",body:v,buttons:{Import:function(){s.importCurrentIntoHistory()},Download:function(){s.downloadCurrent()},Close:function(){s.modal.hideOrDestroy;s.modal=null}}});$(".peek").html(w.get("peek"));var r=_.template(this.templateHistorySelectInModal(),{histories:t.models});$(this.modal.elMain).find(".buttons").prepend(r);if(s.lastSelectedHistory.length>0){$(this.modal.elMain).find("#dataset_import_single").val(s.lastSelectedHistory)}this.modal.bindEvents();this.modal.show()},downloadCurrent:function(){this.modal.disableButton("Import");this.modal.disableButton("Download");var r=[];r.push($("#id_row").attr("data-id"));var s="/api/libraries/datasets/download/uncompressed";var t={ldda_ids:r};folderContentView.processDownload(s,t);this.modal.enableButton("Import");this.modal.enableButton("Download")},importCurrentIntoHistory:function(){this.modal.disableButton("Import");this.modal.disableButton("Download");var t=$(this.modal.elMain).find("select[name=dataset_import_single] option:selected").val();this.lastSelectedHistory=t;var r=$("#id_row").attr("data-id");var u=new b();var s=this;u.url=u.urlRoot+t+"/contents";u.save({content:r,source:"library"},{success:function(){n.success("Dataset imported");s.modal.enableButton("Import");s.modal.enableButton("Download")},error:function(){n.error("An error occured! Dataset not imported. Please try again.");s.modal.enableButton("Import");s.modal.enableButton("Download")}})},selectAll:function(s){var r=s.target.checked;that=this;$(":checkbox").each(function(){this.checked=r;$row=$(this.parentElement.parentElement);(r)?that.makeDarkRow($row):that.makeWhiteRow($row)});this.checkTools()},selectClickedRow:function(s){var u="";var r;var t;if(s.target.localName==="input"){u=s.target;r=$(s.target.parentElement.parentElement);t="input"}else{if(s.target.localName==="td"){u=$("#"+s.target.parentElement.id).find(":checkbox")[0];r=$(s.target.parentElement);t="td"}}if(u===""){s.stopPropagation();return}if(u===undefined){s.stopPropagation();return}if(u.checked){if(t==="td"){u.checked="";this.makeWhiteRow(r)}else{if(t==="input"){this.makeDarkRow(r)}}}else{if(t==="td"){u.checked="selected";this.makeDarkRow(r)}else{if(t==="input"){this.makeWhiteRow(r)}}}this.checkTools()},makeDarkRow:function(r){r.removeClass("light");r.find("a").removeClass("light");r.addClass("dark");r.find("a").addClass("dark")},makeWhiteRow:function(r){r.removeClass("dark");r.find("a").removeClass("dark");r.addClass("light");r.find("a").addClass("light")},checkTools:function(){var r=$("#folder_table").find(":checked");if(r.length>0){$("#toolbtn_bulk_import").show();$("#toolbtn_dl").show()}else{$("#toolbtn_bulk_import").hide();$("#toolbtn_dl").hide()}},modalBulkImport:function(){var s=this;var r=new p();r.fetch({success:function(t){var u=_.template(s.templateBulkImportInModal(),{histories:t.models});s.modal=new k.GalaxyModal({destructible:true,title:"Import into History",body:u,buttons:{Import:function(){s.importAllIntoHistory()},Close:function(){s.modal.hideOrDestroy;s.modal=null}}});s.modal.bindEvents();s.modal.show()}})},importAllIntoHistory:function(){this.modal.disableButton("Import");var t=$("select[name=dataset_import_bulk] option:selected").val();var x=$("select[name=dataset_import_bulk] option:selected").text();var z=[];$("#folder_table").find(":checked").each(function(){if(this.parentElement.parentElement.id!=""){z.push(this.parentElement.parentElement.id)}});var y=_.template(this.templateProgressBar(),{history_name:x});$(this.modal.elMain).find(".modal-body").html(y);var u=100/z.length;this.initProgress(u);var r=[];for(var s=z.length-1;s>=0;s--){library_dataset_id=z[s];var v=new b();var w=this;v.url=v.urlRoot+t+"/contents";v.content=library_dataset_id;v.source="library";r.push(v)}this.chainCall(r)},chainCall:function(s){var r=this;var t=s.pop();if(typeof t==="undefined"){n.success("All datasets imported");this.modal.destroy();return}var u=$.when(t.save({content:t.content,source:t.source})).done(function(v){r.updateProgress();responses.push(v);r.chainCall(s)})},initProgress:function(r){this.progress=0;this.progressStep=r},updateProgress:function(){this.progress+=this.progressStep;$(".progress-bar-import").width(Math.round(this.progress)+"%");txt_representation=Math.round(this.progress)+"% Complete";$(".completion_span").text(txt_representation)},templateProgressBar:function(){var r=[];r.push('<div class="import_text">');r.push("Importing selected datasets to history <b><%= _.escape(history_name) %></b>");r.push("</div>");r.push('<div class="progress">');r.push(' <div class="progress-bar progress-bar-import" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 00%;">');r.push(' <span class="completion_span">0% Complete</span>');r.push(" </div>");r.push("</div>");r.push("");return r.join("")},download:function(r,v){var t=[];$("#folder_table").find(":checked").each(function(){if(this.parentElement.parentElement.id!=""){t.push(this.parentElement.parentElement.id)}});var s="/api/libraries/datasets/download/"+v;var u={ldda_ids:t};this.processDownload(s,u,"get")},processDownload:function(s,t,u){if(s&&t){t=typeof t=="string"?t:$.param(t);var r="";$.each(t.split("&"),function(){var v=this.split("=");r+='<input type="hidden" name="'+v[0]+'" value="'+v[1]+'" />'});$('<form action="'+s+'" method="'+(u||"post")+'">'+r+"</form>").appendTo("body").submit().remove();n.info("Your download will begin soon")}},createFolderFromModal:function(){event.preventDefault();event.stopPropagation();var r=this;this.modal=new k.GalaxyModal({destructible:true,title:"Create New Folder",body:this.template_new_folder(),buttons:{Create:function(){r.create_new_folder_event()},Close:function(){r.modal.destroy();r.modal=null}}});this.modal.bindEvents();this.modal.show()},create_new_folder_event:function(){var r=this.serialize_new_folder();if(this.validate_new_folder(r)){var t=new c();t.url=t.urlRoot+"/F2fdbd5c5858e78fb";var s=this;t.save(r,{success:function(u){s.modal.destroy();n.success("Folder created");s.render({id:"F2fdbd5c5858e78fb"})},error:function(){n.error("An error occured :(")}})}else{n.error("Folder's name is missing")}return false},serialize_new_folder:function(){return{name:$("input[name='Name']").val(),description:$("input[name='Description']").val()}},validate_new_folder:function(r){return r.name!==""},template_new_folder:function(){tmpl_array=[];tmpl_array.push('<div id="new_folder_modal">');tmpl_array.push("<form>");tmpl_array.push('<input type="text" name="Name" value="" placeholder="Name">');tmpl_array.push('<input type="text" name="Description" value="" placeholder="Description">');tmpl_array.push("</form>");tmpl_array.push("</div>");return tmpl_array.join("")}});var a=Backbone.View.extend({el:"#center",events:{"click #create_new_library_btn":"show_library_modal"},initialize:function(){},template_library_list:function(){tmpl_array=[];tmpl_array.push('<div id="library_container" style="width: 90%; margin: auto; margin-top: 2em; overflow: auto !important; ">');tmpl_array.push("");tmpl_array.push('<h3>New Data Libraries. This is work in progress. Report problems & ideas to <a href="mailto:marten@bx.psu.edu?Subject=DataLibraries_Feedback" target="_blank">Marten</a>.</h3>');tmpl_array.push('<a href="" id="create_new_library_btn" class="btn btn-primary file ">New Library</a>');tmpl_array.push('<table class="table table-condensed">');tmpl_array.push(" <thead>");tmpl_array.push(' <th class="button_heading"></th>');tmpl_array.push(" <th>name</th>");tmpl_array.push(" <th>description</th>");tmpl_array.push(" <th>synopsis</th> ");tmpl_array.push(" <th>model type</th> ");tmpl_array.push(" </thead>");tmpl_array.push(" <tbody>");tmpl_array.push(" <% _.each(libraries, function(library) { %>");tmpl_array.push(" <tr>");tmpl_array.push(' <td><button title="Open this library" type="button" data-id="<%- library.get("root_folder_id") %>" class="btn_open_folder btn btn-default btn-xs">');tmpl_array.push(' <span class="fa fa-folder-open"></span> browse</td>');tmpl_array.push(' <td><%- library.get("name") %></td>');tmpl_array.push(' <td><%= _.escape(library.get("description")) %></td>');tmpl_array.push(' <td><%= _.escape(library.get("synopsis")) %></td>');tmpl_array.push(' <td><%= _.escape(library.get("model_class")) %></td>');tmpl_array.push(" </tr>");tmpl_array.push(" <% }); %>");tmpl_array.push(" </tbody>");tmpl_array.push("</table>");tmpl_array.push("</div>");return tmpl_array.join("")},render:function(){$("#center").css("overflow","auto");var r=this;libraries=new o();libraries.fetch({success:function(s){var t=_.template(r.template_library_list(),{libraries:s.models});r.$el.html(t)},error:function(t,s){if(s.statusCode().status===403){n.error("Please log in first. Redirecting to login page in 3s.");setTimeout(r.redirectToLogin,3000)}else{n.error("An error occured. Please try again.")}}})},redirectToHome:function(){window.location="../"},redirectToLogin:function(){window.location="/user/login"},modal:null,show_library_modal:function(s){s.preventDefault();s.stopPropagation();var r=this;this.modal=new k.GalaxyModal({destructible:true,title:"Create New Library",body:this.template_new_library(),buttons:{Create:function(){r.create_new_library_event()},Close:function(){r.modal.hideOrDestroy();r.modal=null}}});this.modal.bindEvents();this.modal.show()},create_new_library_event:function(){var t=this.serialize_new_library();if(this.validate_new_library(t)){var s=new f();var r=this;s.save(t,{success:function(u){r.modal.destroy();r.clear_library_modal();r.render();n.success("Library created")},error:function(){n.error("An error occured :(")}})}else{n.error("Library's name is missing")}return false},clear_library_modal:function(){$("input[name='Name']").val("");$("input[name='Description']").val("");$("input[name='Synopsis']").val("")},serialize_new_library:function(){return{name:$("input[name='Name']").val(),description:$("input[name='Description']").val(),synopsis:$("input[name='Synopsis']").val()}},validate_new_library:function(r){return r.name!==""},template_new_library:function(){tmpl_array=[];tmpl_array.push('<div id="new_library_modal">');tmpl_array.push("<form>");tmpl_array.push('<input type="text" name="Name" value="" placeholder="Name">');tmpl_array.push('<input type="text" name="Description" value="" placeholder="Description">');tmpl_array.push('<input type="text" name="Synopsis" value="" placeholder="Synopsis">');tmpl_array.push("</form>");tmpl_array.push("</div>");return tmpl_array.join("")}});var g=Backbone.View.extend({folderContentView:null,galaxyLibraryview:null,initialize:function(){folderContentView=new m();galaxyLibraryview=new a();library_router=new q();library_router.on("route:libraries",function(){galaxyLibraryview.render()});library_router.on("route:folder_content",function(r){folderContentView.render({id:r})});library_router.on("route:download",function(r,s){if($("#center").find(":checked").length===0){library_router.navigate("folders/"+r,{trigger:true,replace:true})}else{folderContentView.download(r,s);library_router.navigate("folders/"+r,{trigger:false,replace:true})}});Backbone.history.start();return this}});return{GalaxyApp:g}});
\ No newline at end of file
diff -r ef98f48690fc9ce5d1b8f662a2e91f384514013d -r f2e3d2e41c1d2e90d089c1ac9ea4d626b620a94d static/scripts/packed/galaxy.modal.js
--- a/static/scripts/packed/galaxy.modal.js
+++ b/static/scripts/packed/galaxy.modal.js
@@ -1,1 +1,1 @@
-define([],function(){var a=Backbone.View.extend({elMain:"#everything",optionsDefault:{title:"galaxy-modal",body:"",backdrop:true,height:null,width:null},options:{},initialize:function(b){self=this;if(b){this.create(b)}},bindEvents:function(b){$(document).on("keyup",function(c){if(c.keyCode==27){self.hide();$(".modal").remove()}});$("html").on("click",function(c){self.hide();$(".modal").remove()});$(".modal-content").on("click",function(c){c.stopPropagation()})},unbindEvents:function(b){$(document).off("keyup",function(c){if(c.keyCode==27){self.hide();$(".modal").remove()}});$("html").off("click",function(c){self.hide();$(".modal").remove()});$(".modal-content").off("click",function(c){c.stopPropagation()})},destroy:function(){this.hide();this.unbindEvents();$(".modal").remove()},show:function(b){this.initialize(b);if(this.options.height){this.$body.css("height",this.options.height);this.$body.css("overflow","hidden")}else{this.$body.css("max-height",$(window).height()/2)}if(this.options.width){this.$dialog.css("width",this.options.width)}if(this.visible){this.$el.show()}else{this.$el.fadeIn("fast")}this.visible=true},hide:function(){this.$el.fadeOut("fast");this.visible=false;this.unbindEvents()},create:function(c){this.options=_.defaults(c,this.optionsDefault);if(this.options.body=="progress"){this.options.body=$('<div class="progress progress-striped active"><div class="progress-bar progress-bar-info" style="width:100%"></div></div>')}if(this.$el){this.$el.remove()}this.setElement(this.template(this.options.title));this.$dialog=(this.$el).find(".modal-dialog");this.$body=(this.$el).find(".modal-body");this.$footer=(this.$el).find(".modal-footer");this.$buttons=(this.$el).find(".buttons");this.$backdrop=(this.$el).find(".modal-backdrop");this.$body.html(this.options.body);if(!this.options.backdrop){this.$backdrop.removeClass("in")}if(this.options.buttons){var b=this;$.each(this.options.buttons,function(d,e){b.$buttons.append($('<button id="'+String(d).toLowerCase()+'"></button>').text(d).click(e)).append(" ")})}else{this.$footer.hide()}$(this.elMain).append($(this.el))},enableButton:function(b){this.$buttons.find("#"+String(b).toLowerCase()).prop("disabled",false)},disableButton:function(b){this.$buttons.find("#"+String(b).toLowerCase()).prop("disabled",true)},hideButton:function(b){this.$buttons.find("#"+String(b).toLowerCase()).hide()},showButton:function(b){this.$buttons.find("#"+String(b).toLowerCase()).show()},scrollTop:function(){return this.$body.scrollTop()},template:function(b){return'<div class="modal"><div class="modal-backdrop fade in" style="z-index: -1;"></div><div class="modal-dialog"><div class="modal-content"><div class="modal-header"><button type="button" class="close" style="display: none;">×</button><h4 class="title">'+b+'</h4></div><div class="modal-body"></div><div class="modal-footer"><div class="buttons" style="float: right;"></div></div></div</div></div>'}});return{GalaxyModal:a}});
\ No newline at end of file
+define([],function(){var a=Backbone.View.extend({elMain:"#everything",optionsDefault:{title:"galaxy-modal",body:"",backdrop:true,height:null,width:null},options:{destructible:false},initialize:function(b){self=this;if(b){this.create(b)}},hideOrDestroy:function(){if(this.options.destructible){self.destroy()}else{self.hide()}},bindEvents:function(){$(document).on("keyup",function(b){if(b.keyCode==27){self.hideOrDestroy()}});$("html").on("click",function(b){self.hideOrDestroy()});$(".modal-content").on("click",function(b){b.stopPropagation()})},unbindEvents:function(){$(document).off("keyup",function(b){if(b.keyCode==27){self.hideOrDestroy()}});$("html").off("click",function(b){self.hideOrDestroy()});$(".modal-content").off("click",function(b){b.stopPropagation()})},show:function(b){this.initialize(b);if(this.options.height){this.$body.css("height",this.options.height);this.$body.css("overflow","hidden")}else{this.$body.css("max-height",$(window).height()/2)}if(this.options.width){this.$dialog.css("width",this.options.width)}if(this.visible){this.$el.show()}else{this.$el.fadeIn("fast")}this.visible=true},hide:function(){this.$el.fadeOut("fast");this.visible=false;this.unbindEvents()},destroy:function(){this.visible=false;this.unbindEvents();this.$el.remove()},create:function(c){this.options=_.defaults(c,this.optionsDefault);if(this.options.body=="progress"){this.options.body=$('<div class="progress progress-striped active"><div class="progress-bar progress-bar-info" style="width:100%"></div></div>')}if(this.$el){this.$el.remove()}this.setElement(this.template(this.options.title));this.$dialog=(this.$el).find(".modal-dialog");this.$body=(this.$el).find(".modal-body");this.$footer=(this.$el).find(".modal-footer");this.$buttons=(this.$el).find(".buttons");this.$backdrop=(this.$el).find(".modal-backdrop");this.$body.html(this.options.body);if(!this.options.backdrop){this.$backdrop.removeClass("in")}if(this.options.buttons){var b=this;$.each(this.options.buttons,function(d,e){b.$buttons.append($('<button id="'+String(d).toLowerCase()+'"></button>').text(d).click(e)).append(" ")})}else{this.$footer.hide()}$(this.elMain).append($(this.el))},enableButton:function(b){this.$buttons.find("#"+String(b).toLowerCase()).prop("disabled",false)},disableButton:function(b){this.$buttons.find("#"+String(b).toLowerCase()).prop("disabled",true)},hideButton:function(b){this.$buttons.find("#"+String(b).toLowerCase()).hide()},showButton:function(b){this.$buttons.find("#"+String(b).toLowerCase()).show()},scrollTop:function(){return this.$body.scrollTop()},template:function(b){return'<div class="modal"><div class="modal-backdrop fade in" style="z-index: -1;"></div><div class="modal-dialog"><div class="modal-content"><div class="modal-header"><button type="button" class="close" style="display: none;">×</button><h4 class="title">'+b+'</h4></div><div class="modal-body"></div><div class="modal-footer"><div class="buttons" style="float: right;"></div></div></div</div></div>'}});return{GalaxyModal:a}});
\ No newline at end of file
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0